@casualoffice/sheets 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/LICENSE +200 -0
  2. package/dist/embed/embed-runtime.js +537 -0
  3. package/dist/embed/embed.html +29 -0
  4. package/dist/embed/parser.worker.js +48474 -0
  5. package/dist/embed.cjs +225 -0
  6. package/dist/embed.cjs.map +1 -0
  7. package/dist/embed.d.cts +100 -0
  8. package/dist/embed.d.ts +100 -0
  9. package/dist/embed.js +204 -0
  10. package/dist/embed.js.map +1 -0
  11. package/dist/index.cjs +1549 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.cts +9 -0
  14. package/dist/index.d.ts +9 -0
  15. package/dist/index.js +1530 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/parser.worker.cjs +48469 -0
  18. package/dist/parser.worker.cjs.map +1 -0
  19. package/dist/parser.worker.js +48474 -0
  20. package/dist/parser.worker.js.map +1 -0
  21. package/dist/protocol--KyBQUjU.d.cts +171 -0
  22. package/dist/protocol-cEzy7S0i.d.ts +171 -0
  23. package/dist/sheets.cjs +677 -0
  24. package/dist/sheets.cjs.map +1 -0
  25. package/dist/sheets.d.cts +177 -0
  26. package/dist/sheets.d.ts +177 -0
  27. package/dist/sheets.js +658 -0
  28. package/dist/sheets.js.map +1 -0
  29. package/dist/signing.cjs +706 -0
  30. package/dist/signing.cjs.map +1 -0
  31. package/dist/signing.d.cts +141 -0
  32. package/dist/signing.d.ts +141 -0
  33. package/dist/signing.js +683 -0
  34. package/dist/signing.js.map +1 -0
  35. package/dist/styles.cjs +10 -0
  36. package/dist/styles.cjs.map +1 -0
  37. package/dist/styles.d.cts +2 -0
  38. package/dist/styles.d.ts +2 -0
  39. package/dist/styles.js +8 -0
  40. package/dist/styles.js.map +1 -0
  41. package/dist/types-s_O0u6Cg.d.cts +90 -0
  42. package/dist/types-s_O0u6Cg.d.ts +90 -0
  43. package/dist/univer.cjs +220 -0
  44. package/dist/univer.cjs.map +1 -0
  45. package/dist/univer.d.cts +60 -0
  46. package/dist/univer.d.ts +60 -0
  47. package/dist/univer.js +187 -0
  48. package/dist/univer.js.map +1 -0
  49. package/dist/xlsx.cjs +3388 -0
  50. package/dist/xlsx.cjs.map +1 -0
  51. package/dist/xlsx.d.cts +383 -0
  52. package/dist/xlsx.d.ts +383 -0
  53. package/dist/xlsx.js +3383 -0
  54. package/dist/xlsx.js.map +1 -0
  55. package/package.json +293 -0
  56. package/src/embed/EmbedHostTransport.ts +226 -0
  57. package/src/embed/EmbedTransport.ts +323 -0
  58. package/src/embed/EmbedTransport.unit.test.ts +161 -0
  59. package/src/embed/index.ts +40 -0
  60. package/src/embed/protocol.ts +258 -0
  61. package/src/embed-runtime/embed.html +29 -0
  62. package/src/embed-runtime/index.tsx +440 -0
  63. package/src/index.ts +16 -0
  64. package/src/sheets/CasualSheets.tsx +319 -0
  65. package/src/sheets/CasualSheetsIframe.tsx +220 -0
  66. package/src/sheets/api.ts +108 -0
  67. package/src/sheets/index.ts +11 -0
  68. package/src/signing/SigningPane.tsx +374 -0
  69. package/src/signing/SigningProvider.tsx +126 -0
  70. package/src/signing/captures.tsx +316 -0
  71. package/src/signing/controller.ts +151 -0
  72. package/src/signing/controller.unit.test.ts +133 -0
  73. package/src/signing/index.ts +44 -0
  74. package/src/signing/types.ts +89 -0
  75. package/src/styles.ts +16 -0
  76. package/src/univer/index.ts +17 -0
  77. package/src/univer/lazy-plugins.ts +280 -0
  78. package/src/xlsx/_perf.ts +14 -0
  79. package/src/xlsx/_snapshot-constants.ts +15 -0
  80. package/src/xlsx/comments-resource.ts +209 -0
  81. package/src/xlsx/constants.ts +9 -0
  82. package/src/xlsx/data-validation-resource.ts +219 -0
  83. package/src/xlsx/import.ts +35 -0
  84. package/src/xlsx/index.ts +40 -0
  85. package/src/xlsx/page-setup-resource.ts +205 -0
  86. package/src/xlsx/parse-impl.ts +418 -0
  87. package/src/xlsx/parse-in-worker.ts +82 -0
  88. package/src/xlsx/parser.worker.ts +39 -0
  89. package/src/xlsx/passthrough-resource.ts +175 -0
  90. package/src/xlsx/pivot-passthrough.ts +359 -0
  91. package/src/xlsx/style-mapping.ts +171 -0
  92. package/src/xlsx/tables-resource.ts +211 -0
