Перейти к содержимому
FastOffice

Среды исполнения и сквозной сценарий

Что меняется между native, WASM и браузером, а что остаётся одинаковым; полный путь «открыть байты → изменить → вернуть XLSX».

Что меняется между средами, а что остаётся одинаковым

Заголовок раздела «Что меняется между средами, а что остаётся одинаковым»
Среда Структурный API События пользователя Изменение документа
Browser/WASM Через session facade поверх WASM dispatch. Click, dblclick, contextmenu, keyboard, selection. Command → WASM Rust.
Desktop/native Тот же facade; transport вызывает native session. Те же semantic events Web Shell. Command → native Rust process.
Mobile WebView Тот же facade с меньшим viewport. Tap, double-tap, long-press преобразуются в те же события. Command → WASM; нужен mobile lifecycle.
Server/agent Headless session: queries и commands. Нет click; есть command/session/change events. Command → native worker/service host.

Selection, ContextTarget, Operation, Revision, Result и SavedArtifact не должны зависеть от транспорта. На мобильном long-press становится contextMenuOpening. На сервере человеческого клика нет, но агент может запросить тот же CellDetails или выполнить ту же operation с теми же правами и отказами.

  • Web: нельзя отдавать неограниченную структуру всей большой книги; запросы bounded.

  • Mobile: сессия должна переживать suspend через checkpoint/reopen, а не ссылку на WebView memory.

  • Server: требуется tenant isolation, quotas, cancellation и audit; внутренний service-host ещё требует productization.

  • Agent: разрешены только allowlisted queries/commands; credentials и raw M/VBA bodies не попадают в event payload.

8 · РАБОЧИЙ NATIVE-ПРИМЕР

Открыть байты → прочитать → изменить → сохранить

Заголовок раздела «Открыть байты → прочитать → изменить → сохранить»

Пример ниже использует тот же протокол, что и canary в репозитории. Индексы row/col нулевые. viewport ограничивает публикуемую область; sourceBytesBase64 позволяет host-приложению не давать Rust доступ к своей файловой системе.

Часть 1: открыть документ и получить структуру

import { spawn } from "node:child_process";
import { readFile } from "node:fs/promises";
import readline from "node:readline";
const cli = spawn("./target/release/compute_contract_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);

9 · NATIVE: ЧТЕНИЕ И ЗАПИСЬ

Адресная операция, revision guard и возврат XLSX-байтов

Заголовок раздела «Адресная операция, revision guard и возврат XLSX-байтов»

После открытия subsetId идентифицирует живую Rust-сессию книги. Все последующие запросы несут этот идентификатор. Изменение передаёт expectedRevision; при устаревшей ревизии ядро отказывает до мутации, а не частично применяет данные.

Часть 2: адресное чтение, атомарная правка и сохранение

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");

10 · WASM И WEB-СОБЫТИЯ

Тот же Rust-контракт в браузере; адаптер событий поверх Canvas

Заголовок раздела «Тот же Rust-контракт в браузере; адаптер событий поверх Canvas»

WASM session: JavaScript владеет транспортом, Rust — семантикой

import init, { WasmComputeSession } from "./f1_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"
});

Сегодня shell уже испускает внутреннее событие F1:surface-object-selected и имеет собственные обработчики click, dblclick и contextmenu. Для пилота их следует завернуть в один facade. Прямую подписку ниже допустимо использовать только во внутренней сборке, не как долгосрочный API внешнее приложение.

Внутренний bridge событий, который надо стабилизировать как SDK

// INTERNAL / UNSTABLE: адаптер внутри поставки F1.
surface.addEventListener("F1: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);
});
Граница ответственности внешнее приложение подписывается на objectSelected/doubleClicked/contextMenuOpening и получает ContextTarget. Только код F1 знает внутреннее имя события, DOM Canvas и hit-test реализацию.

11 · ЧТО МОЖНО ОТВЕТИТЬ внешнее приложение

Ассистент документации

Ответ собран из документации и может быть неточным — сверяйтесь с источниками.