@casualoffice/sheets 0.11.1 → 0.12.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/dist/sheets.cjs CHANGED
@@ -34,7 +34,9 @@ var sheets_exports = {};
34
34
  __export(sheets_exports, {
35
35
  CasualSheets: () => CasualSheets,
36
36
  CasualSheetsIframe: () => CasualSheetsIframe,
37
- createCasualSheetsAPI: () => createCasualSheetsAPI
37
+ applyReadOnly: () => applyReadOnly,
38
+ createCasualSheetsAPI: () => createCasualSheetsAPI,
39
+ getEditable: () => getEditable
38
40
  });
39
41
  module.exports = __toCommonJS(sheets_exports);
40
42
 
@@ -495,6 +497,53 @@ function applyAppearance(api, container, appearance) {
495
497
  }
496
498
  }
497
499
 
500
+ // src/sheets/read-only.ts
501
+ var import_core3 = require("@univerjs/core");
502
+ var import_sheets2 = require("@univerjs/sheets");
503
+ var READONLY_BLOCK = /(set-cell-edit-visible|set-activate-cell-edit|set-range-values|set-style|set-bold|set-italic|set-underline|set-strike|set-font|set-background|set-text|set-horizontal|set-vertical|set-wrap|set-rotation|set-border|set-number-format|insert-|delete-|remove-|clear-selection|cut-content|paste|move-range|move-rows|move-cols|merge|split|add-worksheet|set-worksheet-name|set-worksheet-row|set-worksheet-col|auto-fill|reorder|set-defined-name|set-tab-color|set-frozen-cancel)/;
504
+ function applyReadOnly(univerApi, unitId, onBlock) {
505
+ const injector = univerApi._injector;
506
+ const cmd = injector?.get(import_core3.ICommandService);
507
+ const vetoDisposable = cmd?.beforeCommandExecuted((info) => {
508
+ if (READONLY_BLOCK.test(info.id)) {
509
+ onBlock?.(info.id);
510
+ throw new import_core3.CustomCommandExecutionError(`read-only: blocked ${info.id}`);
511
+ }
512
+ });
513
+ const svc = injector?.get(import_core3.IPermissionService);
514
+ const id = new import_sheets2.WorkbookEditablePermission(unitId).id;
515
+ let prev;
516
+ if (svc) {
517
+ try {
518
+ const existing = svc.getPermissionPoint(id);
519
+ if (existing) {
520
+ prev = existing.value;
521
+ svc.updatePermissionPoint(id, false);
522
+ } else {
523
+ const point = new import_sheets2.WorkbookEditablePermission(unitId);
524
+ point.value = false;
525
+ svc.addPermissionPoint(point);
526
+ }
527
+ } catch {
528
+ }
529
+ }
530
+ return () => {
531
+ vetoDisposable?.dispose();
532
+ try {
533
+ svc?.updatePermissionPoint(id, prev === void 0 ? true : prev);
534
+ } catch {
535
+ }
536
+ };
537
+ }
538
+ function getEditable(univerApi, unitId) {
539
+ const injector = univerApi._injector;
540
+ const svc = injector?.get(import_core3.IPermissionService);
541
+ if (!svc) return void 0;
542
+ const id = new import_sheets2.WorkbookEditablePermission(unitId).id;
543
+ const point = svc.getPermissionPoint(id);
544
+ return point ? point.value : void 0;
545
+ }
546
+
498
547
  // src/sheets/CasualSheetsIframe.tsx
499
548
  var import_react2 = require("react");
500
549
 
@@ -770,7 +819,7 @@ var CasualSheetsIframe = (0, import_react2.forwardRef)(
770
819
  apiRef.current = {
771
820
  setViewMode: (mode) => transportRef.current?.sendSetViewMode({ viewMode: mode }),
772
821
  iframe: () => iframeRef.current,
773
- executeCommand: (command) => transportRef.current?.sendCommandExecute({ command })
822
+ executeCommand: (command, args) => transportRef.current?.sendCommandExecute({ command, args })
774
823
  };
775
824
  }
776
825
  const url = `${embedBasePath}/embed.html?app=sheet&docId=${encodeURIComponent(docId)}&viewMode=${viewMode}`;
