Минимальный лист
Сколько строк нужно, чтобы получить работающий лист.

В рамке выше работает тот самый проект, чей код лежит ниже, — собранный, но не переписанный: те же файлы, только пропущенные через сборку. Считает в нём не ядро, а дублёр транспорта, поэтому числа в ячейках ничего не значат: смотреть нужно на код.
Страница, которую открывает браузер.
<!doctype html><!-- МИНИМАЛЬНЫЙ ЛИСТ — НАСТОЯЩИЙ МАЛЕНЬКИЙ ПРОЕКТ.
Это не «страница витрины», а тот проект, который встраивающий скопирует к себе целиком: точка входа, один модуль, немного стилей и манифест с зависимостями. Витрина ЗАПУСКАЕТ эти файлы (рамка грузит именно этот index.html) и ПОКАЗЫВАЕТ их же — расхождению между показанным и работающим взяться неоткуда.--><html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Минимальный лист — @f1</title> </head> <body> <!-- Лист монтируется в ВАШ узел: своей страницы он не заводит и корень документа не забирает. --> <div id="app" data-example="minimal"></div> <script type="module" src="./src/main.ts"></script> </body></html>Точка входа: здесь лист создаётся и сюда же приходят события.
import { createSheetsCore } from "@f1/preset-sheets-core";import { transport } from "./book.js";import { hostAsks } from "./host.js";import "./styles.css";
/** * МИНИМАЛЬНЫЙ ЛИСТ: сколько кода нужно, чтобы получить работающую таблицу. * * Три строки — импорт, вызов, уборка. Всё остальное в этом проекте * необязательно: книга-заглушка (`book.ts`) в продукте заменяется настоящей * транспортом контракта, а чтение темы и языка из адреса (`host.ts`) — это то, * что у встраивающего уже есть своё. *//* Тема и язык — от ХОЗЯИНА страницы (см. `host.ts`): он ставит их на свой корень документа, а листу язык передаёт свойством. */const { locale } = hostAsks();
const core = createSheetsCore({ mount: document.getElementById("app")!, transport, /* Язык листа — СВОЙСТВОМ сборки, а не чтением документа: два листа на одной странице вправе говорить на разном. */ locale,});
/* Уборка — обязанность хозяина: лист снимается вместе с вашим экраном. `import.meta.hot` здесь только для разработки витрины. */import.meta.hot?.dispose(() => core.destroy());Книга примера: данные, с которыми лист открывается.
import { createMockTransport } from "@f1/contract";
/** * КНИГА-ЗАГЛУШКА — ВМЕСТО НАСТОЯЩЕГО ЯДРА. * * Транспорт контракта — единственное место, где лист разговаривает с ядром * (инвариант №1: считает ядро, оболочка только показывает). В продукте здесь * стоит `wasm` или мост хоста, и меняется РОВНО эта строка — всё остальное в * проекте остаётся как есть. */export const transport = createMockTransport({ cellAt: (row, col) => (col === 0 ? `строка ${row + 1}` : `${(row + 1) * (col + 1)}`),});Сторона хозяина страницы: что приложение отвечает листу и что от него получает.
import { applyLocaleAttributes, resolveLocale, setLocale, type Locale } from "@f1/i18n";
/** * ЧТО ХОЗЯИН СТРАНИЦЫ ДАЁТ ЛИСТУ — тема и язык. * * У встраивающего это уже есть своё: тему он знает из настроек, язык — из * профиля пользователя. Здесь и то и другое читается из адреса * (`?theme=dark&locale=ar`) ровно затем, чтобы на любое состояние можно было * дать ссылку и чтобы его проверял обход: настройка, доступная только щелчком, * недоступна ни ссылке, ни автоматической проверке. * * ПРИЗНАКИ СТАВЯТСЯ НА КОРЕНЬ ДОКУМЕНТА — потому что это делает ХОЗЯИН, а не * лист. Лист читает тему оттуда, где стоит (`:host-context`), а язык получает * СВОЙСТВОМ сборки: два листа на одной странице вправе говорить на разном. * * НАПРАВЛЕНИЕ ПИСЬМА — ИНТЕРФЕЙСА, А НЕ ДОКУМЕНТА. `dir="rtl"` зеркалит хром * страницы; «Лист справа налево» из Excel — свойство КНИГИ, и сетка от * арабского языка интерфейса не переворачивается. Это не пропуск, а решение * (`locale.ts`). * * ЛИСТУ ОТДАЁТСЯ СТРОКА ХОЗЯИНА КАК ЕСТЬ, А НЕ СУЖЕННАЯ, и это не * мелочь. Язык листа — это ЕЩЁ И РАСКЛАДКА ВВОДА: сборка передаёт эту же строку * ядру (`inputLocale`), и от неё зависит, чем окажется набранное — «3,5» без * метки ложится текстом, с «ru-RU» становится числом 3.5. Сужай мы строку * здесь — пример показывал бы лист, до которого язык хозяина не доходит вовсе, * то есть учил бы неправде о самой заметной своей настройке. Сужение осталось * там, где оно и нужно: для КОРНЯ ДОКУМЕНТА, которым распоряжается хозяин. * * ЗАПИСЬ КОДА МОЖНО ДАВАТЬ СВОЮ. `ru_RU`, `ru_RU.UTF-8` (POSIX), `ru-RU` * (BCP-47) — сборка приведёт тег к правописанию, которого ждёт ядро, и * толковать его всё равно будет оно. Отказать ядро может, но по существу: тег * длиннее 255 байт оно не примет (`raw-input-invalid-locale`), и сборка об * этом СКАЖЕТ, а не промолчит. */export function hostAsks(): { readonly theme: "light" | "dark"; readonly locale: string } { const params = new URLSearchParams(location.search); const theme = params.get("theme") === "dark" ? "dark" : "light"; const asked = params.get("locale") ?? undefined; /* Признакам корня нужен язык, на котором поверхность ПРАВДА говорит. */ const shown: Locale = resolveLocale(asked, "ru");
document.documentElement.setAttribute("data-theme", theme); /* Язык — один на процесс: словарь модульный, и ставит его хозяин страницы. */ setLocale(shown); applyLocaleAttributes(document.documentElement, shown);
return { theme, locale: asked ?? shown };}Оформление страницы вокруг листа.
/* ЛИСТ СТИЛЕЙ НАБОРА КОМПОНЕНТОВ — ЕДИНСТВЕННАЯ СТРОКА, КОТОРУЮ НЕЛЬЗЯ ЗАБЫТЬ. Собственное оформление лист приносит в теневые корни сам, но утилиты раскладки, которыми он держит свои органы, компилируются ЭТИМ листом. Без него страница не просто выглядит иначе — вкладка ЗАВИСАЕТ (проверено: бесконечный цикл замера у сетки без размеров). У потребителя вне репозитория это `@fastoffices/formula-kit` из его node_modules. */@import "@fastoffices/formula-kit"; /* см. примечание выше *//* * ОСТАЛЬНОЕ — СТИЛИ ХОЗЯИНА, А НЕ ЛИСТА. * * Лист приносит своё оформление сам — оно живёт в теневых корнях и наружу не * течёт (ради этого и затевались web-components). Здесь только то, что нужно * СТРАНИЦЕ: убрать поля документа и отдать листу всю высоту. */html,body,#app { height: 100%; margin: 0;}
#app { display: flex; flex-direction: column;}Из каких пакетов собран пример.
{ "name": "f1-example-minimal", "private": true, "type": "module", "description": "Минимальный лист: один импорт, один вызов, один destroy().", "scripts": { "dev": "vite", "build": "vite build" }, "dependencies": { "@f1/preset-sheets-core": "*", "@f1/contract": "*", "@f1/i18n": "*" }, "devDependencies": { "vite": "^7.0.0" }}- Остальные примеры — Примеры F1.
- Правила, на которые ссылается код, — пять правил.
- Из чего собирается лист — Архитектура.
- Что можно вызвать сегодня — справочник Compute Contract.