This is the abridged developer documentation for FastOffice
# FastOffice
> Редакторы документов и таблиц внутри вашего продукта, под вашим именем и в вашем
контуре. Файлы Word и Excel остаются у вас: ни чужого облака, ни чужого интерфейса.
Microsoft Office ушёл из России, а договоры, сметы и акты никуда не делись: они по-прежнему живут в DOCX и XLSX. Замену продают целыми пакетами на рабочее место — но встроить её в свой продукт нельзя. **FastOffice продаёт именно это: документный и табличный процессоры внутрь чужого продукта**, под его именем и в его контуре. A4 работает с документами Word, F1 — с таблицами Excel. ## Чем это доказано [Заголовок раздела «Чем это доказано»](#чем-это-доказано) Ниже — не планы. Каждое число снято с установленных сборок, а не выписано из дорожной карты: при каждом изменении документации сборка сверяет их с кодом продуктов и не проходит, если они разошлись. **28**команд документного сеанса — все принимает ядро выпуска **203**операции табличного контракта; 185 из них принимает ядро последнего выпуска **13**табличных форматов, включая XLSX, XLSB, XLSM, ODS и CSV **1 000 000**строк открываются в браузере — прокрутите и увидите цену кадра Смыслом документа владеет собственное ядро на Rust: оно разбирает файл, считает вёрстку и формулы, ведёт историю и записывает результат. Это не обёртка вокруг чужого редактора — [что это значит рядом с Excel](/f1/atlas/). ## Попробовать прямо здесь [Заголовок раздела «Попробовать прямо здесь»](#попробовать-прямо-здесь) Оба редактора открываются прямо на сайте, в браузере, без установки: это не запись экрана, а работающая программа. Документ и книга живут в вашей собственной кабинке — правки видите только вы. Если площадка в этот момент не поднята, на месте рамки будет снимок и подпись об этом. Документ в A4 Договор на две страницы: заголовки, списки, таблица со сроками и суммой. Поправьте абзац — и увидите, как это выглядит внутри чужого приложения. [Открыть живой редактор →](/a4/demo/) Смета в F1 Таблица с формулами. Измените количество или цену — суммы, доли и оба итога пересчитает ядро, а не разметка страницы. [Открыть живую таблицу →](/f1/demo/) ## Два движка, одна модель встраивания [Заголовок раздела «Два движка, одна модель встраивания»](#два-движка-одна-модель-встраивания) A4 — документы Word Редактор и процессор документов: открывает файл Word, размечает страницы, даёт править, сохраняет и открывает заново — всё внутри вашего окна. Снаружи это 28 команд одного протокола, и другого пути к документу нет. [Обзор A4](/a4/overview/) · [Быстрый старт](/a4/quickstart/) · [Справочник](/reference/a4/overview/) F1 — таблицы Excel Табличный процессор: формулы, история правок, чтение и сохранение книг. В память берётся только то, что нужно сейчас, поэтому большие книги открываются. Снаружи это один контракт операций — тот же самый и на рабочем месте, и в браузере, и на сервере: 203 операции описаны, 185 принимает ядро последнего выпуска. [Обзор F1](/f1/overview/) · [Быстрый старт](/f1/quickstart/) · [Справочник](/reference/f1/overview/) ## Что это меняет для вашего продукта [Заголовок раздела «Что это меняет для вашего продукта»](#что-это-меняет-для-вашего-продукта) Ваш продукт, а не чужое окно Пользователь открывает документ из привычной карточки. Редактор появляется в том же окне и под тем же брендом; новая версия возвращается в существующий контур хранения и аудита. Один источник правды о документе Модель, формулы, история и запись файла живут в ядре. Второго движка на стороне страницы нет — значит документ значит одно и то же на рабочем месте, в браузере и на сервере. Отказ вместо тихой порчи Устаревшая ревизия, защищённый лист или неподдержанная операция дают явный отказ с причиной — **до** того, как файл изменится. Документа, изменённого наполовину, не возникает. Агент работает как одна сделка Он ходит тем же путём, что и человек: посмотреть, составить план, показать предпросмотр, дождаться согласия, применить, сохранить и открыть заново, записать в аудит. Не щелчки по чужому интерфейсу и не правка файла в обход редактора. ## С чего начать: выберите, куда встраиваете [Заголовок раздела «С чего начать: выберите, куда встраиваете»](#с-чего-начать-выберите-куда-встраиваете) Три пути, и они разные с первого шага. Откройте свой — там всё по шагам, от «что взять из поставки» до «какой экран увидит пользователь». А если разбираться не хочется — [отдайте задание Клоду](/start/kit/), и он подключит редактор за вас. В веб-приложение Ваш продукт живёт в браузере. Редактор открывается прямо на вашей странице, файл остаётся в вашем контуре. [Четыре шага →](/start/web/) В настольное приложение Учётная система, отраслевая платформа, надстройка над 1С. Ядро работает рядом отдельным процессом и говорит с вами строками JSON. [Три шага →](/start/desktop/) В мобильное приложение Телефон и планшет: что работает сегодня, каким путём это делается и чего пока нет — без обещаний, которые нечем подтвердить. [Рабочий путь →](/start/mobile/) Сборки есть для macOS, Windows и Linux. Под Windows — обе архитектуры, x64 и arm64; под Linux пока только x86-64. Для macOS у F1 есть сборки и под Apple Silicon, и под Intel, у A4 сейчас выложена только под Apple Silicon. Продукт в активной работе, поэтому сборки мы отдаём по заявке и вместе с ними предупреждаем об ограничениях текущей версии. Что делать после получения — [установка A4](/a4/install/) и [установка F1](/f1/install/). ## Примеры кода [Заголовок раздела «Примеры кода»](#примеры-кода) Двадцать четыре проекта показаны целиком, файл за файлом: видно, сколько кода нужно написать для каждой задачи. В листах на месте ядра стоит дублёр, поэтому смотреть в них стоит на объём кода, а не на числа в ячейках — настоящий пересчёт живёт в [живой таблице](/f1/demo/) выше. * [Живой пример A4: редактор в браузереработающий редактор документов прямо на странице и весь проект, которым он подключается](/a4/demo/) * [Минимальный листсколько строк нужно, чтобы получить работающий лист](/f1/examples/minimal/) * [Лист с лентойкак добавить нашу ленту, если она нужна](/f1/examples/ribbon/) * [Встраивание в чужую страницучто будет с чужой страницей вокруг листа: её шрифт, фон и прокрутка](/f1/examples/embedded/) * [Миллион строкцена трёх жестов на предельной книге — прокрутки, протяжки и масштаба: кадр p50 и p95 и обращения к ядру числами, а не обещанием](/f1/examples/stress/) * [Перекрестьеподсветка строки и столбца активной ячейки: выключена по умолчанию и включается свойством](/f1/examples/crosshair/) * [Только чтениекнига видна, но не правится: команды серые с причиной, а не спрятаны, и правка не начинается вовсе](/f1/examples/read-only/) * [Свои заголовкиподписи строк и столбцов задаёт встраивающий, а ширина полосы считается по его тексту](/f1/examples/custom-headers/) * [Лист без полос заголовковполосы прячутся одним свойством, и освободившееся место занимает сам лист](/f1/examples/no-headings/) * [Свой пункт менючужой пункт в нашем меню: за чертой, под именем владельца и со своей командой — притвориться нашим он не может](/f1/examples/custom-menu/) * [Своё сочетание клавишчужое сочетание срабатывает, а занятые листом (Ctrl+D, Ctrl+R, F2, Shift+Пробел) остаются за листом — отказ виден словами](/f1/examples/custom-shortcut/) * [Свой языкязыка нет в наших трёх — его приносит хозяин страницы: подписи переводятся, непереведённое идёт запасной цепочкой, а не пропадает](/f1/examples/custom-locale/) * [Перетащите книгу сюдачужой документ открывается байтами: оболочка доносит файл, разбор остаётся за швом контракта, а отказ приходит словами того, кто открывал — на витрине это заглушка, она берёт csv, tsv и txt, а на xlsx честно отказывает](/f1/examples/open-bytes/) * [Двое в одном документедве рамки рядом: двигаете выделение в одной — во второй виден чужой курсор с именем; и прямо на экране написано, что видны только окна этого браузера](/f1/examples/presence/) * [Помощники ведут листдо десяти внешних участников, каждый в своём процессе: ходит по своей колонке, выделяет участок и вписывает итог — намерениями через тот же канал; отказ в лишнем виден и ему, и человеку; не запустили — страница так и говорит](/f1/examples/driver/) * [Своё событиедве точки расширения наружу: onCommand отдаёт любое намерение поверхности (наше и чужое), onGesture — цену закончившегося жеста числами](/f1/examples/custom-event/) * [Гиперссылкассылка — свойство ячейки: одна операция ядра ставит её вместе с текстом, снимает, оставляя текст, и сама называет ссылки листа](/f1/examples/hyperlink/) * [Найти и заменитьпоиск — один запрос, замена — два, и первый книгу не меняет: ядро вычисляет план правок, а пишет их та же дорога, что ввод](/f1/examples/find-replace/) * [Примечанияшесть пунктов «Рецензирования» — одна операция контракта и поле intent; текст, номер и нормализованный адрес называет ядро](/f1/examples/annotations/) * [Структура листавосемь действий — одна операция: оболочка называет диапазон, ось и действие, а уровни и что спрятать считает ядро](/f1/examples/outline/) * [Разметка страницгде ляжет разрыв — единственный метод SDK, который спрашивает и ничего не меняет; саму печать делает хост, и это граница, а не пробел](/f1/examples/page-breaks/) * [Разрешить изменение диапазоновшесть намерений одной операции: пароль уходит за шов и не возвращается — обратно приходит только способ защиты](/f1/examples/protected-ranges/) * [Связи книгиформула считает по чужой книге: список связей, их состояние и адрес источника приходят от ядра, а открывает книгу хозяин окна](/f1/examples/workbook-links/) * [Голова без экранакнига открывается, правится и читается из Node — без единого обращения к DOM](/f1/examples/headless/) Все примеры по продуктам: [A4](/a4/examples/) и [F1](/f1/examples/). ## Куда идти дальше [Заголовок раздела «Куда идти дальше»](#куда-идти-дальше) [Что вы получаете](/platform/story/)Продуктовая формула: что вы приносите, что делает платформа, что получается на выходе. [Функциональный паритет](/platform/parity/)Что означает паритет с Word и Excel и чем он подтверждается. [Пилот и приёмка](/platform/pilot/)Шестинедельный план, что проверяется на каждом слое и по чему решают — берём или нет. [AI-инструменты и MCP](/start/ai/)Подключить документацию к Claude, Cursor или ChatGPT как MCP-сервер. [Связаться с нами](/start/contact/)Запросить пилот, попасть в бета-тестирование или задать вопрос по встраиванию.
# Страница не найдена
> Такого адреса на сайте нет. Ниже — точки входа в документацию FastOffice: продукты, справочники и способ связаться.
Такого адреса здесь нет. Возможно, страницу переименовали или ссылка пришла из старой переписки. Поиск вверху ищет по всей документации, а ниже — точки входа. ## Начать заново [Заголовок раздела «Начать заново»](#начать-заново) * [Что такое FastOffice](/start/intro/) — платформа за пять минут. * [Как читать эту документацию](/start/how-to-read/) — маршруты чтения под разные задачи. * [Глоссарий](/start/glossary/) — если непонятен термин. ## Продукты [Заголовок раздела «Продукты»](#продукты) * [A4 — редактор документов DOCX](/a4/overview/): [установка](/a4/install/), [быстрый старт](/a4/quickstart/), [справочник](/reference/a4/overview/). * [F1 — табличный процессор XLSX](/f1/overview/): [установка](/f1/install/), [быстрый старт](/f1/quickstart/), [справочник](/reference/f1/overview/). ## Если оценивают платформу [Заголовок раздела «Если оценивают платформу»](#если-оценивают-платформу) * [Свой офис вместо Microsoft Office](/platform/replace-office/) — зачем это нужно и что даёт. * [Риски и честные границы](/platform/risks/) — что пока не закрыто. * [Пилот и приёмка](/platform/pilot/) — как проверить на своих документах. ## Не нашли нужного [Заголовок раздела «Не нашли нужного»](#не-нашли-нужного) Напишите на — подскажем, где это описано. Если сюда привела ссылка с самого сайта, сообщите её: [письмо с готовой темой](mailto:support@plantype.ru?subject=%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D1%8F).
# Архитектура: объекты и связи
> Семь объектов решения, граница ответственности между ядром на Rust и поверхностью редактора и правило, по которому решается, кто владеет операцией.
*Для первого понимания достаточно семи объектов. Каждый имеет одну ясную ответственность.*
```plaintext
[Пользователь / агент]
| действие
v
[Ваш продукт: бренд, права, карточка, процесс]
| встроить, открыть, сохранить, получать события
v
[Поверхность] <--> [Мост к приложению и контракт сеанса]
| типизированные команды
v
[Ядро на Rust]
модель → вёрстка → сцена → запись в файл
|
v
[Байты DOCX и хранилище]
```
| Объект | Что делает | Владелец | | ----------------------------------- | ------------------------------------------------------------------ | ------------------------------- | | Ваше приложение | Окно продукта, бренд, права, карточка документа и рабочий процесс. | Вы | | Поверхность редактора (Web Surface) | Лента, страницы, ввод, выделение, диалоги и отклик на действия. | A4, настраивается под ваш бренд | | Мост к приложению | Связывает ваше приложение и редактор; сам документ не хранит. | Общая граница | | Сеанс документа | Жизненный цикл одного документа и его ревизии. | Контракт A4 | | Ядро на Rust | Читает, моделирует, вычисляет, проверяет и записывает документ. | A4 | | Хранилище | Исходный файл, версии, сроки хранения и журнал обращений. | Вы | | Профиль возможностей | Доступные функции и ограничения конкретной поставки. | Согласуется | *Это не дублирование. Rust и Web решают разные классы задач и соединены узким контрактом.* | Ядро на Rust — процессор | Web Surface — поверхность | | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | Владеет главной моделью документа и его ревизией. | Показывает страницы, команды, выделение и состояния. | | Разбирает DOCX и сохраняет поддержанную структуру. | Собирается как обычное веб-приложение и работает внутри WebView. | | Считает вёрстку, строит печатную сцену и геометрию. | Получает готовую сцену и рисует то, что разрешено контрактом. | | Проверяет команды, защиту, устаревшую ревизию и ограничения. | Превращает жест, щелчок или ввод в типизированное намерение. | | Выполняет тяжёлые операции — второго движка на JavaScript нет. | Управляет фокусом, языком, темой, режимом чтения и событиями для вашего приложения. | | Возвращает результат, предупреждение или явный отказ с причиной. | Не записывает DOCX напрямую и не подменяет собой документ. | ## Почему Rust [Заголовок раздела «Почему Rust»](#почему-rust) Документ — это не только видимый текст. Внутри DOCX находятся связи, стили, секции, таблицы, колонтитулы, рецензирование и объекты. Чтобы записать это без потерь, нужен один владелец смысла, строгие типы и одно и то же ядро для настольного, серверного и мобильного вариантов. ## Почему Web [Заголовок раздела «Почему Web»](#почему-web) Web-технологии позволяют встроить одну поверхность в Electron, WebView2, WKWebView, Android WebView или другой совместимый контейнер. Вам не нужно переписывать редактор под каждый набор родных элементов интерфейса, а внешний вид продукта остаётся вашим. ## Дальше [Заголовок раздела «Дальше»](#дальше) * Что происходит от открытия документа до сохранения — [Жизненный цикл](/a4/lifecycle/). * Что продукт делает с документом — [карта возможностей](/a4/capabilities/). * Первый вызов на своей машине — [Быстрый старт](/a4/quickstart/).
# Что A4 умеет с документом
> Карта возможностей документной части по задачам: текст и таблицы, оформление и колонтитулы, поля и оглавления, рецензирование, рассылка, печать и защита — с вызовами, которыми это делается.
Сравнивать редакторы по числу кнопок бессмысленно, а «поддерживаем DOCX» ничего не обещает. Ниже — карта по задачам, ради которых документ вообще открывают: что A4 делает и каким вызовом. Список собран из самого процессора, а не из планов: он перечисляет 96 одноразовых команд по `--help`, и вместе с 28 командами сеанса это всё, что доступно снаружи. ## Пока документ открыт [Заголовок раздела «Пока документ открыт»](#пока-документ-открыт) Это делает сеанс: открыли файл, работаете, сохранили. Подробности — в [объектах запросов и ответов](/a4/envelope/). | Задача | Чем делается | | -------------------------------------------------------------------- | ------------------------------ | | Правка текста: вставка, удаление, разрывы строк, объединение абзацев | `mutate` с видом правки | | Выделение и его положение | `set-selection` | | Отмена и повтор | `undo`, `redo` | | Смысловая модель документа, вёрстка, страницы | `model`, `compose`, `paginate` | | Примечания, исправления, стили | проекции `review` и `style` | | Сохранение и контрольная точка | `save`, `save-checkpoint` | ## Оформление документа [Заголовок раздела «Оформление документа»](#оформление-документа) | Задача | Чем делается | | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | Колонтитулы: создать, связать с предыдущим разделом, отвязать, изменить, убрать | `header-footer-action-docx` | | Разделы: ориентация страницы, поля, размер бумаги | `section-page-setup-action-docx` | | Водяной знак с выбором текста, шрифта и размера, прочее оформление | `design-tools-docx` | | Таблицы: копирование и вставка содержимого ячеек, работа с диапазоном строк и столбцов | `table-docx` | | Списки и их проверка | `lists-proof-docx` | | Изображения, фигуры и надписи: вставка, замена картинки, правка текста внутри фигуры, размер, стиль и выравнивание | девять вызовов, среди них `insert-image-object`, `insert-shape-object`, `replace-image-media`, `resize-drawing-object`, `distribute-drawing-objects` | ## Поля, оглавления, указатели [Заголовок раздела «Поля, оглавления, указатели»](#поля-оглавления-указатели) | Задача | Чем делается | | ------------------------------------------------ | ---------------------------------------------------------------- | | Оглавление: обновить или убрать | `update-toc-docx`, `remove-toc-docx` | | Список иллюстраций по подписи | `update-table-of-figures-docx` | | Библиография и стиль ссылок | `apply-bibliography-command-docx`, `set-bibliography-style-docx` | | Указатель и таблица судебных источников | `apply-index-authorities-command-docx` | | Поля: гиперссылки, коды и результаты, блокировка | `field-tools-docx` | ## Совместная работа над договором [Заголовок раздела «Совместная работа над договором»](#совместная-работа-над-договором) | Задача | Чем делается | | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Сличить две версии и получить документ с исправлениями | `review-compare-combine-docx --mode compare` | | Свести правки нескольких рецензентов | `review-compare-combine-docx --mode combine` | | Примечания: добавить, ответить, разрешить; принять или отклонить правку | `review-docx` с `--insert-comment`, `--reply-comment`, `--resolve-comment`, `--accept-change`, `--reject-change` | | Состояние записи исправлений | `track-revisions-state` | | Проверка правописания | `proofing-docx` | | Рассылка по списку получателей из CSV | `mail-merge-business-docx` | ## Печать и выпуск [Заголовок раздела «Печать и выпуск»](#печать-и-выпуск) | Задача | Чем делается | | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Печать с выбором страниц, чётных или нечётных, порядка и режима показа исправлений | `print-docx` | | Проверка комплекта печати и профилей устройств | `validate-print-bundle`, `validate-print-devices` | | Выгрузка в PDF, в том числе со встроенными шрифтами | `export-pdf`, `export-pdf-embedded` | | Остальные форматы — ODT, RTF, HTML, текст, EPUB, FB2 | [что с каким файлом можно](/a4/overview/#%D1%81-%D0%BA%D0%B0%D0%BA%D0%B8%D0%BC%D0%B8-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%D0%BC%D0%B8-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D1%82) | ## Безопасность файла [Заголовок раздела «Безопасность файла»](#безопасность-файла) | Задача | Чем делается | | ------------------------------------------ | -------------------- | | Посмотреть состав макросов, не запуская их | `inspect-vba` | | Узнать, чем защищён документ | `inspect-protection` | | Проверить готовность к архивному PDF/A-1b | `preflight-pdfa-1b` | Полное описание каждого вызова с параметрами — в [справочнике](/reference/a4/overview/). Для таблиц то же самое собрано [картой возможностей рядом с Excel](/f1/atlas/). Дальше по документной части: [как получить структуру документа](/a4/document-structure/), [определение попадания по координате](/a4/hit-test/), [рецепты интеграции](/a4/recipes/) и [история версий](/a4/changelog/).
# История версий A4
> Что появлялось, менялось и исправлялось в A4 от версии к версии.
Каждая строка ниже — изменение, которое видно в самом продукте. Версии идут от новых к старым. Верхняя запись может опережать то, что выложено: список изменений пополняется по мере готовности выпуска, а выкладка идёт отдельным шагом. Какая версия доступна сейчас, видно в каталоге выпусков — на него ведёт страница установки. Номер версии складывается из года, квартала и номера исправления внутри квартала: в `2026.3.4` это четвёртое исправление к выпуску третьего квартала 2026 года. Счёт исправлений начинается заново с каждым кварталом, поэтому за `2026.3.19` следует `2026.4.0`, а не `2026.3.20`. В именах скачиваемых файлов к номеру добавлена ещё одна часть — сквозной номер сборки, например `2026.3.4.25`. На сравнение версий он не влияет и нужен, чтобы отличать файлы одной версии, собранные в разное время. Кандидаты и тестовые сборки помечены суффиксом: `2026.4.0-rc1`, `2026.4.0-beta1`. Установленную версию приложение сверяет с той, что предлагает канал обновлений, и предлагает обновиться, когда выходит старшая. Проверить вручную можно пунктом меню «Проверить обновления…». Всего выпусков: 64, счёт идёт с квартала `2023.2`. ## Выпуски 2026.3 [Заголовок раздела «Выпуски 2026.3»](#выпуски-20263) ### 2026.3.19 [Заголовок раздела «2026.3.19»](#2026319) **Появилось** * Ctrl+Backspace и Ctrl+Delete удаляют слово целиком. * Размеченный при записи исправлений текст вставляется общим путём правки. **Изменилось** * Сохранение в ODT переносит свойства ячеек и строк таблицы, геометрию таблицы, объединение ячеек по вертикали, дробные проценты ширины, разрывы страниц, участки без проверки правописания, положение и кернинг знаков. * Стрелки перехода по результатам поиска убраны из титульной полосы; назад по результатам ведёт Shift+Enter. * Лента «Главная» приведена к макету. **Исправлено** * Масштаб знаков и отступ абзаца применяются и внутри ячеек таблиц. * Явно заданная высота строки больше не переносится на соседнюю таблицу. * Повторяющиеся пробелы сохраняются при переносе текста в ячейке. * Списки внутри ячеек таблицы не теряются при повторном открытии ODT. * Смена регистра доходит до файла, а не остаётся только на экране. * Заливка абзаца рисуется одной полосой вместо двух. ### 2026.3.18 [Заголовок раздела «2026.3.18»](#2026318) **Появилось** * Плотные документы средней величины отрисовываются по частям: страница появляется, не дожидаясь остального документа. **Исправлено** * Встроенные картинки и объекты переживают разделение и слияние абзацев, сохранение и повторное открытие. * Закладки переживают отмену разделения абзаца и последующее сохранение. * Правка абзацев с письмом справа налево ставит контрольную точку, и её можно отменить. * Отказ при открытии показывается поверх окна работы с файлом, а не прячется за ним. * Окно «Файл изменился на диске» приведено к общему виду остальных окон. * Принятое форматирование рисуется под живым выделением, а не поверх него. ### 2026.3.17 [Заголовок раздела «2026.3.17»](#2026317) **Появилось** * Возврат к последней точке правки. * Переход по результатам поиска и обход плавающих объектов с клавиатуры. * Смена регистра выделенного текста по Shift+F3 и через галерею «Регистр». * Ctrl+Z сразу после автозамены отменяет только автозамену, а не набранное слово. * Поле даты удаляется как одно целое; текст вокруг него переживает набор, удаление и отмену. * Правка списка получателей при слиянии писем. **Изменилось** * Сохранение в ODT переносит цвет текста, подчёркивание с цветом, зачёркивание, выделение цветом, ПРОПИСНЫЕ и малые прописные, подстрочный и надстрочный знак, сочетания начертаний. * Открытие EPUB и FB2 пропускает картинки и разбирает таблицы вместо отказа от всего файла. * Концевые сноски встают в конец документа и в конец раздела. **Исправлено** * Новый документ создаётся с одним размером шрифта: буквы больше не прыгают, первые набранные знаки не пропадают. * Строка перестала менять высоту при наборе. ### 2026.3.16 [Заголовок раздела «2026.3.16»](#2026316) **Появилось** * Вставка полей слияния общим путём правки; слияние писем переведено на общий механизм работы с файлом. * Сохранение в EPUB и FB2 переносит сноски и концевые сноски отдельным разделом, заголовки, нумерованные списки, картинки и адреса ссылок. * Сохранение в HTML, RTF и EPUB переносит многоколоночную вёрстку, вертикальное выравнивание раздела, начальный номер страницы и отступы колонтитулов. **Изменилось** * Составные поля Word и поле времени пересчитываются при открытии документа. * Время пометки исправления идёт за последней правкой, как в Word. **Исправлено** * Удаление сноски при включённой записи исправлений помечает её, а не вырезает молча. * Отказ выделения объясняется человеку понятной фразой. * Выделить всё в документе с пустым телом больше не приводит к сбою. ### 2026.3.15 [Заголовок раздела «2026.3.15»](#2026315) **Исправлено** * Вставка внутрь размеченного текста и набор на границе удаления при включённой записи исправлений. * Примечания к диапазону внутри ячеек таблицы разделяются так же, как в Word. * Длинные примечания листаются постранично и не выходят за лист. * Enter поверх выделения ведёт себя как в Word. * Быстрая замена содержимого ячейки таблицы и отмена набора нелатинского текста в ячейке. * Неактивные кнопки перестали отзываться на нажатие. ### 2026.3.14 [Заголовок раздела «2026.3.14»](#2026314) **Исправлено** * Примечание к диапазону переживает сохранение, разделение абзаца и правку рядом с ним. * Быстрый Enter больше не накапливает отставание и не глушит отрисовку. * Набор сразу после закладки, правка пустого абзаца и форматирование через разрыв страницы. * Меню вставки, галерея объектов и палитра цвета шрифта больше не пропускают нажатие на документ под собой. * Правка переживает повторное открытие того же файла. ### 2026.3.13 [Заголовок раздела «2026.3.13»](#2026313) **Появилось** * Сохранение в HTML, RTF, ODT, EPUB и FB2 переносит оформление: выравнивание, отступы и интервалы абзаца, цвет текста и выделение цветом, разрядку, ПРОПИСНЫЕ и малые прописные, подстрочный и надстрочный знак, контур, тень, приподнятый и утопленный текст. * Таблицы в этих форматах сохраняют ширину столбцов и всей таблицы, высоту строк, границы и заливку ячеек, вертикальное выравнивание. **Исправлено** * Пунктирное, штриховое и волнистое подчёркивание читаемо при стопроцентном масштабе и рисуется своим стилем, а не сплошной линией. ### 2026.3.12 [Заголовок раздела «2026.3.12»](#2026312) **Появилось** * Вставка даты обновляемым полем: поле переживает сохранение и обновляется при открытии документа. * Кнопка списка умеет снять список, как в Word. **Изменилось** * При включённой записи исправлений помечаются также вставка и удаление строк и столбцов таблицы, свойства раздела, поля страницы и замена текста; исправление в ячейке подписывается именем рецензента. * Ответ на примечание получил собственную личность: удаление примечания уносит его записи целиком, а удаление вопроса не оставляет ответ без родителя. * У примечания появились постоянный номер и время. * Под листом появилась тень бумаги вместо нарисованной рамки. **Исправлено** * Ложное предупреждение о несохранённых изменениях снято. * Шрифт документа больше не подменяется на глазах при запуске. * Окно перестало менять ширину на каждой отрисовке. * Значение «Авто» у цвета шрифта снимает цвет и не прерывает правку. * Нумерованный список получает все девять уровней, Tab уводит на следующий. ### 2026.3.11 [Заголовок раздела «2026.3.11»](#2026311) **Появилось** * При включённой записи исправлений помечаются начертание, шрифт, цвет, размер, ПРОПИСНЫЕ, зачёркивание, выделение цветом, выравнивание, отступ и интервал абзаца, стиль абзаца и заливка. * У исправления два времени: местное и всемирное. **Изменилось** * Одна команда форматирования даёт одно исправление, а не несколько. **Исправлено** * Приёмка удалённого знака абзаца склеивает абзацы так же, как в Word. * Страницы длинных документов ложатся как в Word: уровень нумерации больше не перебивает отступ, заданный стилем. * Знаки символьных шрифтов восстанавливаются как обычный текст. * Малые прописные и рамка абзаца доходят до колонтитула. ### 2026.3.10 [Заголовок раздела «2026.3.10»](#2026310) **Появилось** * Режим записи исправлений: набор одновременно печатает и помечает, как в Word; подпись исправления несёт имя пользователя и настоящее время. * Шаблон «Рецепт» и карточки с содержимым на стартовом экране. **Изменилось** * Сочетания клавиш навигации не зависят от раскладки клавиатуры. * Окно расширенного поиска и замены и панель навигации переведены. * Колонтитул переносится по словам настоящими метриками шрифта. **Исправлено** * Кнопка «Исправления» больше не уводит документ с экрана. * Кнопки размера шрифта видят выделение, сделанное с клавиатуры. * Вставка поверх выделенного целиком абзаца. * Список в новом документе больше не блокирует сохранение. * Молчаливая потеря знака при записи исправлений заменена названным отказом. * Набор в абзаце с полем формы и в абзаце с табуляцией больше не запрещён. ### 2026.3.9 [Заголовок раздела «2026.3.9»](#202639) **Появилось** * Окно «Перейти» по сочетаниям клавиш Word: закрывается Escape, не зависит от раскладки. * Переход на предыдущую и следующую страницу. * Вставка эмодзи тем же путём, каким её делает Word. **Изменилось** * Отрисовка стала быстрее: убраны лишние пересчёты на каждой перерисовке. * Контрольная точка сохраняет ссылку на сноску, и абзац со сноской остаётся доступным для правки. **Исправлено** * Выделение протяжкой дотягивается до слов, как в Word. * Решённое примечание больше не подсвечивает текст; карточка примечания растёт под объём текста. * Полужирное начертание для сложных письменностей больше не делает полужирной латиницу. * Переключение форматирования работает, когда каретка стоит внутри слова. * PageUp снова прокручивает экран. * Набор в переносимом абзаце больше не рисует текст поверх текста. * Вставка символа из диалога больше не ломает отмену и положение каретки. * Окно «Добавить название» закрывается по Escape при любом фокусе. ### 2026.3.8 [Заголовок раздела «2026.3.8»](#202638) **Появилось** * Переход в начало и в конец документа. * Заливка абзаца с выбором цвета, включая произвольный. **Исправлено** * Вставка из буфера действительно вставляет, а не рапортует об успехе. * Вырезание удаляет текст, а не только копирует его. * Удаление выделенного больше не стирает вдвое больше выделенного. * Shift и щелчок расширяют выделение, а не схлопывают его. * Сочетания перемещения каретки на macOS доходят до документа. * Tab работает и в начале абзаца, и в середине текста. * Причина отказа доходит до человека вместо молчания или общей фразы. * Экранный диктор больше не читает «недоступно» о работающей кнопке. * Два документа с переносами или с примечаниями больше не пишутся в один общий файл. * Диаграмма рисует оси и подписи категорий. ### 2026.3.7 [Заголовок раздела «2026.3.7»](#202637) **Появилось** * Примечания на полотне документа: карточки на полях, ответы, прокрутка длинных обсуждений, отметка «Нравится». * Свойства таблицы как в Word: границы и заливка, обтекание текстом, положение на странице, альтернативный текст, преобразование таблицы в текст, параметры сортировки. **Исправлено** * В поле ответа на примечание работают Enter, вставка, копирование, вырезание и «Выделить всё». * Черновик ответа не теряется при переключении окна. * Один отказ ядра больше не запирает форматирование до конца сеанса. * Многоуровневый список больше не ломает остальные действия. * Предупреждение «Файл изменился на диске» больше не всплывает на документ, которого никто не трогал. * Кнопки правки не активны там, где документ править нельзя. * Зачёркивание и выделение цветом работают там же, где полужирное начертание. ### 2026.3.6 [Заголовок раздела «2026.3.6»](#202636) **Появилось** * Помощница поддержки: обращение заводится прямо из разговора, вместе с документом, о котором идёт речь, и с выбором того, что уйдёт в отчёте. **Изменилось** * Первая отрисовка больших документов ограничена по времени. * Отмена в открытом сеансе идёт постоянным путём и переживает перезапуск. **Исправлено** * Список, созданный в новом документе, доходит до файла. * Причины недоступности действий с таблицей называются словами. * Обращение из формы уходит вместе с документом. ### 2026.3.5 [Заголовок раздела «2026.3.5»](#202635) **Появилось** * Переход к следующему и предыдущему слову по сочетанию клавиш. * Мягкий перенос по штатному сочетанию клавиш Word. * Смена регистра выделенного текста. **Изменилось** * Мягкий перенос и неразрывный дефис записываются на диск теми же элементами, что пишет Word. * Списки, выделение цветом и зачёркивание идут общим путём правки. **Исправлено** * Собственная запись в файл больше не выглядит как правка со стороны. ### 2026.3.4 [Заголовок раздела «2026.3.4»](#202634) **Появилось** * Неразрывный дефис по штатному сочетанию клавиш Word. * Переход в конец видимой строки как в Word. **Изменилось** * Заливка и границы абзаца идут общим путём правки. **Исправлено** * Обновление на macOS показывает ход загрузки, вызывается из меню и говорит на шести языках. * Номер страницы в колонтитуле больше не выключает правку документа. * В сообщении об ошибке — понятная фраза, а не внутренний текст. ### 2026.3.3 [Заголовок раздела «2026.3.3»](#202633) **Появилось** * Обращение в поддержку работает в любой установке продукта. **Исправлено** * Выравнивание ширины столбцов срабатывает и там, где ширина ячейки не задана, и не меняет таблицу при повторном применении. * Щелчок по перенесённой строке ставит каретку туда же, куда её ставит Word. * Пакет для Linux больше не конфликтует с F1 на одной машине. ### 2026.3.2 [Заголовок раздела «2026.3.2»](#202632) **Изменилось** * Отмена и повтор стали единой историей: действия с таблицами, направление текста в ячейке, подстрочный и надстрочный знак идут тем же путём, что и остальная правка. * Правка нескольких абзацев отменяется целиком, а не по частям. **Исправлено** * Повтор структурного изменения таблицы возвращает именно то, что было отменено. ### 2026.3.1 [Заголовок раздела «2026.3.1»](#202631) **Появилось** * Значок приложения во всех размерах и для всех экранов. **Изменилось** * Описание выпуска доходит до канала обновлений вместе с самим обновлением. ### 2026.3.0 [Заголовок раздела «2026.3.0»](#202630) Первый выпуск линии. Ниже — то, с чем продукт в неё вошёл. **Появилось** * Открытие документов DOCX и правка прямо в открытом файле. * Сохранение в тот же файл: запись целиком либо отсутствие записи, резервная копия предыдущей версии, восстановление после аварийного завершения. * Понятные отказы при конфликте с изменением на диске и при файле только для чтения. * Постраничная вёрстка и печать. * Таблицы, в том числе рисование таблицы мышью. * Отмена и повтор действий. * Набор текста и перемещение каретки по правилам Word: границы слов, границы строк, переход между страницами. * Разрыв строки и неразрывный пробел по штатным сочетаниям клавиш. * Параметры страницы. * Автообновление приложения. * Обращение в поддержку прямо из приложения. **Изменилось** * Отклик на набор в больших документах ускорился примерно в восемь раз. ## Выпуски 2026.2 [Заголовок раздела «Выпуски 2026.2»](#выпуски-20262) ### 2026.2.14 [Заголовок раздела «2026.2.14»](#2026214) **Изменилось** * Открытие плотного документа не ждёт разбора всего файла: первая страница появляется сразу, остальные догружаются по мере прокрутки. * Память при работе с документом в несколько сотен страниц держится ровнее: разобранные страницы, до которых читатель не дошёл, освобождаются. **Исправлено** * Курсор не сбивается в начало, когда фоновая вёрстка догоняет место правки. ### 2026.2.13 [Заголовок раздела «2026.2.13»](#2026213) **Появилось** * Автосохранение с восстановлением после аварийного завершения: при следующем запуске предлагается вернуться к несохранённой работе. **Исправлено** * Копия для восстановления пишется целиком или не пишется вовсе — наполовину записанного файла не остаётся. ### 2026.2.12 [Заголовок раздела «2026.2.12»](#2026212) **Появилось** * Проверка правописания для русского и английского с учётом участков, помеченных как не требующие проверки. ### 2026.2.11 [Заголовок раздела «2026.2.11»](#2026211) **Изменилось** * Печать и экспорт в PDF используют ту же вёрстку, что и экран, поэтому разбиение на страницы совпадает. **Исправлено** * Колонтитулы первой страницы не подставляются на остальные, когда для них задан отдельный вид. ### 2026.2.10 [Заголовок раздела «2026.2.10»](#2026210) **Появилось** * Слияние с источником данных: подстановка полей и постраничный просмотр результата до записи файла. ## Выпуски 2026.1 [Заголовок раздела «Выпуски 2026.1»](#выпуски-20261) ### 2026.1.9 [Заголовок раздела «2026.1.9»](#202619) **Появилось** * Чтение и запись ODT: текст, абзацы, стили, таблицы и списки. **Изменилось** * Формат файла определяется по содержимому, а не только по расширению. ### 2026.1.8 [Заголовок раздела «2026.1.8»](#202618) **Появилось** * Запись исправлений: правки помечаются автором, их можно принять или отклонить по одной. ### 2026.1.7 [Заголовок раздела «2026.1.7»](#202617) **Появилось** * Примечания к выделенному участку текста с ответами в ветке. **Исправлено** * Привязка примечания переживает разделение и слияние абзацев. ### 2026.1.6 [Заголовок раздела «2026.1.6»](#202616) **Изменилось** * Отмена и повтор действий ведут единую историю на весь документ, а не отдельную на каждый вид правки. ## Выпуски 2025.4 [Заголовок раздела «Выпуски 2025.4»](#выпуски-20254) ### 2025.4.8 [Заголовок раздела «2025.4.8»](#202548) **Появилось** * Готовая поверхность редактора, которую можно открыть в браузере или встроить в своё окно. ### 2025.4.7 [Заголовок раздела «2025.4.7»](#202547) **Появилось** * Тот же контракт через WebSocket: процесс сам выбирает свободный порт на локальном интерфейсе и выдаёт одноразовый пропуск. ### 2025.4.6 [Заголовок раздела «2025.4.6»](#202546) **Изменилось** * Отказ приходит до изменения документа, а не после: устаревшая ревизия или неподдерживаемая операция отклоняются целиком. ### 2025.4.5 [Заголовок раздела «2025.4.5»](#202545) **Появилось** * Оформление под чужой бренд: тема, язык интерфейса, режим только для чтения. ### 2025.4.4 [Заголовок раздела «2025.4.4»](#202544) **Появилось** * Постоянный сеанс: документ остаётся открытым между командами, состояние не собирается заново на каждый вызов. ## Выпуски 2025.3 [Заголовок раздела «Выпуски 2025.3»](#выпуски-20253) ### 2025.3.7 [Заголовок раздела «2025.3.7»](#202537) **Появилось** * Типизированная правка текста с проверкой ожидаемой ревизии документа и выделения. ### 2025.3.6 [Заголовок раздела «2025.3.6»](#202536) **Появилось** * Снимок документа в виде дерева объектов: абзацы, участки форматирования, таблицы, изображения. ### 2025.3.5 [Заголовок раздела «2025.3.5»](#202535) **Изменилось** * Позиция в документе адресуется устойчивым идентификатором узла, а не порядковым номером, который менялся при любой правке. ### 2025.3.4 [Заголовок раздела «2025.3.4»](#202534) **Появилось** * Разовые команды над файлом без запуска интерфейса — для пакетной обработки. ## Выпуски 2025.2 [Заголовок раздела «Выпуски 2025.2»](#выпуски-20252) ### 2025.2.6 [Заголовок раздела «2025.2.6»](#202526) **Появилось** * Поиск и замена по документу с учётом регистра и целых слов. ### 2025.2.5 [Заголовок раздела «2025.2.5»](#202525) **Появилось** * Стили абзаца и знака: применение, изменение и создание своих. **Исправлено** * Изменение стиля доходит до файла, а не остаётся на экране. ### 2025.2.4 [Заголовок раздела «2025.2.4»](#202524) **Появилось** * Многоуровневые списки с нумерацией, продолжающейся через весь документ. ### 2025.2.3 [Заголовок раздела «2025.2.3»](#202523) **Исправлено** * Интервалы до и после абзаца не складываются дважды на границе страницы. ## Выпуски 2025.1 [Заголовок раздела «Выпуски 2025.1»](#выпуски-20251) ### 2025.1.5 [Заголовок раздела «2025.1.5»](#202515) **Появилось** * Колонтитулы, включая отдельный вид для первой страницы и для чётных страниц. ### 2025.1.4 [Заголовок раздела «2025.1.4»](#202514) **Появилось** * Разрывы страниц и разделов; параметры страницы задаются на раздел. ### 2025.1.3 [Заголовок раздела «2025.1.3»](#202513) **Изменилось** * Вёрстка строк учитывает кернинг и масштаб знаков. ### 2025.1.2 [Заголовок раздела «2025.1.2»](#202512) **Исправлено** * Перенос слов не разрывает неразрывный пробел и дефис. ## Выпуски 2024.4 [Заголовок раздела «Выпуски 2024.4»](#выпуски-20244) ### 2024.4.4 [Заголовок раздела «2024.4.4»](#202444) **Появилось** * Изображения в тексте: вставка, замена, размер и обтекание. ### 2024.4.3 [Заголовок раздела «2024.4.3»](#202443) **Появилось** * Объединение и разделение ячеек таблицы. **Исправлено** * Ширина столбцов в процентах не округляется до целых. ### 2024.4.2 [Заголовок раздела «2024.4.2»](#202442) **Появилось** * Границы и заливка ячеек, повтор строки заголовка на каждой странице. ### 2024.4.1 [Заголовок раздела «2024.4.1»](#202441) **Исправлено** * Таблица, начинающаяся в конце страницы, переносится целиком, если помещается на следующей. ## Выпуски 2024.3 [Заголовок раздела «Выпуски 2024.3»](#выпуски-20243) ### 2024.3.3 [Заголовок раздела «2024.3.3»](#202433) **Появилось** * Таблицы: чтение, отображение и правка содержимого ячеек. ### 2024.3.2 [Заголовок раздела «2024.3.2»](#202432) **Изменилось** * Разметка, которую редактор не понимает, сохраняется без изменений и возвращается в файл при записи. ### 2024.3.1 [Заголовок раздела «2024.3.1»](#202431) **Исправлено** * Повторное открытие сохранённого файла даёт тот же документ, а не приблизительно похожий. ## Выпуски 2024.2 [Заголовок раздела «Выпуски 2024.2»](#выпуски-20242) ### 2024.2.3 [Заголовок раздела «2024.2.3»](#202423) **Появилось** * Запись DOCX: собранный пакет открывается сторонними редакторами. ### 2024.2.2 [Заголовок раздела «2024.2.2»](#202422) **Изменилось** * Файл записывается целиком во временный и заменяет исходный одним действием — прерывание не оставляет испорченный документ. ### 2024.2.1 [Заголовок раздела «2024.2.1»](#202421) **Исправлено** * Порядок частей внутри пакета не меняется при каждом сохранении. ## Выпуски 2024.1 [Заголовок раздела «Выпуски 2024.1»](#выпуски-20241) ### 2024.1.2 [Заголовок раздела «2024.1.2»](#202412) **Появилось** * Правка текста: ввод, удаление, разделение и слияние абзацев. ### 2024.1.1 [Заголовок раздела «2024.1.1»](#202411) **Появилось** * Выделение мышью и клавиатурой, перемещение курсора по словам и строкам. ## Выпуски 2023.4 [Заголовок раздела «Выпуски 2023.4»](#выпуски-20234) ### 2023.4.2 [Заголовок раздела «2023.4.2»](#202342) **Появилось** * Отображение страниц с полями, отступами и выравниванием абзацев. ### 2023.4.1 [Заголовок раздела «2023.4.1»](#202341) **Изменилось** * Разбиение на строки и страницы считается в ядре, а не в интерфейсе. ## Выпуски 2023.3 [Заголовок раздела «Выпуски 2023.3»](#выпуски-20233) ### 2023.3.1 [Заголовок раздела «2023.3.1»](#202331) **Появилось** * Начертания и размеры шрифта, цвет текста и выделение цветом. ### 2023.3.0 [Заголовок раздела «2023.3.0»](#202330) **Появилось** * Показ содержимого документа с сохранением структуры абзацев. ## Выпуски 2023.2 [Заголовок раздела «Выпуски 2023.2»](#выпуски-20232) ### 2023.2.1 [Заголовок раздела «2023.2.1»](#202321) **Изменилось** * Разбор пакета документа перенесён в ядро на Rust. ### 2023.2.0 [Заголовок раздела «2023.2.0»](#202320) **Появилось** * Чтение DOCX: части пакета, связи между ними, поток текста. ## До перехода на нумерацию по кварталам [Заголовок раздела «До перехода на нумерацию по кварталам»](#до-перехода-на-нумерацию-по-кварталам) Самые ранние сборки выходили под номерами другого вида и раздавались как альфа — для показа, а не для работы. Отдельной истории изменений по ним нет: тогда продукт менялся быстрее, чем выходили версии.
# Живой пример: редактор в браузере
> Работающий редактор A4 на странице документации и весь маленький проект, которым он подключается: страница, код рамки и хост.
Ниже — настоящий редактор A4, тот же, что и в поставке, с обычным договором: заголовки, списки, таблица со сроками и суммой. Когда демонстрационная площадка поднята, им можно пользоваться прямо здесь: документ открывается в вашей собственной кабинке, правки видите только вы, они никуда не сохраняются, а следующий посетитель получит документ исходным. Когда площадка не поднята, на её месте — снимок того же редактора, и об этом сказано под рамкой. [Редактор A4 с открытым договором](/demo/?doc=demo) На весь экран[открыть отдельно ↗](/demo/?doc=demo)Настоящий маленький проект: рамка выше запускает именно те файлы, что показаны ниже, из `examples/a4-embed` ## Из чего состоит этот пример [Заголовок раздела «Из чего состоит этот пример»](#из-чего-состоит-этот-пример) В рамке выше работает тот же редактор и тот же способ встраивания, но площадка поднята у нас: документ ей даёт наша демонстрационная служба. Чтобы то же самое заработало у вас, нужны три вещи — страница с местом под рамку, код подключения и ваш сервер с двумя адресами. Ровно они и показаны ниже: это цельный проект, который запускается сам, а не выдержки из него. Смотреть его стоит в таком порядке: сначала страница, потом подключение, потом сервер. Первые два файла отвечают на вопрос «где появляется редактор», третий — на вопрос «откуда документ и куда уходят правки». Страница вашего приложения. Редактора в ней нет — есть карточка документа и место, куда он встанет. index.html
```html
Договор №42 — документыДокументы…
Договор №42
docx · загружен 14 марта · только просмотр
```
Всё подключение целиком. Рамка на адрес вашего домена; никакого модуля в браузер не загружается. src/embed.js
```js
/**
* Подключение редактора — весь клиентский код примера.
*
* Редактор открывается рамкой на адрес `/fastdoc/?doc=<идентификатор>`.
* Никакого модуля в браузер не загружается: документ считает ядро на
* сервере, а рамка показывает его страницу.
*
* Событий наружу редактор не шлёт, и слушать здесь нечего. Связь с вашим
* приложением идёт другой дорогой: правки редактор отправляет на ваш же
* сервер, в «файл по идентификатору». Что произошло с документом, вы
* узнаёте оттуда, а не из браузера.
*/
const DOC_ID = "contract-42";
const stage = document.querySelector("[data-stage]");
const edit = document.querySelector("[data-edit]");
const state = document.querySelector("[data-state]");
const who = document.querySelector("[data-who]");
/* Имя пользователя берём из своего же API — того самого, которое потом
спросит редактор. Одна личность на всё приложение. */
fetch("/v1/me")
.then((res) => (res.ok ? res.json() : null))
.then((user) => {
who.textContent = user ? user.name : "не выполнен вход";
})
.catch(() => {
who.textContent = "не выполнен вход";
});
edit.addEventListener("click", () => {
const frame = document.createElement("iframe");
frame.src = `/fastdoc/?doc=${encodeURIComponent(DOC_ID)}`;
frame.title = "Редактор документа";
/* Буфер обмена нужен явным разрешением: без него копирование между
документом и остальным браузером работать не будет. */
frame.allow = "clipboard-read; clipboard-write";
stage.replaceChildren(frame);
stage.hidden = false;
edit.disabled = true;
state.textContent = "открыт на правку";
});
```
Оформление вашей страницы. Редактор его не касается: внутри рамки свой документ, снаружи ваш интерфейс. src/styles.css
```css
/* Оформление вашей страницы. Редактор его не касается: внутри рамки свой
документ, снаружи — ваш интерфейс. */
:root {
color-scheme: light;
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}
body {
margin: 0;
background: #f6f7f9;
color: #1a1d21;
}
.bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem 1.25rem;
background: #fff;
border-bottom: 1px solid #e3e6ea;
}
.brand {
font-weight: 600;
}
.who {
color: #6b7280;
font-size: 0.875rem;
}
.card {
max-width: 64rem;
margin: 1.5rem auto;
padding: 1.25rem;
background: #fff;
border: 1px solid #e3e6ea;
border-radius: 0.5rem;
}
h1 {
margin: 0 0 0.25rem;
font-size: 1.25rem;
}
.meta {
margin: 0 0 1rem;
color: #6b7280;
font-size: 0.875rem;
}
button {
padding: 0.5rem 1rem;
border: 0;
border-radius: 0.375rem;
background: #2563eb;
color: #fff;
font: inherit;
cursor: pointer;
}
button[disabled] {
background: #9aa4b2;
cursor: default;
}
/* Рамке нужна заданная высота: редактор занимает её целиком, а сам её не
назначает — снаружи виднее, сколько места ему отдать. */
.stage {
margin-top: 1rem;
height: 75vh;
border: 1px solid #e3e6ea;
border-radius: 0.375rem;
overflow: hidden;
}
.stage iframe {
display: block;
width: 100%;
height: 100%;
border: 0;
}
```
Ваш сервер: кто пришёл, файл по идентификатору, приём правок. Здесь же проброс на веб-хост A4 — в рабочем хозяйстве это правило nginx. server/host.mjs
```js
/**
* Ваш сервер: два адреса, которых редактору достаточно, и приём правок.
*
* Редактор своих учётных записей не имеет и своего хранилища тоже. Кто
* пришёл и какой файл ему отдать — решаете вы, этими двумя ответами:
*
* GET /v1/me — кто пришёл
* GET /v1/files/{id}/content — сам документ, если этому человеку можно
* POST /v1/files/{id}/replace — сохранение правок обратно к вам
*
* Если у вас есть личный кабинет и файлы, оба первых адреса уже написаны.
*
* Здесь же для полноты картины лежит и то, что в вашем хозяйстве обычно
* делает nginx: раздача страницы и проброс `/fastdoc/` на веб-хост A4.
* Отдельным сервером это или правилом в вашем — дело вкуса; важно, что
* редактор и ваш API отвечают на одном домене и с теми же куками.
*/
import { createServer } from "node:http";
import { readFile, writeFile } from "node:fs/promises";
import { extname, join, normalize } from "node:path";
const arg = (name, fallback) => {
const at = process.argv.indexOf(name);
return at > 0 && process.argv[at + 1] ? process.argv[at + 1] : fallback;
};
const docPath = arg("--doc", "./document.docx");
const port = Number(arg("--port", "8080"));
/* Адрес веб-хоста A4 — того самого процесса, который поднимает кабинки. */
const webhost = arg("--webhost", "http://127.0.0.1:8090");
const root = new URL("../", import.meta.url).pathname;
const TYPES = {
".html": "text/html; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
};
const DOCX =
"application/vnd.openxmlformats-officedocument.wordprocessingml.document";
/**
* Правки приходят как `multipart/form-data` — одной частью с именем `file`.
*
* Тело обязательно РАЗБИРАТЬ. Если записать в документ сырое тело запроса,
* к началу файла приклеятся служебные строки формы, и .docx перестанет
* открываться — молча, без ошибки в журнале. Признак этой беды при
* распаковке: «extra bytes at beginning».
*/
const filePart = (body, contentType) => {
const boundary = /boundary=(?:"([^"]+)"|([^;]+))/i.exec(contentType ?? "");
if (!boundary) return null;
const mark = Buffer.from(`--${boundary[1] ?? boundary[2]}`);
let at = body.indexOf(mark);
while (at !== -1) {
const headEnd = body.indexOf("\r\n\r\n", at);
if (headEnd === -1) return null;
const head = body.subarray(at, headEnd).toString("utf8");
const next = body.indexOf(mark, headEnd);
if (next === -1) return null;
/* Перед следующей границей стоит CRLF, который к содержимому не
относится: он разделитель, а не байт документа. */
if (/name="file"/i.test(head)) return body.subarray(headEnd + 4, next - 2);
at = next;
}
return null;
};
const readBody = (req) =>
new Promise((resolve, reject) => {
const chunks = [];
req.on("data", (chunk) => chunks.push(chunk));
req.on("end", () => resolve(Buffer.concat(chunks)));
req.on("error", reject);
});
const server = createServer(async (req, res) => {
const url = new URL(req.url, `http://127.0.0.1:${port}`);
/* Кто пришёл. В настоящем приложении здесь ваша сессия; отказ отвечайте
кодом 401 или 403 — редактор покажет его человеку словами. */
if (url.pathname === "/v1/me") {
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
res.end(JSON.stringify({ id: "u-1", name: "Анна Петрова" }));
return;
}
/* Файл по идентификатору. Право проверяете вы: не отдали файл — редактор
не откроется, и это правильный ответ. */
if (url.pathname.endsWith("/content") && req.method === "GET") {
res.writeHead(200, {
"content-type": DOCX,
/* Имя файла отсюда попадёт в заголовок вкладки и в имя копии, если
документ понадобится второму человеку. */
"content-disposition":
"attachment; filename*=UTF-8''" + encodeURIComponent("Договор №42.docx"),
});
res.end(await readFile(docPath));
return;
}
/* Правки обратно. Здесь — ваше хранилище: версия, аудит, уведомления. */
if (url.pathname.endsWith("/replace") && req.method === "POST") {
const body = await readBody(req);
const bytes = filePart(body, req.headers["content-type"]);
if (!bytes) {
res.writeHead(400, { "content-type": "application/json" });
res.end(JSON.stringify({ error: "ожидалась часть с именем file" }));
return;
}
await writeFile(docPath, bytes);
console.log(`сохранено ${bytes.length} байт`);
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: true }));
return;
}
/* Проброс на веб-хост A4. Куки идут насквозь: по ним он и спросит у вас,
кто пришёл. В рабочем хозяйстве это правило nginx. */
if (url.pathname === "/fastdoc/" || url.pathname.startsWith("/fastdoc/")) {
const target = new URL(url.pathname + url.search, webhost);
const upstream = await fetch(target, {
method: req.method,
headers: { ...req.headers, host: new URL(webhost).host },
body: req.method === "GET" || req.method === "HEAD" ? undefined : req,
redirect: "manual",
duplex: "half",
}).catch(() => null);
if (!upstream) {
res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
res.end("веб-хост A4 не отвечает");
return;
}
res.writeHead(upstream.status, Object.fromEntries(upstream.headers));
res.end(Buffer.from(await upstream.arrayBuffer()));
return;
}
/* Ваша страница и её файлы. */
const name = url.pathname === "/" ? "/index.html" : url.pathname;
const path = join(root, normalize(name).replace(/^(\.\.[/\\])+/, ""));
const body = await readFile(path).catch(() => null);
if (!body) {
res.writeHead(404);
res.end();
return;
}
res.writeHead(200, { "content-type": TYPES[extname(path)] ?? "application/octet-stream" });
res.end(body);
});
server.listen(port, () => {
console.log(`пример открыт: http://127.0.0.1:${port}/`);
console.log(`редактор берётся с ${webhost}`);
});
```
## На чём легко ошибиться [Заголовок раздела «На чём легко ошибиться»](#на-чём-легко-ошибиться) Правки приходят к вам как `multipart/form-data` — одной частью с именем `file`. Тело обязательно разбирать. Если записать в документ сырое тело запроса, к началу файла приклеятся служебные строки формы, и документ перестанет открываться — молча, без ошибки в журнале. Признак этой беды при распаковке: «extra bytes at beginning». На этом попадались и мы сами, поэтому разбор в примере выписан целиком, а не сведён к вызову библиотеки. ## Чем этот пример отличается от рабочего встраивания [Заголовок раздела «Чем этот пример отличается от рабочего встраивания»](#чем-этот-пример-отличается-от-рабочего-встраивания) | Что | В этом примере | В вашем приложении | | ------------------- | ------------------------------------------------ | --------------------------------------------------- | | Кто пользователь | Гость: личность одна на всех посетителей | Ваш вошедший пользователь | | Откуда документ | Один и тот же демонстрационный файл | Ваш файл по идентификатору | | Куда идут правки | Никуда: сохранение принимается и не записывается | В ваше хранилище, вашим же API | | Права | Не проверяются | Проверяете вы, в том числе повторно во время работы | | Проброс `/fastdoc/` | Сделан тем же процессом, для краткости | Обычно правило nginx | Всё остальное совпадает: тот же редактор, то же ядро, тот же способ встраивания. ## Дальше [Заголовок раздела «Дальше»](#дальше) * Как подключить у себя, с тремя шагами и обязательными условиями — [Редактор в браузере](/a4/webhost/). * Где проходят границы ответственности — [Ответственность и безопасность](/a4/responsibility/). * Другие способы разместить A4 — [Платформы и размещение](/a4/deployment/).
# Платформы и размещение
> Шесть способов разместить A4 и что доказано для каждого: настольное приложение, готовая сборка, сервер без интерфейса, браузер, Android и iOS.
*Меняется адаптер размещения. Документная истина и базовый контракт остаются в ядре на Rust.* | Профиль | Как устроено | Что доказано на сегодня | | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Внутри настольного приложения | Ваше приложение запускает Surface Host, открывает его адрес в системном окне просмотра веб-страниц и получает события. | На WKWebView пройден полный круг: документ готов → появились несохранённые изменения → сохранён → открыт заново. Для WebView2 адаптер предусмотрен. | | Готовое приложение | Оболочка на Electron поставляется как приложение A4. | Самый полный набор возможностей для пользователя. Внутренние каналы этого приложения публичным интерфейсом не становятся. | | Сервер без интерфейса | Ваш сервис управляет сеансом ядра через строки JSON или WebSocket и сам отвечает за хранилище; интерфейс не нужен. | Процессор работает. Обслуживание многих клиентов сразу, проверка доступа, квоты и отказоустойчивость — отдельная работа. | | Браузер | Поверхность работает в браузере, а документ считает ядро на сервере: каждому пользователю поднимается отдельный процесс. См. [Редактор в браузере](/a4/webhost/). | В браузер уходит обычный код страницы и отрисовка на полотне. Сборки ядра под WebAssembly у A4 нет, и для этого пути она не нужна. | | Android | Ядро собирается библиотекой, подключается через JNI и работает под Android WebView. | Пройден сквозной путь: открыть файл, изменить, сохранить и проверить результат. Набор операций в мобильном ядре пока узкий — это доказательство пути, а не редактор на телефоне. | | iOS | Ядро собирается библиотекой, подключается из Swift и работает под WKWebView. | Обвязка для сборки готова; работоспособность на устройствах пока не заявляется. | ## Серверный вариант [Заголовок раздела «Серверный вариант»](#серверный-вариант)
```plaintext
[Клиент: браузер или телефон] → [Ваш сервис: вход, хранилище, аудит]
| команды и события
v
[Адаптер сеанса A4]
|
v
[Ядро на Rust]
```
## Где про это подробнее [Заголовок раздела «Где про это подробнее»](#где-про-это-подробнее) * Редактор внутри веб-приложения, без установки чего-либо пользователю — [Редактор в браузере](/a4/webhost/). * Встраивание поверхности в WebView или iframe — [Встраивание Surface](/a4/embedding/), запуск — [Web Surface](/a4/surface/). * Что доступно внутри окна настольного приложения и почему это не публичный SDK — [Внутренние объекты окна приложения](/a4/electron/). * Управление сеансом из службы без интерфейса — [Тот же контракт через WebSocket](/a4/websocket/). * Общая картина режимов для обоих продуктов — [Режимы встраивания](/platform/deployment/).
# Диагностика и контрольный список
> Что проверить, если первый запуск не работает: шесть типовых симптомов и контрольный список из десяти шагов перед передачей в работу.
Если первый запуск не идёт, найдите симптом в таблице: рядом с ним — что именно проверить. | Симптом | Что проверить | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Приложение не найдено | Путь установки: `/Applications/A4.app` на macOS, `C:\Program Files\A4` на Windows (каталог можно было сменить при установке), `/opt/A4` на Linux — если ставили пакетом `deb` или `rpm`. У поставки `AppImage` каталога установки нет: файлы лежат внутри самого образа, и достать их можно распаковкой — `./A4-….AppImage --appimage-extract`, дальше `squashfs-root/resources/`. Проверьте также, что скачана сборка под вашу архитектуру | | Процессор не запускается | Пакет приложения повреждён или скопирован не полностью. На macOS и Linux проверьте, что у файла есть право на запуск | | В ответе `ok: false` | Прочитайте поля `error`, `cmd` и `id`: в них сказано, какая команда отклонена и почему. Проверьте путь к файлу и поля операции | | Файл не изменился | Сохраняете ли вы в новый путь; что показывают ревизия и признак несохранённых изменений; прочитайте результат заново командой `model-docx` | | Поверхность отключилась | Перезагрузка страницы, пропуск, порт локального интерфейса, жизненный цикл дочернего процесса и переподключение после разрыва | | `window.fastdocShell` не определён | Код выполняется вне окна самого приложения A4 — там этих объектов нет. Для встраивания используйте внешний контракт: обмен строками JSON, WebSocket или поверхность | ## Контрольный список первой интеграции [Заголовок раздела «Контрольный список первой интеграции»](#контрольный-список-первой-интеграции) 1. Найти установленное приложение и `fastdoc-cli` внутри него — пути для трёх систем даны в [Установке и проверке](/a4/install/). 2. На macOS и Linux проверить, что у `fastdoc-cli` стоит флаг исполняемого файла; на Windows такого флага нет. 3. `ping` → `ok: true`, `pong: true`. 4. `open` с указанием `document_id`. 5. `model` — сохранить исходный текст для сравнения. 6. Правка с `transaction_order: 1`. 7. Сохранить в новый `output.docx`. 8. Повторно прочитать `output.docx` тем же процессором. 9. Открыть `output.docx` в A4 и увидеть изменение. 10. `shutdown` и завершение процесса. ## Что приложить к обращению [Заголовок раздела «Что приложить к обращению»](#что-приложить-к-обращению) Если по таблице выше причина не нашлась, напишите на . Разбор идёт быстрее, когда в письме сразу есть: * версия сборки A4 и система, на которой это видно; * имя команды (`cmd`) и текст отказа (`error`) целиком, вместе с `id` запроса; * одна строка запроса и одна строка ответа как есть, без пересказа; * наименьший обезличенный DOCX, на котором это повторяется; * повторяется ли это в новом процессе — после `shutdown` и запуска заново. ## Куда смотреть дальше [Заголовок раздела «Куда смотреть дальше»](#куда-смотреть-дальше) * Формат запроса и ответа, включая поля, которых может не быть, — [Объекты запросов и ответов](/a4/envelope/). * Полный перечень команд с параметрами — [справочник Session Protocol](/reference/a4/session-protocol/). * Коды и тексты отказов транспорта — [Транспорт и формат сообщений A4](/reference/a4/transport/). * Поведение после разрыва соединения — [Встраивание Surface](/a4/embedding/).
# Структура документа: как её получить
> Архитектура и границы ответственности, путь одного пользовательского действия, смысловая модель и текущий Rust Session API для чтения.
## Архитектура и границы ответственности [Заголовок раздела «Архитектура и границы ответственности»](#архитектура-и-границы-ответственности) Путь данных один и тот же — в настольном приложении, в системном окне просмотра веб-страниц и при работе без интерфейса: | Слой | Что делает | Чего не делает | | ----------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------- | | Ваше приложение | Окно, бренд, вход, хранилище, рабочие процессы и их жизненный цикл | Не владеет моделью документа и его вёрсткой | | Поверхность редактора (Web Surface) | Полотно, лента, ввод, показ меню, доставка событий | Не правит DOCX напрямую | | Адаптер к приложению | Запуск процесса, разбор приветственной строки с адресом, обмен по JSON или WebSocket, события окна | Не разбирает разметку документа | | Сеанс документа | Команды, выделение, история, проекции модели и вёрстки, сохранение | Не отдаёт произвольный доступ к разметке страницы | | Ядро на Rust | Пакет OOXML, смысл документа, вёрстка, запись обратно в файл | Не зависит от оформления оболочки | | Файл DOCX | То, что сохраняется и открывается заново | Не подменяется снимком HTML | ### Путь одного пользовательского действия [Заголовок раздела «Путь одного пользовательского действия»](#путь-одного-пользовательского-действия) * **Событие указателя.** Поверхность получает координаты щелчка, щелчка правой кнопкой или двойного щелчка. * **Определение попадания.** Координата сопоставляется со страницей, строкой и позицией курсора, а затем с объектом документа. * **Сбор контекста.** Собираются сам объект, выделение, его родители в структуре документа, номер ревизии и список разрешённых команд. * **Решение интерфейса.** A4 и ваша сторона составляют контекстное меню — документ при этом не меняется. * **Типизированная команда.** Выбранный пункт превращается в команду с указанием ожидаемой ревизии. * **Изменение документа.** Ядро сверяет ревизию и разрешения, меняет модель и возвращает подтверждение с новой ревизией. * **Перерисовка и сохранение.** Проекции обновляются; сохранение пишет DOCX, который открывается заново той же моделью. ## Как получить структуру документа [Заголовок раздела «Как получить структуру документа»](#как-получить-структуру-документа) Термин «структура документа» неоднозначен. A4 разделяет пять представлений, чтобы вы запрашивали только то, что действительно нужно. | Представление | Команда | Назначение | Пример данных | | ---------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------- | | Смысловая модель | `model`, `model-docx` | Абзацы, участки форматирования, таблицы, ссылки на стили и нумерацию, пометки о поддержке | `body.blocks[]`, `paragraph_index`, `text`, `runs[]` | | Вёрстка | `compose`, `paginate` | Разделы, страницы, строки, переносы, геометрия | `page_index`, границы строк, порядок страниц | | Сцена | `print`, `print-window`, `print-delta` | Готовые к отрисовке страницы и примитивы рисования | сцены страниц и их отпечатки | | Рецензирование и стили | `review`, `style` | Примечания, записанные исправления, разрешение стилей | проекции рецензирования и стилей | | Состояние правки | `status`, `set-selection` | Ревизия, наличие несохранённых изменений, отмена и повтор, выделение и состояние вида | `accepted_revision`, `selection_revision` | ### Пример: смысловая модель [Заголовок раздела «Пример: смысловая модель»](#пример-смысловая-модель)
```bash
# $CLI — путь к процессору, см. «Установка и проверка»
"$CLI" model-docx /data/contract.docx --format json
```
```json
{
"model_version": "fastdoc-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}
}]
}]}
}
```
## Как запустить сеанс сегодня и прочитать документ [Заголовок раздела «Как запустить сеанс сегодня и прочитать документ»](#как-запустить-сеанс-сегодня-и-прочитать-документ) ### Сеанс JSONL: обмен через стандартный ввод и вывод [Заголовок раздела «Сеанс JSONL: обмен через стандартный ввод и вывод»](#сеанс-jsonl-обмен-через-стандартный-ввод-и-вывод) Процесс печатает первую строку готовности со схемой `fastdoc.session-serve.v1`. Дальше каждая строка на входе — запрос в JSON, каждая строка на выходе — ответ на него, связанный по идентификатору.
```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"}
```
```json
{
"id": "model-1",
"ok": true,
"cmd": "model",
"elapsed_ms": 2,
"result": { "model_version": "...", "body": { "blocks": [] } }
}
```
### WebSocket на локальном адресе [Заголовок раздела «WebSocket на локальном адресе»](#websocket-на-локальном-адресе)
```bash
"$CLI" serve --ws 0
# stdout: {"schema":"fastdoc.session-ws-bootstrap.v1",
# "host":"127.0.0.1","port":49172,"token":"..."}
```
* Привязка — только 127.0.0.1. Порт 0 выбирается автоматически. * Токен — секрет длиной 256 бит из системного источника случайности. Его нельзя писать в журналы и передавать в аналитику. * Команды одинаковы. Обмен строками JSON и обмен через WebSocket идут через один и тот же сеанс и одну схему запросов. ## Дальше [Заголовок раздела «Дальше»](#дальше) * Определение объекта по координате — [Попадание по точке](/a4/hit-test/). * Что ещё делает процессор — [карта возможностей](/a4/capabilities/).
# Внутренние объекты окна приложения A4
> Что доступно внутри окна настольного A4, зачем это нужно и почему внутренний контракт окна не является публичным интерфейсом.
| Объект | Назначение | Примеры методов | | --------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `window.fastdocShell` | Документ, команды редактора, состояние, сохранение, меню | `getState()`, `getCurrentDocumentTruth()`, `openDocxPath()`, `onStateChanged()` | | `window.a4Update` | Обновления приложения | `status()`, `check()`, `start()`, `dismiss()`, `reveal()`, `openNotes()`, `onState()` | | `window.a4Help` | Версия и поддержка | `version()`, `reportProblem()`, `supportBundle()`, `checkUpdates()`, `reportBadge()`, `onReportBadge()` | | `window.a4Vizor` | Заметки встроенной записи окна: она живёт в памяти вкладки около минуты и уходит только вместе с отчётом о проблеме | `onNote()` | ## Примеры внутри окна самого приложения [Заголовок раздела «Примеры внутри окна самого приложения»](#примеры-внутри-окна-самого-приложения)
```ts
const state = await window.fastdocShell.getState();
const truth = await window.fastdocShell.getCurrentDocumentTruth();
const version = await window.a4Help.version();
const stopState = window.fastdocShell.onStateChanged((next) => {
console.log("document state", next);
});
const stopUpdate = window.a4Update.onState((next) => {
console.log("update state", next);
});
await window.a4Update.check();
// При размонтировании компонента обязательно отписаться.
stopState();
stopUpdate();
```
## Как выбрать правильный API [Заголовок раздела «Как выбрать правильный API»](#как-выбрать-правильный-api) * Пишете своё настольное приложение или серверную обвязку — `fastdoc-cli serve` с обменом строками JSON. * Оболочка на веб-технологиях и нужен асинхронный обмен — `serve --ws 0`. * Нужно показать встраивание на машине пользователя — команда `surface` и окно WebView или `iframe`; правка в этой поверхности ограничена первым абзацем. * Ваш продукт живёт в браузере, а ядро должно работать на сервере — [редактор в браузере](/a4/webhost/). * Дорабатываете само приложение A4 — внутренний контракт его окна, по отдельному согласованию.
# Встраивание Surface
> Как вставить поверхность редактора в своё приложение: iframe и WebView, вставка и удаление, подписка на события, переподключение и рабочая установка.
Поверхность запускается локально и открывается по адресу из bootstrap. Эта страница — о том, как встроить её в своё приложение. Поверхность правит только первый абзац Встраивать её как полноценный редактор нельзя: она объявляет открытие, правку **первого абзаца**, сохранение и события. Это проверяемая демонстрация пути, а не редактор для пользователя — см. [Запуск Web Surface](/a4/surface/). Полный редактор в браузере — это [веб-хост](/a4/webhost/). ## Как запустить поверхность [Заголовок раздела «Как запустить поверхность»](#как-запустить-поверхность)
```bash
# $CLI — путь к процессору, см. «Установка и проверка»
"$CLI" surface --port 0 \
--doc /data/input.docx --save /data/output.docx
# первая строка stdout: fastdoc.surface-host-bootstrap.v1
# адрес из bootstrap.url открыть в WKWebView, WebView2 или браузере
```
## Вставить поверхность и убрать её [Заголовок раздела «Вставить поверхность и убрать её»](#вставить-поверхность-и-убрать-её) Что поверхность принимает молча, а что молча отбрасывает В `cssVariables` попадают только имена, начинающиеся на `--fastdoc-`, и только строковые значения: `"16px"` дойдёт, а `16` — нет. Всё остальное поверхность пропускает без ошибки и без предупреждения. Список того, что она понимает, — на странице [Настройка под свой бренд](/platform/white-label/). Поле `locale` тоже строгое: если передать не строку или не передать вовсе, язык поверхности станет английским. `readOnly` здесь выключает ввод в самой поверхности — гасит поле правки и кнопку сохранения. Защитой документа это не является: команды, отправленные по сеансу напрямую, ему не подчиняются. За запрет на уровне ядра отвечает режим защиты самого документа — в нём отклоняется любая команда правки.
```ts
const handle = window.fastdocSurface.mount(container, {
theme: "light",
locale: "ru",
readOnly: false,
cssVariables: { "--fastdoc-accent": "#2f5bff" }
});
// при закрытии вкладки или документа
handle.unmount();
```
## Как получать события [Заголовок раздела «Как получать события»](#как-получать-события)
```js
window.addEventListener("fastdoc-host-event", (event) => {
const message = event.detail;
if (message.schema !== "fastdoc.surface-host-event.v1") return;
switch (message.type) {
case "ready": console.log(message.documentPath); break;
case "dirty": setUnsavedBadge(message.dirty); break;
case "saved": console.log(message.path, message.byteCount); break;
case "error": showError(message.operation, message.message); break;
case "unmounted": releasePartnerResources(); break;
}
});
```
| Среда | Где встречается | Канал события | | ---------------------------- | ----------------- | ----------------------------------------------------- | | WKWebView, WebKitGTK | macOS, iOS, Linux | обработчик сообщений с именем `fastdocHost` | | WebView2 | Windows | `window.chrome.webview`, событие `WebMessageReceived` | | Обычный браузер, без обёртки | везде | событие DOM `fastdoc-host-event` | Выбирать канал не нужно: каждое событие поверхность отправляет во все три сразу. Хост слушает тот, который есть у него, и не знает про остальные. ## Встраивание [Заголовок раздела «Встраивание»](#встраивание)
```html
```
Поверхность сообщает о своих состояниях — `ready`, `dirty`, `saved`, `error` и `unmounted` — событием окна `fastdoc-host-event`. Этого хватает, чтобы знать, что происходит с документом, но не чтобы им управлять: для полного доступа к модели и всему набору операций работайте параллельно через обмен строками JSON или WebSocket. ## Что предусмотреть в рабочей установке [Заголовок раздела «Что предусмотреть в рабочей установке»](#что-предусмотреть-в-рабочей-установке) Поверхность поднимается на случайном свободном порту, а не на заранее известном: занятый порт и чужие подключения к нему — лишний способ помешать работе. Пропуск выдаётся на каждый сеанс заново и не должен попадать в журналы веб-сервера вместе с адресом страницы. После разрыва соединение восстанавливается, но правка вслепую не повторяется: сначала нужно убедиться, что она не применилась до обрыва, иначе документ получит её дважды. Сохранение сначала пишет новую копию, и только по явному решению пользователя ею заменяется исходный файл. После разрыва и после сохранения **После разрыва соединения** не повторяйте последнюю правку вслепую: сначала прочитайте актуальное состояние документа. Соединение могло оборваться уже после того, как правка применилась, и повтор продублирует её. **После события `saved`** откройте сохранённый DOCX заново и убедитесь, что изменение на месте. Это дешёвая проверка, которая ловит расхождение между тем, что показала поверхность, и тем, что легло на диск. **Оператору показывайте состояние сеанса** — подключение, готовность, несохранённые правки, сохранение, сохранено, ошибка. Человек должен видеть, можно ли сейчас закрывать окно. ## Точные имена [Заголовок раздела «Точные имена»](#точные-имена) Имена событий, схем, глобального объекта и параметров запуска собраны в справочнике [Интерфейс поверхности в браузере](/reference/a4/surface-api/). Берите их оттуда: на этой странице они приведены для примера и могут отстать.
# Объекты запросов и ответов
> Минимальная последовательность команд, обязательные и рекомендуемые поля запроса, формат ответа и обработка ошибок.
Каждое сообщение — обычный JSON-объект. Поле `id` выбираете вы, ядро возвращает его без изменения. Поэтому ответы можно сопоставлять с запросами даже при параллельной работе. ## Минимальная последовательность [Заголовок раздела «Минимальная последовательность»](#минимальная-последовательность)
```json
{"id":"ping-1","cmd":"ping"}
{"id":"open-1","cmd":"open","path":"/absolute/input.docx","document_id":"my-document"}
{"id":"model-1","cmd":"model"}
{"id":"insert-1","cmd":"mutate","operation_kind":"insert-text","paragraph_index":0,"offset":0,"text":"Привет из API. ","document_id":"my-document","transaction_order":1,"transition_kind":"api-insert"}
{"id":"save-1","cmd":"save","path":"/absolute/output.docx"}
{"id":"shutdown-1","cmd":"shutdown"}
```
## Обязательные и рекомендуемые поля [Заголовок раздела «Обязательные и рекомендуемые поля»](#обязательные-и-рекомендуемые-поля) | Поле | Назначение | Правило | | ------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Корреляция запроса и ответа | Необязательно. Любое значение JSON — строка, число. Возвращается без изменений, поэтому должно быть уникальным среди запросов, ответ на которые ещё не получен | | `cmd` | Имя команды сеанса | Одно из имён, перечисленных в поле `supported_commands` приветственного сообщения | | `path` | Входной или выходной файл | Только абсолютный путь | | `document_id` | Стабильная идентичность документа | Передавайте в `open` и в правках | | `transaction_order` | Порядок изменений | Монотонно растёт: 1, 2, 3… | | `transition_kind` | Причина или тип перехода | Короткая диагностическая метка | | `operation_kind` | Семантическая операция | Обязательно для `mutate`. Например `insert-text` | | `paragraph_index` | Номер абзаца, к которому относится правка | Обязательно для `mutate`. Нумерация с нуля | ## Формат ответа [Заголовок раздела «Формат ответа»](#формат-ответа) Успех:
```json
{"id":"ping-1","ok":true,"cmd":"ping","elapsed_ms":0,"result":{"pong":true}}
```
Отказ:
```json
{"id":"bad-1","ok":false,"cmd":"mutate","elapsed_ms":0,"error":"..."}
```
# Примеры A4
> Работающие проекты: что именно нужно написать, чтобы встроить редактор документов в своё приложение.
Пример — это не выдержка из кода, а маленький проект целиком: страница, подключение и хост с двумя адресами. Весь его код показан на странице, файл за файлом. * [Живой пример: редактор в браузереработающий редактор A4 прямо на странице и весь проект, которым он подключается](/a4/demo/) ## Дальше [Заголовок раздела «Дальше»](#дальше) * Как подключить у себя, с тремя шагами и обязательными условиями — [Редактор в браузере](/a4/webhost/). * Из чего состоит система — [Архитектура](/a4/architecture/). * Что можно вызвать сегодня — [справочник A4](/reference/a4/overview/).
# С чего начать пилот
> Что дать трём вашим специалистам, чтобы они решили — берём или нет: порядок чтения, пять вопросов для первой встречи и признаки удачного пилота.
## Как встроить работающий пример за 30 минут [Заголовок раздела «Как встроить работающий пример за 30 минут»](#как-встроить-работающий-пример-за-30-минут) *Цель первого запуска — не промышленная эксплуатация. Цель проще: открыть свой документ внутри своего приложения, изменить его, получить события и убедиться, что сохранённый файл в порядке.* | Время | Действие | Ожидаемый результат | | --------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 0–5 мин | Взять поверхность редактора и любой свой документ; выбрать, в каком окне будете показывать. | Известны три пути: сама программа, исходный документ и отдельный файл для результата. | | 5–10 мин | Запустить поверхность на свободном локальном порту. | В ответ приходят адрес страницы, разовый пропуск к ней и имя схемы с номером версии — отдельного поля с версией в строке запуска нет, номер стоит в самом имени. | | 10–15 мин | Создать окно просмотра веб-страниц и открыть в нём этот адрес. | Редактор загружается внутри вашего окна. | | 15–20 мин | Подключить обработчик сообщений от редактора. | Приходит событие `ready` — можно показать, что документ открыт. | | 20–25 мин | Изменить первый абзац и сохранить. | Приходят события `dirty` и `saved`, появляется файл с результатом. | | 25–30 мин | Открыть сохранённый файл заново и остановить поверхность. | Правка на месте; подписки, соединение и процесс закрыты и ничего за собой не оставили. | ### Чем запускается поверхность [Заголовок раздела «Чем запускается поверхность»](#чем-запускается-поверхность)
```bash
# $CLI — путь к процессору, см. «Установка и проверка»
"$CLI" surface --port 0 \
--doc /absolute/input.docx \
--save /absolute/output.docx
```
### Порядок вызовов на стороне хоста [Заголовок раздела «Порядок вызовов на стороне хоста»](#порядок-вызовов-на-стороне-хоста)
```plaintext
bootstrap = startSurface(input, output)
webview.onMessage(event => handleA4Event(event))
webview.open(bootstrap.url)
waitFor('ready')
// пользователь редактирует документ
waitFor('dirty'); requestSave(); waitFor('saved')
webview.close(); stopSurface()
```
## Что дать трём специалистам, чтобы решить — берём или нет [Заголовок раздела «Что дать трём специалистам, чтобы решить — берём или нет»](#что-дать-трём-специалистам-чтобы-решить--берём-или-нет) *Сначала общая картина и живой пример. Потом устройство изнутри, контракт сеанса и коммерческие условия.* ### Рекомендуемый порядок чтения [Заголовок раздела «Рекомендуемый порядок чтения»](#рекомендуемый-порядок-чтения) 1. Раздел [A4 — что это](/a4/overview/) и страницы рядом с ним: объекты и связи, разделение Rust и Web, варианты размещения и [быстрый старт](/a4/quickstart/). 2. **Спецификация архитектуры** — отдельный документ поставки (см. [Состав поставки](/platform/delivery/)): протокол, жизненный цикл, безопасность, версионирование, развёртывание и приёмка в промышленной эксплуатации. 3. **Evaluation Kit** — отдельный артефакт поставки (см. [Состав поставки](/platform/delivery/)): Surface Host, примеры хоста, три DOCX, схемы и типы, известные ограничения и быстрый старт. 4. **Матрица возможностей** — что доказано для выбранной платформы и на ваших реальных документах. 5. **План пилота** — один маршрут «открыть → изменить → событие → сохранить → открыть заново» с условиями, по которым его признают удачным. ### Пять вопросов для первой встречи [Заголовок раздела «Пять вопросов для первой встречи»](#пять-вопросов-для-первой-встречи) 1. Где должен работать редактор: на рабочих местах под Windows или macOS, в браузере, на телефонах или службой на ваших серверах? 2. Как ваше приложение передаёт документ и куда должно получить новую версию? 3. Какие десять пользовательских действий обязательны в первом пилоте? 4. Какие документы и какого размера ходят у вас на самом деле? 5. Что обязательно по единому входу, аудиту, сети, обновлениям, длительной поддержке версии и месту хранения данных? ### Критерий успешного первого пилота [Заголовок раздела «Критерий успешного первого пилота»](#критерий-успешного-первого-пилота) ### Что читать после этой страницы [Заголовок раздела «Что читать после этой страницы»](#что-читать-после-этой-страницы) Спецификация архитектуры встраиваемого редактора A4 — подробный документ для архитекторов и инженеров, к которому переходят, разобрав базовую модель продукта. Он выдаётся вместе с комплектом для оценки; на сайте те же темы разобраны по частям: [устройство редактора](/a4/architecture/), [жизненный цикл сеанса](/a4/lifecycle/), [контракт сеанса](/reference/a4/session-protocol/), [варианты размещения](/a4/deployment/) и [безопасность](/platform/security/). ### Что описано здесь, а что фиксируется отдельно [Заголовок раздела «Что описано здесь, а что фиксируется отдельно»](#что-описано-здесь-а-что-фиксируется-отдельно) Здесь описана проверенная архитектурная основа продукта. Публичный набор для разработчика, перечень команд, обязательства по доступности, лицензия и итоговый профиль возможностей фиксируются отдельными приложениями к пилоту и договору. ## Предлагаемый следующий шаг [Заголовок раздела «Предлагаемый следующий шаг»](#предлагаемый-следующий-шаг) * 60–90 минут: на одном реальном документе согласовать, какие объекты адресуются, какие события приходят и что показывается в контекстном меню. * Зафиксировать версию контракта взаимодействия и профиль возможностей пилота. * Собрать один сквозной пример: щелчок правой кнопкой по ячейке таблицы → ваше контекстное меню → типизированная правка → сохранение и открытие заново. * После приёмки расширять список поддержанных объектов частями, а не обещать весь набор возможностей Word одним заявлением.
# Попадание по координате, выделение и правка документа
> Перевод координаты щелчка в позицию документа, простая точечная правка с сохранением и предпочтительная типизированная транзакция.
## Координата → позиция документа [Заголовок раздела «Координата → позиция документа»](#координата--позиция-документа)
```bash
# $CLI — путь к процессору, см. «Установка и проверка»
"$CLI" hit-test-docx /data/contract.docx \
--x 100000 --y 100000 --format json
```
```json
{
"model_version": "fastdoc-d6-hit-test-snapshot-v2",
"hit_test_mode": "body-line-boxes-line-edge-caret-only",
"result_kind": "body-line-hit",
"page_index": 0,
"paragraph_index": 0,
"paragraph_location": "word/document.xml:body.block[0]",
"line_index": 0,
"caret_slot_index": 1,
"line_scene_x_milli_px": 96000,
"line_scene_y_milli_px": 96000
}
```
Что этот вызов умеет и чего не умеет Команда отвечает по строкам основного текста и возвращает позицию курсора в строке — это и означает `body-line-boxes-line-edge-caret-only` в поле `hit_test_mode`. Таблицы, врезки и графические объекты она не различает: щелчок по ячейке таблицы даст позицию в строке, а не саму ячейку. В постоянном сеансе команды попадания нет вовсе — этот вызов отдельный, разовый, и открытого сеанса не требует. Настольное приложение умеет больше: попадание в текст и в ячейку таблицы. Наружу эти режимы пока не выведены — прежде чем открывать их, нужно определить, какие виды объектов вправе трогать встроившее приложение. ## Простая точечная правка и сохранение [Заголовок раздела «Простая точечная правка и сохранение»](#простая-точечная-правка-и-сохранение) Команда `mutate` адресуется номером абзаца и смещением в нём — отсюда «точечная»: правка кладётся ровно в названное место и дальше него не выходит. Она ничего не проверяет: если между чтением и правкой документ изменился, правка ляжет по старым координатам. Годится для сценариев, где сеанс один и между чтением и записью ничего не происходит.
```json
{"id":"edit-1","cmd":"mutate",
"operation_kind":"insert-text","paragraph_index":0,
"offset":7,"text":"A4 "}
{"id":"save-1","cmd":"save","path":"/data/contract-edited.docx"}
```
## Предпочтительная типизированная транзакция [Заголовок раздела «Предпочтительная типизированная транзакция»](#предпочтительная-типизированная-транзакция) Команда `apply-text-transaction` несёт идентификаторы запроса, документа, сеанса и вида, а вместе с ними ожидаемые ревизии документа и выделения. Ядро сверяет их до изменения: если документ или выделение ушли вперёд, запрос отклоняется целиком, а не применяется наполовину. Поэтому для интеграции предпочтителен этот путь — см. [Типы типизированной правки](/reference/a4/typed-dto/).
```json
{
"id": "tx-1", "cmd": "apply-text-transaction",
"request": {
"request_id": "req-8", "document_id": "contract-42",
"session_id": "session-1", "view_id": "canvas-1",
"expected_revision": 42, "expected_selection_revision": 17,
"command": {"type":"insert_text",
"at":{"story_id":"body",
"node_id":"paragraph:body.block[0]",
"utf16_offset":7,"affinity":"downstream"},
"text":"A4 "}
}
}
```
# Host API: команды, события и агенты
> Минимальный устойчивый контракт для пилота: жизненный цикл, сохранение, вид, запросы состояния и команды; события ready, dirty, saved и error; маршрут агента.
Контракт для пилота, а не текущий API Ниже — состав контракта, который предлагается зафиксировать с вами перед пилотом. Из перечисленных методов сегодня в поверхности A4 есть `mount` и `unmount`; остальные имена — предмет договорённости, а не готовые вызовы. Что работает сейчас: [Rust Session Protocol](/reference/a4/session-protocol/) — 28 команд документного сеанса, и [Интерфейс поверхности в браузере](/reference/a4/surface-api/) — монтирование поверхности и события хоста. *Для встраивания нужен небольшой устойчивый контракт, а не доступ к сотням внутренних каналов настольного приложения.* ## Минимальный Host API для пилота [Заголовок раздела «Минимальный Host API для пилота»](#минимальный-host-api-для-пилота) | Группа | Примеры | Зачем нужна | | ----------------- | ---------------------------------------------------------------- | -------------------------------------------------- | | Жизненный цикл | `mount`, `unmount`, `open`, `close` | Создать сеанс и корректно его закрыть. | | Сохранение | `save`, `saveAs`, `checkpoint` | Вернуть подтверждённый результат в ваше хранилище. | | Вид | `focus`, `setReadOnly`, `setLocale`, `setTheme` | Вписать поверхность в окружение вашего приложения. | | Запросы состояния | `getVersion`, `getCapabilities`, `getStatus` | Узнать версию контракта и какие функции доступны. | | Команды | Правка, форматирование, отмена и повтор, действия рецензирования | Выполнить только разрешённые изменения. | ## События, которые получает ваше приложение [Заголовок раздела «События, которые получает ваше приложение»](#события-которые-получает-ваше-приложение) * **`ready`** — документ разобран и поверхность готова принимать согласованные действия. * **`dirty`** — изменение принято, появилась несохранённая ревизия. * **`saved`** — запись завершена, результат можно регистрировать как версию у себя. * **`error`** — операция не выполнена; приходит код и описание причины, в котором нет содержимого документа. * **`unmounted`** — поверхность убрана из вашего окна; после него сеанс уже не отвечает. ## Агенты и автоматизация [Заголовок раздела «Агенты и автоматизация»](#агенты-и-автоматизация) Агент не должен изображать пользователя щелчками. Порядок другой: открыть сеанс, узнать доступные возможности, запросить ограниченный участок документа, найти устойчивую ссылку на нужное место, предложить точечное изменение, применить его с указанием ожидаемой ревизии, проверить результат, сохранить и открыть файл заново. Каждый шаг такого маршрута можно записать в журнал, ограничить правилами и повторить — с щелчками по экрану ничего из этого невозможно.
# Установка и проверка
> Где внутри установленного A4 находится Rust-процессор fastdoc-cli, как проверить сборку и какие маршруты интеграции доступны.
A4 — готовое приложение для macOS, Windows и Linux. Вместе с интерфейсом внутри пакета поставляется нативный исполняемый файл fastdoc-cli. Для встраивания это внешний процессор документов: его можно запускать из своего настольного приложения, серверной обвязки или локального веб-хоста. ## Скачать и проверить [Заголовок раздела «Скачать и проверить»](#скачать-и-проверить) Актуальная сборка публикуется на [dl.plantype.ru](https://dl.plantype.ru/a4/release/latest/). Там же лежит `SHA256SUMS` — список контрольных сумм всех файлов выпуска. Сверьте скачанное до установки:
```bash
base=https://dl.plantype.ru/a4/release/latest
curl -fsSLO $base/SHA256SUMS
# имя файла с номером версии берём из того же списка
curl -fLO $base/$(awk '/arm64.dmg$/ {print $2; exit}' SHA256SUMS)
shasum -a 256 -c SHA256SUMS --ignore-missing
```
Для Windows замените образец имени на `arm64.msi` или `x64.msi`, для Linux — на `amd64.deb`, и считайте сумму средствами своей системы: `Get-FileHash` в PowerShell, `sha256sum -c` в Linux. Образец с `arm64.dmg` выбран не случайно: сборка A4 для macOS есть только под Apple Silicon, для Mac на процессоре Intel в каталоге выпуска её нет. Для Windows публикуются обе архитектуры, для Linux — только x86-64. Часть поставки для Linux подписана отдельно: рядом с `deb` и `AppImage` лежат файлы `.asc`, и там же открытый ключ, которым подпись проверяется. У пакета `rpm` отделённой подписи в каталоге выпуска нет — для него остаётся сверка контрольной суммы.
```bash
curl -fsSLO $base/fastoffices-signing-key.asc
gpg --import fastoffices-signing-key.asc
curl -fsSLO $base/$(awk '/amd64.deb.asc$/ {print $2; exit}' SHA256SUMS)
gpg --verify A4-*-amd64.deb.asc A4-*-amd64.deb
```
На macOS и Windows подпись проверяет сама система при установке: там работает платформенная подпись, а не эта. Образ для macOS подписан сертификатом Developer ID и заверен у Apple — `spctl` отвечает `accepted, source=Notarized Developer ID`. В сертификате стоит имя юридического лица, а не название продукта: `Developer ID Application: HEALTH INFORMATIC SECURITY SYSTEM SL (K58GV7E7X4)`. Так и должно быть — сверяйте с этим идентификатором команды, `K58GV7E7X4`. Установщики для Windows в текущем выпуске цифровой подписи не несут: система покажет предупреждение о неизвестном издателе. Проверяйте такой файл контрольной суммой из `SHA256SUMS` — она для него есть. * macOS
```bash
CLI="/Applications/A4.app/Contents/Resources/runtime/native/fastdoc-cli"
test -x "$CLI" && echo "процессор найден"
# Показать доступные одноразовые команды
"$CLI" --help | head -n 20
```
* Windows
```powershell
$CLI = "C:\Program Files\A4\resources\runtime\native\fastdoc-cli.exe"
if (Test-Path $CLI) { "процессор найден" }
# Показать доступные одноразовые команды
& $CLI --help | Select-Object -First 20
```
* Linux
```bash
CLI="/opt/A4/resources/runtime/native/fastdoc-cli"
test -x "$CLI" && echo "процессор найден"
# Показать доступные одноразовые команды
"$CLI" --help | head -n 20
```
Это путь для пакетов `deb` и `rpm`. Поставка `AppImage` работает без установки, каталога у неё нет — распакуйте образ и возьмите процессор изнутри:
```bash
./A4-….AppImage --appimage-extract
CLI="$PWD/squashfs-root/resources/runtime/native/fastdoc-cli"
```
## Архитектура внешней интеграции [Заголовок раздела «Архитектура внешней интеграции»](#архитектура-внешней-интеграции) **Ваша оболочка** → **сеанс JSONL или WebSocket** → **`fastdoc-cli`** → **DOCX и модель документа** | Маршрут | Когда использовать | Транспорт | Готовность | | ---------------- | ----------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------ | | Сеанс JSONL | Первый прототип, настольная или серверная обвязка | stdin и stdout | Готов; с него стоит начинать | | WebSocket | Веб- или настольная оболочка с асинхронными запросами | `ws://127.0.0.1` и пропуск | Готов | | Web Surface | Посмотреть встраивание на живом документе | HTTP и WebSocket сеанса | Работает, но правка ограничена первым абзацем; после перезагрузки страницы нужно переподключение | | Разовый вызов | Пакетные операции и автоматизация | аргументы процесса и JSON | Готов | | Electron preload | Только внутри собственного окна A4 | contextBridge и IPC | Внутренний контракт, для встраивания не предназначен | ## Что дальше [Заголовок раздела «Что дальше»](#что-дальше) Процессор на месте — дальше есть три пути, в зависимости от того, что нужно проверить первым. * **Убедиться, что API отвечает.** [Быстрый старт](/a4/quickstart/): открыть DOCX, изменить абзац командой и сохранить результат. * **Посмотреть встраивание вживую.** [Запуск Web Surface](/a4/surface/): поверхность открывается в браузере поверх того же процессора. Правка в ней ограничена первым абзацем — это демонстрация пути, не редактор. * **Понять формат запросов.** [Объекты запросов и ответов](/a4/envelope/) и полный перечень в [справочнике команд](/reference/a4/session-protocol/). Если что-то не запускается, начните с [диагностики](/a4/diagnostics/).
# Контракт взаимодействия для пилота
> Устойчивая ссылка на место в документе, минимальный набор методов и события, которые фиксируются до начала интеграции.
Это предлагаемый контракт, а не текущий API Ниже — контракт, который предлагается зафиксировать в спецификации и закрепить версиями. Сегодняшняя поверхность отдаёт наружу меньше: что именно из этого уже работает, сказано в [Готовности и границах](/a4/status/). Писать код по этой странице рано — по ней согласовывают, каким интерфейс станет. ## Семантическая цель [Заголовок раздела «Семантическая цель»](#семантическая-цель) Чтобы ваше приложение могло сказать «что сделать» вместо «куда щёлкнуть», нужна общая единица адресации: устойчивая ссылка на место в документе. `DocumentTarget` описывает именно её — вид элемента, его идентификатор и ревизию, в которой этот идентификатор действителен. Ссылок на DOM-узлы и координаты в этом контракте нет намеренно: они меняются при перерисовке.
```ts
export interface DocumentTarget {
kind: "paragraph" | "run" | "table" | "tableCell" |
"image" | "comment" | "field" | "header" | "footer" |
"hostObject";
nodeId: string; // стабильный ID в пределах revision
revision: number;
ancestors: Array<{ kind: string; nodeId: string }>;
capabilities: string[]; // read, editText, insertRow, openProperties...
hostData?: { type: string; id: string };
}
```
## Минимальный набор методов сеанса [Заголовок раздела «Минимальный набор методов сеанса»](#минимальный-набор-методов-сеанса) Набор методов, которого хватает для пилота: прочитать состояние документа, определить объект по точке, узнать, доступна ли команда, выполнить одну команду или пакет, сохранить. Всё, что меняет документ, возвращает подтверждение с новой ревизией — по нему вы понимаете, что изменение принято.
```ts
interface A4DocumentSession {
getDocumentSnapshot(options?: { depth: "semantic" }): Promise;
getProjection(request: ProjectionRequest): Promise;
hitTest(point: PagePoint): Promise;
getSelection(): Promise;
getCommandState(target: DocumentTarget): Promise;
execute(request: ExecuteRequest): Promise;
executeBatch(request: AtomicBatchRequest): Promise;
save(request: SaveRequest): Promise;
}
```
## События [Заголовок раздела «События»](#события) | Событие | Когда приходит | Обязательные поля | | -------------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | | `selectionChanged` | Изменилось выделение или положение курсора | выделение, ревизия, источник изменения | | `elementActivated` | Щелчок, двойной щелчок или активация с клавиатуры | вид жеста, объект, точка попадания, выделение | | `contextMenuRequested` | Перед показом контекстного меню | идентификатор запроса, объект, точка, встроенные пункты | | `contentChanged` | Команда изменила документ | новая ревизия, подтверждение, какие узлы изменились | | `commandStateChanged` | Команда стала доступна или недоступна, изменилась её отметка | объект, новые состояния | | dirtyChanged, saved, error | Изменения, сохранение и ошибки | ревизия, путь к файлу или описание ошибки | ## Дальше [Заголовок раздела «Дальше»](#дальше) * Как поверхность встраивается в ваше окно — [Встраивание Surface](/a4/embedding/). * Что приходит в событиях — [Host API](/a4/host-api/).
# Жизненный цикл документа
> Семь шагов от передачи документа до повторного открытия и основные объекты интерфейса: сеанс, команда, конверт результата, событие, профиль возможностей, артефакт.
*Интеграция строится вокруг сеанса, команд, ревизий и событий, а не вокруг прямого доступа к внутренней разметке редактора.* 1. Ваше приложение получает документ. Ваша система проверяет пользователя и передаёт байты разрешённого DOCX либо безопасный путь к файлу. 2. A4 открывает сеанс. Ядро разбирает файл, строит основную модель документа, профиль доступных возможностей и первую ревизию. 3. Поверхность показывает документ. Пользователь видит страницы и доступные команды, а ваше приложение получает событие готовности. 4. Приходит намерение. Команда несёт тип операции, устойчивую ссылку на место в документе и ожидаемую ревизию; оболочка сама разметку не правит. 5. Ядро применяет или отклоняет. При успехе появляется новая ревизия и признак несохранённых изменений. Отказ не оставляет документ изменённым наполовину. 6. Ваше приложение сохраняет результат. Ядро записывает изменения в файл и отдаёт DOCX, а вы заводите версию и запись в журнале. 7. Повторное открытие подтверждает. Сохранённый файл открывается заново в новом процессе — это доказывает, что изменение дошло до диска, а не осталось на экране. ## Основные объекты API [Заголовок раздела «Основные объекты API»](#основные-объекты-api) | Объект | Назначение | | ---------------------- | ---------------------------------------------------------------------------------------------------------------- | | Сеанс | Изолированный жизненный цикл одного открытого документа. | | Команда, или намерение | Типизированное действие: что сделать, с чем и при какой ожидаемой ревизии. | | Конверт результата | Успех, предупреждения, явный отказ с причиной, диагностика и идентификатор, по которому ответ связан с запросом. | | Событие | Готовность, несохранённые изменения, сохранение, ошибка и прочие смены состояния. | | Профиль возможностей | Перечень доступных команд и ограничений — без умолчаний. | | Артефакт | Байты DOCX, контрольная точка или диагностика — всё, чем распоряжается ваше приложение. | ## Дальше [Заголовок раздела «Дальше»](#дальше) * Как устроены запросы и ответы — [Объекты запросов и ответов](/a4/envelope/). * Что продукт делает с документом — [карта возможностей](/a4/capabilities/). * Границы сегодня — [Готовность и границы](/a4/status/).
# A4 — что это
> Встраиваемый DOCX-редактор для документарных систем: что A4 добавляет вашему продукту, что остаётся на вашей стороне и как это выглядит для сотрудника.
**A4** — документная часть платформы FastOffice: DOCX-совместимый редактор и процессор документов, который встраивается в ваше приложение, оставляя документную истину в ядре на Rust. *Свой продукт заменять не нужно: в него добавляется документный процессор и готовая пользовательская поверхность.* ## Что уже умеет ваша система [Заголовок раздела «Что уже умеет ваша система»](#что-уже-умеет-ваша-система) Карточка документа, пользователи и роли, маршруты согласования, версии, электронная подпись, архив, аудит, интеграции с вашими системами — перевозок, ресурсов предприятия, работы с клиентами — и правила хранения остаются на вашей стороне. ## Что добавляет A4 [Заголовок раздела «Что добавляет A4»](#что-добавляет-a4) * **Открыть** — получить DOCX от вашей системы и открыть управляемый сеанс документа. * **Показать** — отрисовать страницы и дать пользователю привычную поверхность редактирования. * **Изменить** — принять действия пользователя или агента как ограниченные команды, а не произвольную правку ZIP/XML. * **Сохранить** — собрать корректный DOCX и вернуть файл или подтверждённый результат обратно в вашу систему. * **Сообщить** — передать события ready, dirty, saved, error и другие согласованные состояния. ## Как это выглядит для сотрудника [Заголовок раздела «Как это выглядит для сотрудника»](#как-это-выглядит-для-сотрудника) Сотрудник открывает документ из привычной карточки. Редактор появляется внутри того же окна и под тем же брендом. Пользователь редактирует согласованные элементы, а при сохранении новая версия возвращается в существующий контур хранения и аудита — без ручной выгрузки в отдельный Word и обратной загрузки. ## С какими файлами работает [Заголовок раздела «С какими файлами работает»](#с-какими-файлами-работает) Основной формат — DOCX: его открывает и правит сеанс, им же документ сохраняется обратно. Остальные форматы обслуживаются отдельными вызовами процессора, а не сеансом: они переводят файл в документ и обратно. PDF — единственный, который идёт только на выход: печатать в него можно, открывать из него нельзя. | Формат | В документ | Из документа | Чем делается | | ------------------ | ---------- | ------------ | --------------------------------------------------------------------------------------------------- | | DOCX | Да | Да | Основной формат: открыть, править и сохранить обратно тем же сеансом — `open`, `mutate`, `save` | | PDF | — | Да | `export-pdf`, `export-pdf-embedded` | | ODT | Да | Да | `import-odt`, `export-odt`; правимая копия — `create-editable-odt-copy`, `mutate-editable-odt-copy` | | RTF | Да | Да | `import-rtf`, `export-rtf` | | HTML | Да | Да | `import-html`, `export-html` | | Обычный текст | Да | Да | `import-txt`, `export-txt` | | EPUB | Да | Да | `import-epub`, `export-epub` | | FB2 | Да | Да | `import-fb2`, `export-fb2` | | XODT — плоский ODT | Да | Да | `import-xodt`, `export-xodt` | Плоский ODT — тот же OpenDocument, но одним файлом XML вместо zip-пакета: удобно, когда документ хранится в системе контроля версий или его правит внешний инструмент. У таблиц то же самое называется FODS. Отдельно есть конвертация файла целиком — `convert-doc` — и каталога разом: `convert-doc-batch` с указанием расширения и, при нужде, обходом вложенных папок. Про PDF/A-1b — формат архивного хранения Выгрузка обычного PDF работает, в том числе со встроенными шрифтами (`export-pdf-embedded`). Полного PDF/A-1b продукт пока не обещает: команда `preflight-pdfa-1b` проверяет документ на готовность и честно отвечает, что выгрузка в PDF/A ещё закрыта, а `validate-pdfa-1b` проводит её через внешнюю программу проверки, которую вы указываете сами. Если архивный формат обязателен по вашим правилам хранения, это условие пилота, а не готовая возможность. Полный перечень вызовов — в [справочнике](/reference/a4/overview/). Для таблиц то же самое собрано матрицей: [что можно с каждым форматом](/reference/f1/formats/). ## Что ещё делает процессор, кроме правки [Заголовок раздела «Что ещё делает процессор, кроме правки»](#что-ещё-делает-процессор-кроме-правки) Документ в работе редко просто правят. Ниже — вызовы, которые закрывают остальную часть договорного оборота; все они одноразовые, как и работа с форматами, и сеанса для них не нужно. | Задача | Что происходит | Вызов | | ------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | Сличить две версии | Из исходного и изменённого файла собирается третий, где расхождения показаны исправлениями, с указанием автора и даты | `review-compare-combine-docx --mode compare` | | Свести правки рецензентов | Правки из нескольких файлов сводятся в один документ | `review-compare-combine-docx --mode combine` | | Разослать по списку | Шаблон и список получателей в CSV дают пачку готовых документов; можно взять только часть списка | `mail-merge-business-docx --source … --output-dir …` | | Посмотреть макросы, не запуская | Из файла с макросами выдаётся их состав — без выполнения | `inspect-vba` | | Проверить защиту файла | Показывает, чем документ защищён | `inspect-protection` | | Обновить оглавление | Перестраивает оглавление или список иллюстраций, либо убирает оглавление | `update-toc-docx`, `update-table-of-figures-docx`, `remove-toc-docx` | Это лишь часть: колонтитулы, разделы, водяные знаки, поля форм, библиография, печать и рассылка собраны по задачам на отдельной странице — [что A4 умеет с документом](/a4/capabilities/). Термины, которые встречаются во всех разделах — сеанс, ревизия, проекция, явный отказ, — собраны в [глоссарии](/start/glossary/). ## Word-класс внутри вашего продукта [Заголовок раздела «Word-класс внутри вашего продукта»](#word-класс-внутри-вашего-продукта) | Слой | Ответственность | Что это даёт | | ------------------------ | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | Ваше приложение | Окно, бренд, вход, хранилище, процессы и их жизненный цикл. | Продукт остаётся вашим продуктом. | | Адаптер на вашей стороне | Запускает процесс редактора, читает параметры подключения, передаёт события окна, корректно завершает работу. | Встраивание остаётся под вашим управлением. | | Поверхность редактора | Полотно документа и элементы управления, ввод пользователя, вызовы к вашему приложению и события наружу. | Сотрудник работает с документом внутри вашего окна. | | Служба поверхности | Отдаёт файлы редактора и параметры подключения, держит защищённый канал и один сеанс документа. | Работает на самой машине и слушает только локальный адрес — наружу ничего не открывается. | | Сеанс документа | Команды, выделение, история правок, модель документа, вёрстка и сохранение. | Один документ — один сеанс, с общей историей и общими событиями. | | Ядро и формат документа | Разбор файла, смысловая модель, сохранение частей, которые редактор не показывает, вёрстка и запись обратно. | Содержимое файла определяет формат Word, а не разметка на экране: сохранить и открыть заново можно без потерь. | ### Что получает пользователь в Word-коридоре [Заголовок раздела «Что получает пользователь в Word-коридоре»](#что-получает-пользователь-в-word-коридоре) * Открытие согласованных DOCX-документов внутри вашего приложения без отдельного окна A4. * Правка, разбивка на страницы, сохранение, повторное открытие и проверка того, что документ остался собой. * В базовом наборе — события `ready`, `dirty`, `saved`, `error` и `unmounted`; для поставки под вашей маркой к ним добавляются события выделения, содержимого и состояния команд. * Оформление под ваш бренд: тема, язык, режим только для чтения, переменные оформления и собственная оболочка вместо нашей. * Работа без интерфейса — для пакетной обработки, служб, конвейеров и действий агентов. ### Граница честного обещания [Заголовок раздела «Граница честного обещания»](#граница-честного-обещания) * Openability не равна fidelity: capability profile должен отдельно фиксировать чтение, отображение, редактирование, сохранение и preservation по семействам OOXML. * Полная совместимость с Microsoft Word, Office.js и выполнение VBA-макросов не входят без отдельного соглашения. * Production-поставка требует acceptance по платформам, save/reopen, lifecycle, security и packaging.
# Быстрый старт: первый JSONL-сеанс
> За 10–15 минут: запустить fastdoc-cli, открыть DOCX, изменить первый абзац командой mutate, сохранить результат и проверить его без GUI.
Это самый короткий путь от установленного A4 до доказанного цикла «команда → изменение модели → запись DOCX → повторное чтение». Что нужно до начала Команды ниже выполняются в подготовленной папке `~/Desktop/A4-API-first-run`. Её вместе со скриптами `a4-api-demo.mjs` и `start-web-demo.sh` даёт **Evaluation Kit** — отдельный артефакт поставки (см. [Состав поставки](/platform/delivery/)). В установщик A4 он не входит и с [dl.plantype.ru](https://dl.plantype.ru/a4/release/latest/) не скачивается: его запрашивают у поставщика. **Если Evaluation Kit ещё не получен**, установленного A4 достаточно, чтобы пройти тот же путь вручную: `fastdoc-cli` лежит внутри приложения (см. [Установка и проверка](/a4/install/)), формат запросов описан в [Объектах запросов и ответов](/a4/envelope/), полный перечень команд — в [справочнике](/reference/a4/session-protocol/). Пример полностью выписанного скрипта есть у F1: [проверенный скрипт](/f1/reference-script/). ## Первый настоящий вызов: JSONL-сеанс [Заголовок раздела «Первый настоящий вызов: JSONL-сеанс»](#первый-настоящий-вызов-jsonl-сеанс) JSONL — самый простой контракт для первой интеграции: один JSON-запрос в строке stdin, один JSON-ответ в строке stdout. Rust хранит состояние открытого документа между командами. ### Подготовка [Заголовок раздела «Подготовка»](#подготовка) 1. Откройте терминал. 2. Проверьте Node.js командой `node --version` — нужна версия 22 LTS или новее. 3. Перейдите в подготовленную папку на рабочем столе. 4. Запустите демонстрационный клиент, передав явные пути входного и выходного файла. * macOS
```bash
cd ~/Desktop/A4-API-first-run
node --version
node a4-api-demo.mjs \
"/Applications/A4.app/Contents/Resources/app/fixtures/docx/simple_text.docx" \
"$PWD/api-demo-result.docx"
```
* Windows
```powershell
cd $HOME\Desktop\A4-API-first-run
node --version
node a4-api-demo.mjs `
"C:\Program Files\A4\resources\app\fixtures\docx\simple_text.docx" `
"$PWD\api-demo-result.docx"
```
* Linux
```bash
cd ~/Desktop/A4-API-first-run
node --version
node a4-api-demo.mjs \
"/opt/A4/resources/app/fixtures/docx/simple_text.docx" \
"$PWD/api-demo-result.docx"
```
### Что делает скрипт [Заголовок раздела «Что делает скрипт»](#что-делает-скрипт) 1. Запускает `fastdoc-cli serve` из пакета приложения. 2. Читает приветствие протокола и список из 28 команд. 3. Отправляет `ping`, затем `open` и `model`. 4. Командой `mutate` вставляет текст «API DEMO:» в начало первого абзаца. 5. Читает `status` и сохраняет копию командой `save`. 6. Завершает процесс командой `shutdown`. ### Фактический вывод проверенного прогона [Заголовок раздела «Фактический вывод проверенного прогона»](#фактический-вывод-проверенного-прогона)
```plaintext
Connected: fastdoc.session-serve.v1
Commands available: 28
Ping: true
Session: rust-session-20320-1
Before: This is a sample word document. It has two pages, but no headers or footers.
After: API DEMO: This is a sample word document. It has two pages, but no headers or footers.
Revision: 1; dirty: true
Saved: /Users/tester/Desktop/A4-API-first-run/api-demo-result.docx
```
## Открыть результат и увидеть изменение [Заголовок раздела «Открыть результат и увидеть изменение»](#открыть-результат-и-увидеть-изменение) * macOS
```bash
open -a A4 ~/Desktop/A4-API-first-run/api-demo-result.docx
```
* Windows
```powershell
& "C:\Program Files\A4\A4.exe" "$HOME\Desktop\A4-API-first-run\api-demo-result.docx"
```
* Linux
```bash
/opt/A4/a4 ~/Desktop/A4-API-first-run/api-demo-result.docx
```
В начале первого абзаца должен появиться префикс API DEMO:. Это подтверждает полный цикл: команда пришла в Rust, изменила модель документа, записалась в DOCX и была прочитана графическим редактором. Рисунок 1. Реальный результат API-прогона в установленной A4: первый абзац начинается с «API DEMO:». Скриншот сделан 26 августа 2026. ### Проверить сохранённый файл без GUI [Заголовок раздела «Проверить сохранённый файл без GUI»](#проверить-сохранённый-файл-без-gui) * macOS
```bash
CLI="/Applications/A4.app/Contents/Resources/runtime/native/fastdoc-cli"
"$CLI" model-docx \
"$HOME/Desktop/A4-API-first-run/api-demo-result.docx" \
--format json
```
* Windows
```powershell
$CLI = "C:\Program Files\A4\resources\runtime\native\fastdoc-cli.exe"
& $CLI model-docx `
"$HOME\Desktop\A4-API-first-run\api-demo-result.docx" `
--format json
```
* Linux
```bash
CLI="/opt/A4/resources/runtime/native/fastdoc-cli"
"$CLI" model-docx \
"$HOME/Desktop/A4-API-first-run/api-demo-result.docx" \
--format json
```
## Что дальше [Заголовок раздела «Что дальше»](#что-дальше) * Разобрать формат запроса и ответа — [Объекты запросов и ответов](/a4/envelope/). * Те же команды, но по WebSocket — [Тот же контракт через WebSocket](/a4/websocket/). * Показать документ пользователю — [Запуск Web Surface](/a4/surface/), а затем [Встраивание Surface](/a4/embedding/). * Полный перечень команд сеанса — [справочник](/reference/a4/session-protocol/). * Что-то не запускается — [Диагностика и контрольный список](/a4/diagnostics/).
# Рецепты интеграции
> Реакция на щелчок и двойной щелчок, контекстное меню для ячейки таблицы, выполнение внешней команды и полный сценарий транспортной системы от события до DOCX.
Это предлагаемый API, а не текущий Примеры ниже показывают, как предлагается устроить реакцию на действия пользователя: события элементов, поставщики контекстного меню и отправку внешних команд. Такого API в текущем контракте A4 **нет** — писать по этим примерам рабочий код сегодня нельзя. Что доступно сейчас: [Rust Session Protocol](/reference/a4/session-protocol/) — 28 команд документного сеанса, и [Интерфейс поверхности в браузере](/reference/a4/surface-api/) — встраивание поверхности и события хоста. Форма будущего профиля описана в [контракте взаимодействия](/a4/interaction/); она фиксируется договором до начала интеграции. ## Примеры: щелчок, двойной щелчок и меню [Заголовок раздела «Примеры: щелчок, двойной щелчок и меню»](#примеры-щелчок-двойной-щелчок-и-меню) ### Как отвечать на щелчок и двойной щелчок [Заголовок раздела «Как отвечать на щелчок и двойной щелчок»](#как-отвечать-на-щелчок-и-двойной-щелчок)
```js
editor.interactions.on("elementActivated", async (event) => {
const { gesture, target } = event;
if (gesture === "click" && target.kind === "comment") {
hostSidebar.openComment(target.nodeId);
}
if (gesture === "doubleClick" &&
target.hostData?.type === "shipment") {
await hostApp.openShipment(target.hostData.id);
}
});
```
### Контекстное меню для ячейки таблицы [Заголовок раздела «Контекстное меню для ячейки таблицы»](#контекстное-меню-для-ячейки-таблицы)
```js
editor.contextMenu.registerProvider("transport-suite", async (ctx) => {
if (ctx.target.kind !== "tableCell") return [];
return [
{ id: "transport.openCargo", label: "Открыть карточку груза",
enabled: Boolean(ctx.target.hostData?.id) },
{ id: "transport.bindRoute", label: "Привязать к маршруту",
enabled: !ctx.readOnly },
{ type: "separator" },
{ id: "a4.table.insertRowBelow", label: "Вставить строку ниже",
command: { type: "insert_table_row", position: "below" } }
];
});
```
### Выполнение внешней команды [Заголовок раздела «Выполнение внешней команды»](#выполнение-внешней-команды)
```js
editor.commands.register("transport.bindRoute", async (ctx) => {
const route = await hostUi.chooseRoute();
if (!route) return;
return ctx.session.execute({
operationId: crypto.randomUUID(),
expectedRevision: ctx.revision,
target: ctx.target,
command: { type: "replace_target_text", text: route.displayName }
});
});
```
## Сценарий транспортной системы от события до DOCX [Заголовок раздела «Сценарий транспортной системы от события до DOCX»](#сценарий-транспортной-системы-от-события-до-docx) ### Разбор по шагам: щелчок правой кнопкой по строке груза [Заголовок раздела «Разбор по шагам: щелчок правой кнопкой по строке груза»](#разбор-по-шагам-щелчок-правой-кнопкой-по-строке-груза) * **Пользователь щёлкает правой кнопкой.** Поверхность переводит координаты щелчка в координаты страницы документа и спрашивает ядро, во что попали. * **Ядро отвечает, что под указателем.** Например ячейка таблицы: её устойчивый идентификатор, номера строки и столбца, объемлющие элементы и номер ревизии, в которой всё это верно. * **Редактор спрашивает ваши поставщики пунктов меню**, передавая им этот контекст — и только его, а не ссылку на элемент разметки. * **Ваш поставщик добавляет свои действия.** «Открыть карточку груза» и «Привязать к маршруту» встают рядом с разрешёнными встроенными пунктами. * **Пользователь выбирает маршрут.** Ваш интерфейс возвращает его идентификатор; команда на изменение документа собирается отдельно — выбор в вашей системе и правка в документе остаются разными действиями. * **Ядро принимает команду или отклоняет её.** Ожидаемая ревизия и перечень допустимых действий не дают применить правку к устаревшему документу или к объекту, для которого она не предусмотрена. * **Поверхность обновляется.** События `contentChanged` и `commandStateChanged` приносят новый номер ревизии, и перерисовываются только затронутые страницы, а не весь документ. * **Сохранение и повторное открытие подтверждают результат.** Документ сохраняется отдельным файлом либо публикуется разом, а затем читается заново — так проверяется, что на диск легло именно то, что видел пользователь. ### Как выглядит переданный контекст [Заголовок раздела «Как выглядит переданный контекст»](#как-выглядит-переданный-контекст)
```json
{
"schema": "a4.interaction-context.v1",
"gesture": "contextMenu",
"revision": 42,
"readOnly": false,
"target": {
"kind": "tableCell",
"nodeId": "body.block[18].row[2].cell[1]",
"ancestors": [{"kind":"table","nodeId":"body.block[18]"}],
"capabilities": ["read","editText","insertRow"],
"hostData": {"type":"shipment","id":"SHP-009173"}
},
"hit": {"pageIndex":3,"xMilliPx":412000,"yMilliPx":286000}
}
```
### Что приходит при устаревшей ревизии [Заголовок раздела «Что приходит при устаревшей ревизии»](#что-приходит-при-устаревшей-ревизии)
```json
{
"ok": false,
"error": {
"code": "revision_conflict",
"expectedRevision": 42,
"actualRevision": 43,
"retry": "refresh_target_and_command_state"
}
}
```
# Ответственность и безопасность
> Что относится к вам, что к A4 и что согласуется совместно; где можно настраивать под свой бренд, какие короткие пути запрещены и как устроена минимальная модель безопасности.
*Поставка под своим брендом даёт свободу в оформлении и процессах, но не позволяет завести вторую версию документа — в JavaScript или в коде расширений.* | За что отвечаете вы | За что отвечает A4 | Согласуется совместно | | ---------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------- | | Бренд, навигация, карточка документа | Ядро на Rust: модель, вёрстка, сцена, запись в файл | Профиль возможностей и набор команд | | Пользователи, вход, роли | Жизненный цикл поверхности и события документа | Требования безопасности и журналирование | | Хранилище, версии, сроки хранения, аудит | Поддержанная часть формата DOCX | Форматы, размеры файлов, набор документов для приёмки | | Смежные системы и процессы | Явные отказы и защита от правки по устаревшей ревизии | Упаковка, обновления, срок поддержки версии, обязательства по доступности | | Распространение продукта | Происхождение сборки и версия контракта | Порядок пилота и модель поддержки | ## Что можно менять под свой бренд [Заголовок раздела «Что можно менять под свой бренд»](#что-можно-менять-под-свой-бренд) Название, логотип, внешняя навигация, тема, язык интерфейса, режим только для чтения, разрешённые переменные оформления, видимость согласованных команд, справка и отраслевые панели вашего приложения. ## Запрещённые короткие пути [Заголовок раздела «Запрещённые короткие пути»](#запрещённые-короткие-пути) Прямая запись в файл документа из поверхности; попытка изменить документ правкой того, что нарисовано на экране; выдача внутренних каналов настольного приложения за публичный интерфейс; обращение к сети или чтение файлов без объявленной возможности; запись в журнал пропусков, содержимого документа и секретов; тихий обходной путь, меняющий смысл результата. ## Минимальная модель безопасности [Заголовок раздела «Минимальная модель безопасности»](#минимальная-модель-безопасности) Служба поверхности слушает только локальный адрес — снаружи к ней не подключиться. Каждый её запуск получает новый случайный пропуск длиной 256 бит, и действует он ровно столько, сколько работает сама поверхность. Документы не покидают ваш контур, пока вы сами не подключите внешний сервис. Ошибка приходит явным отказом с причиной, и изменение не оставляет наполовину записанный файл. Промышленная поставка фиксирует контрольные суммы, версию, платформу и архитектуру, порядок работы с уязвимостями и правила обновления. ## Дальше [Заголовок раздела «Дальше»](#дальше) * Как это проверяется до договора — [Пилот и приёмка](/platform/pilot/). * Что настраивается под ваш бренд — [Настройка под свой бренд](/platform/white-label/).
# Готовность и границы
> Граница между реализованным и пилотным, список договорённостей до старта и готовая формулировка ответа внешней команде.
Ниже — граница между тем, что работает сегодня, и тем, что фиксируется контрактом пилота. Что продукт делает с документом по задачам — колонтитулы, оглавления, сравнение версий, печать — собрано в [карте возможностей](/a4/capabilities/). ## Что доступно сейчас, а что входит в пилот [Заголовок раздела «Что доступно сейчас, а что входит в пилот»](#что-доступно-сейчас-а-что-входит-в-пилот) | Возможность | Состояние | Как использовать или при каком условии | | --------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------- | | Смысловая структура | Доступно | команда `model` в сеансе или `model-docx` отдельным вызовом | | Вёрстка, страницы, сцена | Доступно | `compose`, `paginate`, `print`, `print-window`, `print-delta` | | Рецензирование и стили | Доступно | проекции `review` и `style` | | Правка текста | Доступно | `mutate` или `apply-text-transaction` | | Выделение, история, сохранение | Доступно | `set-selection`, отмена и повтор, `save` и контрольные точки | | Отдельный поиск попадания | В описанных границах | Строка основного текста, её край и позиция курсора; остальные типы объектов добавляются постепенно | | Поиск попадания в текст и таблицы внутри приложения | Работает, но контрактом не является | Не обращаться напрямую: маршрут внутренний и меняется без предупреждения | | Контекстные меню текста и таблиц | Работает, но контрактом не является | Внутри продукта работает; способ подключить своё меню ещё не зафиксирован | | События Surface Host | Доступно в описанном наборе | `ready`, `dirty`, `saved`, `error`, `unmounted` | | События выделения, содержимого и команд | Фиксируется в пилоте | Нужно договориться о составе, порядке доставки, повторах и жизненном цикле | | Своё контекстное меню | Фиксируется в пилоте | Описательный список пунктов и типизированная отправка команд | | Прямая правка разметки страницы или файла | Запрещено | Только через команды сеанса и запись файла ядром | ### Что подтверждает реализуемость [Заголовок раздела «Что подтверждает реализуемость»](#что-подтверждает-реализуемость) * **Попадание в текст.** По координате щелчка возвращается страница, строка, позиция в строке и сам текст. * **Попадание в ячейку таблицы.** Возвращается страница, блок, строка и колонка, включая вложенные таблицы. * **Контекстные меню.** Отдельные маршруты обслуживают текст и таблицы. * **Двойной щелчок.** Обслуживаются текст, таблицы, границы и автоподбор ширины. ## Что зафиксировать до начала интеграции [Заголовок раздела «Что зафиксировать до начала интеграции»](#что-зафиксировать-до-начала-интеграции) ### Профиль возможностей [Заголовок раздела «Профиль возможностей»](#профиль-возможностей) | Решение | Что зафиксировать | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | | Какие объекты адресуются | Абзац, участок форматирования, таблица, ячейка, изображение, примечание, поле и ваши собственные объекты | | События | Щелчок, двойной щелчок, вызов контекстного меню, активация с клавиатуры; порядок доставки и возможность отменить | | Контракт меню | Сколько ждать ответа вашей стороны, в каком порядке объединяются пункты, какие идентификаторы зарезервированы, перевод, значки и разделители | | Контракт команд | Ожидаемая ревизия, ключ защиты от повторного применения, неделимый пакет команд, состав подтверждения и перечень ошибок | | Сохранение | Куда сохранять, контрольные точки, что делать при конфликте, приёмка по кругу «сохранил — открыл заново» | | Безопасность | Токен локального доступа, выдача возможностей, что пишется в журнал и что из него вырезается, отсутствие обходных путей мимо контракта | | Версионирование | Версии схем, срок совместимости, порядок снятия возможностей с поддержки | | Платформы | Какой профиль принимается в первом пилоте: настольный, внутри окна WebView, серверный или мобильный | ### Минимальный набор приёмочных проверок [Заголовок раздела «Минимальный набор приёмочных проверок»](#минимальный-набор-приёмочных-проверок) * Открыть реальный документ и получить смысловой снимок, не потеряв те части файла, которые редактор не разбирает: разметка Word (OOXML) переносится в новый файл как есть. * Щелчок по тексту и по ячейке даёт правильный объект документа и выделение. * Двойной щелчок выполняет согласованное действие один раз и не меняет документ сам по себе. * Контекстное меню меняется в зависимости от объекта и доступных возможностей и открывается с клавиатуры. * Команда с актуальной ревизией применяется; команда с устаревшей ревизией отклоняется с сообщением вида «stale revision: expected 5, received 3». * Отмена и повтор восстанавливают результат; сохранение и повторное открытие подтверждают, что изменения дошли до файла. * Закрытие освобождает слушателей, соединение WebSocket и процесс ядра — зависших процессов не остаётся. ### Что понадобится с вашей стороны [Заголовок раздела «Что понадобится с вашей стороны»](#что-понадобится-с-вашей-стороны) * 5–10 реальных документов и перечень элементов, по которым нужны действия. * Таблицу «тип элемента → щелчок → двойной щелчок → пункты меню → команда документа». * Требования к настольной, серверной и встраиваемой работе, к работе без сети, к хранилищу, учётным записям и аудиту. * Список ваших объектов и способ связать их с узлами документа — без второй модели файла на вашей стороне.
# Запуск Web Surface
> Запустить готовую поверхность редактора локально, открыть DOCX в браузере, изменить абзац и сохранить результат.
Что нужно до начала Команды ниже выполняются в подготовленной папке `~/Desktop/A4-API-first-run`. Её вместе со скриптами `a4-api-demo.mjs` и `start-web-demo.sh` даёт **Evaluation Kit** — отдельный артефакт поставки (см. [Состав поставки](/platform/delivery/)). В установщик A4 он не входит и с [dl.plantype.ru](https://dl.plantype.ru/a4/release/latest/) не скачивается: его запрашивают у поставщика. **Если Evaluation Kit ещё не получен**, установленного A4 достаточно, чтобы пройти тот же путь вручную: `fastdoc-cli` лежит внутри приложения (см. [Установка и проверка](/a4/install/)), формат запросов описан в [Объектах запросов и ответов](/a4/envelope/), полный перечень команд — в [справочнике](/reference/a4/session-protocol/). Пример полностью выписанного скрипта есть у F1: [проверенный скрипт](/f1/reference-script/). Surface — готовая локальная страница редактора поверх того же Rust-процессора. Это самый быстрый способ увидеть встраивание: запускаете локальный host и помещаете его URL в WebView или iframe. Что поверхность умеет сегодня Она объявляет ровно четыре возможности: открыть документ, изменить **первый абзац**, сохранить и отдавать события вашему приложению. Это проверяемая демонстрация пути «открыл → изменил → сохранил», а не полноценный редактор в браузере: произвольная правка, выделение и контекстное меню наружу пока не выведены. Полный редактор в браузере — [другой путь](/a4/webhost/): там работает то же настольное приложение, а документ считает ядро на сервере. Поверхность поднимается одной командой процессора. Путь к нему зависит от системы, остальные аргументы одинаковы: `--doc` — какой документ открыть, `--save` — куда сохранять (по умолчанию в тот же файл). * macOS
```bash
CLI="/Applications/A4.app/Contents/Resources/runtime/native/fastdoc-cli"
"$CLI" surface --doc ~/Desktop/A4-API-first-run/contract.docx \
--save ~/Desktop/A4-API-first-run/api-demo-web-result.docx
```
* Windows
```powershell
$CLI = "C:\Program Files\A4\resources\runtime\native\fastdoc-cli.exe"
& $CLI surface --doc "$HOME\Desktop\A4-API-first-run\contract.docx" `
--save "$HOME\Desktop\A4-API-first-run\api-demo-web-result.docx"
```
* Linux
```bash
CLI="/opt/A4/resources/runtime/native/fastdoc-cli"
"$CLI" surface --doc ~/Desktop/A4-API-first-run/contract.docx \
--save ~/Desktop/A4-API-first-run/api-demo-web-result.docx
```
В ответ процессор печатает строку JSON с готовым адресом — откройте его в браузере. Адрес содержит одноразовый пропуск, поэтому подставлять ничего не нужно. В Evaluation Kit для того же есть готовый скрипт `start-web-demo.sh`; он запускает ту же команду с заранее заданными портом и пропуском. 1. Дождитесь загрузки документа. 2. Поставьте курсор в первый абзац и добавьте текст. 3. Нажмите кнопку **Save DOCX** — она подписана по-английски, как и в самой поверхности. 4. Проверьте файл, указанный в `--save`. 5. Остановите поверхность сочетанием Control+C в том же окне терминала. ## Что дальше [Заголовок раздела «Что дальше»](#что-дальше) Поверхность запущена и работает. Дальше: * встроить её в свой продукт — [Встраивание Surface](/a4/embedding/); * точные имена событий, схем и параметров — [Интерфейс поверхности в браузере](/reference/a4/surface-api/).
# Редактор в браузере: встраивание через iframe
> Как встроить редактор A4 в веб-приложение: два адреса вашего API, три шага до рабочего редактора и что происходит внутри.
Если ваш продукт живёт в браузере, редактор можно встроить в него без установки чего-либо пользователю: страница редактора открывается внутри вашего приложения, а документ считает ядро на сервере. ## Что нужно от вашего приложения [Заголовок раздела «Что нужно от вашего приложения»](#что-нужно-от-вашего-приложения) Два обязательных адреса вашего API и третий, необязательный. Если у вас есть личный кабинет и файлы, оба обязательных уже существуют. | Адрес | Зачем | Что должен отвечать | | --------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | «кто я» | Узнать пользователя | Принимает куки или заголовок браузера, отвечает успехом и идентификатором пользователя, иначе — отказом | | «файл по идентификатору» | Отдать документ | По тому же удостоверению отдаёт DOCX только тому, у кого есть право. Можно отвечать и подписанной ссылкой — редактор по ней перейдёт | | «создать файл» *(необязательный)* | Открыть второму его копию | Принимает байты DOCX, в ответе — идентификатор нового файла | Своей базы, учётных записей и паролей у редактора нет: пользователей и права знаете вы. ## Три шага [Заголовок раздела «Три шага»](#три-шага) ### Запустить [Заголовок раздела «Запустить»](#запустить) Управляющий процесс поднимается одной командой и слушает только локальный интерфейс. Первой строкой он печатает готовность.
```bash
FASTDOC_CLI_BIN="$CLI" \
WEBHOST_WS_DIR=/opt/fastdoc/webhost \
node webhost/cabin-manager.mjs \
--template /opt/fastdoc/blank.docx \
--docs-dir /opt/fastdoc/cabins \
--port 8090 \
--auth-url 'https://ваш-хост/api/me' \
--attach-url 'https://ваш-хост/api/files/{id}/content'
```
Две переменные обязательны, и без них процесс поднимется, но работать не будет. `FASTDOC_CLI_BIN` — путь к процессору: это тот же `$CLI`, что и на странице [Установка и проверка](/a4/install/). `WEBHOST_WS_DIR` — полный путь к тому самому каталогу `webhost`, из которого запускается менеджер: по нему он находит свои файлы. Перед запуском полезно проверить готовность площадки: `doctor.mjs` называет человеческим языком, чего не хватает — ядра, шаблона, занятого порта или доступности ваших адресов. ### Пробросить через свой сервер [Заголовок раздела «Пробросить через свой сервер»](#пробросить-через-свой-сервер) Обычный обратный прокси с одним обязательным условием: **переключение на WebSocket должно проходить**. Без этого редактор не свяжется с ядром. Готовый образец для nginx лежит в поставке, в каталоге `examples`. Важны три вещи: передача заголовков переключения протокола, передача кук вашей авторизации и достаточный тайм-аут чтения. Обязательные условия Редактор должен открываться **по https и на том же домене**, что и ваше приложение: иначе браузер не отдаст ему защищённые куки вашей авторизации, и пользователь увидит «нужен вход». ### Дать ссылку [Заголовок раздела «Дать ссылку»](#дать-ссылку) В вашем интерфейсе у файла появляется пункт «Редактировать»:
```html
```
Пользователь, вошедший в ваше приложение, попадает в редактор со своим документом. Правки возвращаются в ваш файл вашим же API. Как приходит сохранённый файл Правка приходит как `multipart/form-data` — одной частью с именем `file`. Ваш обработчик обязан **разобрать** составное тело, а не записать его в файл целиком. Если записать целиком, документы будут портиться после каждого сохранения — молча, без ошибок в журнале. Признак беды: при попытке открыть такой DOCX распаковщик сообщает о лишних байтах в начале файла. Случай не выдуманный: на нём споткнулся образец хоста в самой поставке. ## Что происходит внутри [Заголовок раздела «Что происходит внутри»](#что-происходит-внутри) * **У каждого пользователя своя кабинка** — отдельный процесс и свой документ. Чужой документ не виден и подсунуть его нельзя. * **Права остаются за вами.** Не отдали файл — редактор не откроется. И не только на входе: раз в несколько минут редактор переспрашивает, вправе ли человек всё ещё открывать документ, который у него открыт. Отобрали права — кабинка гаснет, и человек видит ваш же ответ. * **Правки уходят вашим API** с удостоверением того же пользователя. * **Отказ в записи виден человеку.** Если вы отвечаете отказом навсегда — сессия истекла или прав больше нет, — вверху страницы появляется полоса: правки сейчас не сохраняются, работа не потеряна. Когда запись снова проходит, полоса уходит. * **Кабинка гаснет после простоя** и поднимается заново при следующем входе. Часть кабинок можно держать прогретыми заранее: тогда вход в новый документ занимает доли секунды вместо ожидания запуска процесса. Состояние площадки видно по адресу `/health`: версия сборки, сколько кабинок живо, сколько отказов в записи было за последний час. ## Что видит пользователь без входа [Заголовок раздела «Что видит пользователь без входа»](#что-видит-пользователь-без-входа) Редактор не имеет своих учётных записей и паролей: личность он берёт из вашего приложения. Поэтому запрос без вашей сессии получает отказ, а человек видит короткое объяснение — «нужен вход, войдите в систему и откройте документ снова». Это не ошибка встраивания, а нормальное поведение: если такое видит вошедший пользователь, значит до редактора не доехали куки — проверьте, что он открывается по https и на том же домене, а прокси передаёт куки. ## Двое в одном документе [Заголовок раздела «Двое в одном документе»](#двое-в-одном-документе) Совместного редактирования сейчас нет Если пустить двоих в один документ, обе правки уедут к вам и вторая молча затрёт первую. Поэтому второму человеку открывается **копия**: отдельный файл у вас, свой адрес в браузере и полоса «это ваша копия, исходный файл сейчас редактирует другой человек». Для этого и нужен третий, необязательный адрес — «создать файл». Не дадите его — ничего не сломается: второй человек получит честный отказ вместо копии, и проверка готовности предупредит об этом заранее. Копия получает осмысленное имя, если ваш адрес отдачи файла присылает обычный заголовок с именем: тогда она называется «Договор №42 (копия).docx», а не датой создания. Сколько раз за час двоим понадобился один документ, видно в том же `/health`. По этой цифре и стоит решать, нужно ли вам совместное редактирование вообще. ## Верхняя полоса окна [Заголовок раздела «Верхняя полоса окна»](#верхняя-полоса-окна) В настольном приложении сверху идёт полоса окна с именем документа и кнопками. В браузере имя документа уже написано на вкладке, и получаются два заголовка друг над другом. Поэтому вид полосы настраивается: | Значение | Что делает | Кому подходит | | -------------------------------- | ----------------------------------- | --------------------------------------- | | `none` — по умолчанию в браузере | Полосы нет, сверху сразу лента | Всем, кто открывает редактор в браузере | | `slim` | Полоса есть, но без имени документа | Кому нужны её кнопки под рукой | | `full` | Как в настольном приложении | Кому нужен прежний вид один в один | Меняется только видимость: геометрию документа считает ядро, страница не сдвигается. Сохранение при выключенной полосе остаётся во вкладке «Файл». ## Проверка [Заголовок раздела «Проверка»](#проверка) В поставке есть три проверки, и все запускаются одной командой. | Что проверить | Чем | | -------------------------------- | --------------------------------------------------- | | Готовность площадки до запуска | `doctor.mjs` | | Правильность встраивания целиком | `acceptance.mjs` — печатает таблицу «ждали / вышло» | | Поведение под нагрузкой | `load-test.mjs` — на ваших же живых сессиях | Ноль в строке «не сошлось» у приёмки означает, что встраивание сделано правильно. ## Дальше [Заголовок раздела «Дальше»](#дальше) * Что вообще даёт встраивание и где проходят границы — [Ответственность и безопасность](/a4/responsibility/). * Другие способы разместить A4 — [Платформы и размещение](/a4/deployment/). * Формат запросов к ядру — [Объекты запросов и ответов](/a4/envelope/).
# Тот же контракт через WebSocket
> Запуск fastdoc-cli с WebSocket на локальном адресе, порядок подключения с пропуском, минимальный клиентский цикл и отличия от обмена строками JSON.
Что нужно до начала Команды ниже выполняются в подготовленной папке `~/Desktop/A4-API-first-run`. Её вместе со скриптами `a4-api-demo.mjs` и `start-web-demo.sh` даёт **Evaluation Kit** — отдельный артефакт поставки (см. [Состав поставки](/platform/delivery/)). В установщик A4 он не входит и с [dl.plantype.ru](https://dl.plantype.ru/a4/release/latest/) не скачивается: его запрашивают у поставщика. **Если Evaluation Kit ещё не получен**, установленного A4 достаточно, чтобы пройти тот же путь вручную: `fastdoc-cli` лежит внутри приложения (см. [Установка и проверка](/a4/install/)), формат запросов описан в [Объектах запросов и ответов](/a4/envelope/), полный перечень команд — в [справочнике](/reference/a4/session-protocol/). Пример полностью выписанного скрипта есть у F1: [проверенный скрипт](/f1/reference-script/). WebSocket удобен, если оболочка вашего продукта уже асинхронная и построена на веб-технологиях. Ядро само выбирает свободный локальный порт и печатает одну строку с адресом подключения и разовым пропуском. * macOS
```bash
cd ~/Desktop/A4-API-first-run
node a4-websocket-demo.mjs
```
* Windows
```powershell
cd $HOME\Desktop\A4-API-first-run
node a4-websocket-demo.mjs
```
* Linux
```bash
cd ~/Desktop/A4-API-first-run
node a4-websocket-demo.mjs
```
## Порядок подключения [Заголовок раздела «Порядок подключения»](#порядок-подключения) 1. Запустите `fastdoc-cli serve --ws 0` как дочерний процесс. Ноль означает «любой свободный порт». 2. Прочитайте первую строку stdout — в ней `host`, `port`, `token` и `schema`. 3. Подключитесь к `ws://127.0.0.1:/?token=`, подставив значения из этой строки. 4. Дождитесь приветствия сеанса со схемой `fastdoc.session-serve.v1`. 5. Отправляйте те же объекты JSON, что и через JSONL. 6. При закрытии приложения сначала отправьте `shutdown`, затем закройте сокет и дочерний процесс. ## Фактический результат [Заголовок раздела «Фактический результат»](#фактический-результат)
```plaintext
1. WebSocket: ws://127.0.0.1:52601
2. Session protocol: fastdoc.session-serve.v1
3. Ping: true
4. Before: This is a sample word document. It has two pages, but no headers or footers.
5. After: WS API DEMO: This is a sample word document. It has two pages, but no headers or footers.
6. Saved: /Users/tester/Desktop/A4-API-first-run/api-demo-websocket-result.docx
7. Reopened from disk: WS API DEMO: This is a sample word document. It has two pages, but no headers or footers.
RESULT: PASS
```
## Минимальный клиентский цикл [Заголовок раздела «Минимальный клиентский цикл»](#минимальный-клиентский-цикл)
```ts
const child = spawn(cli, ["serve", "--ws", "0"]);
const bootstrap = JSON.parse(await firstLine(child.stdout));
const ws = new WebSocket(
`ws://${bootstrap.host}:${bootstrap.port}/?token=${bootstrap.token}`
);
await new Promise(resolve =>
ws.addEventListener("open", resolve, { once: true }));
ws.send(JSON.stringify({ id: "p1", cmd: "ping" }));
```
# Архитектура: четыре слоя и объекты
> Одна система из четырёх слоёв ответственности, точное место ядра, перечень объектов и главные связи между ними.
Система разделена на четыре слоя, и у каждого свой владелец. Таблица читается по последнему столбцу: он говорит, чего слой делать не должен, — именно там проходят границы, которые нельзя нарушать при встраивании. | Слой | Владелец | Ответственность | Не должен делать | | ------------------ | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | | Ваше приложение | Вы | Пользователи, права, карточка документа, версии, аудит, процессы согласования. Документооборот здесь лишь один из случаев: с тем же успехом это учётная система или отраслевая платформа. | Знать внутреннее устройство файла XLSX не обязано. | | Оболочка редактора | Мы, под вашим брендом | Показывает редактор, собирает намерение пользователя, отображает ответ. | Формулы не считает и файл сама не меняет. | | Контракт операций | Общая граница, за которую отвечаем оба | Описывает команды, результаты, отказы, ревизии и возможности. | От того, где именно работает ядро, не зависит. | | Ядро на Rust | Мы | Единственный владелец смысла книги: расчёт, изменения, история, сохранение. | Карточкой документа и вашими пользователями не управляет. | ## Где именно работает ядро [Заголовок раздела «Где именно работает ядро»](#где-именно-работает-ядро) Ядро — не сервер и не экран. Это переносимый вычислительный модуль. В браузере тот же код собирается в WebAssembly. В настольном приложении или на сервере он запускается как обычный исполняемый файл. Меняется способ запуска; смысл рабочей книги и контракт операций остаются одними и теми же. * **Оболочка** — то, что видит пользователь, и порядок вызовов. * **Контракт операций** — команды в JSON и ответы известного вида. * **Ядро** — единая модель книги и операции над ней. ## Какие объекты существуют и как они связаны [Заголовок раздела «Какие объекты существуют и как они связаны»](#какие-объекты-существуют-и-как-они-связаны) | Объект | Содержит / знает | Жизненный цикл | | -------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | | Сеанс интеграции | Идентификатор документа, права пользователя, настройки продукта. | Создаётся вашей системой, когда открывают редактор. | | Сеанс книги | Открытую книгу, номер текущей ревизии, историю правок, способ исполнения и состояние сохранения. | Живёт от открытия до закрытия; от других книг отделён. | | Книга | Листы, имена, стили, настройки, связи и служебные части файла. | Загружается из файла и меняется только ядром. | | Лист | Ячейки, строки и столбцы, таблицы, объекты и параметры печати. | Опознаётся внутри книги по ключу, который не меняется. | | Окно просмотра | Только видимую часть листа и сведения о ней. | Меняется при прокрутке и изменении масштаба; всей книгой не является. | | Операция, она же намерение | Действие, его параметры, ожидаемый номер ревизии и ограничения. | Один запрос через контракт операций. | | Результат и событие | Новое состояние, что именно изменилось, предупреждение или отказ и данные для показа. | Приходят оболочке и вашему приложению после каждой операции. | | Сохранённый файл | Содержимое файла, его формат и сведения о сохранении. | Уходит в вашу систему документооборота новой версией. | ### Главные связи [Заголовок раздела «Главные связи»](#главные-связи) * **Сеанс интеграции** создаёт и ограничивает → **сеанс книги** * **Сеанс книги** владеет состоянием → **книга, номер ревизии, история правок** * **Книга** отдаёт наружу только нужное → **окно просмотра** * **Операция** сверяет ревизию и меняет → **сеанс книги** * **Сохранение** создаёт → **готовый файл → новую версию в вашей системе** ## Дальше [Заголовок раздела «Дальше»](#дальше) * Что происходит от открытия книги до сохранения — [Жизненный цикл](/f1/lifecycle/). * Что умеет ядро рядом с Excel — [карта возможностей](/f1/atlas/). * Первый проект — [Быстрый старт](/f1/quickstart/).
# Карта возможностей рядом с Excel
> Что у F1 описано контрактом и проверяемо, чем он отличается от Excel и чего мы не утверждаем.
F1 сравнивают с табличным редактором, и на этом уровне все участники выглядят одинаково: сетка, формулы, форматы. Разница начинается там, где таблица перестаёт быть отдельным приложением и становится частью чужого продукта. ## Обозначения [Заголовок раздела «Обозначения»](#обозначения) | Знак | Что означает | | ---- | ------------------------------------------------------------------- | | ● | Есть: работает как родная возможность и проверяется | | ◐ | Частично: работает в объявленном подмножестве или другим механизмом | | ◇ | Контракт: операции описаны, в опубликованный выпуск ещё не вошли | | — | Нет: в официальной документации родного эквивалента не нашлось | | ? | Требует уточнения: твёрдого источника у нас пока нет | Прочерк означает ровно то, что написано: в изученной официальной документации Microsoft родной возможности не найдено. Это не доказательство, что её нельзя добавить надстройкой. Если вы знаете источник, который опровергает любую клетку, — напишите, поправим и обновим дату. ## Сравнение по возможностям [Заголовок раздела «Сравнение по возможностям»](#сравнение-по-возможностям) | Возможность | F1 | Excel | Что за этим стоит | | ------------------------------------- | -- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Таблицы и формулы | ● | ● | Базовый уровень; сам по себе не отличает один продукт от другого. | | Форматы Excel | ● | ● | У F1 — объявленная матрица возможностей и правило сохранения того, что редактор не понимает. | | Сводные таблицы | ● | ● | Одиннадцать операций контракта: вычисляемые поля и элементы, группировка и детализация, макет, фильтры и ранжирование, настройки полей значений, страницы фильтра отчёта. Модель данных OLAP доступна только для чтения. | | Условное форматирование | ● | ● | Правила применяются, изменяются и переносятся между диапазонами тремя операциями контракта. Две из них — применение и перенос — есть в последнем выпуске, третья, изменение правил, пока только в контракте. | | Защита книги, листа и диапазонов | ● | ● | Права на отдельные диапазоны, защита листа и жизненный цикл защиты книги — три операции; четвёртой показывается, чем именно защищён файл. | | Power Query | ◐ | ● | Подключение, преобразование, объединение, загрузка и обновление — в объявленном подмножестве. Полного совпадения с Excel контракт не обещает: это записано в нём отдельной строкой. | | Язык M | ◐ | ● | Разбор и вычисление в ядре на Rust со своей библиотекой функций. Это подмножество языка, а не полная среда выполнения M. | | Зависимости запросов и обновление | ● | ● | Граф зависимостей, отмена и вытеснение устаревшего обновления. | | Приватность и учётные данные запросов | ● | ● | Уровни приватности источников; секреты не попадают в проекции книги. | | Запуск макросов VBA | ◐ | ● | Ядро не истолковывает VBA: оно осматривает проект, планирует запуск, показывает предпросмотр правок и переносит результат в книгу, а исполняет макрос отдельный провайдер. | | Объектная модель Excel | ● | ● | В F1 среда выполнения отделена от слоя объектной модели. | | События книги и листа | ● | ● | Механизмы различаются; семантику событий Excel приходится воспроизводить отдельно. | | Формы и модель взаимодействия | ● | ● | В F1 модель смысловая, а не попиксельное повторение форм Excel. | | COM, OLE, ActiveX, Win32 | ◐ | ● | В F1 это слой совместимости с явными границами, а не общая среда Windows. | | Редактор в браузере | ◇ | ● | У F1 в браузере работает то же ядро на Rust через WebAssembly. | | Обработка без интерфейса | ◇ | ◐ | Серверную обвязку вокруг ядра вы строите сами. | | Встраивание и OEM | ◇ | ◐ | У F1 — контракт операций и ваша собственная оболочка. | | Агенты как клиенты вычислителя | ◇ | ◐ | В F1 порядок описан явно: осмотреть, предложить, получить разрешение, выполнить, проверить. | ## Чем F1 отличается на самом деле [Заголовок раздела «Чем F1 отличается на самом деле»](#чем-f1-отличается-на-самом-деле) ### Подготовка данных встроена в саму таблицу [Заголовок раздела «Подготовка данных встроена в саму таблицу»](#подготовка-данных-встроена-в-саму-таблицу) Power Query в F1 — не надстройка, а часть продукта со своим жизненным циклом: источники, редактор запросов с шагами преобразования, вычислитель языка M, объединение, загрузка результата, граф зависимостей, обновление, уровни приватности и учётные данные. Это не «умеем открывать файлы с запросами», а работа с запросами как с объектами книги. ### Автоматизация описана как слои совместимости [Заголовок раздела «Автоматизация описана как слои совместимости»](#автоматизация-описана-как-слои-совместимости) F1 разделяет разбор языка VBA, среду выполнения, слой объектной модели Excel, события, модель взаимодействия и границы обращения к внешним объектам. Благодаря этому по каждому сценарию можно сказать, что работает как родное, что — через совместимость, а что не поддерживается вовсе. Ответ «поддерживаем VBA» без такого разделения ничего не значит. ### Одно смысловое ядро для разных сред [Заголовок раздела «Одно смысловое ядро для разных сред»](#одно-смысловое-ядро-для-разных-сред) Настольное приложение, браузер через WebAssembly, серверная обвязка и агенты обращаются к одному и тому же контракту операций. Табличная семантика не переносится в каждую среду заново — а именно так возникают расхождения, когда одна и та же книга считается по-разному в разных местах. ### Агент — часть контракта, а не надстройка над интерфейсом [Заголовок раздела «Агент — часть контракта, а не надстройка над интерфейсом»](#агент--часть-контракта-а-не-надстройка-над-интерфейсом) Порядок работы агента: осмотреть ограниченное состояние, предложить изменение, получить разрешение, выполнить через контракт, проверить результат. Агент не становится вторым владельцем смысла документа и не щёлкает по интерфейсу вместо человека. ## Чего мы не утверждаем [Заголовок раздела «Чего мы не утверждаем»](#чего-мы-не-утверждаем) Эти четыре утверждения не следует делать ни в переписке, ни в сравнениях — они не выдержат проверки: * **«F1 полнее Excel».** Excel остаётся эталоном собственной семантики, и Power Query, язык M, VBA и объектная модель у него родные. * **«Поддерживаем COM, ActiveX и Win32».** Этот слой в F1 помечен как совместимость, а не как полноценная среда Windows. * **«Серверная и мобильная версии готовы».** Там, где статус архитектурный, готового продукта нет. * **«В Excel этого нет».** Если вывод основан только на отсутствии возможности в публичной документации, это не доказательство. ## Где посмотреть подробности [Заголовок раздела «Где посмотреть подробности»](#где-посмотреть-подробности) * Как устроено встраивание и что пересекает границу — [Интеграция в приложение](/f1/integration/). * Работа в браузере — [Вариант Web и WebAssembly](/f1/wasm/). * Серверный сценарий и его границы — [F1 как процессор на сервере](/f1/server/). * Сильные стороны и честные компромиссы — [Сильные стороны и компромиссы](/f1/tradeoffs/). * Полный перечень операций — [справочник F1](/reference/f1/overview/). О датах и источниках Сравнение опирается на официальную документацию Microsoft, проверено **30 августа 2026 года**. Excel меняется, поэтому перед тем как принимать решение по этой таблице, стоит перепроверить актуальные данные и провести собственные замеры на своих книгах. ## Чем эта таблица отличается от рекламной [Заголовок раздела «Чем эта таблица отличается от рекламной»](#чем-эта-таблица-отличается-от-рекламной) **Где Excel сильнее — мы так и пишем.** Таблица, где у другой стороны всё плохо, не убеждает никого и проверяется за пять минут. Excel — зрелый продукт с сорокалетней историей, и там, где он делает больше, в нашей колонке стоит «частично» или «контракт». **Наша колонка проверяется машиной, а не редактором.** За каждой строкой с пометкой «есть» стоят операции опубликованного выпуска: их список снят с самого ядра и лежит в репозитории, а сборка документации сверяет с ним текст и не проходит, если они разошлись. Поэтому «есть» здесь — не мнение. **Разница между «есть» и «контракт» — не формальность.** В контракте 203 операции, в опубликованном выпуске — 185. Всё, что описано, но ещё не вошло в сборку, помечено «контрактом», а не зелёным знаком. ## Дальше по табличной части [Заголовок раздела «Дальше по табличной части»](#дальше-по-табличной-части) * Как лист отвечает на щелчки, двойные щелчки и вызов меню — [Реакция на щелчки и контекстное меню](/f1/interaction/). * Что поверхность сообщает наружу — [События поверхности](/f1/surface-events/). * Куда движется продукт — [От концепции к пилоту](/f1/roadmap/). * Что менялось от версии к версии — [История версий F1](/f1/changelog/). ## Источники [Заголовок раздела «Источники»](#источники) * [Power Query](https://learn.microsoft.com/en-us/power-query/) * [Язык Power Query M](https://learn.microsoft.com/en-us/powerquery-m/) * [Excel VBA](https://learn.microsoft.com/en-us/office/vba/api/overview/excel) * [Надстройки Excel](https://learn.microsoft.com/en-us/office/dev/add-ins/excel/)
# История версий F1
> Что появлялось, менялось и исправлялось в F1 от версии к версии.
Каждая строка ниже — изменение, которое видно в самом продукте. Версии идут от новых к старым. Верхняя запись может опережать то, что выложено: список изменений пополняется по мере готовности выпуска, а выкладка идёт отдельным шагом. Какая версия доступна сейчас, видно в каталоге выпусков — на него ведёт страница установки. Номер версии складывается из года, квартала и номера исправления внутри квартала: в `2026.3.4` это четвёртое исправление к выпуску третьего квартала 2026 года. Счёт исправлений начинается заново с каждым кварталом, поэтому за `2026.3.19` следует `2026.4.0`, а не `2026.3.20`. В именах скачиваемых файлов к номеру добавлена ещё одна часть — сквозной номер сборки, например `2026.3.4.25`. На сравнение версий он не влияет и нужен, чтобы отличать файлы одной версии, собранные в разное время. Кандидаты и тестовые сборки помечены суффиксом: `2026.4.0-rc1`, `2026.4.0-beta1`. Установленную версию приложение сверяет с той, что предлагает канал обновлений, и предлагает обновиться, когда выходит старшая. Проверить вручную можно пунктом меню «Проверить обновления…». Всего выпусков: 61, счёт идёт с квартала `2023.2`. ## Выпуски 2026.3 [Заголовок раздела «Выпуски 2026.3»](#выпуски-20263) ### 2026.3.18 [Заголовок раздела «2026.3.18»](#2026318) **Появилось** * Мост к внешним приложениям: передача и возврат массивов, в том числе многомерных и байтовых; обращение к уже запущенному приложению, к файлу и к объекту по имени; полная таблица кодов ошибок; пустая ссылка как аргумент. * Данные реального времени: цикл обновления и менеджер источников, который держит пул серверов по их именам и раздаёт подписки на темы. * Долгая операция объявляет себя человеку в строке состояния. * Макросы: тип листа отвечается по факту, именованные диапазоны читаются и по номеру, и по имени. **Изменилось** * Резкость сетки перестала зависеть от увеличения. * Формат «Общий» ограничен пятнадцатью значащими цифрами, как в Excel. **Исправлено** * Плашка защищённого просмотра отжимает ленту и не перекрывает содержимое. * Книги-шаблоны несут объявленную таблицу стилей. * Старые книги читаются терпимее к повреждённым участкам. ### 2026.3.17 [Заголовок раздела «2026.3.17»](#2026317) **Появилось** * Вложенные в книгу объекты: разбор, извлечение содержимого, исходное имя упакованного файла, возврат правок вложения обратно в книгу. **Исправлено** * Дубликат объекта больше не теряется при сохранении. * «Формат по образцу» и окно сортировки отпускают Escape. * Меню ленты передаёт управление окну «Формат ячеек». ### 2026.3.16 [Заголовок раздела «2026.3.16»](#2026316) **Появилось** * Разбор пользовательских форм макросов: подписи и значения элементов управления, группы переключателей, иерархия рамок и многостраничников. * Перенос форматов из буфера обмена через ядро. * Динамические массивы: границы области разлива закрыты. **Исправлено** * Жесты по полотну считаются от полотна, а не от рамки окна. * Первый читаемый кадр больше не выдаёт себя за готовую сетку. * Формат даты «д-ммм» вернулся в список форматов. ### 2026.3.15 [Заголовок раздела «2026.3.15»](#2026315) **Появилось** * Книга открывается в два шага: сначала видимая часть, потом остальное. **Изменилось** * Система дат книги соблюдается при вычислении, при открытии и при сохранении; функции листа считают по системе дат самой книги. * Макросы: функция форматирования переписана по составу — скобки, дробные части, масштабирующая запятая. **Исправлено** * Правила проверки данных в таблицах сохраняются в книге. * Окно восстановления после сбоя принадлежит своему документу. ### 2026.3.14 [Заголовок раздела «2026.3.14»](#2026314) **Появилось** * Макросы: отбор подмножества массива, создание массива из значений, обход массива в цикле по элементам. * Вычислительное ядро поставляется в составе того же выпуска двумя сборками: обычной и для браузера. **Изменилось** * Режим сравнения строк, объявленный в модуле, влияет на сравнение. * Макросы, которые Excel не компилирует, больше не исполняются. **Исправлено** * В функциях поиска режим, заданный ссылкой на ячейку, больше не подменяется. * Поиск по возрастанию сравнивает текст без учёта регистра, как Excel. * Пакет для Linux больше не конфликтует с A4 на одной машине. ### 2026.3.13 [Заголовок раздела «2026.3.13»](#2026313) **Появилось** * Макросы: перечисления Excel связаны целиком — больше двух тысяч именованных констант. * Защита листа паролем, режим «только для интерфейса» и перечень разрешённых действий. * Переход по адресу с позиционными аргументами. **Изменилось** * Сортировка большого текста считается быстрее. * Чтение логического значения перестало заводить строку на каждое обращение. **Исправлено** * Сравнение строки с логическим значением из ячейки больше не даёт ошибку типа. ### 2026.3.12 [Заголовок раздела «2026.3.12»](#2026312) **Появилось** * Макросы: модули классов — объявление типа, публичные поля как члены, создание объекта; номера строк; объявления внешних процедур и события; оператор сравнения по образцу; функции даты и времени. * Функции листа доступны макросам по общему списку — 366 имён. **Исправлено** * Фильтр таблицы вернул поведение Excel. * Построитель формул снова открывается. * Изображение полотна при изменении масштаба стало устойчивым. ### 2026.3.11 [Заголовок раздела «2026.3.11»](#2026311) **Появилось** * Макросы: разбиение и склейка строк, вырезка слева, справа и по середине, обрезка пробелов, поиск подстроки, замена, преобразование числа в текст. * Окно сообщения показывается по-настоящему. * Запуск макроса из продукта открывает сеанс исполнения; идущий макрос можно остановить, откатить и опросить о состоянии. * Запись в диапазон из нескольких областей доходит до каждой области. **Изменилось** * Прокрутка больших книг переиспользует уже построенные проекции. **Исправлено** * Порог перехода к экспоненциальной записи чисел совпадает с Excel. * Корень из отрицательного числа даёт ту же ошибку, что и Excel. * Используемый диапазон учитывает любую отформатированную пустую ячейку. ### 2026.3.10 [Заголовок раздела «2026.3.10»](#2026310) **Появилось** * Пакет анализа данных: сглаживание, ранг и процентиль, преобразование Фурье, генерация случайных чисел, выборка. * Сценарии: подгрузка и объединение данных, лежащих вне листа. * Макросы: предпросмотр показывает, что макрос изменит в книге, до его запуска; результат макроса можно зафиксировать в файл; макрос не выходит за границы книги без объявленного разрешения. **Исправлено** * Сохранение книги в байты больше не запирает её от последующего сохранения. * Повторная сортировка сохраняет порядок отмены и повтора. ### 2026.3.9 [Заголовок раздела «2026.3.9»](#202639) **Появилось** * Номер сборки рядом с номером версии. ### 2026.3.8 [Заголовок раздела «2026.3.8»](#202638) **Появилось** * Портативный архив для Windows вернулся под понятным именем. **Изменилось** * Заметки к выпуску — только человеческая сводка на русском. **Исправлено** * Кнопки «Работа с данными» и «Текст по столбцам» доступны так же, как в Excel. * Неверно записанная формула отклоняется, а не принимается молча. * Сбой при сборке описания выпуска больше не останавливает сам выпуск. ### 2026.3.7 [Заголовок раздела «2026.3.7»](#202637) **Появилось** * Расширенный фильтр. * Удаление дубликатов и проверка данных перешли в ядро; итоговая строка таблицы и вычисляемые столбцы приведены к поведению Excel. * Создание графических объектов в открытой книге. * Подпись пакета проверяется в целевой системе перед установкой; пакет для централизованной установки на macOS заверяется наравне с образом диска. **Исправлено** * Расширенный фильтр больше не включает стрелки автофильтра. * Всплывающее окно фильтра обращается к своему столбцу. * Диапазон фильтра покрывает всю область данных, а не один столбец. * «Применить повторно» остаётся доступной, пока фильтр в силе. * Каталог тем попадает в пакет и загружается в установленном приложении. * Служебные ключи перевода больше не показываются человеку. ### 2026.3.6 [Заголовок раздела «2026.3.6»](#202636) **Появилось** * Макросы: функции листа и поиск совпадения отвечают через общий источник; чтение свойств листа через типизированную переменную. **Изменилось** * Версия внутри приложения показывается без пометки кандидата. **Исправлено** * «Очистить фильтр» активна, пока в списке есть снятые галочки. * Один жест в списке значений выполняет одно действие. * Взаимодействие со сводными таблицами вернулось к поведению Excel. * Установщик обновления запускается средствами системы. * Вкладка «Формулы»: исправлены дефекты вида и переходов фокуса. ### 2026.3.5 [Заголовок раздела «2026.3.5»](#202635) **Появилось** * Пункт меню «Проверить обновления…» с ответом человеку. * Макросы: вставка ячеек, строк и столбцов со сдвигом. **Исправлено** * Список значений фильтра не закрывается после каждой галочки и после переключения автоприменения. * В меню фильтра один общий шеврон вместо трёх разных. * Окно «О программе» называет систему по факту. * Стрелка фильтра в заголовке стала тонким шевроном. ### 2026.3.4 [Заголовок раздела «2026.3.4»](#202634) **Появилось** * Автообновление на Windows и Linux. * Адрес хранилища обновлений задаётся настройкой; при сбое загрузка повторяется, есть защита от зацикливания и порог минимальной версии. **Исправлено** * Применение фильтра больше не снимает стрелки с других столбцов. * Автоприменение переехало к кнопкам и не выглядит значением фильтра. * Кнопка расширенного фильтра не выходит за границу своей группы. ### 2026.3.3 [Заголовок раздела «2026.3.3»](#202633) **Появилось** * Окно сортировки как в Excel: заголовки, кнопки, варианты «от старых к новым» для дат, сортировка слева направо. * У окон продукта появилась собственная рамка с настоящими кнопками, включая разворачивание на весь экран. **Изменилось** * Вкладка «Данные» приведена к макету. **Исправлено** * Сортировка слева направо действительно переставляет столбцы. * Окно сортировки показывает тот столбец, который в нём выбран, и предлагает для дат правильные варианты порядка. * Предупреждение о сортировке не появляется на столбце с формулами. * Буквы алфавита на значках сортировки переведены. * Перетащенное окно остаётся на экране. * Кнопка «Фильтр» рисует стрелки в заголовках. * Макросы: недопустимый размер шрифта отвергается как значение; объединение диапазонов принимается; выделение из нескольких областей действует на все области. ### 2026.3.2 [Заголовок раздела «2026.3.2»](#202632) **Появилось** * Откат обновления, если новая версия не запустилась. **Исправлено** * Приложение больше не пишет внутрь собственного пакета — из-за этого ломалась подпись. ### 2026.3.1 [Заголовок раздела «2026.3.1»](#202631) **Изменилось** * Первое сервисное обновление линии. ### 2026.3.0 [Заголовок раздела «2026.3.0»](#202630) Первый выпуск линии. Ниже — то, с чем продукт в неё вошёл. **Появилось** * Открытие и сохранение книг XLSX. * Вычислительное ядро: пересчёт по зависимостям и частичный пересчёт только того, что изменилось. * Язык формул: арифметика, ссылки и диапазоны; суммы, средние, счёт значений, минимум и максимум; дата и время; текстовые функции — длина, вырезка, поиск, замена, склейка, регистр; проверка типа значения; степень, корень, округление, логарифмы, тригонометрия; статистика; поиск значений в таблице. * Обработка и свёртка данных запросом. * Работа с данными: сортировка, фильтр, проверка данных, удаление дубликатов, текст по столбцам, сводные таблицы. * Библиотека функций, построитель формулы и проверка формул. * Макросы: собственный движок исполняет процедуры, модули классов, пользовательские типы, перечисления, условную компиляцию и переходы; объектная модель книги, листа и диапазона; защита листа и книги; буфер обмена; именованные диапазоны; выделение целых строк и столбцов; удаление строк со сдвигом; область печати. * Автообновление приложения на macOS. ## Выпуски 2026.2 [Заголовок раздела «Выпуски 2026.2»](#выпуски-20262) ### 2026.2.13 [Заголовок раздела «2026.2.13»](#2026213) **Изменилось** * Книга держится в памяти разрежённо: пустые области листа места не занимают, и файл на сотни тысяч строк открывается без разворачивания всей сетки. **Исправлено** * Прокрутка к дальнему краю листа не пересчитывает лист целиком. ### 2026.2.12 [Заголовок раздела «2026.2.12»](#2026212) **Появилось** * Оценка совместимости книги до открытия: какие возможности файла поддержаны полностью, какие частично. ### 2026.2.11 [Заголовок раздела «2026.2.11»](#2026211) **Изменилось** * Пересчёт затрагивает только зависимые ячейки, а не весь лист. ### 2026.2.10 [Заголовок раздела «2026.2.10»](#2026210) **Появилось** * Защита листа и книги: заблокированные ячейки отклоняют правку с понятным отказом. ## Выпуски 2026.1 [Заголовок раздела «Выпуски 2026.1»](#выпуски-20261) ### 2026.1.9 [Заголовок раздела «2026.1.9»](#202619) **Появилось** * Автоматизация как транзакция: последовательность операций применяется целиком либо не применяется вовсе. ### 2026.1.8 [Заголовок раздела «2026.1.8»](#202618) **Появилось** * Разбор макросов VBA и отчёт о том, какие конструкции поддержаны. ### 2026.1.7 [Заголовок раздела «2026.1.7»](#202617) **Изменилось** * Внешние подключения к данным не выполняются без явного разрешения хоста. ### 2026.1.6 [Заголовок раздела «2026.1.6»](#202616) **Исправлено** * Отмена и повтор для операций над диапазоном записываются одной записью истории, а не по ячейке. ## Выпуски 2025.4 [Заголовок раздела «Выпуски 2025.4»](#выпуски-20254) ### 2025.4.8 [Заголовок раздела «2025.4.8»](#202548) **Появилось** * Сборка для WebAssembly: тот же счёт формул в браузере без отдельного движка на JavaScript. ### 2025.4.7 [Заголовок раздела «2025.4.7»](#202547) **Появилось** * Сервисный режим: обработка книг на сервере, без интерфейса. ### 2025.4.6 [Заголовок раздела «2025.4.6»](#202546) **Изменилось** * Граница между хостом и ядром описана одним типизированным контрактом операций. ### 2025.4.5 [Заголовок раздела «2025.4.5»](#202545) **Появилось** * Окно данных: хосту возвращается только видимая часть листа, а не книга целиком. ## Выпуски 2025.3 [Заголовок раздела «Выпуски 2025.3»](#выпуски-20253) ### 2025.3.7 [Заголовок раздела «2025.3.7»](#202537) **Появилось** * Постоянный сеанс работы с книгой: состояние живёт между вызовами. ### 2025.3.6 [Заголовок раздела «2025.3.6»](#202536) **Появилось** * История с отменой и повтором на стороне ядра, общая для всех способов правки. ### 2025.3.5 [Заголовок раздела «2025.3.5»](#202535) **Изменилось** * Лист адресуется устойчивым ключом, а не именем: переименование листа больше не рвёт ссылки. ### 2025.3.4 [Заголовок раздела «2025.3.4»](#202534) **Исправлено** * Сохранение книги не теряет вычисленные значения формул для программ, которые их читают без пересчёта. ## Выпуски 2025.2 [Заголовок раздела «Выпуски 2025.2»](#выпуски-20252) ### 2025.2.6 [Заголовок раздела «2025.2.6»](#202526) **Появилось** * Сводные таблицы: построение, обновление и сохранение. ### 2025.2.5 [Заголовок раздела «2025.2.5»](#202525) **Появилось** * Диаграммы: чтение из книги, отображение и запись обратно. ### 2025.2.4 [Заголовок раздела «2025.2.4»](#202524) **Исправлено** * Диапазон диаграммы переживает вставку и удаление строк. ### 2025.2.3 [Заголовок раздела «2025.2.3»](#202523) **Появилось** * Условное форматирование по значению и по формуле. ## Выпуски 2025.1 [Заголовок раздела «Выпуски 2025.1»](#выпуски-20251) ### 2025.1.5 [Заголовок раздела «2025.1.5»](#202515) **Появилось** * Сортировка и фильтры по столбцам, включая фильтр по цвету. ### 2025.1.4 [Заголовок раздела «2025.1.4»](#202514) **Появилось** * Именованные диапазоны и их использование в формулах. ### 2025.1.3 [Заголовок раздела «2025.1.3»](#202513) **Изменилось** * Ошибки в формулах возвращаются кодом ошибки, а не пустым значением. ### 2025.1.2 [Заголовок раздела «2025.1.2»](#202512) **Исправлено** * Ссылка на другой лист не сбивается при копировании формулы. ## Выпуски 2024.4 [Заголовок раздела «Выпуски 2024.4»](#выпуски-20244) ### 2024.4.4 [Заголовок раздела «2024.4.4»](#202444) **Появилось** * Форматы чисел: даты, проценты, денежные единицы, разделители разрядов. ### 2024.4.3 [Заголовок раздела «2024.4.3»](#202443) **Появилось** * Объединение ячеек, перенос текста и выравнивание в ячейке. ### 2024.4.2 [Заголовок раздела «2024.4.2»](#202442) **Исправлено** * Ширина столбца и высота строки сохраняются точно, а не округлённо. ### 2024.4.1 [Заголовок раздела «2024.4.1»](#202441) **Исправлено** * Повторяющиеся пробелы внутри ячейки не схлопываются. ## Выпуски 2024.3 [Заголовок раздела «Выпуски 2024.3»](#выпуски-20243) ### 2024.3.3 [Заголовок раздела «2024.3.3»](#202433) **Появилось** * Запись XLSX: собранная книга открывается сторонними программами. ### 2024.3.2 [Заголовок раздела «2024.3.2»](#202432) **Изменилось** * Части книги, которые редактор не понимает, сохраняются без изменений и возвращаются в файл. ### 2024.3.1 [Заголовок раздела «2024.3.1»](#202431) **Изменилось** * Книга записывается во временный файл и заменяет исходный одним действием. ## Выпуски 2024.2 [Заголовок раздела «Выпуски 2024.2»](#выпуски-20242) ### 2024.2.3 [Заголовок раздела «2024.2.3»](#202423) **Появилось** * Правка ячеек: ввод значений, текста и формул. ### 2024.2.2 [Заголовок раздела «2024.2.2»](#202422) **Появилось** * Выделение диапазона мышью и клавиатурой. ### 2024.2.1 [Заголовок раздела «2024.2.1»](#202421) **Исправлено** * Ввод числа с запятой распознаётся по языку интерфейса. ## Выпуски 2024.1 [Заголовок раздела «Выпуски 2024.1»](#выпуски-20241) ### 2024.1.2 [Заголовок раздела «2024.1.2»](#202412) **Появилось** * Счёт формул со ссылками на ячейки и диапазоны, обнаружение циклических ссылок. ### 2024.1.1 [Заголовок раздела «2024.1.1»](#202411) **Появилось** * Первый набор встроенных функций: арифметика, суммы, условия. ## Выпуски 2023.4 [Заголовок раздела «Выпуски 2023.4»](#выпуски-20234) ### 2023.4.2 [Заголовок раздела «2023.4.2»](#202342) **Появилось** * Показ сетки с заголовками строк и столбцов, прокрутка по большому листу. ### 2023.4.1 [Заголовок раздела «2023.4.1»](#202341) **Изменилось** * Значения ячеек хранятся типизированно: число, текст, дата, логическое, ошибка. ## Выпуски 2023.3 [Заголовок раздела «Выпуски 2023.3»](#выпуски-20233) ### 2023.3.1 [Заголовок раздела «2023.3.1»](#202331) **Появилось** * Чтение нескольких листов одной книги и переключение между ними. ### 2023.3.0 [Заголовок раздела «2023.3.0»](#202330) **Появилось** * Показ содержимого книги с сохранением структуры листов. ## Выпуски 2023.2 [Заголовок раздела «Выпуски 2023.2»](#выпуски-20232) ### 2023.2.1 [Заголовок раздела «2023.2.1»](#202321) **Изменилось** * Разбор книги перенесён в ядро на Rust. ### 2023.2.0 [Заголовок раздела «2023.2.0»](#202320) **Появилось** * Чтение XLSX: части пакета, общие строки, значения ячеек. ## До перехода на нумерацию по кварталам [Заголовок раздела «До перехода на нумерацию по кварталам»](#до-перехода-на-нумерацию-по-кварталам) Самые ранние сборки выходили под номерами другого вида и раздавались как альфа — для показа, а не для работы. Отдельной истории изменений по ним нет: тогда продукт менялся быстрее, чем выходили версии.
# Контрольный список готовности
> Что должно быть выполнено, прежде чем считать интеграцию готовой к пилоту.
* [ ] Архив скачан с [dl.plantype.ru](https://dl.plantype.ru/f1/release/latest/), а не получен по почте или со стороннего зеркала. * [ ] Контрольная сумма скачанного файла совпала с указанной в `latest*.yml` рядом со сборкой. * [ ] Подпись приложения проверена — способом своей системы, как показано в разделе [Проверка подписи](/f1/install/#%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%BA%D0%B0-%D0%BF%D0%BE%D0%B4%D0%BF%D0%B8%D1%81%D0%B8-%D0%B8-%D0%BD%D0%BE%D1%82%D0%B0%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE-%D0%B7%D0%B0%D0%B2%D0%B5%D1%80%D0%B5%D0%BD%D0%B8%D1%8F). * [ ] Процессор `compute_contract_cli` найден внутри установленного приложения и запускается. * [ ] Книга открывается вызовом `open_workbook_subset`. * [ ] `subsetId` и ключ листа берутся из ответа, а не собираются на своей стороне. * [ ] Три ячейки изменены одной операцией `apply_cell_edits`, а не тремя подряд: иначе отказ на середине оставит книгу изменённой наполовину. * [ ] `read_cell_details` вернул ожидаемое значение — в примере это 42 в ячейке C1. * [ ] Отмена и повтор выполнены историей ядра, а не собственным стеком на своей стороне. * [ ] Сохранённый файл создан и заново открыт другим процессом — так проверяется, что на диск легла книга, а не полуфабрикат. * [ ] Отказы разбираются по полям `ok` и `code`, и интерфейс не показывает успех, когда операция отклонена. * [ ] Для браузера и сервера предусмотрены отдельный процесс-посредник и список разрешённых адресов файлов. ## Если пункт не отмечается [Заголовок раздела «Если пункт не отмечается»](#если-пункт-не-отмечается) * Не проходит открытие или сохранение — [диагностика](/f1/diagnostics/). * Непонятен состав ответа или код отказа — [Как устроен сеанс API](/f1/session/). * Нужна операция, которой нет в примере, — [справочник Compute Contract](/reference/f1/overview/). * Речь про Web или сервер — [F1 как процессор на сервере](/f1/server/) и [Ядро в браузере через WebAssembly](/f1/wasm/).
# Живой пример: таблица со сметой
> Работающая таблица F1 на странице документации: формулы, доли и оба итога пересчитываются по-настоящему.
Ниже — та же табличная часть, что и в поставке, с обычной сметой: позиции, количества, цены, формулы и доли. Когда демонстрационная площадка поднята, ею можно пользоваться прямо здесь: измените количество или цену — суммы, «Итого» и «К оплате» пересчитаются сами. Книга открывается в вашей собственной кабинке: правки видите только вы, они никуда не сохраняются, а следующий посетитель получит смету исходной. Демонстрационная площадка сейчас не поднята — сама таблица при этом работает. Загляните позже или напишите нам: покажем на живой встрече, на ваших книгах. [F1 с открытой сметой](/demo-f1/) На весь экран[открыть отдельно ↗](/demo-f1/)Настоящая книга, а не снимок: формулы в столбцах «Сумма» и «Доля» считает ядро, а не разметка страницы ## Что именно здесь работает [Заголовок раздела «Что именно здесь работает»](#что-именно-здесь-работает) Рамка выше — не сборка для браузера. Устроено так же, как показано в [Средах выполнения](/f1/environments/): в браузере работает оболочка, а книгу держит отдельный процесс ядра на нашей стороне; между ними — постоянное соединение. У каждого посетителя свой процесс и своя копия книги, поэтому чужие правки сюда не попадают, а ваши не уходят к другим. Отсюда и заметное на глаз свойство: пересчёт идёт не мгновенно, как в таблице внутри страницы, а с задержкой сети. Это цена схемы, а не особенность ядра — в настольном приложении и в сборке для браузера тот же пересчёт происходит на месте. ## Что стоит попробовать [Заголовок раздела «Что стоит попробовать»](#что-стоит-попробовать) * Изменить количество в столбце «Кол-во» — пересчитаются сумма строки, доля и оба итога. * Изменить скидку в `B13` — изменится только строка «К оплате». * Встать на ячейку `D11` и посмотреть в строку формул: там `SUM`, а не записанное число. ## Чем этот пример отличается от рабочего встраивания [Заголовок раздела «Чем этот пример отличается от рабочего встраивания»](#чем-этот-пример-отличается-от-рабочего-встраивания) | Что | В этом примере | В вашем приложении | | ---------------- | -------------------------------------------- | --------------------------------------------------- | | Кто пользователь | Гость: личность одна на всех посетителей | Ваш вошедший пользователь | | Откуда книга | Один и тот же демонстрационный файл | Ваш файл по идентификатору | | Куда идут правки | Никуда: кабинка живёт до простоя и стирается | В ваше хранилище, вашим же API | | Права | Не проверяются | Проверяете вы, в том числе повторно во время работы | ## Дальше [Заголовок раздела «Дальше»](#дальше) * Отдельные умения по одному, каждое своим маленьким проектом — [Примеры F1](/f1/examples/). * Где считает ядро в каждой из сред — [Среды выполнения](/f1/environments/). * Что нужно вашей стороне для встраивания — [Интеграция в приложение](/f1/integration/).
# Диагностика
> Типовые проблемы первого запуска и как их локализовать.
Если первый запуск не идёт, найдите свой случай в таблице: рядом названа причина и то, что проверить. | Симптом | Причина | Действие | | ---------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | | Программа молчит при запуске | Неверный путь или файл не запускается | Убедиться, что файл лежит по указанному пути и запускается вручную | | Открытие вернуло `ok=false` | Ошибка в адресе файла, формате или правах доступа | Передавать адрес файла целиком, от корня — в Node.js его даёт `pathToFileURL` из модуля `node:url`; прочитать `code` и `message` из ответа | | Устаревшая ревизия | Другой запрос уже изменил книгу | Запросить свежую проекцию с новым номером ревизии и осознанно повторить операцию | | Операция уходит не на тот лист | Передано видимое имя вместо ключа листа | Использовать `activeSheet.path` из ответа на `open` | | Сохранённый файл не появился | Неверный адрес назначения или нет прав на запись | Проверить абсолютный URI и каталог | | `subsetId` не найден | Процесс ядра был перезапущен и потерял открытые подмножества | Снова вызвать open\_workbook\_subset | | Формула показывает старое значение | Книга только что открыта в новом процессе | Это значение, сохранённое в файле; чтобы получить свежее, вызовите пересчёт | ## Что приложить к обращению [Заголовок раздела «Что приложить к обращению»](#что-приложить-к-обращению) * версию F1 и платформу; * контрольную сумму sha512 установочного файла — ту, что сверяли при установке; * имя операции (`operation`) и код отказа (`code`); * минимальную обезличенную книгу XLSX, на которой это видно; * одну JSON-строку запроса и одну JSON-строку ответа; * указание, воспроизводится ли после нового процесса. Собранное отправляйте на — этого хватает, чтобы начать разбор, не спрашивая недостающее вторым письмом. Состав ответа при отказе — на странице [Как устроен сеанс API](/f1/session/).
# Набор для встраивания: структура, события, меню
> Модель доступа без DOM, структурные проекции книги, события щелчка и выбора, вклад в контекстное меню и команды вместо прямой записи.
Публичного набора для встраивания пока нет Структурные проекции книги и обработка щелчков, двойных щелчков, выбора объектов и контекстных меню внутри F1 уже работают, но остаются внутренними интерфейсами продукта. Ниже описано, каким должен стать публичный набор для встраивания (Embedding API), а не то, что можно вызвать сегодня. Сам контракт при этом уже зафиксирован отдельной версией: перечень событий, коды отказов, набор настроек редактора и объект управления им объявлены и не меняются произвольно. Нет второй половины — того, что этот контракт выполняет и поставляется вам пакетом. Поэтому писать код против имён и типов можно заранее, а запустить его — пока нет. Что доступно сейчас: [Compute Contract](/reference/f1/overview/) — в контракте 203 операции чтения и изменения книги, из них 185 принимает ядро последнего выпуска. Обращаться к DOM-селекторам, внутренним `CustomEvent` и глобальным точкам входа `__f1…` нельзя: это не контракт, и они меняются без предупреждения. | Статус | Объект | Что это означает | | ------------------ | -------------------------------- | ------------------------------------------------------------------------------------- | | Есть сейчас | Контракт операций в ядре | Чтение структуры, ячеек и объектов, типизированные операции изменения. | | Есть сейчас | Обработка действий в поверхности | Определение попадания, щелчки и вызов меню, выделение и внутренние смысловые события. | | Требует оформления | Публичный набор для встраивания | Устойчивые методы и события, добавление своих пунктов меню, права и версионирование. | ## Какой API нужен для встраивания [Заголовок раздела «Какой API нужен для встраивания»](#какой-api-нужен-для-встраивания) * **Чтение** — узнать состав книги, выделение, ячейку, объект. * **События** — реагировать на действия пользователя. * **Команды** — безопасно изменять книгу. * **Расширение интерфейса** — добавлять свои команды и пункты меню. ## Не DOM документа, а структурные проекции и команды [Заголовок раздела «Не DOM документа, а структурные проекции и команды»](#не-dom-документа-а-структурные-проекции-и-команды) Таблица в F1 не выложена в разметку страницы как дерево элементов. Она рисуется на полотне, а в памяти держится только видимая часть книги — поэтому нужной ячейки или объекта в разметке может не быть вовсе, даже когда пользователь их видит. Обращаться нужно к логической структуре через набор вызовов. | Объект | Что даёт | Откуда берутся данные | | --------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | WorkbookSession | Идентификаторы книги и сеанса, ревизия, признак несохранённых изменений, активный лист, доступные форматы. | Сеанс ядра. | | WorkbookInfo | Листы с устойчивыми идентификаторами, их имена, видимость и какой лист активен. | Проекция из ядра. | | Selection | Лист, выделенный диапазон, активная ячейка и идентификаторы выбранных объектов. | Определение попадания в поверхности плюс идентификаторы из ядра. | | CellDetails | Хранимое значение, отображаемое значение, формула, тип, формат, проверка ввода, примечание и ссылка. | Операция `read_cell_details`. | | RangeSnapshot | Ограниченный участок: значения, формулы, стили и сопутствующие данные. | Запрос к ядру с явными границами участка. | | ObjectInventory | Фигуры, диаграммы, изображения, элементы управления, их привязки, порядок наложения и доступные действия. | Опись объектов из файла. | | ContextTarget | Что находится под указателем: ячейка, таблица, диаграмма, фигура, заголовок. | Смысловое определение попадания. | ### Главное правило [Заголовок раздела «Главное правило»](#главное-правило) Не хранить живые ссылки Вам приходит проекция — снимок состояния с устойчивым идентификатором и номером ревизии. Хранить у себя узел разметки или изменяемый объект книги и писать в него напрямую нельзя: к следующему действию и разметка, и состояние успевают измениться. Перед каждым действием заново проверяются идентификатор, ревизия, права и доступность операции. Так предлагается назвать публичные вызовы — в текущем контракте таких имён нет.
```ts
const session = await F1.open({
mount, documentId, bytes, mode: "edit"
});
const workbook = await session.getWorkbookInfo();
const selection = await session.getSelection();
const cell = await session.getCellDetails(selection.activeCell);
```
## Как получить листы, диапазон, таблицу и графический объект [Заголовок раздела «Как получить листы, диапазон, таблицу и графический объект»](#как-получить-листы-диапазон-таблицу-и-графический-объект) | Задача | Предлагаемый вызов | Что вернётся | | ------------------------ | ------------------------------- | --------------------------------------------------------------------------------------------------- | | Получить листы | `session.getWorkbookInfo()` | Список листов с идентификаторами, именами, видимостью и порядком. | | Прочитать ячейку | `session.getCellDetails(...)` | Хранимое и отображаемое значение, формула, тип, стиль и сопутствующие данные. | | Прочитать диапазон | `session.getRangeSnapshot(...)` | Значения, формулы и стили запрошенного участка — без всей книги. | | Узнать таблицу | `session.getTableAt(...)` | Идентификатор таблицы, её диапазон, столбцы, фильтры, итоги и доступные действия. | | Получить объекты | `session.getObjects(...)` | Опись объектов с идентификаторами, типами, привязками, порядком наложения и признаком изменяемости. | | Узнать объект под точкой | `session.hitTest(...)` | Что находится под указателем: вид объекта и его устойчивый идентификатор. | Пример: найти диаграмму и переименовать через команду
```ts
const objects = await session.getObjects({ sheetId });
const chart = objects.find(x => x.type === "chart");
if (chart?.capabilities.rename) {
await session.execute({
operation: "renameObject",
objectId: chart.stableId,
name: "План перевозок",
expectedRevision: session.revision
});
}
```
### Что проекция ядра отдаёт уже сейчас [Заголовок раздела «Что проекция ядра отдаёт уже сейчас»](#что-проекция-ядра-отдаёт-уже-сейчас) Открытие книги уже возвращает активный лист, список листов, видимую область, предпросмотр ячеек, сведения о таблицах и опись объектов. Для каждого объекта доступны устойчивый идентификатор, лист, тип, имя, привязка, связи с другими частями, порядок наложения, видимость, блокировка и перечень допустимых изменений. Для отдельной ячейки есть операция `read_cell_details`. ## Щелчки, выделение и действия с объектами [Заголовок раздела «Щелчки, выделение и действия с объектами»](#щелчки-выделение-и-действия-с-объектами) | Событие | Когда приходит | Основные поля | | --------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | `selectionChanged` | Выделение изменилось и это подтверждено. | новое и прежнее выделение, источник изменения, ревизия. | | `cellActivated` | Одиночный щелчок или касание сделали ячейку активной. | ссылка на ячейку, диапазон, нажатые клавиши-модификаторы, источник. | | `cellDoubleClicked` | Двойной щелчок по ячейке — после того, как определено, во что именно попали. | ссылка на ячейку, намерение правки, клавиши-модификаторы. | | `objectSelected` | Выбрана фигура, диаграмма, картинка или элемент управления. | устойчивый идентификатор, тип объекта, привязка, доступные действия. | | `objectDoubleClicked` | Двойной щелчок по графическому объекту. | сам объект, действие по умолчанию, клавиши-модификаторы. | | `contextMenuOpening` | Щелчок правой кнопкой или долгое нажатие на телефоне. | объект под указателем, выделение, встроенные пункты меню, доступные действия. | | `workbookChanged` | Ядро применило изменение. | новая ревизия, какая операция, что изменилось, признак несохранённых изменений. | | `operationRefused` | Ядро отказало — до того, как что-либо изменилось. | код и текст отказа, объект, текущая ревизия. | Подписка на смысловые события
```ts
const unsubscribe = session.on("cellDoubleClicked", async event => {
const details = await session.getCellDetails(event.cellRef);
hostPanel.open({ documentId, cell: details });
});
session.on("objectSelected", event => {
propertiesPanel.show(event.target);
});
// При закрытии интеграции:
unsubscribe();
```
### Порядок обработки жеста [Заголовок раздела «Порядок обработки жеста»](#порядок-обработки-жеста) указатель или касание → определение попадания → подтверждение выделения → смысловое событие → действие по умолчанию ## Как менять меню в зависимости от выбранного элемента [Заголовок раздела «Как менять меню в зависимости от выбранного элемента»](#как-менять-меню-в-зависимости-от-выбранного-элемента) Искать внутренний элемент меню в разметке и вставлять туда кнопку не нужно. Вместо этого вы регистрируете поставщика пунктов меню: перед показом F1 определяет, что находится под указателем, и запрашивает у вас дополнительные пункты. | ContextTarget.kind | Примеры данных контекста | | ------------------------ | -------------------------------------------------------------------------------- | | cell / range | sheetId, address, valueType, formula, validation, comment, hyperlink, protected. | | table | tableId, name, range, columnId, totals/filter state, mutation capabilities. | | chart | objectId, chartType, source range, anchor, editable capabilities. | | shape / image | objectId, anchor, zOrder, groupId, metadata, supported mutations. | | rowHeader / columnHeader | track index, hidden state, size, protection. | | `sheetTab` | sheetId, name, visibility, active/grouped state. | | canvas | sheetId, pointer coordinates, no semantic object. | Пример menu contribution для строки таблицы
```ts
session.contextMenu.registerProvider(async context => {
if (context.target.kind !== "table") return [];
return [{
id: "partner.open-shipment-card",
label: "Открыть карточку перевозки",
icon: "external-link",
enabled: Boolean(context.target.rowKey),
group: "partner",
order: 20,
run: () => host.openCard(context.target.rowKey)
}];
});
```
### Правила расширения меню [Заголовок раздела «Правила расширения меню»](#правила-расширения-меню) * У пункта есть собственное имя с вашей приставкой, переводимая подпись, необязательный значок, группа, порядок в ней и правило, по которому он включён или виден. * Поставщик пунктов получает безопасный снимок обстановки, а не внутреннюю разметку и не всю книгу. * Встроенные команды остаются за F1; вы добавляете свои пункты и в оговорённых местах заменяете разрешённые. * Если ваш поставщик пунктов думает слишком долго, ожидание обрывается: меню не должно зависнуть из-за вашей серверной части. ## Команды вместо прямой записи в структуру [Заголовок раздела «Команды вместо прямой записи в структуру»](#команды-вместо-прямой-записи-в-структуру) Любая операция, меняющая документ, проходит через контракт операций в ядре. Так поведение остаётся одинаковым в браузере, в настольном приложении и на сервере, а история изменений — целостной. | Шаг | Что происходит | | -------------------- | -------------------------------------------------------------------------------------------------------- | | 1. Собрать намерение | Имя операции, устойчивая ссылка на объект или диапазон, параметры и ожидаемая ревизия. | | 2. Проверить доступ | Режим только для чтения, защита листа, объявленные возможности, формат исходного файла и правила сеанса. | | 3. Передать ядру | Тот же контракт — через сборку для браузера, настольный процессор или серверную службу. | | 4. Применить целиком | При успехе меняются ревизия и история; при отказе книга остаётся нетронутой — половины правки не бывает. | | 5. Отдать результат | Новая проекция и событие об изменении книги — либо событие об отказе. | Пример правки с проверкой ревизии
```ts
const result = await session.execute({
operation: "setCellValue",
target: { sheetId, row: 12, col: 4 },
value: { kind: "string", value: "Доставлено" },
expectedRevision: session.revision
});
if (!result.ok && result.code.endsWith("-stale-revision")) {
await session.refreshProjection();
}
```
### Запрещённая модель [Заголовок раздела «Запрещённая модель»](#запрещённая-модель)
```js
// НЕЛЬЗЯ: это обходит Rust, историю и сохранение.
document.querySelector("[data-cell='E13']").textContent = "Доставлено";
session.workbook.sheets[0].cells[12][4].value = "Доставлено";
```
# Среды выполнения и сквозной сценарий
> Что меняется между настольным приложением, браузером и сервером, а что остаётся одинаковым; полный путь «открыть байты → изменить → вернуть XLSX».
## Что меняется между средами, а что остаётся одинаковым [Заголовок раздела «Что меняется между средами, а что остаётся одинаковым»](#что-меняется-между-средами-а-что-остаётся-одинаковым) | Среда | Доступ к структуре | События пользователя | Изменение документа | | --------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------- | | Браузер, WebAssembly | Через единый интерфейс сеанса поверх вызовов WebAssembly. | Щелчок, двойной щелчок, контекстное меню, клавиатура, выделение. | Команда уходит в ядро внутри WebAssembly. | | Настольное приложение | Тот же интерфейс; вызовы уходят в локальный сеанс. | Те же события, что и в браузере. | Команда уходит в отдельный процесс ядра. | | WebView на телефоне | Тот же интерфейс, но видимая область меньше. | Касание, двойное касание и долгое нажатие превращаются в те же события. | Команда уходит в WebAssembly; нужен свой жизненный цикл приложения. | | Сервер и агенты | Сеанс без интерфейса: запросы и команды. | Щелчков нет; есть события команды, сеанса и изменения. | Команда уходит в процесс ядра на сервере. | ### Смысловой контракт одинаков [Заголовок раздела «Смысловой контракт одинаков»](#смысловой-контракт-одинаков) Выделение, цель контекстного меню, операция, ревизия, результат и сохранённый файл описаны одинаково независимо от того, как устроен транспорт. На телефоне долгое нажатие превращается в то же событие открытия контекстного меню. На сервере щелчка мышью нет, но агент запрашивает те же сведения о ячейке и выполняет ту же операцию — с теми же правами и теми же отказами. ### Особые ограничения [Заголовок раздела «Особые ограничения»](#особые-ограничения) * В браузере нельзя отдавать структуру большой книги целиком: каждый запрос ограничен участком. * На телефоне сеанс должен переживать выгрузку приложения из памяти — через контрольную точку и повторное открытие, а не через ссылку на память WebView, которой к тому моменту уже нет. * На сервере нужны изоляция клиентов друг от друга, квоты, отмена задач и журнал действий. Готовой серверной платформы поставка не включает. * Агенту разрешён только заранее согласованный список запросов и команд. Учётные данные и исходный текст запросов Power Query и макросов в события не попадают. ## Открыть байты → прочитать → изменить → сохранить [Заголовок раздела «Открыть байты → прочитать → изменить → сохранить»](#открыть-байты--прочитать--изменить--сохранить) Пример ниже использует тот же протокол, которым проверяется сборка. Индексы строк и столбцов считаются с нуля. Поле `viewport` ограничивает область, которая уйдёт в ответ, а `sourceBytesBase64` позволяет передать книгу байтами — тогда ядру не нужен доступ к вашей файловой системе. Часть 1: открыть документ и получить структуру
```ts
import { spawn } from "node:child_process";
import { readFile } from "node:fs/promises";
import readline from "node:readline";
// Путь к процессору внутри установленного приложения — в переменной
// окружения F1_CLI; где он лежит в каждой системе, показано на
// странице «Установка F1».
const cli = spawn(process.env.F1_CLI, ["--session"],
{ stdio: ["pipe", "pipe", "inherit"] });
const lines = readline.createInterface({ input: cli.stdout });
const iterator = lines[Symbol.asyncIterator]();
const next = async () => JSON.parse((await iterator.next()).value);
const ask = async request => {
cli.stdin.write(JSON.stringify(request) + "\n");
return next();
};
const hello = await next();
if (hello.kind !== "compute_contract_cli_session_ready")
throw new Error("F1 core handshake failed");
const bytes = await readFile("./demo.xlsx");
const viewport = { top: 0, left: 0, rows: 40, cols: 12 };
const opened = await ask({
operation: "open_workbook_subset_from_bytes",
sourceName: "demo.xlsx",
sourceBytesBase64: bytes.toString("base64"),
viewport, requestedBackend: "native-desktop"
});
if (!opened.ok) throw new Error(opened.code);
const { subsetId, activeSheet, objectInventory = [] } = opened.payload;
console.log(activeSheet, objectInventory);
```
## Адресная операция, проверка ревизии и возврат байтов XLSX [Заголовок раздела «Адресная операция, проверка ревизии и возврат байтов XLSX»](#адресная-операция-проверка-ревизии-и-возврат-байтов-xlsx) После открытия книги поле `subsetId` называет её живой сеанс в ядре, и все следующие запросы несут этот идентификатор. Запрос на изменение передаёт `expectedRevision`: если ревизия устарела, ядро отказывает до внесения правки, а не применяет её наполовину. Часть 2: адресное чтение, атомарная правка и сохранение
```ts
const cell = await ask({
operation: "read_cell_details", subsetId,
row: 0, col: 0, viewport,
requestedBackend: "native-desktop"
});
const edited = await ask({
operation: "apply_cell_edits", subsetId,
edits: [{ inputKind: "text", sheetKey: activeSheet.path,
row: 1, col: 1, input: "Доставлено" }],
expectedRevision: 0, viewport,
requestedBackend: "native-desktop"
});
if (!edited.ok) throw new Error(`${edited.code}: ${edited.message ?? ""}`);
const saved = await ask({
operation: "save_workbook_subset_to_bytes", subsetId,
preserveCachedValues: true,
requestedBackend: "native-desktop"
});
const output = Buffer.from(saved.payload.bytesBase64, "base64");
// Закрыть ядро: пока порождённый процесс жив, программа не завершится,
// а процессор останется висеть после её конца.
cli.stdin.end();
cli.kill();
```
## Тот же контракт в браузере: адаптер событий поверх полотна [Заголовок раздела «Тот же контракт в браузере: адаптер событий поверх полотна»](#тот-же-контракт-в-браузере-адаптер-событий-поверх-полотна) ### Как выглядит вызов ядра из браузера [Заголовок раздела «Как выглядит вызов ядра из браузера»](#как-выглядит-вызов-ядра-из-браузера) Сеанс в браузере: доставку берёт на себя код страницы, смысл документа остаётся за ядром. Модуль `fastsheet_bridge_wasm.js` рядом со сборкой ядра под WebAssembly — отдельный артефакт поставки: он идёт пакетами движка (см. [Состав поставки](/platform/delivery/)) и в установщик настольного приложения не входит. Пути к процессору со страницы установки для этого случая не годятся: там нативный исполняемый файл, а здесь модуль для браузера.
```ts
import init, { WasmComputeSession } from "./fastsheet_bridge_wasm.js";
await init();
const core = new WasmComputeSession(Date.now(), 0x362cafe);
const dispatch = request =>
JSON.parse(core.dispatch(JSON.stringify(request)));
const opened = dispatch({
operation: "open_workbook_subset_from_bytes",
sourceName: file.name,
sourceBytesBase64: await fileToBase64(file),
viewport: { top: 0, left: 0, rows: 40, cols: 12 },
requestedBackend: "wasm"
});
```
### Что делать с нынешней веб-оболочкой [Заголовок раздела «Что делать с нынешней веб-оболочкой»](#что-делать-с-нынешней-веб-оболочкой) Сегодня оболочка уже шлёт внутреннее событие `fastsheet:surface-object-selected` и сама обрабатывает щелчок, двойной щелчок и вызов контекстного меню. Для пилота их стоит спрятать за одной точкой входа. Подписываться напрямую, как показано ниже, допустимо только во внутренней сборке — но не рассчитывать на это как на долгосрочный интерфейс. **Внутренний мост событий — его ещё предстоит превратить в устойчивый набор вызовов:**
```js
// INTERNAL / UNSTABLE: адаптер внутри поставки F1.
surface.addEventListener("fastsheet:surface-object-selected", event => {
hostEvents.emit("objectSelected", {
target: normalizeObjectTarget(event.detail),
revision: currentRevision()
});
});
surface.addEventListener("dblclick", event => {
const target = semanticHitTest(event.clientX, event.clientY);
hostEvents.emit("doubleClicked", { target });
});
surface.addEventListener("contextmenu", event => {
event.preventDefault();
const target = semanticHitTest(event.clientX, event.clientY);
openComposedMenu(target, hostMenuProviders);
});
```
## Дальше [Заголовок раздела «Дальше»](#дальше) * Где именно работает ядро в каждой среде — [Ядро в браузере](/f1/wasm/), [отдельный процесс](/f1/native/), [на сервере](/f1/server/). * Что фиксируется до интеграции — [Контрольный список](/f1/checklist/).
# Примеры F1
> Двадцать три маленьких проекта: от минимального листа до миллиона строк, совместной работы и связей между книгами.
Каждый пример — работающий маленький проект, а не выдержка из кода. Но считает в них не ядро: на его месте стоит дублёр, а сами проекты собраны из пакетов `@f1/*` — они идут пакетами движка ([Состав поставки](/platform/delivery/)) и в открытый реестр npm не публикуются. Поэтому смотреть здесь нужно на объём кода — сколько его нужно написать вокруг листа для каждой задачи, — а не на числа в ячейках. Настоящее ядро с настоящим пересчётом — в [живом примере](/f1/demo/): там книга открыта в отдельном процессе, и правка ячейки меняет итоги по-честному. Смотреть их стоит по порядку: первые отвечают на вопрос «сколько нужно написать, чтобы получить лист», дальше идут отдельные умения, а в конце — предельные случаи вроде миллиона строк и работы без экрана. * [Минимальный листсколько строк нужно, чтобы получить работающий лист](/f1/examples/minimal/) * [Лист с лентойкак добавить нашу ленту, если она нужна](/f1/examples/ribbon/) * [Встраивание в чужую страницучто будет с чужой страницей вокруг листа: её шрифт, фон и прокрутка](/f1/examples/embedded/) * [Миллион строкцена трёх жестов на предельной книге — прокрутки, протяжки и масштаба: кадр p50 и p95 и обращения к ядру числами, а не обещанием](/f1/examples/stress/) * [Перекрестьеподсветка строки и столбца активной ячейки: выключена по умолчанию и включается свойством](/f1/examples/crosshair/) * [Только чтениекнига видна, но не правится: команды серые с причиной, а не спрятаны, и правка не начинается вовсе](/f1/examples/read-only/) * [Свои заголовкиподписи строк и столбцов задаёт встраивающий, а ширина полосы считается по его тексту](/f1/examples/custom-headers/) * [Лист без полос заголовковполосы прячутся одним свойством, и освободившееся место занимает сам лист](/f1/examples/no-headings/) * [Свой пункт менючужой пункт в нашем меню: за чертой, под именем владельца и со своей командой — притвориться нашим он не может](/f1/examples/custom-menu/) * [Своё сочетание клавишчужое сочетание срабатывает, а занятые листом (Ctrl+D, Ctrl+R, F2, Shift+Пробел) остаются за листом — отказ виден словами](/f1/examples/custom-shortcut/) * [Свой языкязыка нет в наших трёх — его приносит хозяин страницы: подписи переводятся, непереведённое идёт запасной цепочкой, а не пропадает](/f1/examples/custom-locale/) * [Перетащите книгу сюдачужой документ открывается байтами: оболочка доносит файл, разбор остаётся за швом контракта, а отказ приходит словами того, кто открывал — на витрине это заглушка, она берёт csv, tsv и txt, а на xlsx честно отказывает](/f1/examples/open-bytes/) * [Двое в одном документедве рамки рядом: двигаете выделение в одной — во второй виден чужой курсор с именем; и прямо на экране написано, что видны только окна этого браузера](/f1/examples/presence/) * [Помощники ведут листдо десяти внешних участников, каждый в своём процессе: ходит по своей колонке, выделяет участок и вписывает итог — намерениями через тот же канал; отказ в лишнем виден и ему, и человеку; не запустили — страница так и говорит](/f1/examples/driver/) * [Своё событиедве точки расширения наружу: onCommand отдаёт любое намерение поверхности (наше и чужое), onGesture — цену закончившегося жеста числами](/f1/examples/custom-event/) * [Гиперссылкассылка — свойство ячейки: одна операция ядра ставит её вместе с текстом, снимает, оставляя текст, и сама называет ссылки листа](/f1/examples/hyperlink/) * [Найти и заменитьпоиск — один запрос, замена — два, и первый книгу не меняет: ядро вычисляет план правок, а пишет их та же дорога, что ввод](/f1/examples/find-replace/) * [Примечанияшесть действий примечаний одной операцией контракта — перечислить, создать, удалить, ответить, отменить, повторить; текст, номер и нормализованный адрес называет ядро](/f1/examples/annotations/) * [Структура листавосемь действий — одна операция: оболочка называет диапазон, ось и действие, а уровни и что спрятать считает ядро](/f1/examples/outline/) * [Разметка страницгде ляжет разрыв — единственный метод SDK, который спрашивает и ничего не меняет; саму печать делает хост, и это граница, а не пробел](/f1/examples/page-breaks/) * [Разрешить изменение диапазоновшесть намерений из десяти, что есть у операции: пароль уходит за шов и не возвращается — обратно приходит только способ защиты](/f1/examples/protected-ranges/) * [Связи книгиформула считает по чужой книге: список связей, их состояние и адрес источника приходят от ядра, а открывает книгу хозяин окна](/f1/examples/workbook-links/) * [Голова без экранакнига открывается, правится и читается из Node — без единого обращения к DOM](/f1/examples/headless/) ## Как это связано с ядром [Заголовок раздела «Как это связано с ядром»](#как-это-связано-с-ядром) Примеры показывают оболочку — то, что видит и трогает пользователь. Считает не она: за вычисления, формулы, историю и сохранение отвечает ядро, и разговаривают они через одно-единственное место — транспорт. В примерах на месте ядра стоит дублёр: `createMockTransport` возвращает придуманные значения, чтобы лист было на чём показать. Поэтому цифры в них ничего не значат — важно, сколько кода нужно написать вокруг. В вашем продукте меняется ровно эта строка: вместо дублёра ставится настоящий транспорт — сборка ядра в браузере или мост к процессу рядом. Остальной код примера остаётся как есть. Что именно умеет ядро по ту сторону транспорта, описано в [справочнике Compute Contract](/reference/f1/overview/): это те самые операции, которые оболочка и вызывает. ## Пять правил, на которые ссылается код [Заголовок раздела «Пять правил, на которые ссылается код»](#пять-правил-на-которые-ссылается-код) Комментарии в примерах часто объясняют решение ссылкой на правило: «второй владелец книги не заводится (инвариант №2)». Правил всего пять, они общие для всех примеров и звучат так. | Правило | О чём оно | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | №1 | Считает ядро, оболочка только показывает. Ни формул, ни истории, ни записи файла на стороне интерфейса нет. | | №2 | Второй владелец книги не заводится: любая правка идёт той же дорогой, что и ввод с клавиатуры, — своей у неё нет. | | №3 | Не показывать несделанное сделанным. Нарисовать в примере то, чего продукт не умеет, нельзя даже картинкой; в комментариях это названо `fake-green`. | | №4 | Реестр команд принадлежит листу. Команды встраивающего в него не попадают — они живут своей стороной и не смешиваются с нашими. | | №5 | Событие ничего не применяет и ничего не требует: лист поднимает намерение, а решает хозяин страницы. | ## Как ими пользоваться [Заголовок раздела «Как ими пользоваться»](#как-ими-пользоваться) Файлы каждого примера показаны на его странице целиком. Проект лежит в `examples/f1/<имя примера>` — можно взять его как заготовку и заменить книгу на свою. ## Дальше [Заголовок раздела «Дальше»](#дальше) * Как устроен лист внутри — [Архитектура](/f1/architecture/). * Что можно вызвать сегодня — [справочник Compute Contract](/reference/f1/overview/).
# Примечания
> Шесть действий примечаний одной операцией контракта — перечислить, создать, удалить, ответить, отменить, повторить; текст, номер и нормализованный адрес называет ядро.
[шесть действий примечаний одной операцией контракта — перечислить, создать, удалить, ответить, отменить, повторить; текст, номер и нормализованный адрес называет ядро](/examples/f1/app/projects/annotations/index.html) На весь экран[открыть в новой вкладке ↗](/examples/f1/app/projects/annotations/index.html)Работающий пример. Ниже — его код целиком, файл за файлом. В рамке выше работает тот самый проект, чей код лежит ниже, — собранный, но не переписанный: те же файлы, только пропущенные через сборку. Считает в нём не ядро, а дублёр транспорта, поэтому числа в ячейках ничего не значат: смотреть нужно на код. Страница, которую открывает браузер. index.html
```html
Примечания — @f1