Структура документа: как её получить
Архитектура и границы ответственности, путь одного пользовательского действия, семантическая модель и текущий Rust Session API для чтения.
Архитектура и границы ответственности
Заголовок раздела «Архитектура и границы ответственности»Базовый поток данных неизменен для desktop, системного WebView и headless-интеграции:
| Слой | Что делает | Чего не делает |
|---|---|---|
| Partner Host | Окно, бренд, вход, хранилище, бизнес-процессы, lifecycle | Не владеет моделью документа и layout |
| Web Surface | Canvas, ribbon/UI, ввод, показ меню, доставка событий | Не правит DOCX напрямую |
| Host Adapter | Запуск процесса, bootstrap URL, JSONL/WS, WebView events | Не интерпретирует OOXML |
| Document Session | Команды, selection, history, model/layout projections, save | Не отдаёт произвольный DOM-контроль |
| Rust Core | OOXML package, семантическая истина, layout, writeback | Не зависит от конкретного бренда оболочки |
| DOCX | Сохраняемая и повторно открываемая истина | Не подменяется HTML-снимком |
Путь одного пользовательского действия
Заголовок раздела «Путь одного пользовательского действия»-
Pointer event. Web Surface получает координаты click/right-click/double-click.
-
Canonical hit-test. D6 связывает координату с page/line/caret и далее с семантическим target.
-
Interaction context. Формируются target, selection, ancestors, revision и разрешённые команды.
-
UI decision. A4 и внешний provider составляют контекстное меню без изменения документа.
-
Typed command. Выбранный пункт превращается в версионированную команду Rust-сессии.
-
Truth update. Rust проверяет revision/capability, меняет модель и возвращает receipt.
-
Repaint + persistence. Проекции обновляются; save пишет DOCX, который повторно открывается той же моделью.
| Главное правило расширения внешнее приложение может добавлять поведение вокруг процессора, но не создавать вторую истину документа в DOM, JavaScript-состоянии или вручную изменённом OOXML. |
|---|
Как получить структуру документа
Заголовок раздела «Как получить структуру документа»Термин «структура документа» неоднозначен. A4 разделяет четыре представления, чтобы интегратор запрашивал только то, что ему действительно нужно.
| Представление | Команда | Назначение | Пример данных |
|---|---|---|---|
| Semantic model | model / model-docx | Абзацы, runs, таблицы, ссылки на стили/нумерацию, support markers | body.blocks[], paragraph_index, text, runs[] |
| Layout | compose / paginate | Секции, страницы, линии, переносы, геометрия | page_index, line boxes, page stack |
| Scene | print / print-window / print-delta | Материализованные страницы и paint primitives | page scenes, fingerprints |
| Review & styles | review / style | Комментарии, изменения, style resolution | review/style projections |
| Editing state | status / set-selection | Revision, dirty, undo/redo, selection/view state | accepted_revision, selection_revision |
Пример: семантическая модель
Заголовок раздела «Пример: семантическая модель»STANDALONE ЧТЕНИЕ БЕЗ ДОЛГОЖИВУЩЕЙ СЕССИИ
target/debug/A4-cli model-docx /data/contract.docx --format jsonФАКТИЧЕСКАЯ ФОРМА ОТВЕТА MODEL-DOCX
{ "model_version": "A4-d1-document-model-v1", "block_count": 1, "paragraph_count": 1, "body": {"blocks": [{ "kind": "paragraph", "block_index": 0, "text": "A4 basic paragraph.", "style_ref": null, "numbering_ref": null, "runs": [{ "run_index": 0, "text": "A4 basic paragraph.", "direct_format": {"bold": null, "italic": null} }] }]}}| Что считать стабильной ссылкой Индекс абзаца удобен для bounded-команд, но для -расширений нужен стабильный nodeId + revision. Поэтому публичный target DTO следует заморозить отдельно и не выдавать внутренние CSS-селекторы или DOM-узлы. |
|---|
Текущий Rust Session API: запуск и чтение
Заголовок раздела «Текущий Rust Session API: запуск и чтение»3.1. Сборка и JSONL stdio
Заголовок раздела «3.1. Сборка и JSONL stdio»ЗАПУСК ЛОКАЛЬНОГО ПРОЦЕССОРА
Процесс печатает первую строку ready со схемой A4.session-serve.v1. После этого каждая строка stdin — JSON-запрос, каждая строка stdout — коррелированный JSON-ответ.
ЗАПРОСЫ В ОДНУ ДОЛГОЖИВУЩУЮ СЕССИЮ
{"id":"open-1","cmd":"open", "path":"/data/contract.docx","document_id":"contract-42"}{"id":"model-1","cmd":"model"}{"id":"pages-1","cmd":"paginate"}{"id":"review-1","cmd":"review"}{"id":"state-1","cmd":"status"}ОБЩИЙ ENVELOPE ОТВЕТА
{ "id": "model-1", "ok": true, "cmd": "model", "elapsed_ms": 2, "result": { "model_version": "...", "body": { "blocks": [] } }}3.2. Loopback WebSocket
Заголовок раздела «3.2. Loopback WebSocket»ЯЗЫКОНЕЗАВИСИМЫЙ ЛОКАЛЬНЫЙ ТРАНСПОРТ
target/debug/A4-cli serve --ws 0# stdout: {"schema":"A4.session-ws-bootstrap.v1",# "host":"127.0.0.1","port":49172,"token":"..."}-
Привязка — только 127.0.0.1. Порт 0 выбирается автоматически.
-
Token — 256-bit secret. Его нельзя писать в логи и аналитику.
-
Команды одинаковы. JSONL и WS используют один SessionHost и одну схему запросов.
| Рекомендация для первого пилота Начать с отдельного Rust-процесса и JSONL/loopback WS: это уже проверяемая языконезависимая граница. In-process Rust crate/C ABI можно заморозить позже, не смешивая внутренние Rust-типы с публичным SDK. |
|---|