@@ -1 +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 * Formula compute runs on the main thread by default; pass `formula={{ worker }}`\n * to move it off-thread (the SDK then wires `UniverRPCMainThreadPlugin` to the\n * host's worker — see the `formula` prop).\n *\n * Intentionally NOT included (host can layer on top via FUniver):\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 {\n lazy,\n Suspense,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n} 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, CalculationMode } 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// Type-only — erased at build, so `@univerjs/rpc` stays a runtime-optional peer\n// (loaded via dynamic import only when a formula worker is passed).\nimport type { UniverRPCMainThreadPlugin as RpcMainThreadPluginType } from '@univerjs/rpc';\n\nimport { createCasualSheetsAPI, type CasualSheetsAPI } from './api';\nimport { eagerLoadForSnapshot, idleLoadAll, setUniverForLazyLoad } from '../univer/lazy-plugins';\n// Chrome is lazy-loaded from the `@casualoffice/sheets/chrome` subpath (NOT a\n// relative import — that would inline under this build's splitting:false). The\n// subpath is externalised in tsup, so the consumer's bundler code-splits it and\n// `chrome=\"none\"` hosts (the default + the apps/web reference host) never load\n// the chrome chunk.\nconst ChromeTop = lazy(() =>\n import('@casualoffice/sheets/chrome').then((m) => ({ default: m.ChromeTop })),\n);\nconst ChromeBottom = lazy(() =>\n import('@casualoffice/sheets/chrome').then((m) => ({ default: m.ChromeBottom })),\n);\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 /** Explicit save — fired when the user presses Ctrl/Cmd+S inside the editor\n * (the browser's save dialog is suppressed). The host persists the snapshot.\n * Part of the \"host owns storage\" contract: the SDK never writes a store. */\n onSave?: (snapshot: IWorkbookData) => void;\n /** Fired once when the editor unmounts, with the final snapshot — the host's\n * last chance to persist before the workbook is disposed. */\n onExit?: (snapshot: IWorkbookData) => void;\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 /** Escape hatch fired after the SDK registers its built-in plugins but BEFORE\n * the workbook unit is created — the host can `univer.registerPlugin(...)`\n * additional plugins here (e.g. an off-main formula worker via\n * `UniverRPCMainThreadPlugin`, crosshair-highlight, zen-editor). Anything\n * registered after `createUnit` would miss the unit's plugin-init pass, so\n * register-time extras must go through this hook. Power hosts (the reference\n * app) use it to share the SDK editor core while keeping their extra plugins;\n * most integrators never need it. NOT covered by semver — it hands you the\n * raw `Univer` instance. */\n onBeforeCreateUnit?: (univer: Univer) => void;\n /** Off-main formula compute. By default the formula engine runs on the MAIN\n * thread (fine for typical sheets, zero host setup). Provide a Web Worker (or\n * its URL) to move compute off-thread so paste / sort / fill on large\n * workbooks don't freeze the UI: the SDK then registers the formula plugins\n * with `notExecuteFormula` and wires `UniverRPCMainThreadPlugin` to your\n * worker. The host owns the worker (the SDK never bundles one — that's brittle\n * across bundlers) and must have `@univerjs/rpc` installed. The worker script\n * is the standard Univer formula worker (see the reference app's\n * `apps/web/src/univer/formula-worker.ts`). */\n formula?: {\n /** A constructed `Worker`, or a URL/string the RPC plugin loads. */\n worker?: Worker | string;\n };\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 /** Office chrome rendered around the grid:\n * - `'none'` (default): bare grid — the host supplies its own chrome.\n * - `'minimal'` / `'full'`: the built-in Office shell — a menu bar\n * (Edit/Insert/Format/Data/View), a formatting toolbar (font family/size,\n * bold/italic/underline/strike, text & fill colour, borders, h/v align,\n * wrap, merge, number formats, clear format, AutoSum), a formula bar with a\n * name box + function autocomplete, a worksheet tab strip (switch/add/\n * rename/delete), and a status bar (Average/Count/Numerical Count/Min/Max/\n * Sum + zoom). All driven through the facade, themed via `--cs-chrome-*`\n * (light/dark). `'minimal'` and `'full'` currently render the same shell;\n * `'full'` is where richer panels (find/replace, charts, …) will land. */\n chrome?: 'none' | 'minimal' | 'full';\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 onSave,\n onExit,\n lazyPlugins = true,\n onBeforeCreateUnit,\n formula,\n locale = LocaleType.EN_US,\n locales,\n logLevel = LogLevel.WARN,\n ui,\n theme = defaultTheme,\n appearance = 'light',\n chrome = 'none',\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 // Latest save/exit callbacks, called via refs so they fire without re-running\n // the boot effect. onExit is read in cleanup; onSave on the Ctrl/Cmd+S handler.\n const onSaveRef = useRef(onSave);\n onSaveRef.current = onSave;\n const onExitRef = useRef(onExit);\n onExitRef.current = onExit;\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 // The live API as state, so the built-in chrome (FormulaBar) re-renders and\n // subscribes once the editor is ready. Only set when chrome is shown — the\n // bare-grid path never triggers this re-render. A single post-mount setState\n // doesn't disturb the grid (Univer owns its canvas outside React).\n const [chromeApi, setChromeApi] = useState<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 // `formula.worker` → off-main compute. Default = main thread (fine for\n // typical sheets, zero host setup).\n const offMain = !!formula?.worker;\n\n let cancelled = false;\n let changeTimer: ReturnType<typeof setTimeout> | null = null;\n let changeSub: { dispose: () => void } | undefined;\n\n void (async () => {\n // Plugin registration runs here (not synchronously) so the OPTIONAL RPC\n // transport can be `await import`ed FIRST and registered in its correct\n // slot — right after the formula engine, before sheets. Registering it out\n // of order (or after createUnit) leaves the formula engine's worker channel\n // unwired → cells stay 0. Dynamic import keeps `@univerjs/rpc` a true\n // optional peer (only loaded when a worker is passed).\n let RPCMainThreadPlugin: typeof RpcMainThreadPluginType | null = null;\n if (offMain && formula?.worker) {\n RPCMainThreadPlugin = (await import('@univerjs/rpc')).UniverRPCMainThreadPlugin;\n if (cancelled) return;\n }\n\n univer.registerPlugin(UniverRenderEnginePlugin);\n univer.registerPlugin(\n UniverFormulaEnginePlugin,\n offMain ? { notExecuteFormula: true } : undefined,\n );\n if (RPCMainThreadPlugin && formula?.worker) {\n univer.registerPlugin(RPCMainThreadPlugin, { workerURL: formula.worker });\n }\n univer.registerPlugin(UniverUIPlugin, uiOpts);\n univer.registerPlugin(UniverDocsPlugin);\n univer.registerPlugin(UniverDocsUIPlugin);\n univer.registerPlugin(UniverSheetsPlugin, offMain ? { notExecuteFormula: true } : undefined);\n univer.registerPlugin(UniverSheetsUIPlugin);\n univer.registerPlugin(\n UniverSheetsFormulaPlugin,\n offMain\n ? { notExecuteFormula: true, initialFormulaComputing: CalculationMode.NO_CALCULATION }\n : undefined,\n );\n univer.registerPlugin(UniverSheetsFormulaUIPlugin);\n univer.registerPlugin(UniverSheetsNumfmtPlugin);\n univer.registerPlugin(UniverSheetsNumfmtUIPlugin);\n\n // Lazy-loader holder (the loader is @internal so a relative import shares\n // no cross-instance state) + host plugin escape hatch — both before\n // createUnit.\n if (lazyPlugins) setUniverForLazyLoad(univer);\n onBeforeCreateUnit?.(univer);\n\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 // Hand the live API to the built-in chrome (FormulaBar subscribes to it).\n // Only when chrome is shown, so bare-grid consumers never re-render.\n if (!cancelled && chrome !== 'none') setChromeApi(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 // Last-chance persist: emit the final snapshot before the workbook is\n // disposed (disposal is deferred via microtask below, so it's still alive).\n if (onExitRef.current) {\n const snap = apiRef.current?.getSnapshot();\n if (snap) onExitRef.current(snap);\n }\n apiRef.current = null;\n setChromeApi(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 // Ctrl/Cmd+S anywhere in the editor → onSave (suppress the browser dialog).\n // Capture phase so we beat Univer's own key handling on the canvas.\n const onKeyDownCapture = (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if ((e.metaKey || e.ctrlKey) && (e.key === 's' || e.key === 'S')) {\n e.preventDefault();\n const snap = apiRef.current?.getSnapshot();\n if (snap) onSaveRef.current?.(snap);\n }\n };\n\n // chrome=\"none\" (default) keeps the exact bare-grid shape existing consumers\n // rely on (embed-runtime, hosts that bring their own shell). Any other level\n // wraps the grid in a flex column with the built-in chrome above it; the grid\n // container (hostRef, where Univer mounts) fills the remaining space.\n if (chrome === 'none') {\n return (\n <div\n ref={hostRef}\n onKeyDownCapture={onKeyDownCapture}\n style={{ ...DEFAULT_STYLE, ...style }}\n className={className}\n data-testid={testId}\n />\n );\n }\n\n // The built-in chrome components read their colours from `--cs-chrome-*` CSS\n // vars. Phase 4: each var now resolves to a `@schnsrw/design-system` token\n // (loaded by the host via `tokens.css`), with the prior hardcoded value as a\n // FALLBACK so the chrome still renders standalone for hosts that don't ship the\n // design system. `data-theme` on the wrapper (below) swaps the DS tokens\n // light/dark; the fallbacks keep `appearance` working without the DS too.\n const dark = appearance === 'dark';\n const chromeVars = {\n '--cs-chrome-bg': `var(--color-surface-strip, ${dark ? '#2a2e35' : '#eef1f5'})`,\n '--cs-chrome-fg': `var(--color-text, ${dark ? '#e6e6e6' : '#201f1e'})`,\n '--cs-chrome-muted': `var(--color-text-secondary, ${dark ? '#b0b3ba' : '#605e5c'})`,\n '--cs-chrome-border': `var(--color-divider, ${dark ? '#24272d' : '#edeff3'})`,\n '--cs-chrome-input-bg': `var(--color-surface, ${dark ? '#1b1e23' : '#ffffff'})`,\n '--cs-chrome-hover': `var(--color-hover, ${dark ? 'rgba(255,255,255,0.06)' : 'rgba(15,23,42,0.045)'})`,\n '--cs-chrome-active': `var(--color-selected, ${dark ? 'rgba(34,211,238,0.20)' : 'rgba(14,116,144,0.11)'})`,\n '--cs-chrome-active-fg': `var(--color-accent, ${dark ? '#22d3ee' : '#0e7490'})`,\n } as CSSProperties;\n\n return (\n <div\n className={className}\n data-testid={testId}\n data-theme={dark ? 'dark' : 'light'}\n onKeyDownCapture={onKeyDownCapture}\n style={{\n ...DEFAULT_STYLE,\n ...chromeVars,\n ...style,\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n {/* Bars appear once their lazy chunk loads (a tick after first paint); the\n grid host is OUTSIDE Suspense so Univer mounts immediately. */}\n <Suspense fallback={null}>\n <ChromeTop api={chromeApi} />\n </Suspense>\n <div ref={hostRef} style={{ flex: '1 1 auto', minHeight: 0, position: 'relative' }} />\n <Suspense fallback={null}>\n <ChromeBottom api={chromeApi} />\n </Suspense>\n </div>\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 * Surface:\n * getSnapshot / loadSnapshot / getSelection / executeCommand / setTheme /\n * importXlsx / exportXlsx / univer\n *\n * `importXlsx` / `exportXlsx` lazy-load the converters via\n * `import('@casualoffice/sheets/xlsx')` — a BARE subpath, not a relative\n * `import('../xlsx')`. The main tsup config is `splitting:false`, so a relative\n * dynamic import would be inlined and balloon the editor entry from ~24KB to\n * ~200KB of ExcelJS for hosts that never touch a file. The subpath is\n * externalised in tsup.config.ts so it stays a separate chunk the consumer\n * code-splits.\n *\n * `attachCollab` is NOT a method here — it ships as a standalone\n * `attachCollab(api, opts)` on the `@casualoffice/sheets/collab` subpath so the\n * editor stays collab-unaware (and collab-free in the bundle) until opted in.\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 /** Parse an `.xlsx` and load it as the active workbook. Accepts a `File` /\n * `Blob` (e.g. from an `<input type=file>`), an `ArrayBuffer`, or a\n * `Uint8Array`. The ExcelJS parser is lazy-loaded as a separate chunk, so\n * hosts that never import a file don't pay for it. When a `File` is passed,\n * its name + on-disk size are recorded on the snapshot (surfaced by the\n * built-in Properties dialog). Resolves to the loaded snapshot. */\n importXlsx(input: ArrayBuffer | Uint8Array | Blob): Promise<IWorkbookData>;\n /** Serialize the current workbook to an `.xlsx` `Blob`. Covers the core\n * fidelity (values/formulas, styles, merges, number formats, borders,\n * hyperlinks, comments, data validation, tables, page setup, named ranges,\n * VBA passthrough) — everything carried on the snapshot. App-level extras\n * (chart/pivot/sparkline models) are a power-host concern and aren't included\n * here. The converter (ExcelJS) is lazy-loaded as a separate chunk. Rejects\n * if there is no active workbook. */\n exportXlsx(): Promise<Blob>;\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 // Extracted so importXlsx can reuse the exact same swap semantics.\n const loadSnapshot = (data: IWorkbookData) => {\n const current = univerAPI.getActiveWorkbook();\n if (current) univerAPI.disposeUnit(current.getId());\n univerAPI.createWorkbook(data);\n };\n\n return {\n univer: univerAPI,\n\n getSnapshot() {\n return univerAPI.getActiveWorkbook()?.save() ?? null;\n },\n\n loadSnapshot,\n\n async importXlsx(input) {\n // Normalise to ArrayBuffer. Blob/File expose arrayBuffer(); a Uint8Array\n // view is sliced to its exact window so we don't hand the parser a larger\n // backing buffer.\n let buffer: ArrayBuffer;\n if (input instanceof ArrayBuffer) {\n buffer = input;\n } else if (input instanceof Uint8Array) {\n // `.slice` of a (possibly SharedArrayBuffer-backed) view; uploads are\n // never shared, so narrow to ArrayBuffer for the parser.\n buffer = input.buffer.slice(\n input.byteOffset,\n input.byteOffset + input.byteLength,\n ) as ArrayBuffer;\n } else {\n buffer = await input.arrayBuffer();\n }\n // Bare subpath import → separate chunk (see file header + tsup external).\n const { xlsxToWorkbookData } = await import('@casualoffice/sheets/xlsx');\n const data = await xlsxToWorkbookData(buffer);\n // A File carries the original name + size; surface them on the snapshot so\n // the built-in Properties dialog shows the real file (not the snapshot).\n if (typeof Blob !== 'undefined' && input instanceof Blob && 'name' in input) {\n const file = input as File;\n data.name = file.name.replace(/\\.(xlsx|xlsm)$/i, '') || data.name;\n data.custom = { ...data.custom, sourceBytes: file.size, sourceName: file.name };\n }\n loadSnapshot(data);\n return data;\n },\n\n async exportXlsx() {\n const snap = univerAPI.getActiveWorkbook()?.save();\n if (!snap) throw new Error('exportXlsx: no active workbook to export');\n // Bare subpath import → separate chunk (see file header + tsup external).\n const { workbookDataToXlsx } = await import('@casualoffice/sheets/xlsx');\n return workbookDataToXlsx(snap as IWorkbookData);\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// Save / exit notifications (editor → host, fire-and-forget)\n// ---------------------------------------------------------------\n//\n// The lightweight counterpart to the bytes-carrying load/save *request*\n// pair above. These mirror the SDK's React `onSave` / `onExit` hooks one\n// for one — the \"one shape, two surfaces\" save/exit contract — so a host\n// that frames the iframe gets the same persistence signals a host that\n// renders `<CasualSheets>` directly does. Fire-and-forget: the editor\n// never owns storage; the host decides what to do with the snapshot.\n//\n// `casual.save.request` (above) stays the WOPI-style path for hosts that\n// want xlsx bytes + etag round-trips; these notifications are the simpler\n// \"here's the current state, persist it however you like\" path.\n\n/** Editor → host: the user explicitly asked to save — Ctrl/Cmd+S inside\n * the iframe, or the host's `casual.command.save`. Carries the full\n * editor snapshot as JSON (sheet app: Univer's `IWorkbookData`). Mirror\n * of the React `onSave` hook. v0.9+. */\nexport interface SaveNotifyData {\n /** App-specific snapshot JSON. Sheet: `IWorkbookData`. */\n snapshot: unknown;\n /** What triggered the save: the in-editor shortcut or a host command. */\n reason: 'shortcut' | 'host';\n}\n\n/** Editor → host: the editor is unmounting / navigating away. Carries\n * the final snapshot so the host can persist on exit. Mirror of the\n * React `onExit` hook. v0.9+. */\nexport interface ExitData {\n /** App-specific snapshot JSON. Sheet: `IWorkbookData`. */\n snapshot: unknown;\n}\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 SaveNotifyData,\n type ExitData,\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 (WOPI-style, carries xlsx bytes). */\n onSaveRequest?: (data: SaveRequestData) => Promise<SaveResponseData> | SaveResponseData;\n /** Editor fired its lightweight save notification (Ctrl/Cmd+S or a\n * host save command). Carries the full snapshot JSON; fire-and-forget.\n * Mirror of the React `onSave` hook. */\n onSaveNotify?: (data: SaveNotifyData) => void;\n /** Editor is unmounting; carries the final snapshot. Mirror of the\n * React `onExit` hook. */\n onExit?: (data: ExitData) => void;\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.save.notify':\n this.handlers.onSaveNotify?.(env.data as SaveNotifyData);\n return;\n case 'casual.exit':\n this.handlers.onExit?.(env.data as ExitData);\n return;\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"]}
1
+ {"version":3,"sources":["../src/sheets/index.ts","../src/sheets/CasualSheets.tsx","../src/sheets/api.ts","../src/univer/lazy-plugins.ts","../src/sheets/read-only.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 { applyReadOnly, getEditable } from './read-only';\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 * Formula compute runs on the main thread by default; pass `formula={{ worker }}`\n * to move it off-thread (the SDK then wires `UniverRPCMainThreadPlugin` to the\n * host's worker — see the `formula` prop).\n *\n * Intentionally NOT included (host can layer on top via FUniver):\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 {\n lazy,\n Suspense,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n} 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, CalculationMode } 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// Type-only — erased at build, so `@univerjs/rpc` stays a runtime-optional peer\n// (loaded via dynamic import only when a formula worker is passed).\nimport type { UniverRPCMainThreadPlugin as RpcMainThreadPluginType } from '@univerjs/rpc';\n\nimport { createCasualSheetsAPI, type CasualSheetsAPI } from './api';\nimport { eagerLoadForSnapshot, idleLoadAll, setUniverForLazyLoad } from '../univer/lazy-plugins';\n// Chrome is lazy-loaded from the `@casualoffice/sheets/chrome` subpath (NOT a\n// relative import — that would inline under this build's splitting:false). The\n// subpath is externalised in tsup, so the consumer's bundler code-splits it and\n// `chrome=\"none\"` hosts (the default + the apps/web reference host) never load\n// the chrome chunk.\nconst ChromeTop = lazy(() =>\n import('@casualoffice/sheets/chrome').then((m) => ({ default: m.ChromeTop })),\n);\nconst ChromeBottom = lazy(() =>\n import('@casualoffice/sheets/chrome').then((m) => ({ default: m.ChromeBottom })),\n);\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 /** Explicit save — fired when the user presses Ctrl/Cmd+S inside the editor\n * (the browser's save dialog is suppressed). The host persists the snapshot.\n * Part of the \"host owns storage\" contract: the SDK never writes a store. */\n onSave?: (snapshot: IWorkbookData) => void;\n /** Fired once when the editor unmounts, with the final snapshot — the host's\n * last chance to persist before the workbook is disposed. */\n onExit?: (snapshot: IWorkbookData) => void;\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 /** Escape hatch fired after the SDK registers its built-in plugins but BEFORE\n * the workbook unit is created — the host can `univer.registerPlugin(...)`\n * additional plugins here (e.g. an off-main formula worker via\n * `UniverRPCMainThreadPlugin`, crosshair-highlight, zen-editor). Anything\n * registered after `createUnit` would miss the unit's plugin-init pass, so\n * register-time extras must go through this hook. Power hosts (the reference\n * app) use it to share the SDK editor core while keeping their extra plugins;\n * most integrators never need it. NOT covered by semver — it hands you the\n * raw `Univer` instance. */\n onBeforeCreateUnit?: (univer: Univer) => void;\n /** Off-main formula compute. By default the formula engine runs on the MAIN\n * thread (fine for typical sheets, zero host setup). Provide a Web Worker (or\n * its URL) to move compute off-thread so paste / sort / fill on large\n * workbooks don't freeze the UI: the SDK then registers the formula plugins\n * with `notExecuteFormula` and wires `UniverRPCMainThreadPlugin` to your\n * worker. The host owns the worker (the SDK never bundles one — that's brittle\n * across bundlers) and must have `@univerjs/rpc` installed. The worker script\n * is the standard Univer formula worker (see the reference app's\n * `apps/web/src/univer/formula-worker.ts`). */\n formula?: {\n /** A constructed `Worker`, or a URL/string the RPC plugin loads. */\n worker?: Worker | string;\n };\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 /** Office chrome rendered around the grid:\n * - `'none'` (default): bare grid — the host supplies its own chrome.\n * - `'minimal'` / `'full'`: the built-in Office shell — a menu bar\n * (Edit/Insert/Format/Data/View), a formatting toolbar (font family/size,\n * bold/italic/underline/strike, text & fill colour, borders, h/v align,\n * wrap, merge, number formats, clear format, AutoSum), a formula bar with a\n * name box + function autocomplete, a worksheet tab strip (switch/add/\n * rename/delete), and a status bar (Average/Count/Numerical Count/Min/Max/\n * Sum + zoom). All driven through the facade, themed via `--cs-chrome-*`\n * (light/dark). `'minimal'` and `'full'` currently render the same shell;\n * `'full'` is where richer panels (find/replace, charts, …) will land. */\n chrome?: 'none' | 'minimal' | 'full';\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 onSave,\n onExit,\n lazyPlugins = true,\n onBeforeCreateUnit,\n formula,\n locale = LocaleType.EN_US,\n locales,\n logLevel = LogLevel.WARN,\n ui,\n theme = defaultTheme,\n appearance = 'light',\n chrome = 'none',\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 // Latest save/exit callbacks, called via refs so they fire without re-running\n // the boot effect. onExit is read in cleanup; onSave on the Ctrl/Cmd+S handler.\n const onSaveRef = useRef(onSave);\n onSaveRef.current = onSave;\n const onExitRef = useRef(onExit);\n onExitRef.current = onExit;\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 // The live API as state, so the built-in chrome (FormulaBar) re-renders and\n // subscribes once the editor is ready. Only set when chrome is shown — the\n // bare-grid path never triggers this re-render. A single post-mount setState\n // doesn't disturb the grid (Univer owns its canvas outside React).\n const [chromeApi, setChromeApi] = useState<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 // `formula.worker` → off-main compute. Default = main thread (fine for\n // typical sheets, zero host setup).\n const offMain = !!formula?.worker;\n\n let cancelled = false;\n let changeTimer: ReturnType<typeof setTimeout> | null = null;\n let changeSub: { dispose: () => void } | undefined;\n\n void (async () => {\n // Plugin registration runs here (not synchronously) so the OPTIONAL RPC\n // transport can be `await import`ed FIRST and registered in its correct\n // slot — right after the formula engine, before sheets. Registering it out\n // of order (or after createUnit) leaves the formula engine's worker channel\n // unwired → cells stay 0. Dynamic import keeps `@univerjs/rpc` a true\n // optional peer (only loaded when a worker is passed).\n let RPCMainThreadPlugin: typeof RpcMainThreadPluginType | null = null;\n if (offMain && formula?.worker) {\n RPCMainThreadPlugin = (await import('@univerjs/rpc')).UniverRPCMainThreadPlugin;\n if (cancelled) return;\n }\n\n univer.registerPlugin(UniverRenderEnginePlugin);\n univer.registerPlugin(\n UniverFormulaEnginePlugin,\n offMain ? { notExecuteFormula: true } : undefined,\n );\n if (RPCMainThreadPlugin && formula?.worker) {\n univer.registerPlugin(RPCMainThreadPlugin, { workerURL: formula.worker });\n }\n univer.registerPlugin(UniverUIPlugin, uiOpts);\n univer.registerPlugin(UniverDocsPlugin);\n univer.registerPlugin(UniverDocsUIPlugin);\n univer.registerPlugin(UniverSheetsPlugin, offMain ? { notExecuteFormula: true } : undefined);\n univer.registerPlugin(UniverSheetsUIPlugin);\n univer.registerPlugin(\n UniverSheetsFormulaPlugin,\n offMain\n ? { notExecuteFormula: true, initialFormulaComputing: CalculationMode.NO_CALCULATION }\n : undefined,\n );\n univer.registerPlugin(UniverSheetsFormulaUIPlugin);\n univer.registerPlugin(UniverSheetsNumfmtPlugin);\n univer.registerPlugin(UniverSheetsNumfmtUIPlugin);\n\n // Lazy-loader holder (the loader is @internal so a relative import shares\n // no cross-instance state) + host plugin escape hatch — both before\n // createUnit.\n if (lazyPlugins) setUniverForLazyLoad(univer);\n onBeforeCreateUnit?.(univer);\n\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 // Hand the live API to the built-in chrome (FormulaBar subscribes to it).\n // Only when chrome is shown, so bare-grid consumers never re-render.\n if (!cancelled && chrome !== 'none') setChromeApi(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 // Last-chance persist: emit the final snapshot before the workbook is\n // disposed (disposal is deferred via microtask below, so it's still alive).\n if (onExitRef.current) {\n const snap = apiRef.current?.getSnapshot();\n if (snap) onExitRef.current(snap);\n }\n apiRef.current = null;\n setChromeApi(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 // Ctrl/Cmd+S anywhere in the editor → onSave (suppress the browser dialog).\n // Capture phase so we beat Univer's own key handling on the canvas.\n const onKeyDownCapture = (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if ((e.metaKey || e.ctrlKey) && (e.key === 's' || e.key === 'S')) {\n e.preventDefault();\n const snap = apiRef.current?.getSnapshot();\n if (snap) onSaveRef.current?.(snap);\n }\n };\n\n // chrome=\"none\" (default) keeps the exact bare-grid shape existing consumers\n // rely on (embed-runtime, hosts that bring their own shell). Any other level\n // wraps the grid in a flex column with the built-in chrome above it; the grid\n // container (hostRef, where Univer mounts) fills the remaining space.\n if (chrome === 'none') {\n return (\n <div\n ref={hostRef}\n onKeyDownCapture={onKeyDownCapture}\n style={{ ...DEFAULT_STYLE, ...style }}\n className={className}\n data-testid={testId}\n />\n );\n }\n\n // The built-in chrome components read their colours from `--cs-chrome-*` CSS\n // vars. Phase 4: each var now resolves to a `@schnsrw/design-system` token\n // (loaded by the host via `tokens.css`), with the prior hardcoded value as a\n // FALLBACK so the chrome still renders standalone for hosts that don't ship the\n // design system. `data-theme` on the wrapper (below) swaps the DS tokens\n // light/dark; the fallbacks keep `appearance` working without the DS too.\n const dark = appearance === 'dark';\n const chromeVars = {\n '--cs-chrome-bg': `var(--color-surface-strip, ${dark ? '#2a2e35' : '#eef1f5'})`,\n '--cs-chrome-fg': `var(--color-text, ${dark ? '#e6e6e6' : '#201f1e'})`,\n '--cs-chrome-muted': `var(--color-text-secondary, ${dark ? '#b0b3ba' : '#605e5c'})`,\n '--cs-chrome-border': `var(--color-divider, ${dark ? '#24272d' : '#edeff3'})`,\n '--cs-chrome-input-bg': `var(--color-surface, ${dark ? '#1b1e23' : '#ffffff'})`,\n '--cs-chrome-hover': `var(--color-hover, ${dark ? 'rgba(255,255,255,0.06)' : 'rgba(15,23,42,0.045)'})`,\n '--cs-chrome-active': `var(--color-selected, ${dark ? 'rgba(34,211,238,0.20)' : 'rgba(14,116,144,0.11)'})`,\n '--cs-chrome-active-fg': `var(--color-accent, ${dark ? '#22d3ee' : '#0e7490'})`,\n } as CSSProperties;\n\n return (\n <div\n className={className}\n data-testid={testId}\n data-theme={dark ? 'dark' : 'light'}\n onKeyDownCapture={onKeyDownCapture}\n style={{\n ...DEFAULT_STYLE,\n ...chromeVars,\n ...style,\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n {/* Bars appear once their lazy chunk loads (a tick after first paint); the\n grid host is OUTSIDE Suspense so Univer mounts immediately. */}\n <Suspense fallback={null}>\n <ChromeTop api={chromeApi} />\n </Suspense>\n <div ref={hostRef} style={{ flex: '1 1 auto', minHeight: 0, position: 'relative' }} />\n <Suspense fallback={null}>\n <ChromeBottom api={chromeApi} />\n </Suspense>\n </div>\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 * Surface:\n * getSnapshot / loadSnapshot / getSelection / executeCommand / setTheme /\n * importXlsx / exportXlsx / univer\n *\n * `importXlsx` / `exportXlsx` lazy-load the converters via\n * `import('@casualoffice/sheets/xlsx')` — a BARE subpath, not a relative\n * `import('../xlsx')`. The main tsup config is `splitting:false`, so a relative\n * dynamic import would be inlined and balloon the editor entry from ~24KB to\n * ~200KB of ExcelJS for hosts that never touch a file. The subpath is\n * externalised in tsup.config.ts so it stays a separate chunk the consumer\n * code-splits.\n *\n * `attachCollab` is NOT a method here — it ships as a standalone\n * `attachCollab(api, opts)` on the `@casualoffice/sheets/collab` subpath so the\n * editor stays collab-unaware (and collab-free in the bundle) until opted in.\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 /** Parse an `.xlsx` and load it as the active workbook. Accepts a `File` /\n * `Blob` (e.g. from an `<input type=file>`), an `ArrayBuffer`, or a\n * `Uint8Array`. The ExcelJS parser is lazy-loaded as a separate chunk, so\n * hosts that never import a file don't pay for it. When a `File` is passed,\n * its name + on-disk size are recorded on the snapshot (surfaced by the\n * built-in Properties dialog). Resolves to the loaded snapshot. */\n importXlsx(input: ArrayBuffer | Uint8Array | Blob): Promise<IWorkbookData>;\n /** Serialize the current workbook to an `.xlsx` `Blob`. Covers the core\n * fidelity (values/formulas, styles, merges, number formats, borders,\n * hyperlinks, comments, data validation, tables, page setup, named ranges,\n * VBA passthrough) — everything carried on the snapshot. App-level extras\n * (chart/pivot/sparkline models) are a power-host concern and aren't included\n * here. The converter (ExcelJS) is lazy-loaded as a separate chunk. Rejects\n * if there is no active workbook. */\n exportXlsx(): Promise<Blob>;\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 // Extracted so importXlsx can reuse the exact same swap semantics.\n const loadSnapshot = (data: IWorkbookData) => {\n const current = univerAPI.getActiveWorkbook();\n if (current) univerAPI.disposeUnit(current.getId());\n univerAPI.createWorkbook(data);\n };\n\n return {\n univer: univerAPI,\n\n getSnapshot() {\n return univerAPI.getActiveWorkbook()?.save() ?? null;\n },\n\n loadSnapshot,\n\n async importXlsx(input) {\n // Normalise to ArrayBuffer. Blob/File expose arrayBuffer(); a Uint8Array\n // view is sliced to its exact window so we don't hand the parser a larger\n // backing buffer.\n let buffer: ArrayBuffer;\n if (input instanceof ArrayBuffer) {\n buffer = input;\n } else if (input instanceof Uint8Array) {\n // `.slice` of a (possibly SharedArrayBuffer-backed) view; uploads are\n // never shared, so narrow to ArrayBuffer for the parser.\n buffer = input.buffer.slice(\n input.byteOffset,\n input.byteOffset + input.byteLength,\n ) as ArrayBuffer;\n } else {\n buffer = await input.arrayBuffer();\n }\n // Bare subpath import → separate chunk (see file header + tsup external).\n const { xlsxToWorkbookData } = await import('@casualoffice/sheets/xlsx');\n const data = await xlsxToWorkbookData(buffer);\n // A File carries the original name + size; surface them on the snapshot so\n // the built-in Properties dialog shows the real file (not the snapshot).\n if (typeof Blob !== 'undefined' && input instanceof Blob && 'name' in input) {\n const file = input as File;\n data.name = file.name.replace(/\\.(xlsx|xlsm)$/i, '') || data.name;\n data.custom = { ...data.custom, sourceBytes: file.size, sourceName: file.name };\n }\n loadSnapshot(data);\n return data;\n },\n\n async exportXlsx() {\n const snap = univerAPI.getActiveWorkbook()?.save();\n if (!snap) throw new Error('exportXlsx: no active workbook to export');\n // Bare subpath import → separate chunk (see file header + tsup external).\n const { workbookDataToXlsx } = await import('@casualoffice/sheets/xlsx');\n return workbookDataToXlsx(snap as IWorkbookData);\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","import { CustomCommandExecutionError, ICommandService, IPermissionService } from '@univerjs/core';\nimport type { FUniver } from '@univerjs/core/facade';\nimport { WorkbookEditablePermission } from '@univerjs/sheets';\n\n/**\n * Command ids that MUTATE a sheet — opening the cell editor, writing values,\n * styling, structural edits, clipboard paste. The read-only veto cancels any\n * command whose id matches. Navigation (selection, scroll, zoom, sheet switch),\n * copy, and undo/redo deliberately fall through so preview stays usable.\n *\n * `set-cell-edit-visible` / `set-activate-cell-edit` are the editor-open\n * operations — blocking them is what actually stops keyboard typing, since the\n * cell editor never opens. The rest stop programmatic / paste / menu mutations.\n */\nconst READONLY_BLOCK =\n /(set-cell-edit-visible|set-activate-cell-edit|set-range-values|set-style|set-bold|set-italic|set-underline|set-strike|set-font|set-background|set-text|set-horizontal|set-vertical|set-wrap|set-rotation|set-border|set-number-format|insert-|delete-|remove-|clear-selection|cut-content|paste|move-range|move-rows|move-cols|merge|split|add-worksheet|set-worksheet-name|set-worksheet-row|set-worksheet-col|auto-fill|reorder|set-defined-name|set-tab-color|set-frozen-cancel)/;\n\n/**\n * Make a workbook genuinely READ-ONLY.\n *\n * Two layers, because they cover different host setups:\n *\n * 1. **Command veto** (`beforeCommandExecuted` → throw\n * `CustomCommandExecutionError`) — cancels every mutating command. This is\n * the load-bearing layer for the iframe embed, whose minimal plugin set does\n * NOT enforce `WorkbookEditablePermission` (verified: the editor still\n * accepts edits with the permission flipped off).\n * 2. **Permission flip** (`WorkbookEditablePermission` → false) — on the full\n * `<CasualSheets>` host path this also greys out mutating menu items and\n * stops the editor opening; harmless where unenforced.\n *\n * Returns a disposer that removes the veto and restores the prior editable\n * state (for callers that toggle a live unit between preview/editor).\n */\nexport function applyReadOnly(\n univerApi: FUniver,\n unitId: string,\n onBlock?: (commandId: string) => void,\n): () => void {\n const injector = (univerApi as unknown as { _injector?: { get(t: unknown): unknown } })._injector;\n\n // Layer 1: veto mutating commands — the only layer the minimal embed enforces.\n const cmd = injector?.get(ICommandService) as\n | { beforeCommandExecuted(l: (info: { id: string }) => void): { dispose(): void } }\n | undefined;\n const vetoDisposable = cmd?.beforeCommandExecuted((info) => {\n if (READONLY_BLOCK.test(info.id)) {\n onBlock?.(info.id);\n throw new CustomCommandExecutionError(`read-only: blocked ${info.id}`);\n }\n });\n\n // Layer 2: permission flip (best-effort; load-bearing only on full hosts).\n const svc = injector?.get(IPermissionService) as\n | {\n addPermissionPoint(p: unknown): boolean;\n updatePermissionPoint(id: string, value: unknown): void;\n getPermissionPoint(id: string): { value: unknown } | undefined;\n }\n | undefined;\n\n // Constructing the point yields the deterministic id Univer derives for the\n // workbook-edit permission; we don't keep the instance otherwise.\n const id = new WorkbookEditablePermission(unitId).id;\n let prev: unknown;\n if (svc) {\n try {\n const existing = svc.getPermissionPoint(id);\n if (existing) {\n prev = existing.value;\n svc.updatePermissionPoint(id, false);\n } else {\n const point = new WorkbookEditablePermission(unitId);\n point.value = false;\n svc.addPermissionPoint(point);\n }\n } catch {\n /* best-effort — the veto above is the load-bearing layer */\n }\n }\n\n return () => {\n vetoDisposable?.dispose();\n try {\n svc?.updatePermissionPoint(id, prev === undefined ? true : prev);\n } catch {\n /* swallow */\n }\n };\n}\n\n/**\n * Read the current `WorkbookEditablePermission` value for a unit — `true`\n * (editable), `false` (read-only), or `undefined` if the point isn't\n * registered yet. Lets hosts/tests confirm {@link applyReadOnly} took.\n */\nexport function getEditable(univerApi: FUniver, unitId: string): boolean | undefined {\n const injector = (univerApi as unknown as { _injector?: { get(t: unknown): unknown } })._injector;\n const svc = injector?.get(IPermissionService) as\n | { getPermissionPoint(id: string): { value: unknown } | undefined }\n | undefined;\n if (!svc) return undefined;\n const id = new WorkbookEditablePermission(unitId).id;\n const point = svc.getPermissionPoint(id);\n return point ? (point.value as boolean) : undefined;\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. `args` carries command payloads\n * (e.g. font family/size, colour) — forwarded over the protocol. v0.6+. */\n executeCommand(command: CommandExecuteData['command'], args?: CommandExecuteData['args']): 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, args) =>\n transportRef.current?.sendCommandExecute({ command, args }),\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// Save / exit notifications (editor → host, fire-and-forget)\n// ---------------------------------------------------------------\n//\n// The lightweight counterpart to the bytes-carrying load/save *request*\n// pair above. These mirror the SDK's React `onSave` / `onExit` hooks one\n// for one — the \"one shape, two surfaces\" save/exit contract — so a host\n// that frames the iframe gets the same persistence signals a host that\n// renders `<CasualSheets>` directly does. Fire-and-forget: the editor\n// never owns storage; the host decides what to do with the snapshot.\n//\n// `casual.save.request` (above) stays the WOPI-style path for hosts that\n// want xlsx bytes + etag round-trips; these notifications are the simpler\n// \"here's the current state, persist it however you like\" path.\n\n/** Editor → host: the user explicitly asked to save — Ctrl/Cmd+S inside\n * the iframe, or the host's `casual.command.save`. Carries the full\n * editor snapshot as JSON (sheet app: Univer's `IWorkbookData`). Mirror\n * of the React `onSave` hook. v0.9+. */\nexport interface SaveNotifyData {\n /** App-specific snapshot JSON. Sheet: `IWorkbookData`. */\n snapshot: unknown;\n /** What triggered the save: the in-editor shortcut or a host command. */\n reason: 'shortcut' | 'host';\n}\n\n/** Editor → host: the editor is unmounting / navigating away. Carries\n * the final snapshot so the host can persist on exit. Mirror of the\n * React `onExit` hook. v0.9+. */\nexport interface ExitData {\n /** App-specific snapshot JSON. Sheet: `IWorkbookData`. */\n snapshot: unknown;\n}\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 SaveNotifyData,\n type ExitData,\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 (WOPI-style, carries xlsx bytes). */\n onSaveRequest?: (data: SaveRequestData) => Promise<SaveResponseData> | SaveResponseData;\n /** Editor fired its lightweight save notification (Ctrl/Cmd+S or a\n * host save command). Carries the full snapshot JSON; fire-and-forget.\n * Mirror of the React `onSave` hook. */\n onSaveNotify?: (data: SaveNotifyData) => void;\n /** Editor is unmounting; carries the final snapshot. Mirror of the\n * React `onExit` hook. */\n onExit?: (data: ExitData) => void;\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.save.notify':\n this.handlers.onSaveNotify?.(env.data as SaveNotifyData);\n return;\n case 'casual.exit':\n this.handlers.onExit?.(env.data as ExitData);\n return;\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_core","import_sheets","import_react","import_jsx_runtime","DEFAULT_STYLE","CasualSheetsIframe"]}
package/dist/sheets.d.cts CHANGED
@@ -4,8 +4,8 @@ import { IWorkbookData, Univer, LocaleType, ILocales, LogLevel } from '@univerjs
4
4
  import { defaultTheme } from '@univerjs/themes';
