@casualoffice/sheets 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +200 -0
- package/dist/embed/embed-runtime.js +537 -0
- package/dist/embed/embed.html +29 -0
- package/dist/embed/parser.worker.js +48474 -0
- package/dist/embed.cjs +225 -0
- package/dist/embed.cjs.map +1 -0
- package/dist/embed.d.cts +100 -0
- package/dist/embed.d.ts +100 -0
- package/dist/embed.js +204 -0
- package/dist/embed.js.map +1 -0
- package/dist/index.cjs +1549 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1530 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.worker.cjs +48469 -0
- package/dist/parser.worker.cjs.map +1 -0
- package/dist/parser.worker.js +48474 -0
- package/dist/parser.worker.js.map +1 -0
- package/dist/protocol--KyBQUjU.d.cts +171 -0
- package/dist/protocol-cEzy7S0i.d.ts +171 -0
- package/dist/sheets.cjs +677 -0
- package/dist/sheets.cjs.map +1 -0
- package/dist/sheets.d.cts +177 -0
- package/dist/sheets.d.ts +177 -0
- package/dist/sheets.js +658 -0
- package/dist/sheets.js.map +1 -0
- package/dist/signing.cjs +706 -0
- package/dist/signing.cjs.map +1 -0
- package/dist/signing.d.cts +141 -0
- package/dist/signing.d.ts +141 -0
- package/dist/signing.js +683 -0
- package/dist/signing.js.map +1 -0
- package/dist/styles.cjs +10 -0
- package/dist/styles.cjs.map +1 -0
- package/dist/styles.d.cts +2 -0
- package/dist/styles.d.ts +2 -0
- package/dist/styles.js +8 -0
- package/dist/styles.js.map +1 -0
- package/dist/types-s_O0u6Cg.d.cts +90 -0
- package/dist/types-s_O0u6Cg.d.ts +90 -0
- package/dist/univer.cjs +220 -0
- package/dist/univer.cjs.map +1 -0
- package/dist/univer.d.cts +60 -0
- package/dist/univer.d.ts +60 -0
- package/dist/univer.js +187 -0
- package/dist/univer.js.map +1 -0
- package/dist/xlsx.cjs +3388 -0
- package/dist/xlsx.cjs.map +1 -0
- package/dist/xlsx.d.cts +383 -0
- package/dist/xlsx.d.ts +383 -0
- package/dist/xlsx.js +3383 -0
- package/dist/xlsx.js.map +1 -0
- package/package.json +293 -0
- package/src/embed/EmbedHostTransport.ts +226 -0
- package/src/embed/EmbedTransport.ts +323 -0
- package/src/embed/EmbedTransport.unit.test.ts +161 -0
- package/src/embed/index.ts +40 -0
- package/src/embed/protocol.ts +258 -0
- package/src/embed-runtime/embed.html +29 -0
- package/src/embed-runtime/index.tsx +440 -0
- package/src/index.ts +16 -0
- package/src/sheets/CasualSheets.tsx +319 -0
- package/src/sheets/CasualSheetsIframe.tsx +220 -0
- package/src/sheets/api.ts +108 -0
- package/src/sheets/index.ts +11 -0
- package/src/signing/SigningPane.tsx +374 -0
- package/src/signing/SigningProvider.tsx +126 -0
- package/src/signing/captures.tsx +316 -0
- package/src/signing/controller.ts +151 -0
- package/src/signing/controller.unit.test.ts +133 -0
- package/src/signing/index.ts +44 -0
- package/src/signing/types.ts +89 -0
- package/src/styles.ts +16 -0
- package/src/univer/index.ts +17 -0
- package/src/univer/lazy-plugins.ts +280 -0
- package/src/xlsx/_perf.ts +14 -0
- package/src/xlsx/_snapshot-constants.ts +15 -0
- package/src/xlsx/comments-resource.ts +209 -0
- package/src/xlsx/constants.ts +9 -0
- package/src/xlsx/data-validation-resource.ts +219 -0
- package/src/xlsx/import.ts +35 -0
- package/src/xlsx/index.ts +40 -0
- package/src/xlsx/page-setup-resource.ts +205 -0
- package/src/xlsx/parse-impl.ts +418 -0
- package/src/xlsx/parse-in-worker.ts +82 -0
- package/src/xlsx/parser.worker.ts +39 -0
- package/src/xlsx/passthrough-resource.ts +175 -0
- package/src/xlsx/pivot-passthrough.ts +359 -0
- package/src/xlsx/style-mapping.ts +171 -0
- package/src/xlsx/tables-resource.ts +211 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sheets/index.ts","../src/sheets/CasualSheets.tsx","../src/sheets/api.ts","../src/univer/lazy-plugins.ts","../src/sheets/CasualSheetsIframe.tsx","../src/embed/protocol.ts","../src/embed/EmbedHostTransport.ts"],"sourcesContent":["/**\n * Sheets surface — React wrappers around Univer Sheets.\n */\nexport { CasualSheets, type CasualSheetsProps } from './CasualSheets';\nexport { createCasualSheetsAPI, type CasualSheetsAPI, type RangeRef } from './api';\nexport {\n CasualSheetsIframe,\n type CasualSheetsIframeProps,\n type CasualSheetsIframeRef,\n type HostFileBridge,\n} from './CasualSheetsIframe';\n","/**\n * CasualSheets — minimal React wrapper around Univer Sheets.\n *\n * Boots Univer with the eager plugin set (render + formula engine +\n * UI + docs + sheets + sheets-ui + sheets-formula + numfmt), mounts a\n * single workbook unit from `initialData`, and hands the host the\n * `CasualSheetsAPI` imperative ref via `onReady` (raw FUniver facade\n * available at `api.univer`).\n *\n * Feature plugins (conditional formatting, data validation, drawings,\n * sort, filter, hyperlinks, tables, comments, find/replace) load lazily\n * by default (`lazyPlugins`): eagerly before mount for whatever the\n * snapshot already uses, idle-loaded otherwise. Pass `lazyPlugins={false}`\n * for the minimal editor.\n *\n * Intentionally NOT included (host can layer on top via FUniver):\n * - Formula compute via Web Worker — `notExecuteFormula: false`\n * is the default; the formula engine runs on the main thread.\n * Host wires `UniverRPCMainThreadPlugin` + a worker URL itself\n * if it wants the off-main path.\n * - Snapshot swap (this component mounts a snapshot once; change\n * the React `key` to remount with a fresh snapshot).\n * - Paste-merge hooks, dev helpers, zoom-shortcut overrides,\n * facade extensions — all app concerns.\n *\n * Styles: host must import `@casualoffice/sheets/styles.css`\n * (or the per-plugin CSS) once at app boot. Tree-shaking strips the\n * styles from this entry if the host doesn't reach the styles export.\n */\n\nimport { useEffect, useRef, type CSSProperties } from 'react';\nimport {\n ICommandService,\n LocaleType,\n LogLevel,\n ThemeService,\n Univer,\n UniverInstanceType,\n type ICommandInfo,\n type IExecutionOptions,\n type IWorkbookData,\n type ILocales,\n} from '@univerjs/core';\nimport { FUniver } from '@univerjs/core/facade';\nimport { defaultTheme } from '@univerjs/themes';\n\nimport { UniverRenderEnginePlugin } from '@univerjs/engine-render';\nimport { UniverFormulaEnginePlugin } from '@univerjs/engine-formula';\nimport { UniverUIPlugin } from '@univerjs/ui';\nimport { UniverDocsPlugin } from '@univerjs/docs';\nimport { UniverDocsUIPlugin } from '@univerjs/docs-ui';\nimport { UniverSheetsPlugin } from '@univerjs/sheets';\nimport { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';\nimport { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula';\nimport { UniverSheetsFormulaUIPlugin } from '@univerjs/sheets-formula-ui';\nimport { UniverSheetsNumfmtPlugin } from '@univerjs/sheets-numfmt';\nimport { UniverSheetsNumfmtUIPlugin } from '@univerjs/sheets-numfmt-ui';\n\nimport { createCasualSheetsAPI, type CasualSheetsAPI } from './api';\nimport { eagerLoadForSnapshot, idleLoadAll, setUniverForLazyLoad } from '../univer/lazy-plugins';\n\nexport interface CasualSheetsProps {\n /** Workbook snapshot to mount. Read once on initial mount; change\n * the React `key` on this component to remount with a new\n * workbook. */\n initialData: IWorkbookData;\n /** Called after the workbook unit is created. Hands back the\n * `CasualSheetsAPI` imperative ref — the SDK's stable integration\n * surface (snapshot I/O, xlsx import, selection, command dispatch).\n * The raw FUniver facade is on `api.univer` as the escape hatch. */\n onReady?: (api: CasualSheetsAPI) => void;\n /** Debounced stream of workbook snapshots, emitted after edits\n * settle. This is the \"host persists it\" half of the Excalidraw\n * model — the editor stays storage-unaware and the host writes the\n * snapshot wherever it likes (localStorage, server, …). Driven by\n * Univer's mutation hook (`onMutationExecutedForCollab`), not UI\n * events, so it captures every edit including programmatic ones.\n * May fire for background/structural mutations too; treat each call\n * as \"current state, persist if you care\". */\n onChange?: (snapshot: IWorkbookData) => void;\n /** Debounce window for `onChange`, in ms. Default 400. */\n onChangeDebounceMs?: number;\n /** Lazy-load the feature plugins (conditional formatting, data\n * validation, hyperlinks, notes, tables, comments, drawings, sort,\n * filter, find/replace). Default `true`: plugins whose data is in\n * `initialData` load eagerly before mount (so nothing is dropped on\n * open), the rest idle-load after first paint. Set `false` for the\n * minimal editor (render + formula + numfmt only) — the embed-iframe\n * build does this to stay a single self-contained bundle. */\n lazyPlugins?: boolean;\n /** Locale identifier. Defaults to `LocaleType.EN_US`. */\n locale?: LocaleType;\n /** Locale string bundle. Optional — Univer's default English\n * strings load if omitted. */\n locales?: ILocales;\n /** Univer log level. Defaults to `LogLevel.WARN`. */\n logLevel?: LogLevel;\n /** Univer chrome toggles. Defaults: header / toolbar / footer off,\n * context menu on — matches Casual Sheets' embedded shape. */\n ui?: {\n header?: boolean;\n toolbar?: boolean;\n footer?: boolean;\n contextMenu?: boolean;\n };\n /** Override the Univer theme object (colour palette). Defaults to\n * Univer's `defaultTheme`. Distinct from `appearance` (light/dark). */\n theme?: typeof defaultTheme;\n /** Light or dark mode. Reactive — flipping it re-themes the live\n * editor via `ThemeService.setDarkMode` (canvas colours, notifications,\n * and Univer's own `univer-dark` class). Defaults to light.\n * Note: Univer's Workbench applies the `univer-dark` class to the\n * document root (`<html>`) itself, so dark mode is page-global by\n * Univer's design — a host that embeds the editor inside a light page\n * should scope the editor or accept the global dark CSS. */\n appearance?: 'light' | 'dark';\n /** Container style. Default fills the parent. */\n style?: CSSProperties;\n /** Container className for additional styling hooks. */\n className?: string;\n /** Optional test id for the host container. */\n testId?: string;\n}\n\nconst DEFAULT_STYLE: CSSProperties = {\n width: '100%',\n height: '100%',\n position: 'relative',\n};\n\nconst DEFAULT_UI = {\n header: false,\n toolbar: false,\n footer: false,\n contextMenu: true,\n};\n\nexport function CasualSheets({\n initialData,\n onReady,\n onChange,\n onChangeDebounceMs = 400,\n lazyPlugins = true,\n locale = LocaleType.EN_US,\n locales,\n logLevel = LogLevel.WARN,\n ui,\n theme = defaultTheme,\n appearance = 'light',\n style,\n className,\n testId = 'casual-sheets',\n}: CasualSheetsProps) {\n const hostRef = useRef<HTMLDivElement>(null);\n // Keep the latest onChange callable without re-subscribing (the effect\n // mounts once). The subscription itself is only wired when onChange was\n // present at mount.\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n const hasOnChange = useRef(!!onChange).current;\n // The live FUniver facade, captured at mount so the reactive appearance\n // effect can reach Univer's ThemeService without re-running boot.\n const apiRef = useRef<CasualSheetsAPI | null>(null);\n\n useEffect(() => {\n const container = hostRef.current;\n if (!container) return;\n\n const univer = new Univer({\n theme,\n locale,\n locales,\n logLevel,\n });\n\n const uiOpts = { ...DEFAULT_UI, ...ui, container };\n\n univer.registerPlugin(UniverRenderEnginePlugin);\n // Formula compute runs on the MAIN THREAD (no RPC worker). The library build\n // externalises @univerjs (see tsup.config.ts), so the host provides a single\n // redi/@univerjs copy — the duplicate-redi that previously made the formula\n // plugins fail (and which the SDK worked around by dropping them) is gone.\n // apps/web offloads compute to a worker for perf on huge sheets, but a worker\n // shipped inside a published package is brittle to bundle in arbitrary hosts;\n // main-thread keeps the SDK editor self-contained. SDK restructure Batch 2.\n univer.registerPlugin(UniverFormulaEnginePlugin);\n univer.registerPlugin(UniverUIPlugin, uiOpts);\n univer.registerPlugin(UniverDocsPlugin);\n univer.registerPlugin(UniverDocsUIPlugin);\n univer.registerPlugin(UniverSheetsPlugin);\n univer.registerPlugin(UniverSheetsUIPlugin);\n univer.registerPlugin(UniverSheetsFormulaPlugin);\n univer.registerPlugin(UniverSheetsFormulaUIPlugin);\n univer.registerPlugin(UniverSheetsNumfmtPlugin);\n univer.registerPlugin(UniverSheetsNumfmtUIPlugin);\n\n // Register the lazy-loader's holder so the eager/idle loaders can reach this\n // univer. CasualSheets uses its own (bundled) copy of the loader — the\n // exported `@casualoffice/sheets/univer` is @internal and only the host app's\n // legacy UniverSheet consumes it, so there's no cross-instance state to share.\n if (lazyPlugins) setUniverForLazyLoad(univer);\n\n let cancelled = false;\n let changeTimer: ReturnType<typeof setTimeout> | null = null;\n let changeSub: { dispose: () => void } | undefined;\n\n void (async () => {\n // Eager-load any feature plugin whose data already lives in initialData\n // (CF rules, tables, hyperlinks, …) BEFORE createUnit — Univer's resource\n // manager silently drops keys for plugins that aren't registered when it\n // reads the snapshot. Skipped entirely when lazyPlugins is false.\n if (lazyPlugins) {\n await eagerLoadForSnapshot(univer, initialData);\n if (cancelled) return;\n }\n\n univer.createUnit(UniverInstanceType.UNIVER_SHEET, initialData);\n\n const api = createCasualSheetsAPI(FUniver.newAPI(univer));\n apiRef.current = api;\n // Apply the initial appearance now that the editor exists (the reactive\n // effect below also runs on mount, but apiRef may not be set yet when it\n // first fires — this guarantees dark mode from the first paint).\n applyAppearance(api, container, appearance);\n onReady?.(api);\n\n // Debounced snapshot stream → onChange. Subscribed AFTER createUnit so the\n // initial unit-creation mutations don't fire a spurious first emit. Uses the\n // mutation hook (CLAUDE.md hard rule), never UI events.\n if (hasOnChange) {\n const injector = (api.univer as unknown as { _injector?: { get(t: unknown): unknown } })\n ._injector;\n const cmdSvc = injector?.get(ICommandService) as\n | {\n onMutationExecutedForCollab: (\n l: (info: ICommandInfo, options?: IExecutionOptions) => void,\n ) => { dispose: () => void };\n }\n | undefined;\n changeSub = cmdSvc?.onMutationExecutedForCollab(() => {\n if (changeTimer) clearTimeout(changeTimer);\n changeTimer = setTimeout(() => {\n const snap = api.getSnapshot();\n if (snap) onChangeRef.current?.(snap);\n }, onChangeDebounceMs);\n });\n // If we unmounted during the eager-load await, cleanup already ran with\n // changeSub still undefined — dispose this late subscription.\n if (cancelled) changeSub?.dispose();\n }\n\n // Idle-load the remaining feature plugins so Insert / Data / Format actions\n // are ready when the user reaches them.\n if (lazyPlugins) idleLoadAll(univer);\n })();\n\n return () => {\n cancelled = true;\n if (changeTimer) clearTimeout(changeTimer);\n changeSub?.dispose();\n apiRef.current = null;\n if (lazyPlugins) setUniverForLazyLoad(null);\n // Defer disposal off the React render phase — Univer owns its\n // own React root, and a synchronous unmount mid-render warns\n // and leaves the canvas detached.\n const toDispose = univer;\n queueMicrotask(() => toDispose.dispose());\n };\n // initialData is intentionally NOT in the dep array — the wrapper\n // mounts the snapshot once. Hosts that need to swap workbooks\n // change the React `key` to force a remount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Reactive appearance. Runs after the boot effect (apiRef populated on first\n // mount) and re-runs whenever `appearance` flips, re-theming the live editor.\n useEffect(() => {\n const api = apiRef.current;\n const container = hostRef.current;\n if (!api || !container) return;\n applyAppearance(api, container, appearance);\n }, [appearance]);\n\n return (\n <div\n ref={hostRef}\n style={{ ...DEFAULT_STYLE, ...style }}\n className={className}\n data-testid={testId}\n />\n );\n}\n\n/**\n * Apply light/dark to a live editor. `ThemeService.setDarkMode` is the source of\n * truth — it flips the canvas colours, the internals that subscribe to\n * `darkMode$` (notifications, message containers), AND Univer's Workbench toggles\n * the `univer-dark` class on the document root for its compiled dark CSS. We also\n * mirror the class onto the editor container as race-insurance (the Workbench\n * effect can land a frame after ours). Mirrors the app's ThemeBridge.\n */\nfunction applyAppearance(\n api: CasualSheetsAPI,\n container: HTMLElement,\n appearance: 'light' | 'dark',\n): void {\n const dark = appearance === 'dark';\n container.classList.toggle('univer-dark', dark);\n try {\n const injector = (api.univer as unknown as { _injector?: { get(t: unknown): unknown } })\n ._injector;\n const themeService = injector?.get(ThemeService) as\n | { setDarkMode(b: boolean): void; darkMode: boolean }\n | undefined;\n if (themeService && themeService.darkMode !== dark) themeService.setDarkMode(dark);\n } catch {\n /* ThemeService unavailable — the class toggle still themes visible chrome */\n }\n}\n","/**\n * CasualSheetsAPI — the imperative ref handed to a host via `<CasualSheets onReady>`.\n *\n * This is the SDK's stable integration surface (Excalidraw's model: props +\n * imperative ref). Hosts drive the editor through these methods rather than\n * reaching into Univer directly; `api.univer` is the documented escape hatch and\n * is explicitly NOT covered by semver — everything else here is.\n *\n * Scope of THIS batch (SDK restructure, Phase 1 step 3):\n * getSnapshot / loadSnapshot / getSelection / executeCommand / univer\n *\n * Deferred to later, clearly-scoped batches (kept off the type until they work,\n * so the surface never advertises a method that throws):\n * - importXlsx / exportXlsx — the xlsx I/O batch. importXlsx must NOT be a\n * plain `import('../xlsx')` here: the main tsup config is `splitting:false`,\n * so a dynamic import gets inlined and balloons the editor entry from ~11KB\n * to ~200KB of parser code for hosts that never open a file. The xlsx-I/O\n * batch wires it as its own chunk (and lifts the export converter out of\n * apps/web — the SDK xlsx module is import-only today).\n * - attachCollab — belongs to the storage/collab adapter phase (Phase 2);\n * the editor ships collab-unaware until then.\n */\n\n// Side-effect import: registers the Sheets FUniver mixins\n// (getActiveWorkbook / createWorkbook / FWorkbook.save / FRange.getRange / …)\n// onto the core FUniver facade — both the runtime methods AND the TypeScript\n// type augmentation this file relies on. Without it, FUniver is the bare core\n// facade and these methods exist neither at type-check nor at runtime.\nimport '@univerjs/sheets/facade';\nimport { ThemeService } from '@univerjs/core';\nimport type { FUniver } from '@univerjs/core/facade';\nimport type { IRange, IWorkbookData } from '@univerjs/core';\n\n/** The active selection, as a sheet-scoped range. */\nexport interface RangeRef {\n /** Workbook unit id the selection belongs to. */\n unitId: string;\n /** Worksheet (sub-unit) id the selection belongs to. */\n sheetId: string;\n /** `{ startRow, startColumn, endRow, endColumn }`. */\n range: IRange;\n}\n\nexport interface CasualSheetsAPI {\n /** Current workbook as an `IWorkbookData` snapshot. `null` before the unit\n * is created (shouldn't happen after `onReady`, but typed defensively). */\n getSnapshot(): IWorkbookData | null;\n /** Replace the workbook with a new snapshot. Disposes the current unit and\n * mounts `data` as a fresh one. */\n loadSnapshot(data: IWorkbookData): void;\n /** The active selection, or `null` when there is none. */\n getSelection(): RangeRef | null;\n /** Dispatch a Univer command by id. Resolves to the command's boolean\n * result. */\n executeCommand(id: string, params?: object): Promise<boolean>;\n /** Imperative light/dark switch — the API equivalent of the reactive\n * `appearance` prop. Flips Univer's `ThemeService.setDarkMode` (canvas\n * colours + the `univer-dark` class Univer applies to the document root). */\n setTheme(appearance: 'light' | 'dark'): void;\n /** The FUniver facade — documented escape hatch, NOT covered by semver. */\n univer: FUniver;\n}\n\n/**\n * Build the imperative API over a live FUniver facade. The wrapper holds no\n * state of its own — every call reads the current active workbook, so it stays\n * correct across `loadSnapshot` swaps without the host re-acquiring the ref.\n */\nexport function createCasualSheetsAPI(univerAPI: FUniver): CasualSheetsAPI {\n return {\n univer: univerAPI,\n\n getSnapshot() {\n return univerAPI.getActiveWorkbook()?.save() ?? null;\n },\n\n loadSnapshot(data) {\n const current = univerAPI.getActiveWorkbook();\n if (current) univerAPI.disposeUnit(current.getId());\n univerAPI.createWorkbook(data);\n },\n\n getSelection() {\n const wb = univerAPI.getActiveWorkbook();\n const range = wb?.getActiveRange();\n if (!wb || !range) return null;\n return {\n unitId: wb.getId(),\n sheetId: wb.getActiveSheet().getSheetId(),\n range: range.getRange(),\n };\n },\n\n executeCommand(id, params) {\n return univerAPI.executeCommand(id, params) as Promise<boolean>;\n },\n\n setTheme(appearance) {\n const dark = appearance === 'dark';\n const injector = (univerAPI as unknown as { _injector?: { get(t: unknown): unknown } })\n ._injector;\n const themeService = injector?.get(ThemeService) as\n | { setDarkMode(b: boolean): void; darkMode: boolean }\n | undefined;\n if (themeService && themeService.darkMode !== dark) themeService.setDarkMode(dark);\n },\n };\n}\n","import type { Univer, IWorkbookData } from '@univerjs/core';\n\n/**\n * Lazy plugin loading — pipeline Stage 4. The cheap plugins (render,\n * formula, sheets, sheets-ui, numfmt, docs) stay eager because every\n * workbook needs them. The heavy / feature-specific plugins (CF, DV,\n * hyperlink, table, note, thread-comment, drawing, sort, filter,\n * find-replace) load on demand:\n *\n * 1. Eagerly when a snapshot is about to mount and we detect the\n * plugin's resource key on it (e.g. `SHEET_CONDITIONAL_FORMATTING_PLUGIN`\n * in `data.resources`). This is the safety net: missing this would\n * silently drop plugin data on file open.\n * 2. Lazily when the user reaches for the feature — opens the Data\n * tab (sort/filter), hits Ctrl+F (find-replace), uses the Insert\n * tab (drawing), etc. The shell hooks `ensurePlugin(...)` into\n * these triggers and awaits before the action runs.\n *\n * Each loader returns the plugins-to-register in the correct order\n * (base before UI, same as `plugins.ts`). Registration order matters\n * in Univer; the loaders bundle a small group together to keep that\n * locality explicit.\n */\n\nexport type LazyPluginGroup =\n | 'cf'\n | 'dv'\n | 'hyperlink'\n | 'note'\n | 'table'\n | 'threadComment'\n | 'drawing'\n | 'sort'\n | 'filter'\n | 'findReplace';\n\ntype Loader = () => Promise<Array<[unknown, unknown?]>>;\n\nconst LOADERS: Record<LazyPluginGroup, Loader> = {\n cf: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-conditional-formatting'),\n import('@univerjs/sheets-conditional-formatting-ui'),\n ]);\n return [\n [base.UniverSheetsConditionalFormattingPlugin],\n [ui.UniverSheetsConditionalFormattingUIPlugin],\n ];\n },\n dv: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-data-validation'),\n import('@univerjs/sheets-data-validation-ui'),\n ]);\n return [\n [base.UniverSheetsDataValidationPlugin],\n [ui.UniverSheetsDataValidationUIPlugin],\n ];\n },\n hyperlink: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-hyper-link'),\n import('@univerjs/sheets-hyper-link-ui'),\n ]);\n return [\n [base.UniverSheetsHyperLinkPlugin],\n [ui.UniverSheetsHyperLinkUIPlugin],\n ];\n },\n note: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-note'),\n import('@univerjs/sheets-note-ui'),\n ]);\n return [[base.UniverSheetsNotePlugin], [ui.UniverSheetsNoteUIPlugin]];\n },\n table: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-table'),\n import('@univerjs/sheets-table-ui'),\n ]);\n return [[base.UniverSheetsTablePlugin], [ui.UniverSheetsTableUIPlugin]];\n },\n threadComment: async () => {\n const [tc, tcUi, sheetsTc, sheetsTcUi] = await Promise.all([\n import('@univerjs/thread-comment'),\n import('@univerjs/thread-comment-ui'),\n import('@univerjs/sheets-thread-comment'),\n import('@univerjs/sheets-thread-comment-ui'),\n ]);\n return [\n [tc.UniverThreadCommentPlugin],\n [tcUi.UniverThreadCommentUIPlugin],\n [sheetsTc.UniverSheetsThreadCommentPlugin],\n [sheetsTcUi.UniverSheetsThreadCommentUIPlugin],\n ];\n },\n drawing: async () => {\n const [d, dUi, sd, sdUi] = await Promise.all([\n import('@univerjs/drawing'),\n import('@univerjs/drawing-ui'),\n import('@univerjs/sheets-drawing'),\n import('@univerjs/sheets-drawing-ui'),\n // Side-effect imports: install FWorksheet.insertImage / getImages /\n // updateImages on the facade prototype. Without these, code that\n // reaches in via the FUniver facade (e2e specs, future shell glue)\n // sees an undefined method even though the plugin is registered.\n import('@univerjs/sheets-drawing/facade'),\n import('@univerjs/sheets-drawing-ui/facade'),\n ]);\n return [\n [d.UniverDrawingPlugin],\n [dUi.UniverDrawingUIPlugin],\n [sd.UniverSheetsDrawingPlugin],\n [sdUi.UniverSheetsDrawingUIPlugin],\n ];\n },\n sort: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-sort'),\n import('@univerjs/sheets-sort-ui'),\n ]);\n return [[base.UniverSheetsSortPlugin], [ui.UniverSheetsSortUIPlugin]];\n },\n filter: async () => {\n const [base, ui] = await Promise.all([\n import('@univerjs/sheets-filter'),\n import('@univerjs/sheets-filter-ui'),\n ]);\n return [[base.UniverSheetsFilterPlugin], [ui.UniverSheetsFilterUIPlugin]];\n },\n findReplace: async () => {\n const [base, sheets] = await Promise.all([\n import('@univerjs/find-replace'),\n import('@univerjs/sheets-find-replace'),\n ]);\n return [[base.UniverFindReplacePlugin], [sheets.UniverSheetsFindReplacePlugin]];\n },\n};\n\n/**\n * Map from a Univer `data.resources[].name` to the lazy group that owns\n * it. Used by `eagerLoadForSnapshot` to pre-register plugins whose\n * state already lives on the workbook so file-open never silently\n * drops a CF rule / table / drawing / etc.\n */\nconst RESOURCE_NAME_TO_GROUP: Record<string, LazyPluginGroup> = {\n SHEET_CONDITIONAL_FORMATTING_PLUGIN: 'cf',\n SHEET_DATA_VALIDATION_PLUGIN: 'dv',\n SHEET_HYPER_LINK_PLUGIN: 'hyperlink',\n SHEET_NOTE_PLUGIN: 'note',\n SHEET_TABLE_PLUGIN: 'table',\n SHEET_THREAD_COMMENT_BASE_PLUGIN: 'threadComment',\n SHEET_DRAWING_PLUGIN: 'drawing',\n SHEET_SORT_PLUGIN: 'sort',\n SHEET_FILTER_PLUGIN: 'filter',\n};\n\nconst loaded = new Set<LazyPluginGroup>();\nconst inflight = new Map<LazyPluginGroup, Promise<void>>();\n\n/**\n * Module-level reference to the live Univer instance, set by\n * `UniverSheet.tsx` immediately after `new Univer()`. Lets shell code\n * call `ensurePluginByName(group)` without plumbing the Univer\n * instance through React context (the FUniver facade doesn't expose\n * its host). Cleared on dispose so callers fail loudly if they hit\n * the lazy path after teardown.\n */\nlet currentUniver: Univer | null = null;\n\nexport function setUniverForLazyLoad(univer: Univer | null): void {\n currentUniver = univer;\n}\n\n/**\n * Idempotent: subsequent calls for the same group resolve immediately\n * if the plugin is already loaded; concurrent calls share the same\n * in-flight promise so we never double-register.\n */\nexport function ensurePlugin(univer: Univer, group: LazyPluginGroup): Promise<void> {\n if (loaded.has(group)) return Promise.resolve();\n const existing = inflight.get(group);\n if (existing) return existing;\n const loader = LOADERS[group];\n if (!loader) return Promise.resolve();\n const p = loader().then((plugins) => {\n for (const [PluginCtor, config] of plugins) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n univer.registerPlugin(PluginCtor as any, config);\n }\n loaded.add(group);\n inflight.delete(group);\n });\n inflight.set(group, p);\n return p;\n}\n\n/**\n * Shell-friendly variant of `ensurePlugin` that pulls the Univer\n * instance from the module-level holder set by `UniverSheet.tsx`. Use\n * this anywhere we only have the FUniver facade (toolbar callbacks,\n * panel handlers). Returns a resolved promise if the holder is empty —\n * the worst case is a no-op, never a throw.\n */\nexport function ensurePluginByName(group: LazyPluginGroup): Promise<void> {\n if (loaded.has(group)) return Promise.resolve();\n if (!currentUniver) return Promise.resolve();\n return ensurePlugin(currentUniver, group);\n}\n\n/**\n * Walk a snapshot for plugin-owned resources + side-channel hyperlinks\n * and eagerly load every group that's referenced. Returns a promise\n * the caller MUST await before `createUnit` — Univer's resource manager\n * silently discards keys for plugins that aren't yet registered.\n */\nexport async function eagerLoadForSnapshot(univer: Univer, snapshot: IWorkbookData): Promise<void> {\n const groups = new Set<LazyPluginGroup>();\n const resources = snapshot.resources ?? [];\n for (const r of resources) {\n const g = RESOURCE_NAME_TO_GROUP[r.name];\n if (g) groups.add(g);\n }\n // Hyperlinks live inline in cell.p (Stage 5) — the hyperlink plugin\n // still owns click handling / context-menu actions, so eager-load\n // when any cell has a HYPERLINK customRange.\n if (snapshotHasHyperlinks(snapshot)) groups.add('hyperlink');\n await Promise.all(Array.from(groups).map((g) => ensurePlugin(univer, g)));\n}\n\n/**\n * Idle-load every remaining lazy group AFTER Univer is mounted. The\n * bundle split (each group ships as its own chunk) is the persistent\n * boot-time win — the initial paint doesn't pay for them. This\n * idle-load just ensures they're all eventually registered so a user\n * clicking \"Insert > Table\" doesn't hit a no-op.\n *\n * Loads in parallel via `requestIdleCallback` so they don't compete\n * with the first paint, falling back to `setTimeout(0)` in browsers\n * without rIC (Safari).\n */\nexport function idleLoadAll(univer: Univer): void {\n const groups = Object.keys(LOADERS) as LazyPluginGroup[];\n schedule(() => {\n for (const g of groups) {\n void ensurePlugin(univer, g);\n }\n });\n}\n\nfunction schedule(fn: () => void): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ric = (globalThis as any).requestIdleCallback as\n | ((cb: () => void, opts?: { timeout: number }) => number)\n | undefined;\n if (ric) ric(fn, { timeout: 500 });\n else setTimeout(fn, 0);\n}\n\nfunction snapshotHasHyperlinks(snapshot: IWorkbookData): boolean {\n const sheetOrder = snapshot.sheetOrder ?? [];\n for (const sid of sheetOrder) {\n const sheet = snapshot.sheets?.[sid];\n if (!sheet?.cellData) continue;\n const cellData = sheet.cellData as Record<\n string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Record<string, { p?: any }>\n >;\n for (const r of Object.keys(cellData)) {\n const row = cellData[r];\n for (const c of Object.keys(row)) {\n const ranges = row[c]?.p?.body?.customRanges ?? [];\n if (ranges.some((cr: { rangeType?: number }) => cr.rangeType === 0)) return true;\n }\n }\n }\n return false;\n}\n","/**\n * CasualSheetsIframe — the iframe-mounting variant of `<CasualSheets>`.\n * Sheet sibling of `@casualoffice/docs`'s `<CasualEditorIframe>`;\n * see doc 16 in the parent repo.\n *\n * Differs from CasualEditorIframe in one place: the embed URL params\n * carry `app=sheet` and the embed-runtime inside the iframe knows to\n * convert raw xlsx bytes into an `IWorkbookData` snapshot via\n * `xlsxToWorkbookData` before mounting `<CasualSheets>`.\n *\n * Public surface intentionally identical to CasualSheets. v0.6 will\n * rename CasualSheetsIframe → CasualSheets and the existing direct-\n * mount component → CasualSheetsDirect.\n */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n type CSSProperties,\n type MutableRefObject,\n} from 'react';\n\nimport { EmbedHostTransport } from '../embed/EmbedHostTransport';\nimport type {\n CasualErrorData,\n CommandExecuteData,\n LoadResponseData,\n SaveResponseData,\n SelectionChangedData,\n SelectionFormatStateData,\n TelemetryEventData,\n} from '../embed/protocol';\n\n/** What the host-side load/save handlers consume + return. The wrapper\n * binds these to the host's FileSource via simple adapters. */\nexport interface HostFileBridge {\n open(docId: string): Promise<{ bytes: ArrayBuffer; name: string; etag?: string }>;\n save?(docId: string, bytes: ArrayBuffer, opts?: { etag?: string }): Promise<{ etag: string }>;\n}\n\nexport interface CasualSheetsIframeRef {\n setViewMode(mode: 'preview' | 'editor'): void;\n iframe(): HTMLIFrameElement | null;\n /** Dispatch a formatting / navigation command (bold, italic, undo, …)\n * against the iframe's active selection. v0.6+. */\n executeCommand(command: CommandExecuteData['command']): void;\n}\n\nexport interface CasualSheetsIframeProps {\n /** Host-side bytes bridge. The wrapper round-trips load / save to the\n * iframe through postMessage; bytes never live in the iframe's origin\n * except in-memory while the workbook is open. */\n fileSource: HostFileBridge;\n docId: string;\n /** Default `editor`. Live changes push casual.command.set.viewmode. */\n viewMode?: 'preview' | 'editor';\n /** Default `/embed/sheets`. Consumer copies the SDK's\n * `dist/embed/{embed.html, embed-runtime.js, embed-runtime.css}`\n * to this path. */\n embedBasePath?: string;\n onSelectionChanged?: (data: SelectionChangedData) => void;\n /** Fires when the active cell's format flags change (bold, italic,\n * …). Drive's custom toolbar reflects this state in the button\n * \"pressed\" indicators. v0.6+. */\n onSelectionFormatState?: (data: SelectionFormatStateData) => void;\n onTelemetry?: (data: TelemetryEventData) => void;\n onError?: (data: CasualErrorData) => void;\n style?: CSSProperties;\n className?: string;\n testId?: string;\n}\n\nconst DEFAULT_STYLE: CSSProperties = {\n width: '100%',\n height: '100%',\n border: 'none',\n display: 'block',\n};\n\nexport const CasualSheetsIframe = forwardRef<CasualSheetsIframeRef, CasualSheetsIframeProps>(\n function CasualSheetsIframe(props, ref) {\n const {\n fileSource,\n docId,\n viewMode = 'editor',\n embedBasePath = '/embed/sheets',\n onSelectionChanged,\n onSelectionFormatState,\n onTelemetry,\n onError,\n style,\n className,\n testId = 'casual-sheets-iframe',\n } = props;\n\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const transportRef = useRef<EmbedHostTransport | null>(null);\n const fileSourceRef = useRef(fileSource);\n fileSourceRef.current = fileSource;\n\n const onLoad = useCallback(async (req: { docId: string }): Promise<LoadResponseData> => {\n try {\n const { bytes, name, etag } = await fileSourceRef.current.open(req.docId);\n return {\n ok: true,\n bytes,\n fileName: name,\n ...(etag !== undefined ? { etag } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n code: 'open_failed',\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }, []);\n\n const onSave = useCallback(\n async (req: {\n docId: string;\n bytes: ArrayBuffer;\n baseEtag?: string;\n }): Promise<SaveResponseData> => {\n try {\n if (!fileSourceRef.current.save) {\n return {\n ok: false,\n code: 'save_unsupported',\n message: 'host fileSource does not implement save',\n };\n }\n const opts = req.baseEtag !== undefined ? { etag: req.baseEtag } : undefined;\n const { etag } = await fileSourceRef.current.save(req.docId, req.bytes, opts);\n return { ok: true, etag };\n } catch (err) {\n return {\n ok: false,\n code: 'save_failed',\n message: err instanceof Error ? err.message : String(err),\n };\n }\n },\n [],\n );\n\n const onIframeLoad = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe?.contentWindow) return;\n transportRef.current?.destroy();\n const transport = new EmbedHostTransport({\n app: 'sheet',\n iframeWindow: iframe.contentWindow,\n embedOrigin: window.location.origin,\n });\n transport.on({\n onLoadRequest: onLoad,\n onSaveRequest: onSave,\n ...(onSelectionChanged ? { onSelectionChanged } : {}),\n ...(onSelectionFormatState ? { onSelectionFormatState } : {}),\n ...(onTelemetry ? { onTelemetry } : {}),\n ...(onError ? { onError } : {}),\n onEditorReady: () => {\n transport.sendHostHello({ capabilities: ['load', 'save'] });\n transport.sendSetViewMode({ viewMode });\n },\n });\n transportRef.current = transport;\n }, [\n onLoad,\n onSave,\n onSelectionChanged,\n onSelectionFormatState,\n onTelemetry,\n onError,\n viewMode,\n ]);\n\n useEffect(() => {\n transportRef.current?.sendSetViewMode({ viewMode });\n }, [viewMode]);\n\n useEffect(() => {\n return () => {\n transportRef.current?.destroy();\n transportRef.current = null;\n };\n }, []);\n\n if (ref) {\n const apiRef = ref as MutableRefObject<CasualSheetsIframeRef | null>;\n apiRef.current = {\n setViewMode: (mode) => transportRef.current?.sendSetViewMode({ viewMode: mode }),\n iframe: () => iframeRef.current,\n executeCommand: (command) => transportRef.current?.sendCommandExecute({ command }),\n };\n }\n\n const url =\n `${embedBasePath}/embed.html` +\n `?app=sheet` +\n `&docId=${encodeURIComponent(docId)}` +\n `&viewMode=${viewMode}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={url}\n onLoad={onIframeLoad}\n title=\"Casual Sheets\"\n sandbox=\"allow-scripts allow-same-origin allow-downloads allow-modals\"\n style={{ ...DEFAULT_STYLE, ...style }}\n className={className}\n data-testid={testId}\n />\n );\n },\n);\n","/**\n * Iframe protocol — wire envelopes that match\n * `docs/internal/13-iframe-protocol.md`.\n *\n * Mirror updates to the doc whenever a new envelope shape lands.\n * The discriminator on `type` (always starts with `casual.`) and\n * the per-envelope `data` shape are the contract.\n */\n\nimport type {\n CancelReason,\n SignatureCompletePayload,\n SignatureField,\n SignatureMode,\n SignedFieldPayload,\n} from '../signing/types';\n\nexport type CasualApp = 'docs' | 'sheet';\n\n/** Common envelope shape — every postMessage on the wire matches this. */\nexport interface CasualEnvelope<T = unknown> {\n type: string;\n app: CasualApp;\n /** Per-request id for request/response correlation. Empty for fire-and-forget. */\n id?: string;\n /** Protocol version. Bumped only on breaking changes. */\n v: 1;\n data: T;\n}\n\n// ---------------------------------------------------------------\n// Handshake\n// ---------------------------------------------------------------\n\nexport interface EditorHelloData {\n capabilities: string[];\n version: string;\n commit: string;\n}\n\nexport interface HostHelloData {\n capabilities: string[];\n authToken?: string;\n}\n\n// ---------------------------------------------------------------\n// Load + save (editor → host requests; host → editor responses)\n// ---------------------------------------------------------------\n\nexport interface LoadRequestData {\n docId: string;\n}\n\nexport interface LoadResponseDataOk {\n ok: true;\n bytes: ArrayBuffer;\n etag?: string;\n fileName: string;\n readOnly?: boolean;\n}\n\nexport interface LoadResponseDataErr {\n ok: false;\n code: string;\n message?: string;\n}\n\nexport type LoadResponseData = LoadResponseDataOk | LoadResponseDataErr;\n\nexport interface SaveRequestData {\n docId: string;\n bytes: ArrayBuffer;\n baseEtag?: string;\n}\n\nexport interface SaveResponseDataOk {\n ok: true;\n etag: string;\n}\n\nexport interface SaveResponseDataErr {\n ok: false;\n code: string;\n etag?: string;\n message?: string;\n}\n\nexport type SaveResponseData = SaveResponseDataOk | SaveResponseDataErr;\n\n// ---------------------------------------------------------------\n// Selection + telemetry + lock (editor → host notifications)\n// ---------------------------------------------------------------\n\nexport interface SelectionChangedData {\n docs?: { paraId: string; from: number; to: number; selectedText: string };\n sheet?: { sheet: string; from: string; to: string };\n}\n\nexport interface TelemetryEventData {\n kind: string;\n /** Arbitrary metric fields. */\n [k: string]: unknown;\n}\n\nexport interface LockLostData {\n reason: 'taken_by_other' | 'expired' | 'host_revoked';\n}\n\n// ---------------------------------------------------------------\n// Commands (host → editor)\n// ---------------------------------------------------------------\n\nexport interface CommandSetReadOnlyData {\n readOnly: boolean;\n}\n\nexport interface CommandSetThemeData {\n theme: 'light' | 'dark' | 'system';\n}\n\nexport interface CommandSetLocaleData {\n locale: string;\n}\n\n/** Host → editor: switch chrome density between the two consumer-facing\n * modes without re-mounting. `preview` hides toolbar / formula bar /\n * side panel / status bar / sheet tabs and runs read-only; `editor`\n * shows the full UI. Mirrors the `viewMode` prop on `<CasualSheetsIframe>`. */\nexport interface CommandSetViewModeData {\n viewMode: 'preview' | 'editor';\n}\n\n/** Host → editor: execute a formatting / navigation command against\n * the active selection in the embedded workbook. Hosts (like Casual\n * Drive) build their own toolbar above the iframe and dispatch these\n * commands instead of Univer's built-in ribbon, which the SDK can't\n * ship because the ribbon resolves IRPCChannelService at construction\n * and that service needs a worker the SDK doesn't bundle.\n *\n * v0.6 covered the toggle set; v0.7 adds the rich-format set (font,\n * size, colour, fill) + merge / unmerge. Arg-carrying commands read\n * the relevant field off `args` — every other command ignores it. */\nexport interface CommandExecuteData {\n command: // v0.6 — toggle / nav (no args)\n | 'undo'\n | 'redo'\n | 'bold'\n | 'italic'\n | 'underline'\n | 'strikethrough'\n | 'align-left'\n | 'align-center'\n | 'align-right'\n // v0.7 — rich format (args carry the value)\n | 'set-font-family'\n | 'set-font-size'\n | 'set-text-color'\n | 'reset-text-color'\n | 'set-bg-color'\n | 'reset-bg-color'\n | 'merge'\n | 'unmerge'\n // v0.8 — number formats + freeze + wrap\n | 'numfmt-currency'\n | 'numfmt-percent'\n | 'numfmt-add-decimal'\n | 'numfmt-subtract-decimal'\n | 'numfmt-custom'\n | 'wrap-toggle'\n | 'freeze-first-row'\n | 'freeze-first-column'\n | 'freeze-none';\n args?: {\n /** Used by `set-font-family`. */\n family?: string;\n /** Used by `set-font-size`. Integer point size. */\n size?: number;\n /** Used by `set-text-color` and `set-bg-color`. Hex like `#1a73e8`. */\n color?: string;\n /** Used by `numfmt-custom`. The Excel-style format string, e.g.\n * `\"#,##0.00\"`, `\"$#,##0\"`, `\"0.00%\"`, `\"d-mmm-yy\"`. v0.8+. */\n pattern?: string;\n };\n}\n\n/** Editor → host: emitted whenever the selection's active cell's\n * format flags change. Drive's toolbar mirrors this state in the\n * button \"pressed\" / value indicators so the surface always reflects\n * what the user would see in the cell. v0.7 widens the payload with\n * the rich-format read-back (fontFamily, fontSize, textColor, bgColor)\n * so the toolbar's font picker / size stepper / colour swatches stay\n * in sync without the host having to poll. */\nexport interface SelectionFormatStateData {\n bold: boolean;\n italic: boolean;\n underline: boolean;\n strikethrough: boolean;\n align: 'left' | 'center' | 'right' | null;\n /** Defined font family on the active cell, or null when the cell\n * inherits the workbook default. v0.7+. */\n fontFamily: string | null;\n /** Defined font size on the active cell, or null when the cell\n * inherits the workbook default. v0.7+. */\n fontSize: number | null;\n /** Hex text colour like `#1a73e8`, or null when default. v0.7+. */\n textColor: string | null;\n /** Hex background colour, or null when no fill is set. v0.7+. */\n bgColor: string | null;\n}\n\n// ---------------------------------------------------------------\n// Errors (editor → host fatal signals)\n// ---------------------------------------------------------------\n\n/** Editor → host: a fatal error during boot / load. Hosts surface this\n * via the wrapper's `onError` callback. */\nexport interface CasualErrorData {\n code: 'embed_not_served' | 'load_failed' | 'parse_failed' | 'boot_failed' | 'internal';\n message: string;\n}\n\n// ---------------------------------------------------------------\n// Signing (uniform with the SDK `signing` prop)\n// ---------------------------------------------------------------\n\nexport interface SignatureRequestData {\n fields: SignatureField[];\n mode: SignatureMode;\n banner?: string;\n}\n\nexport interface SignatureRequestAckData {\n ok: boolean;\n code?: string;\n}\n\nexport type SignatureFieldSignedData = SignedFieldPayload;\nexport type SignatureCompleteData = SignatureCompletePayload;\n\nexport interface SignatureCancelData {\n reason: CancelReason;\n}\n\n// ---------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------\n\nexport function isCasualEnvelope(value: unknown): value is CasualEnvelope {\n if (!value || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.type === 'string' &&\n v.type.startsWith('casual.') &&\n (v.app === 'docs' || v.app === 'sheet') &&\n v.v === 1 &&\n 'data' in v\n );\n}\n","/**\n * EmbedHostTransport — the parent side of the embed iframe bridge for\n * the sheet SDK. Mirror of @casualoffice/docs's host transport.\n *\n * Wire shape: `docs/SDK_SIGNING_EMBED.md` (cross-link in the doc repo at\n * `docs/internal/13-iframe-protocol.md`) + `16-sdk-iframe-architecture.md`.\n *\n * Lifetime: constructed once when the wrapper mounts the iframe.\n * `destroy()` removes the event listener; safe to call multiple times.\n */\n\nimport {\n isCasualEnvelope,\n type CasualApp,\n type CasualEnvelope,\n type CasualErrorData,\n type CommandSetReadOnlyData,\n type CommandSetThemeData,\n type CommandSetLocaleData,\n type CommandSetViewModeData,\n type CommandExecuteData,\n type SelectionFormatStateData,\n type EditorHelloData,\n type HostHelloData,\n type LoadRequestData,\n type LoadResponseData,\n type SaveRequestData,\n type SaveResponseData,\n type SelectionChangedData,\n type SignatureCancelData,\n type SignatureCompleteData,\n type SignatureFieldSignedData,\n type SignatureRequestData,\n type TelemetryEventData,\n} from './protocol';\n\nexport interface EmbedHostTransportOptions {\n app: CasualApp;\n /** The iframe's `contentWindow`. */\n iframeWindow: Window;\n /** Origin allowed to send + receive messages. Same-origin internal\n * embed is `window.location.origin`. */\n embedOrigin: string;\n /** Optional injection — tests pass a stub. */\n hostWindow?: Pick<Window, 'addEventListener' | 'removeEventListener'>;\n}\n\nexport interface EmbedHostHandlers {\n onEditorReady?: (data: EditorHelloData) => void;\n /** Editor requests bytes for `docId`. */\n onLoadRequest?: (data: LoadRequestData) => Promise<LoadResponseData> | LoadResponseData;\n /** Editor requests a save. */\n onSaveRequest?: (data: SaveRequestData) => Promise<SaveResponseData> | SaveResponseData;\n onSelectionChanged?: (data: SelectionChangedData) => void;\n onSelectionFormatState?: (data: SelectionFormatStateData) => void;\n onTelemetry?: (data: TelemetryEventData) => void;\n onSignatureFieldSigned?: (data: SignatureFieldSignedData) => void;\n onSignatureComplete?: (data: SignatureCompleteData) => void;\n onSignatureCancel?: (data: SignatureCancelData) => void;\n onError?: (data: CasualErrorData) => void;\n}\n\ntype IframePostMessage = (msg: unknown, targetOrigin: string, transfer?: Transferable[]) => void;\n\nexport class EmbedHostTransport {\n private readonly opts: EmbedHostTransportOptions;\n private handlers: EmbedHostHandlers = {};\n private readonly boundOnMessage: (ev: MessageEvent) => void;\n private destroyed = false;\n\n constructor(opts: EmbedHostTransportOptions) {\n this.opts = opts;\n this.boundOnMessage = this.onMessage.bind(this);\n const target = opts.hostWindow ?? window;\n target.addEventListener('message', this.boundOnMessage);\n }\n\n on(handlers: EmbedHostHandlers): void {\n this.handlers = { ...this.handlers, ...handlers };\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n const target = this.opts.hostWindow ?? window;\n target.removeEventListener('message', this.boundOnMessage);\n }\n\n sendHostHello(data: HostHelloData): void {\n this.post('casual.hello', data);\n }\n\n sendSetViewMode(data: CommandSetViewModeData): void {\n this.post('casual.command.set.viewmode', data);\n }\n\n sendSetReadOnly(data: CommandSetReadOnlyData): void {\n this.post('casual.command.set.readonly', data);\n }\n\n sendSetTheme(data: CommandSetThemeData): void {\n this.post('casual.command.set.theme', data);\n }\n\n sendSetLocale(data: CommandSetLocaleData): void {\n this.post('casual.command.set.locale', data);\n }\n\n sendCommandSave(): void {\n this.post('casual.command.save', null);\n }\n\n sendCommandFocus(): void {\n this.post('casual.command.focus', null);\n }\n\n /** Host → Editor: run a formatting / navigation command (bold,\n * italic, undo, …) against the active selection. v0.6+. */\n sendCommandExecute(data: CommandExecuteData): void {\n this.post('casual.command.execute', data);\n }\n\n sendSignatureRequest(id: string, data: SignatureRequestData): void {\n this.post('casual.signature.request', data, id);\n }\n\n sendSignatureCancel(data: SignatureCancelData): void {\n this.post('casual.signature.cancel', data);\n }\n\n private onMessage(ev: MessageEvent): void {\n if (this.destroyed) return;\n if (ev.origin !== this.opts.embedOrigin) return;\n if (ev.source !== this.opts.iframeWindow) return;\n if (!isCasualEnvelope(ev.data)) return;\n if (ev.data.app !== this.opts.app) return;\n\n void this.dispatch(ev.data);\n }\n\n private async dispatch(env: CasualEnvelope): Promise<void> {\n switch (env.type) {\n case 'casual.ready':\n this.handlers.onEditorReady?.(env.data as EditorHelloData);\n return;\n case 'casual.load.request': {\n if (!this.handlers.onLoadRequest) return;\n const id = env.id ?? '';\n try {\n const resp = await this.handlers.onLoadRequest(env.data as LoadRequestData);\n const transfer: Transferable[] = resp.ok ? [resp.bytes] : [];\n this.post('casual.load.response', resp, id, transfer);\n } catch (err) {\n this.post(\n 'casual.load.response',\n {\n ok: false as const,\n code: 'host_error',\n message: err instanceof Error ? err.message : String(err),\n },\n id,\n );\n }\n return;\n }\n case 'casual.save.request': {\n if (!this.handlers.onSaveRequest) return;\n const id = env.id ?? '';\n try {\n const resp = await this.handlers.onSaveRequest(env.data as SaveRequestData);\n this.post('casual.save.response', resp, id);\n } catch (err) {\n this.post(\n 'casual.save.response',\n {\n ok: false as const,\n code: 'host_error',\n message: err instanceof Error ? err.message : String(err),\n },\n id,\n );\n }\n return;\n }\n case 'casual.selection.changed':\n this.handlers.onSelectionChanged?.(env.data as SelectionChangedData);\n return;\n case 'casual.selection.format-state':\n this.handlers.onSelectionFormatState?.(env.data as SelectionFormatStateData);\n return;\n case 'casual.telemetry.event':\n this.handlers.onTelemetry?.(env.data as TelemetryEventData);\n return;\n case 'casual.signature.field.signed':\n this.handlers.onSignatureFieldSigned?.(env.data as SignatureFieldSignedData);\n return;\n case 'casual.signature.complete':\n this.handlers.onSignatureComplete?.(env.data as SignatureCompleteData);\n return;\n case 'casual.signature.cancel':\n this.handlers.onSignatureCancel?.(env.data as SignatureCancelData);\n return;\n case 'casual.signature.request.ack':\n return;\n case 'casual.error':\n this.handlers.onError?.(env.data as CasualErrorData);\n return;\n default:\n return;\n }\n }\n\n private post(type: string, data: unknown, id?: string, transfer?: Transferable[]): void {\n const env: CasualEnvelope = {\n type,\n app: this.opts.app,\n v: 1,\n data,\n ...(id ? { id } : {}),\n };\n const send = this.opts.iframeWindow.postMessage.bind(\n this.opts.iframeWindow,\n ) as IframePostMessage;\n send(env, this.opts.embedOrigin, transfer);\n }\n}\n"],"mappings":"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","names":["import_core","import_facade","import_react","import_jsx_runtime","DEFAULT_STYLE","CasualSheetsIframe"]}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { CSSProperties } from 'react';
|
|
3
|
+
import { IWorkbookData, IRange, LocaleType, ILocales, LogLevel } from '@univerjs/core';
|
|
4
|
+
import { defaultTheme } from '@univerjs/themes';
|
|
5
|
+
import { FUniver } from '@univerjs/core/facade';
|
|
6
|
+
import { o as SelectionChangedData, p as SelectionFormatStateData, T as TelemetryEventData, b as CasualErrorData, c as CommandExecuteData } from './protocol--KyBQUjU.cjs';
|
|
7
|
+
import './types-s_O0u6Cg.cjs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* CasualSheetsAPI — the imperative ref handed to a host via `<CasualSheets onReady>`.
|
|
11
|
+
*
|
|
12
|
+
* This is the SDK's stable integration surface (Excalidraw's model: props +
|
|
13
|
+
* imperative ref). Hosts drive the editor through these methods rather than
|
|
14
|
+
* reaching into Univer directly; `api.univer` is the documented escape hatch and
|
|
15
|
+
* is explicitly NOT covered by semver — everything else here is.
|
|
16
|
+
*
|
|
17
|
+
* Scope of THIS batch (SDK restructure, Phase 1 step 3):
|
|
18
|
+
* getSnapshot / loadSnapshot / getSelection / executeCommand / univer
|
|
19
|
+
*
|
|
20
|
+
* Deferred to later, clearly-scoped batches (kept off the type until they work,
|
|
21
|
+
* so the surface never advertises a method that throws):
|
|
22
|
+
* - importXlsx / exportXlsx — the xlsx I/O batch. importXlsx must NOT be a
|
|
23
|
+
* plain `import('../xlsx')` here: the main tsup config is `splitting:false`,
|
|
24
|
+
* so a dynamic import gets inlined and balloons the editor entry from ~11KB
|
|
25
|
+
* to ~200KB of parser code for hosts that never open a file. The xlsx-I/O
|
|
26
|
+
* batch wires it as its own chunk (and lifts the export converter out of
|
|
27
|
+
* apps/web — the SDK xlsx module is import-only today).
|
|
28
|
+
* - attachCollab — belongs to the storage/collab adapter phase (Phase 2);
|
|
29
|
+
* the editor ships collab-unaware until then.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** The active selection, as a sheet-scoped range. */
|
|
33
|
+
interface RangeRef {
|
|
34
|
+
/** Workbook unit id the selection belongs to. */
|
|
35
|
+
unitId: string;
|
|
36
|
+
/** Worksheet (sub-unit) id the selection belongs to. */
|
|
37
|
+
sheetId: string;
|
|
38
|
+
/** `{ startRow, startColumn, endRow, endColumn }`. */
|
|
39
|
+
range: IRange;
|
|
40
|
+
}
|
|
41
|
+
interface CasualSheetsAPI {
|
|
42
|
+
/** Current workbook as an `IWorkbookData` snapshot. `null` before the unit
|
|
43
|
+
* is created (shouldn't happen after `onReady`, but typed defensively). */
|
|
44
|
+
getSnapshot(): IWorkbookData | null;
|
|
45
|
+
/** Replace the workbook with a new snapshot. Disposes the current unit and
|
|
46
|
+
* mounts `data` as a fresh one. */
|
|
47
|
+
loadSnapshot(data: IWorkbookData): void;
|
|
48
|
+
/** The active selection, or `null` when there is none. */
|
|
49
|
+
getSelection(): RangeRef | null;
|
|
50
|
+
/** Dispatch a Univer command by id. Resolves to the command's boolean
|
|
51
|
+
* result. */
|
|
52
|
+
executeCommand(id: string, params?: object): Promise<boolean>;
|
|
53
|
+
/** Imperative light/dark switch — the API equivalent of the reactive
|
|
54
|
+
* `appearance` prop. Flips Univer's `ThemeService.setDarkMode` (canvas
|
|
55
|
+
* colours + the `univer-dark` class Univer applies to the document root). */
|
|
56
|
+
setTheme(appearance: 'light' | 'dark'): void;
|
|
57
|
+
/** The FUniver facade — documented escape hatch, NOT covered by semver. */
|
|
58
|
+
univer: FUniver;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build the imperative API over a live FUniver facade. The wrapper holds no
|
|
62
|
+
* state of its own — every call reads the current active workbook, so it stays
|
|
63
|
+
* correct across `loadSnapshot` swaps without the host re-acquiring the ref.
|
|
64
|
+
*/
|
|
65
|
+
declare function createCasualSheetsAPI(univerAPI: FUniver): CasualSheetsAPI;
|
|
66
|
+
|
|
67
|
+
interface CasualSheetsProps {
|
|
68
|
+
/** Workbook snapshot to mount. Read once on initial mount; change
|
|
69
|
+
* the React `key` on this component to remount with a new
|
|
70
|
+
* workbook. */
|
|
71
|
+
initialData: IWorkbookData;
|
|
72
|
+
/** Called after the workbook unit is created. Hands back the
|
|
73
|
+
* `CasualSheetsAPI` imperative ref — the SDK's stable integration
|
|
74
|
+
* surface (snapshot I/O, xlsx import, selection, command dispatch).
|
|
75
|
+
* The raw FUniver facade is on `api.univer` as the escape hatch. */
|
|
76
|
+
onReady?: (api: CasualSheetsAPI) => void;
|
|
77
|
+
/** Debounced stream of workbook snapshots, emitted after edits
|
|
78
|
+
* settle. This is the "host persists it" half of the Excalidraw
|
|
79
|
+
* model — the editor stays storage-unaware and the host writes the
|
|
80
|
+
* snapshot wherever it likes (localStorage, server, …). Driven by
|
|
81
|
+
* Univer's mutation hook (`onMutationExecutedForCollab`), not UI
|
|
82
|
+
* events, so it captures every edit including programmatic ones.
|
|
83
|
+
* May fire for background/structural mutations too; treat each call
|
|
84
|
+
* as "current state, persist if you care". */
|
|
85
|
+
onChange?: (snapshot: IWorkbookData) => void;
|
|
86
|
+
/** Debounce window for `onChange`, in ms. Default 400. */
|
|
87
|
+
onChangeDebounceMs?: number;
|
|
88
|
+
/** Lazy-load the feature plugins (conditional formatting, data
|
|
89
|
+
* validation, hyperlinks, notes, tables, comments, drawings, sort,
|
|
90
|
+
* filter, find/replace). Default `true`: plugins whose data is in
|
|
91
|
+
* `initialData` load eagerly before mount (so nothing is dropped on
|
|
92
|
+
* open), the rest idle-load after first paint. Set `false` for the
|
|
93
|
+
* minimal editor (render + formula + numfmt only) — the embed-iframe
|
|
94
|
+
* build does this to stay a single self-contained bundle. */
|
|
95
|
+
lazyPlugins?: boolean;
|
|
96
|
+
/** Locale identifier. Defaults to `LocaleType.EN_US`. */
|
|
97
|
+
locale?: LocaleType;
|
|
98
|
+
/** Locale string bundle. Optional — Univer's default English
|
|
99
|
+
* strings load if omitted. */
|
|
100
|
+
locales?: ILocales;
|
|
101
|
+
/** Univer log level. Defaults to `LogLevel.WARN`. */
|
|
102
|
+
logLevel?: LogLevel;
|
|
103
|
+
/** Univer chrome toggles. Defaults: header / toolbar / footer off,
|
|
104
|
+
* context menu on — matches Casual Sheets' embedded shape. */
|
|
105
|
+
ui?: {
|
|
106
|
+
header?: boolean;
|
|
107
|
+
toolbar?: boolean;
|
|
108
|
+
footer?: boolean;
|
|
109
|
+
contextMenu?: boolean;
|
|
110
|
+
};
|
|
111
|
+
/** Override the Univer theme object (colour palette). Defaults to
|
|
112
|
+
* Univer's `defaultTheme`. Distinct from `appearance` (light/dark). */
|
|
113
|
+
theme?: typeof defaultTheme;
|
|
114
|
+
/** Light or dark mode. Reactive — flipping it re-themes the live
|
|
115
|
+
* editor via `ThemeService.setDarkMode` (canvas colours, notifications,
|
|
116
|
+
* and Univer's own `univer-dark` class). Defaults to light.
|
|
117
|
+
* Note: Univer's Workbench applies the `univer-dark` class to the
|
|
118
|
+
* document root (`<html>`) itself, so dark mode is page-global by
|
|
119
|
+
* Univer's design — a host that embeds the editor inside a light page
|
|
120
|
+
* should scope the editor or accept the global dark CSS. */
|
|
121
|
+
appearance?: 'light' | 'dark';
|
|
122
|
+
/** Container style. Default fills the parent. */
|
|
123
|
+
style?: CSSProperties;
|
|
124
|
+
/** Container className for additional styling hooks. */
|
|
125
|
+
className?: string;
|
|
126
|
+
/** Optional test id for the host container. */
|
|
127
|
+
testId?: string;
|
|
128
|
+
}
|
|
129
|
+
declare function CasualSheets({ initialData, onReady, onChange, onChangeDebounceMs, lazyPlugins, locale, locales, logLevel, ui, theme, appearance, style, className, testId, }: CasualSheetsProps): react.JSX.Element;
|
|
130
|
+
|
|
131
|
+
/** What the host-side load/save handlers consume + return. The wrapper
|
|
132
|
+
* binds these to the host's FileSource via simple adapters. */
|
|
133
|
+
interface HostFileBridge {
|
|
134
|
+
open(docId: string): Promise<{
|
|
135
|
+
bytes: ArrayBuffer;
|
|
136
|
+
name: string;
|
|
137
|
+
etag?: string;
|
|
138
|
+
}>;
|
|
139
|
+
save?(docId: string, bytes: ArrayBuffer, opts?: {
|
|
140
|
+
etag?: string;
|
|
141
|
+
}): Promise<{
|
|
142
|
+
etag: string;
|
|
143
|
+
}>;
|
|
144
|
+
}
|
|
145
|
+
interface CasualSheetsIframeRef {
|
|
146
|
+
setViewMode(mode: 'preview' | 'editor'): void;
|
|
147
|
+
iframe(): HTMLIFrameElement | null;
|
|
148
|
+
/** Dispatch a formatting / navigation command (bold, italic, undo, …)
|
|
149
|
+
* against the iframe's active selection. v0.6+. */
|
|
150
|
+
executeCommand(command: CommandExecuteData['command']): void;
|
|
151
|
+
}
|
|
152
|
+
interface CasualSheetsIframeProps {
|
|
153
|
+
/** Host-side bytes bridge. The wrapper round-trips load / save to the
|
|
154
|
+
* iframe through postMessage; bytes never live in the iframe's origin
|
|
155
|
+
* except in-memory while the workbook is open. */
|
|
156
|
+
fileSource: HostFileBridge;
|
|
157
|
+
docId: string;
|
|
158
|
+
/** Default `editor`. Live changes push casual.command.set.viewmode. */
|
|
159
|
+
viewMode?: 'preview' | 'editor';
|
|
160
|
+
/** Default `/embed/sheets`. Consumer copies the SDK's
|
|
161
|
+
* `dist/embed/{embed.html, embed-runtime.js, embed-runtime.css}`
|
|
162
|
+
* to this path. */
|
|
163
|
+
embedBasePath?: string;
|
|
164
|
+
onSelectionChanged?: (data: SelectionChangedData) => void;
|
|
165
|
+
/** Fires when the active cell's format flags change (bold, italic,
|
|
166
|
+
* …). Drive's custom toolbar reflects this state in the button
|
|
167
|
+
* "pressed" indicators. v0.6+. */
|
|
168
|
+
onSelectionFormatState?: (data: SelectionFormatStateData) => void;
|
|
169
|
+
onTelemetry?: (data: TelemetryEventData) => void;
|
|
170
|
+
onError?: (data: CasualErrorData) => void;
|
|
171
|
+
style?: CSSProperties;
|
|
172
|
+
className?: string;
|
|
173
|
+
testId?: string;
|
|
174
|
+
}
|
|
175
|
+
declare const CasualSheetsIframe: react.ForwardRefExoticComponent<CasualSheetsIframeProps & react.RefAttributes<CasualSheetsIframeRef>>;
|
|
176
|
+
|
|
177
|
+
export { CasualSheets, type CasualSheetsAPI, CasualSheetsIframe, type CasualSheetsIframeProps, type CasualSheetsIframeRef, type CasualSheetsProps, type HostFileBridge, type RangeRef, createCasualSheetsAPI };
|
package/dist/sheets.d.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { CSSProperties } from 'react';
|
|
3
|
+
import { IWorkbookData, IRange, LocaleType, ILocales, LogLevel } from '@univerjs/core';
|
|
4
|
+
import { defaultTheme } from '@univerjs/themes';
|
|
5
|
+
import { FUniver } from '@univerjs/core/facade';
|
|
6
|
+
import { o as SelectionChangedData, p as SelectionFormatStateData, T as TelemetryEventData, b as CasualErrorData, c as CommandExecuteData } from './protocol-cEzy7S0i.js';
|
|
7
|
+
import './types-s_O0u6Cg.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* CasualSheetsAPI — the imperative ref handed to a host via `<CasualSheets onReady>`.
|
|
11
|
+
*
|
|
12
|
+
* This is the SDK's stable integration surface (Excalidraw's model: props +
|
|
13
|
+
* imperative ref). Hosts drive the editor through these methods rather than
|
|
14
|
+
* reaching into Univer directly; `api.univer` is the documented escape hatch and
|
|
15
|
+
* is explicitly NOT covered by semver — everything else here is.
|
|
16
|
+
*
|
|
17
|
+
* Scope of THIS batch (SDK restructure, Phase 1 step 3):
|
|
18
|
+
* getSnapshot / loadSnapshot / getSelection / executeCommand / univer
|
|
19
|
+
*
|
|
20
|
+
* Deferred to later, clearly-scoped batches (kept off the type until they work,
|
|
21
|
+
* so the surface never advertises a method that throws):
|
|
22
|
+
* - importXlsx / exportXlsx — the xlsx I/O batch. importXlsx must NOT be a
|
|
23
|
+
* plain `import('../xlsx')` here: the main tsup config is `splitting:false`,
|
|
24
|
+
* so a dynamic import gets inlined and balloons the editor entry from ~11KB
|
|
25
|
+
* to ~200KB of parser code for hosts that never open a file. The xlsx-I/O
|
|
26
|
+
* batch wires it as its own chunk (and lifts the export converter out of
|
|
27
|
+
* apps/web — the SDK xlsx module is import-only today).
|
|
28
|
+
* - attachCollab — belongs to the storage/collab adapter phase (Phase 2);
|
|
29
|
+
* the editor ships collab-unaware until then.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** The active selection, as a sheet-scoped range. */
|
|
33
|
+
interface RangeRef {
|
|
34
|
+
/** Workbook unit id the selection belongs to. */
|
|
35
|
+
unitId: string;
|
|
36
|
+
/** Worksheet (sub-unit) id the selection belongs to. */
|
|
37
|
+
sheetId: string;
|
|
38
|
+
/** `{ startRow, startColumn, endRow, endColumn }`. */
|
|
39
|
+
range: IRange;
|
|
40
|
+
}
|
|
41
|
+
interface CasualSheetsAPI {
|
|
42
|
+
/** Current workbook as an `IWorkbookData` snapshot. `null` before the unit
|
|
43
|
+
* is created (shouldn't happen after `onReady`, but typed defensively). */
|
|
44
|
+
getSnapshot(): IWorkbookData | null;
|
|
45
|
+
/** Replace the workbook with a new snapshot. Disposes the current unit and
|
|
46
|
+
* mounts `data` as a fresh one. */
|
|
47
|
+
loadSnapshot(data: IWorkbookData): void;
|
|
48
|
+
/** The active selection, or `null` when there is none. */
|
|
49
|
+
getSelection(): RangeRef | null;
|
|
50
|
+
/** Dispatch a Univer command by id. Resolves to the command's boolean
|
|
51
|
+
* result. */
|
|
52
|
+
executeCommand(id: string, params?: object): Promise<boolean>;
|
|
53
|
+
/** Imperative light/dark switch — the API equivalent of the reactive
|
|
54
|
+
* `appearance` prop. Flips Univer's `ThemeService.setDarkMode` (canvas
|
|
55
|
+
* colours + the `univer-dark` class Univer applies to the document root). */
|
|
56
|
+
setTheme(appearance: 'light' | 'dark'): void;
|
|
57
|
+
/** The FUniver facade — documented escape hatch, NOT covered by semver. */
|
|
58
|
+
univer: FUniver;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build the imperative API over a live FUniver facade. The wrapper holds no
|
|
62
|
+
* state of its own — every call reads the current active workbook, so it stays
|
|
63
|
+
* correct across `loadSnapshot` swaps without the host re-acquiring the ref.
|
|
64
|
+
*/
|
|
65
|
+
declare function createCasualSheetsAPI(univerAPI: FUniver): CasualSheetsAPI;
|
|
66
|
+
|
|
67
|
+
interface CasualSheetsProps {
|
|
68
|
+
/** Workbook snapshot to mount. Read once on initial mount; change
|
|
69
|
+
* the React `key` on this component to remount with a new
|
|
70
|
+
* workbook. */
|
|
71
|
+
initialData: IWorkbookData;
|
|
72
|
+
/** Called after the workbook unit is created. Hands back the
|
|
73
|
+
* `CasualSheetsAPI` imperative ref — the SDK's stable integration
|
|
74
|
+
* surface (snapshot I/O, xlsx import, selection, command dispatch).
|
|
75
|
+
* The raw FUniver facade is on `api.univer` as the escape hatch. */
|
|
76
|
+
onReady?: (api: CasualSheetsAPI) => void;
|
|
77
|
+
/** Debounced stream of workbook snapshots, emitted after edits
|
|
78
|
+
* settle. This is the "host persists it" half of the Excalidraw
|
|
79
|
+
* model — the editor stays storage-unaware and the host writes the
|
|
80
|
+
* snapshot wherever it likes (localStorage, server, …). Driven by
|
|
81
|
+
* Univer's mutation hook (`onMutationExecutedForCollab`), not UI
|
|
82
|
+
* events, so it captures every edit including programmatic ones.
|
|
83
|
+
* May fire for background/structural mutations too; treat each call
|
|
84
|
+
* as "current state, persist if you care". */
|
|
85
|
+
onChange?: (snapshot: IWorkbookData) => void;
|
|
86
|
+
/** Debounce window for `onChange`, in ms. Default 400. */
|
|
87
|
+
onChangeDebounceMs?: number;
|
|
88
|
+
/** Lazy-load the feature plugins (conditional formatting, data
|
|
89
|
+
* validation, hyperlinks, notes, tables, comments, drawings, sort,
|
|
90
|
+
* filter, find/replace). Default `true`: plugins whose data is in
|
|
91
|
+
* `initialData` load eagerly before mount (so nothing is dropped on
|
|
92
|
+
* open), the rest idle-load after first paint. Set `false` for the
|
|
93
|
+
* minimal editor (render + formula + numfmt only) — the embed-iframe
|
|
94
|
+
* build does this to stay a single self-contained bundle. */
|
|
95
|
+
lazyPlugins?: boolean;
|
|
96
|
+
/** Locale identifier. Defaults to `LocaleType.EN_US`. */
|
|
97
|
+
locale?: LocaleType;
|
|
98
|
+
/** Locale string bundle. Optional — Univer's default English
|
|
99
|
+
* strings load if omitted. */
|
|
100
|
+
locales?: ILocales;
|
|
101
|
+
/** Univer log level. Defaults to `LogLevel.WARN`. */
|
|
102
|
+
logLevel?: LogLevel;
|
|
103
|
+
/** Univer chrome toggles. Defaults: header / toolbar / footer off,
|
|
104
|
+
* context menu on — matches Casual Sheets' embedded shape. */
|
|
105
|
+
ui?: {
|
|
106
|
+
header?: boolean;
|
|
107
|
+
toolbar?: boolean;
|
|
108
|
+
footer?: boolean;
|
|
109
|
+
contextMenu?: boolean;
|
|
110
|
+
};
|
|
111
|
+
/** Override the Univer theme object (colour palette). Defaults to
|
|
112
|
+
* Univer's `defaultTheme`. Distinct from `appearance` (light/dark). */
|
|
113
|
+
theme?: typeof defaultTheme;
|
|
114
|
+
/** Light or dark mode. Reactive — flipping it re-themes the live
|
|
115
|
+
* editor via `ThemeService.setDarkMode` (canvas colours, notifications,
|
|
116
|
+
* and Univer's own `univer-dark` class). Defaults to light.
|
|
117
|
+
* Note: Univer's Workbench applies the `univer-dark` class to the
|
|
118
|
+
* document root (`<html>`) itself, so dark mode is page-global by
|
|
119
|
+
* Univer's design — a host that embeds the editor inside a light page
|
|
120
|
+
* should scope the editor or accept the global dark CSS. */
|
|
121
|
+
appearance?: 'light' | 'dark';
|
|
122
|
+
/** Container style. Default fills the parent. */
|
|
123
|
+
style?: CSSProperties;
|
|
124
|
+
/** Container className for additional styling hooks. */
|
|
125
|
+
className?: string;
|
|
126
|
+
/** Optional test id for the host container. */
|
|
127
|
+
testId?: string;
|
|
128
|
+
}
|
|
129
|
+
declare function CasualSheets({ initialData, onReady, onChange, onChangeDebounceMs, lazyPlugins, locale, locales, logLevel, ui, theme, appearance, style, className, testId, }: CasualSheetsProps): react.JSX.Element;
|
|
130
|
+
|
|
131
|
+
/** What the host-side load/save handlers consume + return. The wrapper
|
|
132
|
+
* binds these to the host's FileSource via simple adapters. */
|
|
133
|
+
interface HostFileBridge {
|
|
134
|
+
open(docId: string): Promise<{
|
|
135
|
+
bytes: ArrayBuffer;
|
|
136
|
+
name: string;
|
|
137
|
+
etag?: string;
|
|
138
|
+
}>;
|
|
139
|
+
save?(docId: string, bytes: ArrayBuffer, opts?: {
|
|
140
|
+
etag?: string;
|
|
141
|
+
}): Promise<{
|
|
142
|
+
etag: string;
|
|
143
|
+
}>;
|
|
144
|
+
}
|
|
145
|
+
interface CasualSheetsIframeRef {
|
|
146
|
+
setViewMode(mode: 'preview' | 'editor'): void;
|
|
147
|
+
iframe(): HTMLIFrameElement | null;
|
|
148
|
+
/** Dispatch a formatting / navigation command (bold, italic, undo, …)
|
|
149
|
+
* against the iframe's active selection. v0.6+. */
|
|
150
|
+
executeCommand(command: CommandExecuteData['command']): void;
|
|
151
|
+
}
|
|
152
|
+
interface CasualSheetsIframeProps {
|
|
153
|
+
/** Host-side bytes bridge. The wrapper round-trips load / save to the
|
|
154
|
+
* iframe through postMessage; bytes never live in the iframe's origin
|
|
155
|
+
* except in-memory while the workbook is open. */
|
|
156
|
+
fileSource: HostFileBridge;
|
|
157
|
+
docId: string;
|
|
158
|
+
/** Default `editor`. Live changes push casual.command.set.viewmode. */
|
|
159
|
+
viewMode?: 'preview' | 'editor';
|
|
160
|
+
/** Default `/embed/sheets`. Consumer copies the SDK's
|
|
161
|
+
* `dist/embed/{embed.html, embed-runtime.js, embed-runtime.css}`
|
|
162
|
+
* to this path. */
|
|
163
|
+
embedBasePath?: string;
|
|
164
|
+
onSelectionChanged?: (data: SelectionChangedData) => void;
|
|
165
|
+
/** Fires when the active cell's format flags change (bold, italic,
|
|
166
|
+
* …). Drive's custom toolbar reflects this state in the button
|
|
167
|
+
* "pressed" indicators. v0.6+. */
|
|
168
|
+
onSelectionFormatState?: (data: SelectionFormatStateData) => void;
|
|
169
|
+
onTelemetry?: (data: TelemetryEventData) => void;
|
|
170
|
+
onError?: (data: CasualErrorData) => void;
|
|
171
|
+
style?: CSSProperties;
|
|
172
|
+
className?: string;
|
|
173
|
+
testId?: string;
|
|
174
|
+
}
|
|
175
|
+
declare const CasualSheetsIframe: react.ForwardRefExoticComponent<CasualSheetsIframeProps & react.RefAttributes<CasualSheetsIframeRef>>;
|
|
176
|
+
|
|
177
|
+
export { CasualSheets, type CasualSheetsAPI, CasualSheetsIframe, type CasualSheetsIframeProps, type CasualSheetsIframeRef, type CasualSheetsProps, type HostFileBridge, type RangeRef, createCasualSheetsAPI };
|