Embedding API: структура, события, меню
Модель доступа без DOM, структурные проекции книги, события клика и выбора, вклад в контекстное меню и команды вместо прямой записи.
Внутри F1 уже есть структурные проекции книги и обработка кликов, двойных кликов, выбора объектов и контекстных меню. Сегодня это внутренние интерфейсы продукта — для внешнего приложения они оформляются как отдельный стабильный Embedding API.
Руководство для команды, встраивающей редактор в документарную систему
| Прямой ответ Внутри F1 уже есть структурные проекции книги и событийная обработка кликов, двойных кликов, выбора объектов и контекстных меню. Но сегодня это внутренние интерфейсы продукта. Для внешнего приложения необходимо оформить отдельный стабильный Embedding API; использовать DOM-селекторы, внутренние CustomEvent и глобальные __f1… hooks напрямую нельзя. |
|---|
| Статус | Объект | Что это означает |
|---|---|---|
| Существует | Rust compute contract | Чтение структуры, ячеек, объектов и типизированные операции изменения. |
| Существует | Web interaction runtime | Hit testing, click/double-click/contextmenu, selection и внутренние semantic events. |
| Требует упаковки | Public Embedding SDK | Стабильные методы, события, menu contributions, permissions и versioning. |
Какой API нужен внешнее приложение
Заголовок раздела «Какой API нужен внешнее приложение»| Read API узнать книгу, выбор, ячейку, объект | Event API реагировать на действия пользователя | Command API безопасно изменять документ | UI Extension API добавлять команды и меню |
|---|
1 · МОДЕЛЬ ДОСТУПА
Не DOM документа, а структурные проекции и команды
Заголовок раздела «Не DOM документа, а структурные проекции и команды»Электронная таблица в F1 не представлена публичным деревом HTML-элементов. Canvas, виртуализация и sparse viewport означают, что нужной ячейки или объекта может вообще не быть в DOM. Поэтому интеграция должна обращаться к логической структуре через SDK.
| Объект SDK | Что предоставляет | Источник истины |
|---|---|---|
| WorkbookSession | documentId, subsetId, revision, dirty, active sheet, format capabilities. | Rust session. |
| WorkbookInfo | Листы, стабильные identity, имена, видимость и активный лист. | Rust projection. |
| Selection | Sheet, range/address, active cell, selected object IDs. | Web hit test + Rust identity. |
| CellDetails | Raw value, display value, formula, type, format, validation, comment/hyperlink. | read_cell_details. |
| RangeSnapshot | Ограниченная матрица значений/formulas/styles и metadata. | Bounded compute request. |
| ObjectInventory | Shapes, charts, images, controls, anchors, z-order, capabilities. | Rust/OOXML inventory. |
| ContextTarget | Что находится под указателем: cell, table, chart, shape, header и т. д. | Semantic hit testing. |
Главное правило
Заголовок раздела «Главное правило»| Не хранить живые ссылки внешнее приложение получает snapshot с stableId и revision. Нельзя хранить DOM node или изменяемый JS-объект книги и затем писать в него напрямую. Перед действием SDK заново проверяет identity, revision, права и capability. |
|---|
Предлагаемый публичный API (не внутреннее имя текущего контракта)
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);2 · ЧТЕНИЕ СТРУКТУРЫ
Как получить листы, диапазон, таблицу и графический объект
Заголовок раздела «Как получить листы, диапазон, таблицу и графический объект»| Задача | Метод публичного SDK | Что возвращается |
|---|---|---|
| Получить листы | session.getWorkbookInfo() | SheetInfo[] со stableId, name, visibility, order. |
| Прочитать ячейку | session.getCellDetails(ref) | CellDetails: raw/display/formula/type/style/metadata. |
| Прочитать диапазон | session.getRangeSnapshot(range, options) | Bounded values/formulas/styles; без всей книги. |
| Узнать таблицу | session.getTableAt(ref) | Table identity, range, columns, filters, totals, capabilities. |
| Получить объекты | session.getObjects({sheetId}) | Inventory с stableId, type, anchor, zOrder, editable. |
| Узнать объект под точкой | session.hitTest({x,y}) | ContextTarget с semantic kind и stable identity. |
Пример: найти диаграмму и переименовать через команду
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 });}Что уже отдаёт текущий Rust projection
Заголовок раздела «Что уже отдаёт текущий Rust projection»Открытие книги возвращает activeSheet, workbookSheets, viewport, previewCells, table metadata, surfaceObjects и objectInventory. Для объектов доступны stableId, sheet, type, name, anchor, relationships, z-order, visibility, lock state и mutationCapabilities. Для конкретной ячейки существует операция read_cell_details.
| Ограничение Генерального запроса «вернуть всё дерево документа» быть не должно: он разрушит sparse-модель и создаст риск утечки больших или закрытых частей книги. Чтение должно быть адресным и ограниченным. |
|---|
3 · СОБЫТИЯ
Click, double-click, selection и действия с объектами
Заголовок раздела «Click, double-click, selection и действия с объектами»| Событие SDK | Когда возникает | Основные поля |
|---|---|---|
| selectionChanged | После подтверждённого изменения выбора. | selection, previousSelection, source, revision. |
| cellActivated | Одиночный click/tap активировал ячейку. | cellRef, range, modifiers, source. |
| cellDoubleClicked | Double-click по ячейке после semantic hit test. | cellRef, editIntent, modifiers. |
| objectSelected | Выбрана shape/chart/image/control. | stableId, objectType, anchor, capabilities. |
| objectDoubleClicked | Double-click по графическому объекту. | target, defaultAction, modifiers. |
| contextMenuOpening | Right-click или mobile long-press. | target, selection, builtInItems, capabilities. |
| workbookChanged | Rust успешно применил mutation. | revision, operation, changedKeys, dirty. |
| operationRefused | Rust отказал до изменения. | code, message, target, currentRevision. |
Подписка на семантические события
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();Порядок обработки жеста
Заголовок раздела «Порядок обработки жеста»pointer/touch → hit test → selection commit → semantic event → default action
| Почему не raw DOM event Canvas node может быть пересоздан, а два клика могут попасть в разные DOM-узлы. SDK должен гарантировать semantic double-click по одному stable target, а не заставлять внешнее приложение самостоятельно угадывать это по браузерным событиям. |
|---|
4 · КОНТЕКСТНОЕ МЕНЮ
Как менять меню в зависимости от выбранного элемента
Заголовок раздела «Как менять меню в зависимости от выбранного элемента»внешнее приложение не должен искать внутренний HTML-элемент меню и вставлять туда кнопку. Вместо этого он регистрирует menu provider. Перед показом F1 формирует ContextTarget и запрашивает у provider дополнительные пункты.
| 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 для строки таблицы
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) }];});Правила расширения меню
Заголовок раздела «Правила расширения меню»-
Пункт имеет уникальный namespace ID, локализуемый label, optional icon, group, order и enabled/visible predicate.
-
Provider получает безопасный snapshot контекста, а не внутренний DOM и не весь workbook.
-
Built-in команды остаются под контролем F1; внешнее приложение добавляет или ограниченно заменяет разрешённые slots.
-
Долгий provider имеет timeout; меню не должно зависнуть из-за backend внешнее приложение.
5 · ИЗМЕНЕНИЕ ДОКУМЕНТА
Команды вместо прямой записи в структуру
Заголовок раздела «Команды вместо прямой записи в структуру»Любая операция, меняющая документ, должна пройти через Rust compute contract. Это сохраняет одинаковое поведение в браузере, desktop и server и обеспечивает atomic history.
| Шаг | Что делает SDK |
|---|---|
| 1. Сформировать intent | Operation name, target stableId/range, параметры и expectedRevision. |
| 2. Проверить доступ | Read-only, protection, capability, source format и session policy. |
| 3. Передать Rust | Тот же contract через WASM/native/service adapter. |
| 4. Применить атомарно | Успех меняет revision/history; refusal оставляет документ без partial delta. |
| 5. Опубликовать результат | Новая projection, workbookChanged либо operationRefused. |
Пример изменения с optimistic concurrency
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 === "stale-revision") { await session.refreshProjection();}Запрещённая модель
Заголовок раздела «Запрещённая модель»// НЕЛЬЗЯ: это обходит Rust, историю и сохранение.document.querySelector("[data-cell='E13']").textContent = "Доставлено";session.workbook.sheets[0].cells[12][4].value = "Доставлено";| Undo/Redo Если операция относится к редактированию книги, Rust создаёт одну central-history entry. внешний команда не должна вести отдельный стек Undo в Web. |
|---|
6 · WEB, MOBILE И SERVER