Текст и редактирование
Редактирование — возможность движка, а не сборка в приложении
Типичная слабость решений на canvas — накрыть canvas HTML-элементом input, когда нужен ввод. Из этого следует целая цепочка проблем: смещённая каретка, уехавшее окно кандидатов IME, рассинхронизированная прокрутка, сломанная доступность.
doper делает редактирование первоклассной возможностью ядра: каретка, выделение, выделение перетаскиванием, выбор слова двойным щелчком, навигация с клавиатуры, композиция IME, положение окна кандидатов, буфер обмена, отмена/повтор, режим только для чтения и пароль реализованы движком. Приложение не создаёт, не позиционирует и не синхронизирует ни одного HTML-элемента ввода.
Через виджеты
import { TextField, TextArea } from "@dopejs/pingo";
TextField({
value: order.note,
revision: order.revision,
semanticLabel: "Примечание к заказу",
inputMode: "text",
onTransaction: (transaction) => order.apply(transaction),
});
TextArea({ value: description, revision, rows: 4 });Через примитив
createElement("editableText", {
value,
revision,
multiline: false,
readOnly: false,
password: false,
maxGraphemes: 200,
inputMode: "email",
onTransaction: (transaction) => apply(transaction),
onSubmit: () => moveToNextCell(),
});Либо с локальным контроллером:
import { useTextEditingController } from "@dopejs/pingo";
const editor = useTextEditingController({ value: cell.value });
createElement("editableText", { controller: editor });Мост ввода и откат
Главный поток подключается к службе текстового ввода операционной системы по приоритету:
- EditContext — привязывается к canvas, получает текст, выделение и композицию и передаёт методу ввода control, selection и character bounds.
- Прокси ввода под управлением движка — если EditContext недоступен, хост держит ровно один глобальный скрытый
textarea, который единообразно обрабатываетbeforeinput, композицию, экранную клавиатуру и буфер обмена.
Второй пункт — это платформенная реализация отката, а не модель компонентов EmbedDOM: в Scene нет DOM, который соответствовал бы каждому редактируемому узлу один к одному. Оба пути проходят один и тот же набор контрактных тестов поведения.
Версионированные транзакции редактирования
Владение состоянием задано явно: оболочка владеет прикладными данными, ядро — мгновенным состоянием активной сессии редактирования.
ввод → ядро проверяет base_revision → сразу применяет и перерисовывает → отправляет назад версионированную EditTransaction
↓
приложение подтверждает или присылает исправленное значение с новой ревизиейУстаревшая транзакция никогда не перезапишет более новое состояние. Это значит, что каждое нажатие клавиши не требует полного прогона сборки TSX, и при этом контролируемые данные и прикладная валидация продолжают работать.
onTransaction: (transaction) => {
// transaction.baseRevision / revision / delta / selection / kind
value = applyDelta(value, transaction);
};Модель позиций в тексте
Веб-API ввода оперируют смещениями UTF-16, строки в Rust — это UTF-8, а границы графем, кластеров шейпинга и видимых глифов различаются между собой. Движок ведёт явное соответствие:
смещение UTF-16 ↔ скаляр Unicode ↔ графема ↔ кластер шейпинга ↔ глиф / строкаНа границе протокола всюду используется UTF-16, чтобы совпадать с EditContext и InputEvent. Удаление, перемещение и выделение никогда не разрезают графему, комбинирующую последовательность, эмодзи с ZWJ или кластер шейпинга — это защищено property-тестами и матрицей фикстур композиции (комбинирующие символы, эмодзи ZWJ, RTL, многосегментные кандидаты CJK).
Пароли и приватность
Текст пароля не попадает ни в запись и воспроизведение, ни в логи, ни в открытый текст devtools, ни в значения доступности; цель-пароль также не пишет в буфер обмена. Ядро выдаёт только маскированные глифы, поэтому открытый текст вообще не доходит до DisplayList. Это подтверждено автоматическими тестами, и вы можете сами проверить DOM в опубликованном Playground.
Известные границы
- Визуальная навигация в bidi появится вместе с поддержкой bidi-текста; сейчас это явно отложенный пункт.
- Схема форматированного текста, разрешение конфликтов при совместной работе, формулы и команды Markdown относятся к верхним слоям, но могут строиться на тех же транзакциях редактирования и том же API выделения.