@@ -0,0 +1,440 @@
1
+ /**
2
+ * embed-runtime — the in-iframe entry point for the sheet SDK's iframe
3
+ * delivery mode. Sheet sibling of `@casualoffice/docs`'s
4
+ * `embed-runtime`; see doc 16 in the parent repo.
5
+ *
6
+ * Lifecycle:
7
+ *
8
+ * 1. Wrapper renders <iframe src="${embedBasePath}/embed.html?...">.
9
+ * 2. iframe loads embed.html which imports + runs `mountEmbedded()`
10
+ * from this module's compiled bundle.
11
+ * 3. `mountEmbedded` parses URL params, opens an EmbedTransport,
12
+ * waits for the host's `casual.hello`, issues a load.request
13
+ * for the docId, parses the returned xlsx bytes via
14
+ * `xlsxToWorkbookData` → IWorkbookData, mounts <CasualSheets>
15
+ * with that snapshot into #casual-embed-root.
16
+ * 4. Selection / autosave / signing events all bubble out via the
17
+ * same transport.
18
+ */
19
+
20
+ import { useEffect, useState } from 'react';
21
+ import { createRoot } from 'react-dom/client';
22
+
23
+ import { EmbedTransport } from '../embed/EmbedTransport';
24
+ import type { CasualApp } from '../embed/protocol';
25
+ import { CasualSheets } from '../sheets/CasualSheets';
26
+ import { xlsxToWorkbookData } from '../xlsx';
27
+ import type { IWorkbookData } from '@univerjs/core';
28
+
29
+ interface EmbedUrlConfig {
30
+ app: CasualApp;
31
+ docId: string;
32
+ viewMode: 'preview' | 'editor';
33
+ }
34
+
35
+ function parseUrlConfig(search: string): EmbedUrlConfig {
36
+ const params = new URLSearchParams(search);
37
+ const app: CasualApp = params.get('app') === 'docs' ? 'docs' : 'sheet';
38
+ const docId = params.get('docId') ?? '';
39
+ const viewModeParam = params.get('viewMode');
40
+ const viewMode: 'preview' | 'editor' = viewModeParam === 'editor' ? 'editor' : 'preview';
41
+ return { app, docId, viewMode };
42
+ }
43
+
44
+ export interface MountEmbeddedOptions {
45
+ root: HTMLElement;
46
+ search?: string;
47
+ hostOrigin?: string;
48
+ identity?: { version: string; commit: string; capabilities?: string[] };
49
+ }
50
+
51
+ /** Public entry — called by embed.html. */
52
+ // Side-effect import: pull in Univer's CSS modules so the embed
53
+ // runtime is fully self-contained. Without these, the embed.html has
54
+ // no <link rel="stylesheet"> and Univer's workbench renders unstyled
55
+ // (the canvas mounts at 0×0 size, all chrome divs have no layout).
56
+ // `injectStyle: true` in tsup turns each import into a runtime
57
+ // <style> tag append.
58
+ import '../styles';
59
+
60
+ // Side-effect imports: augment the FUniver facade with the
61
+ // `getActiveWorkbook` / `getActiveSheet` / `getActiveRange` chain
62
+ // the toolbar bridge calls. The sheet SDK ships these on Univer's
63
+ // sheets / sheets-ui packages as facade extensions; without the
64
+ // import the typings are missing and the runtime calls noop.
65
+ import '@univerjs/sheets/facade';
66
+ import '@univerjs/sheets-ui/facade';
67
+
68
+ export function mountEmbedded(opts: MountEmbeddedOptions): void {
69
+ const search = opts.search ?? (typeof window !== 'undefined' ? window.location.search : '');
70
+ const config = parseUrlConfig(search);
71
+
72
+ const hostOrigin =
73
+ opts.hostOrigin ??
74
+ inferHostOrigin() ??
75
+ (typeof window !== 'undefined' ? window.location.origin : '');
76
+
77
+ const identity = opts.identity ?? {
78
+ version: '0.0.0',
79
+ commit: 'unknown',
80
+ capabilities: ['load', 'save', 'selection', 'signing'],
81
+ };
82
+
83
+ const transport = new EmbedTransport({
84
+ app: config.app,
85
+ hostOrigin,
86
+ version: identity.version,
87
+ commit: identity.commit,
88
+ capabilities: identity.capabilities ?? ['load', 'save', 'selection', 'signing'],
89
+ });
90
+
91
+ opts.root.setAttribute('data-view-mode', config.viewMode);
92
+
93
+ transport.on({
94
+ onCommandSetViewMode: ({ viewMode }) => {
95
+ opts.root.setAttribute('data-view-mode', viewMode);
96
+ // v0.5.x will toggle CasualSheets's `ui` prop in response;
97
+ // v0.5.0 only updates the data attribute so iframe-side CSS
98
+ // can react.
99
+ },
100
+ });
101
+
102
+ transport.sendHello();
103
+ // Also send `casual.ready` so the host's `onEditorReady` fires without
104
+ // waiting for the host to send `casual.hello` first. The protocol's
105
+ // sendReady-on-receiving-hello path stays as a fallback for hosts that
106
+ // do send hello eagerly; this just kicks off the handshake on the
107
+ // iframe side so we don't deadlock when the host's strategy is to
108
+ // wait for `casual.ready`.
109
+ transport.sendReady();
110
+
111
+ const reactRoot = createRoot(opts.root);
112
+ reactRoot.render(
113
+ <EmbeddedSheets transport={transport} docId={config.docId} initialViewMode={config.viewMode} />,
114
+ );
115
+ }
116
+
117
+ /** Inner React component — handles the async xlsx load + workbook
118
+ * mount. Splitting it out lets useEffect orchestrate the
119
+ * request/response without blocking the React tree at mount. */
120
+ function EmbeddedSheets({
121
+ transport,
122
+ docId,
123
+ initialViewMode,
124
+ }: {
125
+ transport: EmbedTransport;
126
+ docId: string;
127
+ initialViewMode: 'preview' | 'editor';
128
+ }) {
129
+ const [data, setData] = useState<IWorkbookData | null>(null);
130
+ const [errorMsg, setErrorMsg] = useState<string | null>(null);
131
+
132
+ const [viewMode, setViewMode] = useState<'preview' | 'editor'>(initialViewMode);
133
+ // Editor mode flips `header: true` so Univer's formula bar (A1 cell
134
+ // ref, fx button) and menubar render at the top of the iframe —
135
+ // visually distinct from preview's canvas-only surface. `toolbar` and
136
+ // `footer` stay off: Univer's ribbon and sheet-tabs slot resolve
137
+ // services (IRPCChannelService, sheet-drawing) at construction that
138
+ // the SDK doesn't bundle a worker for, and turning them on lights up
139
+ // `[redi]: Cannot find "Kb" registered by any injector` and the
140
+ // canvas never paints. Cells stay editable in editor mode via direct
141
+ // keyboard input on the focused cell.
142
+ const ui =
143
+ viewMode === 'editor'
144
+ ? { header: true, toolbar: false, footer: false, contextMenu: true }
145
+ : { header: false, toolbar: false, footer: false, contextMenu: true };
146
+
147
+ useEffect(() => {
148
+ transport.on({
149
+ onCommandSetViewMode: ({ viewMode: next }) => setViewMode(next),
150
+ });
151
+ }, [transport]);
152
+
153
+ useEffect(() => {
154
+ let cancelled = false;
155
+ void (async () => {
156
+ try {
157
+ const resp = await transport.requestLoad(docId);
158
+ if (cancelled) return;
159
+ if (!resp.ok) {
160
+ setErrorMsg(resp.message ?? `load failed: ${resp.code}`);
161
+ transport.sendError({
162
+ code: 'load_failed',
163
+ message: resp.message ?? resp.code,
164
+ });
165
+ return;
166
+ }
167
+ const snapshot = await xlsxToWorkbookData(resp.bytes);
168
+ if (cancelled) return;
169
+ setData(snapshot);
170
+ } catch (err) {
171
+ if (cancelled) return;
172
+ const message = err instanceof Error ? err.message : String(err);
173
+ setErrorMsg(message);
174
+ transport.sendError({ code: 'parse_failed', message });
175
+ }
176
+ })();
177
+ return () => {
178
+ cancelled = true;
179
+ };
180
+ }, [transport, docId]);
181
+
182
+ if (errorMsg) {
183
+ return (
184
+ <div
185
+ style={{
186
+ width: '100%',
187
+ height: '100%',
188
+ display: 'flex',
189
+ alignItems: 'center',
190
+ justifyContent: 'center',
191
+ padding: 24,
192
+ textAlign: 'center',
193
+ color: 'var(--danger, #d63a2f)',
194
+ fontSize: 13,
195
+ }}
196
+ >
197
+ Failed to load workbook: {errorMsg}
198
+ </div>
199
+ );
200
+ }
201
+
202
+ if (!data) {
203
+ return (
204
+ <div
205
+ style={{
206
+ width: '100%',
207
+ height: '100%',
208
+ display: 'flex',
209
+ alignItems: 'center',
210
+ justifyContent: 'center',
211
+ color: 'var(--text-muted, #5a5a5a)',
212
+ fontSize: 13,
213
+ }}
214
+ >
215
+ Loading workbook…
216
+ </div>
217
+ );
218
+ }
219
+
220
+ // Force a remount when viewMode flips — CasualSheets locks the UI
221
+ // config at registerPlugin time and won't pick up new props.
222
+ return (
223
+ <CasualSheets
224
+ key={viewMode}
225
+ initialData={data}
226
+ ui={ui}
227
+ // The embed runtime is bundled as ONE self-contained file (tsup
228
+ // `noExternal: /.*/`). Lazy plugins would emit per-feature dynamic
229
+ // chunks into dist/embed/, breaking that single-file deployment, so the
230
+ // iframe ships the minimal editor. Hosts that need feature plugins use
231
+ // the React `<CasualSheets>` component directly (lazyPlugins defaults on).
232
+ lazyPlugins={false}
233
+ onReady={(api) => {
234
+ // Wire host → editor command.execute (Drive's custom toolbar
235
+ // calls this for bold / italic / undo / …). Maps the small
236
+ // protocol union to the Univer command id the FUniver facade
237
+ // dispatches. The command set is intentionally narrow — v0.6
238
+ // ships the always-relevant operations, font / color / fill
239
+ // land in v0.7 once we lock the cell-mutate payload shape.
240
+ // `api` is now the CasualSheetsAPI imperative ref: executeCommand is a
241
+ // first-class method, and the raw FUniver facade is on `api.univer`.
242
+ // Cast through `unknown` — the FUniver type is augmented via
243
+ // @univerjs/sheets/facade module augmentation (the side-effect
244
+ // import above), but tsc doesn't always pick up the merged
245
+ // signature when the facade is imported as a side effect inside
246
+ // a different module. The runtime behaviour is fine.
247
+ const apiAny = api as unknown as {
248
+ executeCommand(id: string, params?: object): Promise<unknown>;
249
+ univer: { getActiveWorkbook(): { getActiveSheet(): SheetLike | null } | null };
250
+ };
251
+ transport.on({
252
+ onCommandExecute: ({ command, args }) => {
253
+ const id = SHEET_COMMAND_MAP[command];
254
+ if (!id) return;
255
+ const params = buildCommandParams(command, args);
256
+ try {
257
+ void apiAny.executeCommand(id, params);
258
+ } catch {
259
+ /* swallow — bad command id from a stale host shouldn't crash the iframe */
260
+ }
261
+ },
262
+ });
263
+
264
+ // Editor → host: emit format state on a coarse interval. The
265
+ // FUniver facade in 0.24 doesn't expose a stable
266
+ // selection-changed event hook on FWorksheet (the public API
267
+ // is in flux); polling every 200 ms is the smallest reliable
268
+ // surface that keeps the toolbar buttons in sync without
269
+ // hooking the internal command service. Drive throttles its
270
+ // own re-renders, so the wire stays cheap.
271
+ const emit = () => {
272
+ try {
273
+ const wb = apiAny.univer.getActiveWorkbook();
274
+ const sheet = wb?.getActiveSheet();
275
+ const range = sheet?.getActiveRange?.();
276
+ if (!range) return;
277
+ const cell = range.getCell?.(0, 0) ?? range;
278
+ const fmt = readFormatFlags(cell);
279
+ transport.sendSelectionFormatState(fmt);
280
+ } catch {
281
+ /* selection may not exist yet during boot — ignore */
282
+ }
283
+ };
284
+ emit();
285
+ const interval = setInterval(emit, 200);
286
+ // Best-effort cleanup — there's no unmount hook here, but if
287
+ // the iframe navigates away the interval is GC'd with the
288
+ // closure.
289
+ void interval;
290
+ }}
291
+ />
292
+ );
293
+ }
294
+
295
+ /** Maps the host-facing protocol union to Univer command ids the
296
+ * FUniver facade dispatches. Kept inline so the wire surface and the
297
+ * Univer integration evolve together. */
298
+ const SHEET_COMMAND_MAP: Record<string, string> = {
299
+ undo: 'univer.command.undo',
300
+ redo: 'univer.command.redo',
301
+ bold: 'sheet.command.set-range-bold',
302
+ italic: 'sheet.command.set-range-italic',
303
+ underline: 'sheet.command.set-range-underline',
304
+ // The canonical id is `set-range-stroke` (see sheets-ui's
305
+ // SetRangeStrickThroughCommand). 0.6 shipped the wrong id; fixed here.
306
+ strikethrough: 'sheet.command.set-range-stroke',
307
+ 'align-left': 'sheet.command.set-horizontal-text-align',
308
+ 'align-center': 'sheet.command.set-horizontal-text-align',
309
+ 'align-right': 'sheet.command.set-horizontal-text-align',
310
+ // v0.7 — rich format
311
+ 'set-font-family': 'sheet.command.set-range-font-family',
312
+ 'set-font-size': 'sheet.command.set-range-fontsize',
313
+ 'set-text-color': 'sheet.command.set-range-text-color',
314
+ 'reset-text-color': 'sheet.command.reset-range-text-color',
315
+ 'set-bg-color': 'sheet.command.set-background-color',
316
+ 'reset-bg-color': 'sheet.command.reset-background-color',
317
+ merge: 'sheet.command.add-worksheet-merge',
318
+ unmerge: 'sheet.command.remove-worksheet-merge',
319
+ // v0.8 — number formats + freeze + wrap
320
+ 'numfmt-currency': 'sheet.command.numfmt.set.currency',
321
+ 'numfmt-percent': 'sheet.command.numfmt.set.percent',
322
+ 'numfmt-add-decimal': 'sheet.command.numfmt.add.decimal.command',
323
+ 'numfmt-subtract-decimal': 'sheet.command.numfmt.subtract.decimal.command',
324
+ 'numfmt-custom': 'sheet.command.numfmt.set.numfmt',
325
+ 'wrap-toggle': 'sheet.command.set-text-wrap',
326
+ 'freeze-first-row': 'sheet.command.set-first-row-frozen',
327
+ 'freeze-first-column': 'sheet.command.set-first-column-frozen',
328
+ 'freeze-none': 'sheet.command.set-selection-frozen',
329
+ };
330
+
331
+ /** Build the `params` object the Univer command expects. Most v0.6
332
+ * toggles take no params (the command reads the current cell + flips
333
+ * the bit). Align reads `{ value: 'left'|'center'|'right' }`, font /
334
+ * size / colour read `{ value: <typed value> }`. Bad inputs are
335
+ * filtered out so a stale host can't crash the iframe. */
336
+ function buildCommandParams(
337
+ command: string,
338
+ args?: { family?: string; size?: number; color?: string; pattern?: string },
339
+ ): object | undefined {
340
+ switch (command) {
341
+ case 'align-left':
342
+ return { value: 'left' };
343
+ case 'align-center':
344
+ return { value: 'center' };
345
+ case 'align-right':
346
+ return { value: 'right' };
347
+ case 'set-font-family':
348
+ return args?.family ? { value: args.family } : undefined;
349
+ case 'set-font-size':
350
+ return typeof args?.size === 'number' ? { value: args.size } : undefined;
351
+ case 'set-text-color':
352
+ return args?.color ? { value: args.color } : undefined;
353
+ case 'set-bg-color':
354
+ return args?.color ? { value: args.color } : undefined;
355
+ case 'numfmt-custom':
356
+ return args?.pattern ? { value: args.pattern } : undefined;
357
+ case 'wrap-toggle':
358
+ // The set-text-wrap command toggles when called with no args.
359
+ return undefined;
360
+ default:
361
+ return undefined;
362
+ }
363
+ }
364
+
365
+ interface CellLike {
366
+ getFontWeight?: () => unknown;
367
+ getFontStyle?: () => unknown;
368
+ getUnderline?: () => unknown;
369
+ getStrikethrough?: () => unknown;
370
+ getHorizontalAlignment?: () => unknown;
371
+ getFontFamily?: () => unknown;
372
+ getFontSize?: () => unknown;
373
+ getFontColor?: () => unknown;
374
+ getBackground?: () => unknown;
375
+ getBackgroundColor?: () => unknown;
376
+ }
377
+
378
+ interface RangeLike {
379
+ getCell?(row: number, col: number): unknown;
380
+ getFontWeight?: () => unknown;
381
+ getFontStyle?: () => unknown;
382
+ getUnderline?: () => unknown;
383
+ getStrikethrough?: () => unknown;
384
+ getHorizontalAlignment?: () => unknown;
385
+ }
386
+
387
+ interface SheetLike {
388
+ getActiveRange?(): RangeLike | null;
389
+ }
390
+
391
+ function readFormatFlags(cell: unknown): {
392
+ bold: boolean;
393
+ italic: boolean;
394
+ underline: boolean;
395
+ strikethrough: boolean;
396
+ align: 'left' | 'center' | 'right' | null;
397
+ fontFamily: string | null;
398
+ fontSize: number | null;
399
+ textColor: string | null;
400
+ bgColor: string | null;
401
+ } {
402
+ const c = cell as CellLike;
403
+ const bold = String(c.getFontWeight?.() ?? '').toLowerCase() === 'bold';
404
+ const italic = String(c.getFontStyle?.() ?? '').toLowerCase() === 'italic';
405
+ const underline = !!c.getUnderline?.();
406
+ const strikethrough = !!c.getStrikethrough?.();
407
+ const ha = String(c.getHorizontalAlignment?.() ?? '').toLowerCase();
408
+ const align: 'left' | 'center' | 'right' | null =
409
+ ha === 'left' || ha === 'center' || ha === 'right' ? ha : null;
410
+ const ff = c.getFontFamily?.();
411
+ const fontFamily = typeof ff === 'string' && ff ? ff : null;
412
+ const fs = c.getFontSize?.();
413
+ const fontSize = typeof fs === 'number' && fs > 0 ? fs : null;
414
+ const tc = c.getFontColor?.();
415
+ const textColor = typeof tc === 'string' && tc ? tc : null;
416
+ const bg = c.getBackground?.() ?? c.getBackgroundColor?.();
417
+ const bgColor = typeof bg === 'string' && bg ? bg : null;
418
+ return {
419
+ bold,
420
+ italic,
421
+ underline,
422
+ strikethrough,
423
+ align,
424
+ fontFamily,
425
+ fontSize,
426
+ textColor,
427
+ bgColor,
428
+ };
429
+ }
430
+
431
+ function inferHostOrigin(): string | undefined {
432
+ if (typeof document === 'undefined') return undefined;
433
+ const ref = document.referrer;
434
+ if (!ref) return undefined;
435
+ try {
436
+ return new URL(ref).origin;
437
+ } catch {
438
+ return undefined;
439
+ }
440
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @casualoffice/sheets — Casual Sheets SDK
3
+ *
4
+ * Three surfaces:
5
+ * - `./signing` — anchored cell signatures (drawn / typed / uploaded).
6
+ * - `./embed` — iframe postMessage protocol for host integrations.
7
+ * - `./sheets` — `CasualSheets` React wrapper around Univer Sheets.
8
+ *
9
+ * The `./styles` side-effect entry brings in the eager plugin CSS:
10
+ *
11
+ * import '@casualoffice/sheets/styles';
12
+ */
13
+
14
+ export * from './signing';
15
+ export * from './embed';
16
+ export * from './sheets';