5
5
  import { C as CasualSheetsAPI } from './api-CI_qPlRB.cjs';
6
6
  export { R as RangeRef, c as createCasualSheetsAPI } from './api-CI_qPlRB.cjs';
7
+ import { FUniver } from '@univerjs/core/facade';
7
8
  import { q as SelectionChangedData, r as SelectionFormatStateData, T as TelemetryEventData, b as CasualErrorData, c as CommandExecuteData } from './protocol-Cq4Cdoyi.cjs';
8
- import '@univerjs/core/facade';
9
9
  import './types-s_O0u6Cg.cjs';
10
10
 
11
11
  interface CasualSheetsProps {
@@ -114,6 +114,31 @@ interface CasualSheetsProps {
114
114
  }
115
115
  declare function CasualSheets({ initialData, onReady, onChange, onChangeDebounceMs, onSave, onExit, lazyPlugins, onBeforeCreateUnit, formula, locale, locales, logLevel, ui, theme, appearance, chrome, style, className, testId, }: CasualSheetsProps): react.JSX.Element;
116
116
 
117
+ /**
118
+ * Make a workbook genuinely READ-ONLY.
119
+ *
120
+ * Two layers, because they cover different host setups:
121
+ *
122
+ * 1. **Command veto** (`beforeCommandExecuted` → throw
123
+ * `CustomCommandExecutionError`) — cancels every mutating command. This is
124
+ * the load-bearing layer for the iframe embed, whose minimal plugin set does
125
+ * NOT enforce `WorkbookEditablePermission` (verified: the editor still
126
+ * accepts edits with the permission flipped off).
127
+ * 2. **Permission flip** (`WorkbookEditablePermission` → false) — on the full
128
+ * `<CasualSheets>` host path this also greys out mutating menu items and
129
+ * stops the editor opening; harmless where unenforced.
130
+ *
131
+ * Returns a disposer that removes the veto and restores the prior editable
132
+ * state (for callers that toggle a live unit between preview/editor).
133
+ */
134
+ declare function applyReadOnly(univerApi: FUniver, unitId: string, onBlock?: (commandId: string) => void): () => void;
135
+ /**
136
+ * Read the current `WorkbookEditablePermission` value for a unit — `true`
137
+ * (editable), `false` (read-only), or `undefined` if the point isn't
138
+ * registered yet. Lets hosts/tests confirm {@link applyReadOnly} took.
139
+ */
140
+ declare function getEditable(univerApi: FUniver, unitId: string): boolean | undefined;
141
+
117
142
  /** What the host-side load/save handlers consume + return. The wrapper
118
143
  * binds these to the host's FileSource via simple adapters. */
119
144
  interface HostFileBridge {
@@ -132,8 +157,9 @@ interface CasualSheetsIframeRef {
132
157
  setViewMode(mode: 'preview' | 'editor'): void;
133
158
  iframe(): HTMLIFrameElement | null;
134
159
  /** Dispatch a formatting / navigation command (bold, italic, undo, …)
135
- * against the iframe's active selection. v0.6+. */
136
- executeCommand(command: CommandExecuteData['command']): void;
160
+ * against the iframe's active selection. `args` carries command payloads
161
+ * (e.g. font family/size, colour) — forwarded over the protocol. v0.6+. */
162
+ executeCommand(command: CommandExecuteData['command'], args?: CommandExecuteData['args']): void;
137
163
  }
138
164
  interface CasualSheetsIframeProps {
139
165
  /** Host-side bytes bridge. The wrapper round-trips load / save to the
@@ -160,4 +186,4 @@ interface CasualSheetsIframeProps {
160
186
  }
161
187
  declare const CasualSheetsIframe: react.ForwardRefExoticComponent<CasualSheetsIframeProps & react.RefAttributes<CasualSheetsIframeRef>>;
162
188
 
163
- export { CasualSheets, CasualSheetsAPI, CasualSheetsIframe, type CasualSheetsIframeProps, type CasualSheetsIframeRef, type CasualSheetsProps, type HostFileBridge };
189
+ export { CasualSheets, CasualSheetsAPI, CasualSheetsIframe, type CasualSheetsIframeProps, type CasualSheetsIframeRef, type CasualSheetsProps, type HostFileBridge, applyReadOnly, getEditable };