@casualoffice/sheets 0.9.0 → 0.10.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 (67) hide show
  1. package/dist/api-BI2VLYQ6.d.cts +62 -0
  2. package/dist/api-BI2VLYQ6.d.ts +62 -0
  3. package/dist/chrome.cjs +2569 -0
  4. package/dist/chrome.cjs.map +1 -0
  5. package/dist/chrome.d.cts +96 -0
  6. package/dist/chrome.d.ts +96 -0
  7. package/dist/chrome.js +2556 -0
  8. package/dist/chrome.js.map +1 -0
  9. package/dist/collab.cjs +770 -0
  10. package/dist/collab.cjs.map +1 -0
  11. package/dist/collab.d.cts +248 -0
  12. package/dist/collab.d.ts +248 -0
  13. package/dist/collab.js +737 -0
  14. package/dist/collab.js.map +1 -0
  15. package/dist/embed/embed-runtime.js +128 -128
  16. package/dist/embed.cjs +166 -0
  17. package/dist/embed.cjs.map +1 -1
  18. package/dist/embed.d.cts +78 -3
  19. package/dist/embed.d.ts +78 -3
  20. package/dist/embed.js +166 -0
  21. package/dist/embed.js.map +1 -1
  22. package/dist/index.cjs +262 -165
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +4 -3
  25. package/dist/index.d.ts +4 -3
  26. package/dist/index.js +271 -168
  27. package/dist/index.js.map +1 -1
  28. package/dist/{protocol--KyBQUjU.d.cts → protocol-Cq4Cdoyi.d.cts} +19 -2
  29. package/dist/{protocol-cEzy7S0i.d.ts → protocol-DqDaG2yG.d.ts} +19 -2
  30. package/dist/sheets.cjs +102 -16
  31. package/dist/sheets.cjs.map +1 -1
  32. package/dist/sheets.d.cts +49 -63
  33. package/dist/sheets.d.ts +49 -63
  34. package/dist/sheets.js +111 -19
  35. package/dist/sheets.js.map +1 -1
  36. package/package.json +28 -3
  37. package/src/chrome/AutoSumPicker.tsx +176 -0
  38. package/src/chrome/BordersPicker.tsx +171 -0
  39. package/src/chrome/ChromeBottom.tsx +18 -0
  40. package/src/chrome/ChromeTop.tsx +21 -0
  41. package/src/chrome/ColorPicker.tsx +220 -0
  42. package/src/chrome/FindReplace.tsx +370 -0
  43. package/src/chrome/FormulaBar.tsx +378 -0
  44. package/src/chrome/Icon.tsx +43 -0
  45. package/src/chrome/MenuBar.tsx +336 -0
  46. package/src/chrome/NameBox.tsx +347 -0
  47. package/src/chrome/SheetTabs.tsx +346 -0
  48. package/src/chrome/StatusBar.tsx +232 -0
  49. package/src/chrome/Toolbar.tsx +401 -0
  50. package/src/chrome/fonts.ts +42 -0
  51. package/src/chrome/index.ts +24 -0
  52. package/src/collab/attachCollab.ts +151 -0
  53. package/src/collab/bridge-helpers.ts +97 -0
  54. package/src/collab/bridge.ts +885 -0
  55. package/src/collab/bridge.unit.test.ts +160 -0
  56. package/src/collab/index.ts +38 -0
  57. package/src/collab/replay-retry.ts +137 -0
  58. package/src/collab/replay-retry.unit.test.ts +223 -0
  59. package/src/collab/ws-url.ts +20 -0
  60. package/src/collab/ws-url.unit.test.ts +35 -0
  61. package/src/embed/EmbedHostTransport.ts +16 -1
  62. package/src/embed/EmbedTransport.ts +16 -0
  63. package/src/embed/EmbedTransport.unit.test.ts +88 -2
  64. package/src/embed/index.ts +7 -0
  65. package/src/embed/protocol.ts +34 -0
  66. package/src/embed-runtime/index.tsx +20 -0
  67. package/src/sheets/CasualSheets.tsx +204 -33
package/dist/chrome.js ADDED
@@ -0,0 +1,2556 @@
1
+ // src/chrome/Toolbar.tsx
2
+ import { useEffect as useEffect4, useState as useState4 } from "react";
3
+ import { ICommandService } from "@univerjs/core";
4
+
5
+ // src/chrome/Icon.tsx
6
+ import { jsx } from "react/jsx-runtime";
7
+ function Icon({ name, size = 20, style }) {
8
+ return /* @__PURE__ */ jsx(
9
+ "span",
10
+ {
11
+ className: "material-symbols-outlined",
12
+ "aria-hidden": true,
13
+ style: {
14
+ fontFamily: "'Material Symbols Outlined'",
15
+ fontWeight: "normal",
16
+ fontStyle: "normal",
17
+ fontSize: size,
18
+ lineHeight: 1,
19
+ letterSpacing: "normal",
20
+ textTransform: "none",
21
+ display: "inline-block",
22
+ whiteSpace: "nowrap",
23
+ wordWrap: "normal",
24
+ direction: "ltr",
25
+ // Hint the variable-font axes so weight/optical-size are consistent.
26
+ fontVariationSettings: "'opsz' 20, 'wght' 400, 'FILL' 0, 'GRAD' 0",
27
+ ...style
28
+ },
29
+ children: name
30
+ }
31
+ );
32
+ }
33
+
34
+ // src/chrome/fonts.ts
35
+ var LINKS = [
36
+ {
37
+ id: "cs-font-inter",
38
+ href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
39
+ },
40
+ {
41
+ id: "cs-font-material-symbols",
42
+ href: "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,300..700,0..1,-50..200&display=block"
43
+ }
44
+ ];
45
+ var done = false;
46
+ function ensureChromeFonts() {
47
+ if (done || typeof document === "undefined") return;
48
+ done = true;
49
+ const head = document.head;
50
+ for (const { id, href } of LINKS) {
51
+ if (document.getElementById(id)) continue;
52
+ if (document.querySelector(`link[href*="${href.split("?")[0]}"]`)) continue;
53
+ const link = document.createElement("link");
54
+ link.id = id;
55
+ link.rel = "stylesheet";
56
+ link.href = href;
57
+ head.appendChild(link);
58
+ }
59
+ }
60
+
61
+ // src/chrome/ColorPicker.tsx
62
+ import { useEffect, useRef, useState } from "react";
63
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
64
+ var SWATCHES = [
65
+ { hex: "#000000", label: "Black" },
66
+ { hex: "#5f6368", label: "Dark gray" },
67
+ { hex: "#9aa0a6", label: "Gray" },
68
+ { hex: "#d9dce0", label: "Light gray" },
69
+ { hex: "#ffffff", label: "White" },
70
+ { hex: "#d13438", label: "Red" },
71
+ { hex: "#e8710a", label: "Orange" },
72
+ { hex: "#f2c811", label: "Yellow" },
73
+ { hex: "#107c10", label: "Green" },
74
+ { hex: "#0e7490", label: "Teal" },
75
+ { hex: "#1a73e8", label: "Blue" },
76
+ { hex: "#5b2a86", label: "Purple" },
77
+ { hex: "#c2185b", label: "Magenta" },
78
+ { hex: "#795548", label: "Brown" }
79
+ ];
80
+ var ROW_STYLE = {
81
+ display: "inline-flex",
82
+ alignItems: "center",
83
+ position: "relative"
84
+ };
85
+ var BTN_STYLE = {
86
+ width: 30,
87
+ height: 30,
88
+ display: "inline-flex",
89
+ alignItems: "center",
90
+ justifyContent: "center",
91
+ border: "none",
92
+ borderRadius: 6,
93
+ background: "transparent",
94
+ cursor: "pointer",
95
+ color: "var(--cs-chrome-fg, #201f1e)",
96
+ padding: 0
97
+ };
98
+ var POPOVER_STYLE = {
99
+ position: "absolute",
100
+ top: "100%",
101
+ left: 0,
102
+ marginTop: 4,
103
+ zIndex: 1e3,
104
+ padding: 8,
105
+ borderRadius: 8,
106
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
107
+ background: "var(--cs-chrome-input-bg, #fff)",
108
+ boxShadow: "0 6px 20px rgba(0,0,0,0.18)"
109
+ };
110
+ var GRID_STYLE = {
111
+ display: "grid",
112
+ gridTemplateColumns: "repeat(7, 22px)",
113
+ gap: 6
114
+ };
115
+ var SWATCH_STYLE = {
116
+ width: 22,
117
+ height: 22,
118
+ borderRadius: 4,
119
+ border: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.18))",
120
+ cursor: "pointer",
121
+ padding: 0
122
+ };
123
+ var NONE_STYLE = {
124
+ marginTop: 8,
125
+ width: "100%",
126
+ height: 26,
127
+ display: "inline-flex",
128
+ alignItems: "center",
129
+ justifyContent: "center",
130
+ gap: 4,
131
+ border: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.18))",
132
+ borderRadius: 6,
133
+ background: "var(--cs-chrome-input-bg, #fff)",
134
+ color: "var(--cs-chrome-fg, #201f1e)",
135
+ font: "inherit",
136
+ fontSize: 12,
137
+ cursor: "pointer"
138
+ };
139
+ function ColorPicker({ api }) {
140
+ const [open, setOpen] = useState(null);
141
+ const rootRef = useRef(null);
142
+ useEffect(() => {
143
+ if (!open) return;
144
+ const onPointerDown = (e) => {
145
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(null);
146
+ };
147
+ const onKeyDown = (e) => {
148
+ if (e.key === "Escape") setOpen(null);
149
+ };
150
+ document.addEventListener("pointerdown", onPointerDown, true);
151
+ document.addEventListener("keydown", onKeyDown);
152
+ return () => {
153
+ document.removeEventListener("pointerdown", onPointerDown, true);
154
+ document.removeEventListener("keydown", onKeyDown);
155
+ };
156
+ }, [open]);
157
+ const pick = (kind, hex) => {
158
+ if (api) {
159
+ if (kind === "text") {
160
+ void api.executeCommand("sheet.command.set-range-text-color", { value: hex });
161
+ } else if (hex === null) {
162
+ void api.executeCommand("sheet.command.reset-background-color");
163
+ } else {
164
+ void api.executeCommand("sheet.command.set-background-color", { value: hex });
165
+ }
166
+ }
167
+ setOpen(null);
168
+ };
169
+ const toggle = (kind) => setOpen((cur) => cur === kind ? null : kind);
170
+ const renderButton = (kind, icon, label, testid) => {
171
+ const on = open === kind;
172
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
173
+ return /* @__PURE__ */ jsx2(
174
+ "button",
175
+ {
176
+ type: "button",
177
+ title: label,
178
+ "aria-label": label,
179
+ "aria-haspopup": "true",
180
+ "aria-expanded": on,
181
+ "data-testid": testid,
182
+ style: {
183
+ ...BTN_STYLE,
184
+ background: baseBg,
185
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE.color
186
+ },
187
+ onMouseDown: (e) => {
188
+ e.preventDefault();
189
+ toggle(kind);
190
+ },
191
+ onMouseEnter: (e) => {
192
+ if (!on) e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
193
+ },
194
+ onMouseLeave: (e) => {
195
+ e.currentTarget.style.background = baseBg;
196
+ },
197
+ children: /* @__PURE__ */ jsx2(Icon, { name: icon, size: 20 })
198
+ }
199
+ );
200
+ };
201
+ const renderPopover = (kind) => /* @__PURE__ */ jsxs("div", { style: POPOVER_STYLE, "data-testid": "cs-color-popover", role: "menu", children: [
202
+ /* @__PURE__ */ jsx2("div", { style: GRID_STYLE, children: SWATCHES.map((s) => /* @__PURE__ */ jsx2(
203
+ "button",
204
+ {
205
+ type: "button",
206
+ role: "menuitem",
207
+ title: s.label,
208
+ "aria-label": s.label,
209
+ "data-color": s.hex,
210
+ style: { ...SWATCH_STYLE, background: s.hex },
211
+ onMouseDown: (e) => {
212
+ e.preventDefault();
213
+ pick(kind, s.hex);
214
+ }
215
+ },
216
+ s.hex
217
+ )) }),
218
+ /* @__PURE__ */ jsxs(
219
+ "button",
220
+ {
221
+ type: "button",
222
+ role: "menuitem",
223
+ "data-color": "none",
224
+ style: NONE_STYLE,
225
+ onMouseDown: (e) => {
226
+ e.preventDefault();
227
+ pick(kind, null);
228
+ },
229
+ children: [
230
+ /* @__PURE__ */ jsx2(Icon, { name: "format_color_reset", size: 16 }),
231
+ "None"
232
+ ]
233
+ }
234
+ )
235
+ ] });
236
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, style: ROW_STYLE, "data-testid": "cs-color-picker", children: [
237
+ renderButton("text", "format_color_text", "Text color", "cs-color-text"),
238
+ renderButton("fill", "format_color_fill", "Fill color", "cs-color-fill"),
239
+ open && renderPopover(open)
240
+ ] });
241
+ }
242
+
243
+ // src/chrome/BordersPicker.tsx
244
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
245
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
246
+ var OPTIONS = [
247
+ { value: "all", icon: "border_all", label: "All borders" },
248
+ { value: "outside", icon: "border_outer", label: "Outside borders" },
249
+ { value: "inside", icon: "border_inner", label: "Inside borders" },
250
+ { value: "top", icon: "border_top", label: "Top border" },
251
+ { value: "bottom", icon: "border_bottom", label: "Bottom border" },
252
+ { value: "left", icon: "border_left", label: "Left border" },
253
+ { value: "right", icon: "border_right", label: "Right border" },
254
+ { value: "none", icon: "border_clear", label: "No border" }
255
+ ];
256
+ var ROW_STYLE2 = {
257
+ display: "inline-flex",
258
+ alignItems: "center",
259
+ position: "relative"
260
+ };
261
+ var BTN_STYLE2 = {
262
+ width: 30,
263
+ height: 30,
264
+ display: "inline-flex",
265
+ alignItems: "center",
266
+ justifyContent: "center",
267
+ border: "none",
268
+ borderRadius: 6,
269
+ background: "transparent",
270
+ cursor: "pointer",
271
+ color: "var(--cs-chrome-fg, #201f1e)",
272
+ padding: 0
273
+ };
274
+ var POPOVER_STYLE2 = {
275
+ position: "absolute",
276
+ top: "100%",
277
+ left: 0,
278
+ marginTop: 4,
279
+ zIndex: 1e3,
280
+ padding: 4,
281
+ minWidth: 168,
282
+ borderRadius: 8,
283
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
284
+ background: "var(--cs-chrome-input-bg, #fff)",
285
+ boxShadow: "0 6px 20px rgba(0,0,0,0.18)"
286
+ };
287
+ var ITEM_STYLE = {
288
+ display: "flex",
289
+ alignItems: "center",
290
+ gap: 8,
291
+ width: "100%",
292
+ height: 28,
293
+ padding: "0 8px",
294
+ border: "none",
295
+ borderRadius: 6,
296
+ background: "transparent",
297
+ color: "var(--cs-chrome-fg, #201f1e)",
298
+ font: "inherit",
299
+ fontSize: 13,
300
+ cursor: "pointer",
301
+ textAlign: "left"
302
+ };
303
+ function BordersPicker({ api }) {
304
+ const [open, setOpen] = useState2(false);
305
+ const rootRef = useRef2(null);
306
+ useEffect2(() => {
307
+ if (!open) return;
308
+ const onPointerDown = (e) => {
309
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
310
+ };
311
+ const onKeyDown = (e) => {
312
+ if (e.key === "Escape") setOpen(false);
313
+ };
314
+ document.addEventListener("pointerdown", onPointerDown, true);
315
+ document.addEventListener("keydown", onKeyDown);
316
+ return () => {
317
+ document.removeEventListener("pointerdown", onPointerDown, true);
318
+ document.removeEventListener("keydown", onKeyDown);
319
+ };
320
+ }, [open]);
321
+ const pick = (value) => {
322
+ void api?.executeCommand("sheet.command.set-border-position", { value });
323
+ setOpen(false);
324
+ };
325
+ const on = open;
326
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
327
+ return /* @__PURE__ */ jsxs2("div", { ref: rootRef, style: ROW_STYLE2, "data-testid": "cs-borders", children: [
328
+ /* @__PURE__ */ jsx3(
329
+ "button",
330
+ {
331
+ type: "button",
332
+ title: "Borders",
333
+ "aria-label": "Borders",
334
+ "aria-haspopup": "true",
335
+ "aria-expanded": on,
336
+ "data-testid": "cs-borders-button",
337
+ disabled: !api,
338
+ style: {
339
+ ...BTN_STYLE2,
340
+ background: baseBg,
341
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE2.color
342
+ },
343
+ onMouseDown: (e) => {
344
+ e.preventDefault();
345
+ setOpen((v) => !v);
346
+ },
347
+ onMouseEnter: (e) => {
348
+ if (!on) e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
349
+ },
350
+ onMouseLeave: (e) => {
351
+ e.currentTarget.style.background = baseBg;
352
+ },
353
+ children: /* @__PURE__ */ jsx3(Icon, { name: "border_all", size: 20 })
354
+ }
355
+ ),
356
+ open && /* @__PURE__ */ jsx3("div", { style: POPOVER_STYLE2, "data-testid": "cs-borders-popover", role: "menu", children: OPTIONS.map((o) => /* @__PURE__ */ jsxs2(
357
+ "button",
358
+ {
359
+ type: "button",
360
+ role: "menuitem",
361
+ "data-border": o.value,
362
+ "data-testid": `cs-border-${o.value}`,
363
+ style: ITEM_STYLE,
364
+ onMouseEnter: (e) => {
365
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
366
+ },
367
+ onMouseLeave: (e) => {
368
+ e.currentTarget.style.background = "transparent";
369
+ },
370
+ onMouseDown: (e) => {
371
+ e.preventDefault();
372
+ pick(o.value);
373
+ },
374
+ children: [
375
+ /* @__PURE__ */ jsx3(Icon, { name: o.icon, size: 18 }),
376
+ o.label
377
+ ]
378
+ },
379
+ o.value
380
+ )) })
381
+ ] });
382
+ }
383
+
384
+ // src/chrome/AutoSumPicker.tsx
385
+ import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
386
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
387
+ var FUNCS = [
388
+ { fn: "SUM", label: "Sum" },
389
+ { fn: "AVERAGE", label: "Average" },
390
+ { fn: "COUNT", label: "Count numbers" },
391
+ { fn: "MAX", label: "Max" },
392
+ { fn: "MIN", label: "Min" }
393
+ ];
394
+ var ROW_STYLE3 = {
395
+ display: "inline-flex",
396
+ alignItems: "center",
397
+ position: "relative"
398
+ };
399
+ var BTN_STYLE3 = {
400
+ width: 30,
401
+ height: 30,
402
+ display: "inline-flex",
403
+ alignItems: "center",
404
+ justifyContent: "center",
405
+ border: "none",
406
+ borderRadius: 6,
407
+ background: "transparent",
408
+ cursor: "pointer",
409
+ color: "var(--cs-chrome-fg, #201f1e)",
410
+ padding: 0
411
+ };
412
+ var POPOVER_STYLE3 = {
413
+ position: "absolute",
414
+ top: "100%",
415
+ left: 0,
416
+ marginTop: 4,
417
+ zIndex: 1e3,
418
+ padding: 4,
419
+ minWidth: 160,
420
+ borderRadius: 8,
421
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
422
+ background: "var(--cs-chrome-input-bg, #fff)",
423
+ boxShadow: "0 6px 20px rgba(0,0,0,0.18)"
424
+ };
425
+ var ITEM_STYLE2 = {
426
+ display: "block",
427
+ width: "100%",
428
+ textAlign: "left",
429
+ height: 28,
430
+ padding: "0 10px",
431
+ border: "none",
432
+ borderRadius: 6,
433
+ background: "transparent",
434
+ color: "var(--cs-chrome-fg, #201f1e)",
435
+ font: "inherit",
436
+ fontSize: 13,
437
+ cursor: "pointer"
438
+ };
439
+ function AutoSumPicker({ api }) {
440
+ const [open, setOpen] = useState3(false);
441
+ const rootRef = useRef3(null);
442
+ useEffect3(() => {
443
+ if (!open) return;
444
+ const onPointerDown = (e) => {
445
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
446
+ };
447
+ const onKeyDown = (e) => {
448
+ if (e.key === "Escape") setOpen(false);
449
+ };
450
+ document.addEventListener("pointerdown", onPointerDown, true);
451
+ document.addEventListener("keydown", onKeyDown);
452
+ return () => {
453
+ document.removeEventListener("pointerdown", onPointerDown, true);
454
+ document.removeEventListener("keydown", onKeyDown);
455
+ };
456
+ }, [open]);
457
+ const apply = (fn) => {
458
+ setOpen(false);
459
+ const sheet = api?.univer.getActiveWorkbook()?.getActiveSheet();
460
+ const range = sheet?.getActiveRange?.();
461
+ if (!sheet || !range) return;
462
+ const multi = range.getWidth() * range.getHeight() > 1;
463
+ if (multi) {
464
+ const target = sheet.getRange(range.getRow() + range.getHeight(), range.getColumn());
465
+ target.setValue({ f: `=${fn}(${range.getA1Notation()})` });
466
+ target.activate?.();
467
+ } else {
468
+ sheet.getRange(range.getRow(), range.getColumn()).setValue({ f: `=${fn}()` });
469
+ }
470
+ };
471
+ const on = open;
472
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
473
+ return /* @__PURE__ */ jsxs3("div", { ref: rootRef, style: ROW_STYLE3, "data-testid": "cs-autosum", children: [
474
+ /* @__PURE__ */ jsx4(
475
+ "button",
476
+ {
477
+ type: "button",
478
+ title: "AutoSum",
479
+ "aria-label": "AutoSum",
480
+ "aria-haspopup": "true",
481
+ "aria-expanded": on,
482
+ "data-testid": "cs-autosum-button",
483
+ disabled: !api,
484
+ style: {
485
+ ...BTN_STYLE3,
486
+ background: baseBg,
487
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE3.color
488
+ },
489
+ onMouseDown: (e) => {
490
+ e.preventDefault();
491
+ setOpen((v) => !v);
492
+ },
493
+ onMouseEnter: (e) => {
494
+ if (!on) e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
495
+ },
496
+ onMouseLeave: (e) => {
497
+ e.currentTarget.style.background = baseBg;
498
+ },
499
+ children: /* @__PURE__ */ jsx4(Icon, { name: "functions", size: 20 })
500
+ }
501
+ ),
502
+ open && /* @__PURE__ */ jsx4("div", { style: POPOVER_STYLE3, "data-testid": "cs-autosum-popover", role: "menu", children: FUNCS.map((f) => /* @__PURE__ */ jsx4(
503
+ "button",
504
+ {
505
+ type: "button",
506
+ role: "menuitem",
507
+ "data-fn": f.fn,
508
+ "data-testid": `cs-autosum-${f.fn}`,
509
+ style: ITEM_STYLE2,
510
+ onMouseEnter: (e) => {
511
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
512
+ },
513
+ onMouseLeave: (e) => {
514
+ e.currentTarget.style.background = "transparent";
515
+ },
516
+ onMouseDown: (e) => {
517
+ e.preventDefault();
518
+ apply(f.fn);
519
+ },
520
+ children: f.label
521
+ },
522
+ f.fn
523
+ )) })
524
+ ] });
525
+ }
526
+
527
+ // src/chrome/Toolbar.tsx
528
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
529
+ var NO_STYLE = {
530
+ bold: false,
531
+ italic: false,
532
+ underline: false,
533
+ strike: false,
534
+ ht: 0,
535
+ vt: 0,
536
+ tb: 0,
537
+ ff: "",
538
+ fs: 0
539
+ };
540
+ function readActiveStyle(api) {
541
+ const sel = api.getSelection();
542
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
543
+ if (!sel || !sheet) return NO_STYLE;
544
+ const range = sheet.getRange(sel.range.startRow, sel.range.startColumn);
545
+ const s = range.getCellStyleData?.() ?? null;
546
+ return {
547
+ bold: s?.bl === 1,
548
+ italic: s?.it === 1,
549
+ underline: s?.ul?.s === 1,
550
+ strike: s?.st?.s === 1,
551
+ ht: typeof s?.ht === "number" ? s.ht : 0,
552
+ vt: typeof s?.vt === "number" ? s.vt : 0,
553
+ tb: typeof s?.tb === "number" ? s.tb : 0,
554
+ ff: typeof s?.ff === "string" ? s.ff : "",
555
+ fs: typeof s?.fs === "number" ? s.fs : 0
556
+ };
557
+ }
558
+ var FONT_FAMILIES = ["Arial", "Calibri", "Times New Roman", "Courier New", "Georgia", "Verdana"];
559
+ var FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 36, 48, 72];
560
+ var GROUPS = [
561
+ [
562
+ { id: "undo", label: "Undo", command: "univer.command.undo", icon: "undo" },
563
+ { id: "redo", label: "Redo", command: "univer.command.redo", icon: "redo" }
564
+ ],
565
+ [
566
+ { id: "bold", label: "Bold", command: "sheet.command.set-range-bold", icon: "format_bold" },
567
+ {
568
+ id: "italic",
569
+ label: "Italic",
570
+ command: "sheet.command.set-range-italic",
571
+ icon: "format_italic"
572
+ },
573
+ {
574
+ id: "underline",
575
+ label: "Underline",
576
+ command: "sheet.command.set-range-underline",
577
+ icon: "format_underlined"
578
+ },
579
+ {
580
+ id: "strikethrough",
581
+ label: "Strikethrough",
582
+ command: "sheet.command.set-range-stroke",
583
+ icon: "format_strikethrough"
584
+ }
585
+ ],
586
+ [
587
+ {
588
+ id: "align-left",
589
+ label: "Align left",
590
+ command: "sheet.command.set-horizontal-text-align",
591
+ icon: "format_align_left",
592
+ params: { value: 1 }
593
+ },
594
+ {
595
+ id: "align-center",
596
+ label: "Align center",
597
+ command: "sheet.command.set-horizontal-text-align",
598
+ icon: "format_align_center",
599
+ params: { value: 2 }
600
+ },
601
+ {
602
+ id: "align-right",
603
+ label: "Align right",
604
+ command: "sheet.command.set-horizontal-text-align",
605
+ icon: "format_align_right",
606
+ params: { value: 3 }
607
+ }
608
+ ],
609
+ [
610
+ {
611
+ id: "align-top",
612
+ label: "Align top",
613
+ command: "sheet.command.set-vertical-text-align",
614
+ icon: "vertical_align_top",
615
+ params: { value: 1 }
616
+ },
617
+ {
618
+ id: "align-middle",
619
+ label: "Align middle",
620
+ command: "sheet.command.set-vertical-text-align",
621
+ icon: "vertical_align_center",
622
+ params: { value: 2 }
623
+ },
624
+ {
625
+ id: "align-bottom",
626
+ label: "Align bottom",
627
+ command: "sheet.command.set-vertical-text-align",
628
+ icon: "vertical_align_bottom",
629
+ params: { value: 3 }
630
+ },
631
+ {
632
+ id: "wrap-text",
633
+ label: "Wrap text",
634
+ command: "sheet.command.set-text-wrap",
635
+ icon: "wrap_text",
636
+ params: { value: 3 }
637
+ }
638
+ ],
639
+ [
640
+ {
641
+ id: "merge",
642
+ label: "Merge cells",
643
+ command: "sheet.command.add-worksheet-merge-all",
644
+ icon: "cell_merge"
645
+ },
646
+ {
647
+ id: "unmerge",
648
+ label: "Unmerge cells",
649
+ command: "sheet.command.remove-worksheet-merge",
650
+ icon: "splitscreen_vertical_add"
651
+ }
652
+ ],
653
+ [
654
+ {
655
+ id: "currency",
656
+ label: "Currency format",
657
+ command: "sheet.command.numfmt.set.currency",
658
+ icon: "attach_money"
659
+ },
660
+ {
661
+ id: "percent",
662
+ label: "Percent format",
663
+ command: "sheet.command.numfmt.set.percent",
664
+ icon: "percent"
665
+ },
666
+ {
667
+ id: "decimal-increase",
668
+ label: "Increase decimals",
669
+ command: "sheet.command.numfmt.add.decimal.command",
670
+ icon: "decimal_increase"
671
+ },
672
+ {
673
+ id: "decimal-decrease",
674
+ label: "Decrease decimals",
675
+ command: "sheet.command.numfmt.subtract.decimal.command",
676
+ icon: "decimal_decrease"
677
+ }
678
+ ],
679
+ [
680
+ {
681
+ id: "clear-format",
682
+ label: "Clear formatting",
683
+ command: "sheet.command.clear-selection-format",
684
+ icon: "format_clear"
685
+ }
686
+ ]
687
+ ];
688
+ var BAR_STYLE = {
689
+ display: "flex",
690
+ alignItems: "center",
691
+ gap: 2,
692
+ padding: "4px 8px",
693
+ borderBottom: "1px solid var(--cs-chrome-border, #e6e9ee)",
694
+ background: "var(--cs-chrome-bg, #eef1f5)",
695
+ flex: "0 0 auto",
696
+ userSelect: "none"
697
+ };
698
+ var BTN_STYLE4 = {
699
+ width: 30,
700
+ height: 30,
701
+ display: "inline-flex",
702
+ alignItems: "center",
703
+ justifyContent: "center",
704
+ border: "none",
705
+ borderRadius: 6,
706
+ background: "transparent",
707
+ cursor: "pointer",
708
+ color: "var(--cs-chrome-fg, #201f1e)",
709
+ padding: 0
710
+ };
711
+ var DIVIDER_STYLE = {
712
+ width: 1,
713
+ height: 18,
714
+ margin: "0 4px",
715
+ background: "var(--cs-chrome-border, #cdd3db)",
716
+ flex: "0 0 auto"
717
+ };
718
+ var SELECT_STYLE = {
719
+ height: 26,
720
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
721
+ borderRadius: 6,
722
+ background: "var(--cs-chrome-input-bg, #fff)",
723
+ color: "var(--cs-chrome-fg, #201f1e)",
724
+ font: "inherit",
725
+ fontSize: 13,
726
+ padding: "0 4px",
727
+ cursor: "pointer"
728
+ };
729
+ function isActive(id, s) {
730
+ switch (id) {
731
+ case "bold":
732
+ return s.bold;
733
+ case "italic":
734
+ return s.italic;
735
+ case "underline":
736
+ return s.underline;
737
+ case "strikethrough":
738
+ return s.strike;
739
+ case "align-left":
740
+ return s.ht === 1;
741
+ case "align-center":
742
+ return s.ht === 2;
743
+ case "align-right":
744
+ return s.ht === 3;
745
+ case "align-top":
746
+ return s.vt === 1;
747
+ case "align-middle":
748
+ return s.vt === 2;
749
+ case "align-bottom":
750
+ return s.vt === 3;
751
+ case "wrap-text":
752
+ return s.tb === 3;
753
+ default:
754
+ return false;
755
+ }
756
+ }
757
+ function Toolbar({ api }) {
758
+ const [active, setActive] = useState4(NO_STYLE);
759
+ useEffect4(() => {
760
+ ensureChromeFonts();
761
+ }, []);
762
+ useEffect4(() => {
763
+ if (!api) return;
764
+ const refresh = () => setActive(readActiveStyle(api));
765
+ refresh();
766
+ const injector = api.univer._injector;
767
+ const cmd = injector?.get(ICommandService);
768
+ const sub = cmd?.onCommandExecuted(() => refresh());
769
+ return () => sub?.dispose();
770
+ }, [api]);
771
+ const dispatch = (command, params) => void api?.executeCommand(command, params);
772
+ const familyValue = active.ff || "Arial";
773
+ const familyOptions = active.ff && !FONT_FAMILIES.includes(active.ff) ? [active.ff, ...FONT_FAMILIES] : FONT_FAMILIES;
774
+ const sizeValue = active.fs || 11;
775
+ const sizeOptions = active.fs && !FONT_SIZES.includes(active.fs) ? [active.fs, ...FONT_SIZES] : FONT_SIZES;
776
+ return /* @__PURE__ */ jsxs4("div", { style: BAR_STYLE, "data-testid": "casual-sheets-toolbar", role: "toolbar", "aria-label": "Editor", children: [
777
+ /* @__PURE__ */ jsx5(
778
+ "select",
779
+ {
780
+ "aria-label": "Font family",
781
+ "data-testid": "cs-font-family",
782
+ style: { ...SELECT_STYLE, width: 116 },
783
+ value: familyValue,
784
+ onChange: (e) => dispatch("sheet.command.set-range-font-family", { value: e.target.value }),
785
+ children: familyOptions.map((f) => /* @__PURE__ */ jsx5("option", { value: f, children: f }, f))
786
+ }
787
+ ),
788
+ /* @__PURE__ */ jsx5(
789
+ "select",
790
+ {
791
+ "aria-label": "Font size",
792
+ "data-testid": "cs-font-size",
793
+ style: { ...SELECT_STYLE, width: 56, marginLeft: 4 },
794
+ value: sizeValue,
795
+ onChange: (e) => dispatch("sheet.command.set-range-fontsize", { value: Number(e.target.value) }),
796
+ children: sizeOptions.map((s) => /* @__PURE__ */ jsx5("option", { value: s, children: s }, s))
797
+ }
798
+ ),
799
+ /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
800
+ GROUPS.map((group, gi) => /* @__PURE__ */ jsxs4("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
801
+ gi > 0 && /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
802
+ group.map((a) => {
803
+ const on = isActive(a.id, active);
804
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
805
+ return /* @__PURE__ */ jsx5(
806
+ "button",
807
+ {
808
+ type: "button",
809
+ title: a.label,
810
+ "aria-label": a.label,
811
+ "aria-pressed": on,
812
+ "data-action": a.id,
813
+ "data-active": on ? "true" : void 0,
814
+ style: {
815
+ ...BTN_STYLE4,
816
+ background: baseBg,
817
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE4.color
818
+ },
819
+ onMouseDown: (e) => {
820
+ e.preventDefault();
821
+ dispatch(a.command, a.params);
822
+ },
823
+ onMouseEnter: (e) => {
824
+ if (!on)
825
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
826
+ },
827
+ onMouseLeave: (e) => {
828
+ e.currentTarget.style.background = baseBg;
829
+ },
830
+ children: /* @__PURE__ */ jsx5(Icon, { name: a.icon, size: 20 })
831
+ },
832
+ a.id
833
+ );
834
+ })
835
+ ] }, gi)),
836
+ /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
837
+ /* @__PURE__ */ jsx5(ColorPicker, { api }),
838
+ /* @__PURE__ */ jsx5(BordersPicker, { api }),
839
+ /* @__PURE__ */ jsx5("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
840
+ /* @__PURE__ */ jsx5(AutoSumPicker, { api })
841
+ ] });
842
+ }
843
+
844
+ // src/chrome/FormulaBar.tsx
845
+ import {
846
+ useEffect as useEffect6,
847
+ useMemo,
848
+ useRef as useRef5,
849
+ useState as useState6
850
+ } from "react";
851
+ import { ICommandService as ICommandService3 } from "@univerjs/core";
852
+
853
+ // src/chrome/NameBox.tsx
854
+ import { useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react";
855
+ import { ICommandService as ICommandService2 } from "@univerjs/core";
856
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
857
+ function readDefinedNames(api) {
858
+ if (!api) return [];
859
+ try {
860
+ const wb = api.univer.getActiveWorkbook();
861
+ const names = wb?.getDefinedNames?.();
862
+ if (!names) return [];
863
+ return names.map((dn) => ({
864
+ name: dn.getName?.() ?? "",
865
+ ref: dn.getFormulaOrRefString?.() ?? ""
866
+ })).filter((e) => e.name !== "");
867
+ } catch {
868
+ return [];
869
+ }
870
+ }
871
+ function colToLetters(col) {
872
+ let s = "";
873
+ let n = col;
874
+ do {
875
+ s = String.fromCharCode(65 + n % 26) + s;
876
+ n = Math.floor(n / 26) - 1;
877
+ } while (n >= 0);
878
+ return s;
879
+ }
880
+ function readActiveRef(api) {
881
+ const sel = api.getSelection();
882
+ if (!sel) return "";
883
+ const { startRow, startColumn } = sel.range;
884
+ return colToLetters(startColumn) + (startRow + 1);
885
+ }
886
+ var BOX_STYLE = {
887
+ flex: "0 0 auto",
888
+ width: 80,
889
+ height: 24,
890
+ padding: "0 8px",
891
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
892
+ borderRadius: 4,
893
+ background: "var(--cs-chrome-input-bg, #fff)",
894
+ color: "var(--cs-chrome-fg, #201f1e)",
895
+ font: "inherit",
896
+ fontSize: 13,
897
+ textAlign: "center",
898
+ boxSizing: "border-box"
899
+ };
900
+ var ROOT_STYLE = {
901
+ flex: "0 0 auto",
902
+ display: "inline-flex",
903
+ alignItems: "center",
904
+ position: "relative"
905
+ };
906
+ var DROPDOWN_BTN_STYLE = {
907
+ flex: "0 0 auto",
908
+ width: 20,
909
+ height: 24,
910
+ marginLeft: 2,
911
+ display: "inline-flex",
912
+ alignItems: "center",
913
+ justifyContent: "center",
914
+ border: "none",
915
+ borderRadius: 4,
916
+ background: "transparent",
917
+ color: "var(--cs-chrome-fg, #201f1e)",
918
+ cursor: "pointer",
919
+ padding: 0
920
+ };
921
+ var MENU_STYLE = {
922
+ position: "absolute",
923
+ top: "100%",
924
+ left: 0,
925
+ marginTop: 4,
926
+ zIndex: 1e3,
927
+ minWidth: 160,
928
+ maxHeight: 280,
929
+ overflowY: "auto",
930
+ padding: 4,
931
+ borderRadius: 8,
932
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
933
+ background: "var(--cs-chrome-input-bg, #fff)",
934
+ boxShadow: "0 6px 20px rgba(0,0,0,0.18)"
935
+ };
936
+ var MENU_ITEM_STYLE = {
937
+ display: "flex",
938
+ alignItems: "baseline",
939
+ justifyContent: "space-between",
940
+ gap: 12,
941
+ width: "100%",
942
+ height: 28,
943
+ padding: "0 8px",
944
+ border: "none",
945
+ borderRadius: 6,
946
+ background: "transparent",
947
+ color: "var(--cs-chrome-fg, #201f1e)",
948
+ font: "inherit",
949
+ fontSize: 13,
950
+ textAlign: "left",
951
+ cursor: "pointer",
952
+ boxSizing: "border-box"
953
+ };
954
+ var MENU_EMPTY_STYLE = {
955
+ padding: "6px 8px",
956
+ color: "var(--cs-chrome-muted, #8a8886)",
957
+ fontSize: 12,
958
+ whiteSpace: "nowrap"
959
+ };
960
+ var ITEM_REF_STYLE = {
961
+ color: "var(--cs-chrome-muted, #8a8886)",
962
+ fontSize: 11,
963
+ flex: "0 0 auto"
964
+ };
965
+ function NameBox({ api }) {
966
+ const [ref, setRef] = useState5("");
967
+ const [draft, setDraft] = useState5(null);
968
+ const draftRef = useRef4(null);
969
+ draftRef.current = draft;
970
+ const inputRef = useRef4(null);
971
+ const [open, setOpen] = useState5(false);
972
+ const [names, setNames] = useState5([]);
973
+ const rootRef = useRef4(null);
974
+ useEffect5(() => {
975
+ if (!api) return;
976
+ const refresh = () => {
977
+ if (draftRef.current !== null) return;
978
+ setRef(readActiveRef(api));
979
+ };
980
+ refresh();
981
+ const injector = api.univer._injector;
982
+ const cmd = injector?.get(ICommandService2);
983
+ const sub = cmd?.onCommandExecuted(() => refresh());
984
+ return () => sub?.dispose();
985
+ }, [api]);
986
+ useEffect5(() => {
987
+ if (!open) return;
988
+ const onPointerDown = (e) => {
989
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
990
+ };
991
+ const onKeyDown2 = (e) => {
992
+ if (e.key === "Escape") setOpen(false);
993
+ };
994
+ document.addEventListener("pointerdown", onPointerDown, true);
995
+ document.addEventListener("keydown", onKeyDown2);
996
+ return () => {
997
+ document.removeEventListener("pointerdown", onPointerDown, true);
998
+ document.removeEventListener("keydown", onKeyDown2);
999
+ };
1000
+ }, [open]);
1001
+ const navigate = (text) => {
1002
+ const t = text.trim();
1003
+ setDraft(null);
1004
+ if (!api || t === "") {
1005
+ setRef(readActiveRef(api ?? {}));
1006
+ return;
1007
+ }
1008
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1009
+ if (!sheet) return;
1010
+ try {
1011
+ const range = sheet.getRange(
1012
+ t
1013
+ );
1014
+ range.activate();
1015
+ } catch {
1016
+ setRef(readActiveRef(api));
1017
+ }
1018
+ };
1019
+ const toggleDropdown = () => {
1020
+ setOpen((cur) => {
1021
+ const next = !cur;
1022
+ if (next) setNames(readDefinedNames(api));
1023
+ return next;
1024
+ });
1025
+ };
1026
+ const navigateName = (entry) => {
1027
+ setOpen(false);
1028
+ const raw = entry.ref.trim();
1029
+ if (raw === "") return;
1030
+ const afterSheet = raw.includes("!") ? raw.slice(raw.lastIndexOf("!") + 1) : raw;
1031
+ const a1 = afterSheet.replace(/\$/g, "");
1032
+ navigate(a1);
1033
+ };
1034
+ const onKeyDown = (e) => {
1035
+ if (e.key === "Enter") {
1036
+ e.preventDefault();
1037
+ navigate(e.target.value);
1038
+ e.target.blur();
1039
+ } else if (e.key === "Escape") {
1040
+ e.preventDefault();
1041
+ setDraft(null);
1042
+ setRef(api ? readActiveRef(api) : "");
1043
+ e.target.blur();
1044
+ }
1045
+ };
1046
+ const shown = draft ?? ref;
1047
+ return /* @__PURE__ */ jsxs5("div", { ref: rootRef, style: ROOT_STYLE, children: [
1048
+ /* @__PURE__ */ jsx6(
1049
+ "input",
1050
+ {
1051
+ ref: inputRef,
1052
+ type: "text",
1053
+ "aria-label": "Name box",
1054
+ title: "Name box",
1055
+ "data-testid": "cs-namebox-input",
1056
+ style: BOX_STYLE,
1057
+ value: shown,
1058
+ disabled: !api,
1059
+ onChange: (e) => setDraft(e.target.value),
1060
+ onKeyDown,
1061
+ onFocus: (e) => e.currentTarget.select(),
1062
+ onBlur: () => {
1063
+ if (draftRef.current !== null) {
1064
+ setDraft(null);
1065
+ setRef(api ? readActiveRef(api) : "");
1066
+ }
1067
+ }
1068
+ }
1069
+ ),
1070
+ /* @__PURE__ */ jsx6(
1071
+ "button",
1072
+ {
1073
+ type: "button",
1074
+ title: "Defined names",
1075
+ "aria-label": "Defined names",
1076
+ "aria-haspopup": "true",
1077
+ "aria-expanded": open,
1078
+ "data-testid": "cs-namebox-dropdown",
1079
+ style: {
1080
+ ...DROPDOWN_BTN_STYLE,
1081
+ background: open ? "var(--cs-chrome-active, #e6f3f7)" : "transparent",
1082
+ color: open ? "var(--cs-chrome-active-fg, #0e7490)" : DROPDOWN_BTN_STYLE.color
1083
+ },
1084
+ disabled: !api,
1085
+ onMouseDown: (e) => {
1086
+ e.preventDefault();
1087
+ toggleDropdown();
1088
+ },
1089
+ children: /* @__PURE__ */ jsx6(Icon, { name: "expand_more", size: 18 })
1090
+ }
1091
+ ),
1092
+ open && /* @__PURE__ */ jsx6("div", { style: MENU_STYLE, role: "menu", "data-testid": "cs-namebox-menu", children: names.length === 0 ? /* @__PURE__ */ jsx6("div", { style: MENU_EMPTY_STYLE, children: "No names" }) : names.map((entry, i) => /* @__PURE__ */ jsxs5(
1093
+ "button",
1094
+ {
1095
+ type: "button",
1096
+ role: "menuitem",
1097
+ title: entry.ref || entry.name,
1098
+ "data-testid": `cs-namebox-name-${i}`,
1099
+ style: MENU_ITEM_STYLE,
1100
+ onMouseDown: (e) => {
1101
+ e.preventDefault();
1102
+ navigateName(entry);
1103
+ },
1104
+ onMouseEnter: (e) => {
1105
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
1106
+ },
1107
+ onMouseLeave: (e) => {
1108
+ e.currentTarget.style.background = "transparent";
1109
+ },
1110
+ children: [
1111
+ /* @__PURE__ */ jsx6("span", { children: entry.name }),
1112
+ entry.ref && /* @__PURE__ */ jsx6("span", { style: ITEM_REF_STYLE, children: entry.ref })
1113
+ ]
1114
+ },
1115
+ entry.name
1116
+ )) })
1117
+ ] });
1118
+ }
1119
+
1120
+ // src/chrome/FormulaBar.tsx
1121
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1122
+ var FUNCTIONS = [
1123
+ "ABS",
1124
+ "AND",
1125
+ "AVERAGE",
1126
+ "AVERAGEIF",
1127
+ "AVERAGEIFS",
1128
+ "CEILING",
1129
+ "CHOOSE",
1130
+ "COLUMN",
1131
+ "COLUMNS",
1132
+ "CONCAT",
1133
+ "CONCATENATE",
1134
+ "COUNT",
1135
+ "COUNTA",
1136
+ "COUNTBLANK",
1137
+ "COUNTIF",
1138
+ "COUNTIFS",
1139
+ "DATE",
1140
+ "DATEDIF",
1141
+ "DAY",
1142
+ "EDATE",
1143
+ "EOMONTH",
1144
+ "EXACT",
1145
+ "FILTER",
1146
+ "FIND",
1147
+ "FLOOR",
1148
+ "HLOOKUP",
1149
+ "HOUR",
1150
+ "IF",
1151
+ "IFERROR",
1152
+ "IFNA",
1153
+ "IFS",
1154
+ "INDEX",
1155
+ "INDIRECT",
1156
+ "INT",
1157
+ "ISBLANK",
1158
+ "ISERROR",
1159
+ "ISNA",
1160
+ "ISNUMBER",
1161
+ "ISTEXT",
1162
+ "LEFT",
1163
+ "LEN",
1164
+ "LOOKUP",
1165
+ "LOWER",
1166
+ "MATCH",
1167
+ "MAX",
1168
+ "MAXIFS",
1169
+ "MEDIAN",
1170
+ "MID",
1171
+ "MIN",
1172
+ "MINIFS",
1173
+ "MINUTE",
1174
+ "MOD",
1175
+ "MONTH",
1176
+ "NETWORKDAYS",
1177
+ "NOT",
1178
+ "NOW",
1179
+ "OFFSET",
1180
+ "OR",
1181
+ "POWER",
1182
+ "PRODUCT",
1183
+ "PROPER",
1184
+ "RAND",
1185
+ "RANDBETWEEN",
1186
+ "RANK",
1187
+ "REPLACE",
1188
+ "REPT",
1189
+ "RIGHT",
1190
+ "ROUND",
1191
+ "ROUNDDOWN",
1192
+ "ROUNDUP",
1193
+ "ROW",
1194
+ "ROWS",
1195
+ "SEARCH",
1196
+ "SECOND",
1197
+ "SORT",
1198
+ "SQRT",
1199
+ "SUBSTITUTE",
1200
+ "SUM",
1201
+ "SUMIF",
1202
+ "SUMIFS",
1203
+ "SUMPRODUCT",
1204
+ "SWITCH",
1205
+ "TEXT",
1206
+ "TEXTJOIN",
1207
+ "TODAY",
1208
+ "TRANSPOSE",
1209
+ "TRIM",
1210
+ "UNIQUE",
1211
+ "UPPER",
1212
+ "VALUE",
1213
+ "VLOOKUP",
1214
+ "WEEKDAY",
1215
+ "WORKDAY",
1216
+ "XLOOKUP",
1217
+ "XMATCH",
1218
+ "YEAR"
1219
+ ];
1220
+ var TOKEN_RE = /([A-Za-z][A-Za-z0-9._]*)$/;
1221
+ function suggestFor(draft) {
1222
+ if (!draft || !draft.startsWith("=")) return [];
1223
+ const m = TOKEN_RE.exec(draft);
1224
+ if (!m) return [];
1225
+ const prefix = m[1].toUpperCase();
1226
+ return FUNCTIONS.filter((f) => f.startsWith(prefix) && f !== prefix).slice(0, 8);
1227
+ }
1228
+ function colToLetters2(col) {
1229
+ let s = "";
1230
+ let n = col;
1231
+ do {
1232
+ s = String.fromCharCode(65 + n % 26) + s;
1233
+ n = Math.floor(n / 26) - 1;
1234
+ } while (n >= 0);
1235
+ return s;
1236
+ }
1237
+ function readActiveCell(api) {
1238
+ const sel = api.getSelection();
1239
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1240
+ if (!sel || !sheet) return null;
1241
+ const { startRow, startColumn } = sel.range;
1242
+ const range = sheet.getRange(startRow, startColumn);
1243
+ const formula = range.getFormula?.() || "";
1244
+ const raw = range.getValue?.();
1245
+ return {
1246
+ a1: colToLetters2(startColumn) + (startRow + 1),
1247
+ text: formula || (raw == null ? "" : String(raw))
1248
+ };
1249
+ }
1250
+ var BAR_STYLE2 = {
1251
+ display: "flex",
1252
+ alignItems: "center",
1253
+ gap: 6,
1254
+ padding: "3px 6px",
1255
+ borderBottom: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.12))",
1256
+ background: "var(--cs-chrome-bg, #f8f9fa)",
1257
+ flex: "0 0 auto",
1258
+ font: "inherit",
1259
+ fontSize: 13
1260
+ };
1261
+ var FX_STYLE = {
1262
+ flex: "0 0 auto",
1263
+ fontStyle: "italic",
1264
+ color: "var(--cs-chrome-muted, #6b7280)"
1265
+ };
1266
+ var INPUT_STYLE = {
1267
+ width: "100%",
1268
+ height: 24,
1269
+ padding: "0 8px",
1270
+ border: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.18))",
1271
+ borderRadius: 4,
1272
+ background: "var(--cs-chrome-input-bg, #fff)",
1273
+ color: "var(--cs-chrome-fg, #1f2329)",
1274
+ font: "inherit",
1275
+ fontSize: 13,
1276
+ boxSizing: "border-box"
1277
+ };
1278
+ var INPUT_WRAP_STYLE = { position: "relative", flex: "1 1 auto", minWidth: 0 };
1279
+ var AC_STYLE = {
1280
+ position: "absolute",
1281
+ top: "100%",
1282
+ left: 0,
1283
+ marginTop: 2,
1284
+ minWidth: 200,
1285
+ maxHeight: 240,
1286
+ overflowY: "auto",
1287
+ zIndex: 1e3,
1288
+ padding: 4,
1289
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
1290
+ borderRadius: 8,
1291
+ background: "var(--cs-chrome-input-bg, #fff)",
1292
+ boxShadow: "0 6px 20px rgba(0,0,0,0.16)"
1293
+ };
1294
+ var AC_ITEM_STYLE = {
1295
+ display: "block",
1296
+ width: "100%",
1297
+ textAlign: "left",
1298
+ height: 26,
1299
+ padding: "0 8px",
1300
+ border: "none",
1301
+ borderRadius: 6,
1302
+ background: "transparent",
1303
+ color: "var(--cs-chrome-fg, #201f1e)",
1304
+ font: "inherit",
1305
+ fontSize: 13,
1306
+ cursor: "pointer"
1307
+ };
1308
+ function FormulaBar({ api }) {
1309
+ const [cell, setCell] = useState6(null);
1310
+ const [draft, setDraft] = useState6(null);
1311
+ const draftRef = useRef5(null);
1312
+ draftRef.current = draft;
1313
+ const [acIdx, setAcIdx] = useState6(0);
1314
+ const [acDismissed, setAcDismissed] = useState6(false);
1315
+ useEffect6(() => {
1316
+ if (!api) return;
1317
+ const refresh = () => {
1318
+ if (draftRef.current !== null) return;
1319
+ setCell(readActiveCell(api));
1320
+ };
1321
+ refresh();
1322
+ const injector = api.univer._injector;
1323
+ const cmd = injector?.get(ICommandService3);
1324
+ const sub = cmd?.onCommandExecuted(() => refresh());
1325
+ return () => sub?.dispose();
1326
+ }, [api]);
1327
+ const suggestions = useMemo(() => suggestFor(draft), [draft]);
1328
+ const acOpen = !acDismissed && suggestions.length > 0;
1329
+ const commit = (text) => {
1330
+ setDraft(null);
1331
+ setAcDismissed(false);
1332
+ if (!api) return;
1333
+ const sel = api.getSelection();
1334
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1335
+ if (!sel || !sheet) return;
1336
+ const range = sheet.getRange(sel.range.startRow, sel.range.startColumn);
1337
+ const t = text.trim();
1338
+ if (t.startsWith("=")) range.setValue({ f: t });
1339
+ else if (t !== "" && !Number.isNaN(Number(t))) range.setValue(Number(t));
1340
+ else range.setValue(t);
1341
+ };
1342
+ const onType = (v) => {
1343
+ setDraft(v);
1344
+ setAcIdx(0);
1345
+ setAcDismissed(false);
1346
+ };
1347
+ const complete = (fn) => {
1348
+ const base = draft ?? "";
1349
+ setDraft(base.replace(TOKEN_RE, `${fn}(`));
1350
+ setAcIdx(0);
1351
+ };
1352
+ const onKeyDown = (e) => {
1353
+ if (acOpen) {
1354
+ if (e.key === "ArrowDown") {
1355
+ e.preventDefault();
1356
+ setAcIdx((i) => Math.min(i + 1, suggestions.length - 1));
1357
+ return;
1358
+ }
1359
+ if (e.key === "ArrowUp") {
1360
+ e.preventDefault();
1361
+ setAcIdx((i) => Math.max(i - 1, 0));
1362
+ return;
1363
+ }
1364
+ if (e.key === "Enter" || e.key === "Tab") {
1365
+ e.preventDefault();
1366
+ complete(suggestions[acIdx] ?? suggestions[0]);
1367
+ return;
1368
+ }
1369
+ if (e.key === "Escape") {
1370
+ e.preventDefault();
1371
+ setAcDismissed(true);
1372
+ return;
1373
+ }
1374
+ }
1375
+ if (e.key === "Enter") {
1376
+ e.preventDefault();
1377
+ commit(e.target.value);
1378
+ } else if (e.key === "Escape") {
1379
+ e.preventDefault();
1380
+ setDraft(null);
1381
+ e.target.blur();
1382
+ }
1383
+ };
1384
+ const shown = draft ?? cell?.text ?? "";
1385
+ return /* @__PURE__ */ jsxs6("div", { style: BAR_STYLE2, "data-testid": "casual-sheets-formula-bar", children: [
1386
+ /* @__PURE__ */ jsx7(NameBox, { api }),
1387
+ /* @__PURE__ */ jsx7("span", { style: FX_STYLE, "aria-hidden": true, children: "fx" }),
1388
+ /* @__PURE__ */ jsxs6("div", { style: INPUT_WRAP_STYLE, children: [
1389
+ /* @__PURE__ */ jsx7(
1390
+ "input",
1391
+ {
1392
+ type: "text",
1393
+ "aria-label": "Formula bar",
1394
+ "data-testid": "casual-sheets-formula-input",
1395
+ style: INPUT_STYLE,
1396
+ value: shown,
1397
+ onChange: (e) => onType(e.target.value),
1398
+ onKeyDown,
1399
+ onBlur: (e) => {
1400
+ if (draftRef.current !== null) commit(e.target.value);
1401
+ },
1402
+ disabled: !api
1403
+ }
1404
+ ),
1405
+ acOpen && /* @__PURE__ */ jsx7("div", { style: AC_STYLE, "data-testid": "cs-formula-suggestions", role: "listbox", children: suggestions.map((fn, i) => /* @__PURE__ */ jsx7(
1406
+ "button",
1407
+ {
1408
+ type: "button",
1409
+ role: "option",
1410
+ "aria-selected": i === acIdx,
1411
+ "data-testid": `cs-formula-suggestion-${fn}`,
1412
+ style: {
1413
+ ...AC_ITEM_STYLE,
1414
+ background: i === acIdx ? "var(--cs-chrome-active, #e6f3f7)" : "transparent",
1415
+ color: i === acIdx ? "var(--cs-chrome-active-fg, #0e7490)" : AC_ITEM_STYLE.color
1416
+ },
1417
+ onMouseDown: (e) => {
1418
+ e.preventDefault();
1419
+ complete(fn);
1420
+ },
1421
+ onMouseEnter: () => setAcIdx(i),
1422
+ children: fn
1423
+ },
1424
+ fn
1425
+ )) })
1426
+ ] })
1427
+ ] });
1428
+ }
1429
+
1430
+ // src/chrome/StatusBar.tsx
1431
+ import { useEffect as useEffect7, useState as useState7 } from "react";
1432
+ import { ICommandService as ICommandService4 } from "@univerjs/core";
1433
+ import { Fragment, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1434
+ function readStats(api) {
1435
+ const sel = api.getSelection();
1436
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1437
+ if (!sel || !sheet) return null;
1438
+ const { startRow, startColumn, endRow, endColumn } = sel.range;
1439
+ if (startRow === endRow && startColumn === endColumn) return null;
1440
+ const values = sheet.getRange(sel.range).getValues?.();
1441
+ if (!values) return null;
1442
+ let count = 0;
1443
+ let numCount = 0;
1444
+ let sum = 0;
1445
+ let min = Infinity;
1446
+ let max = -Infinity;
1447
+ for (const row of values) {
1448
+ for (const v of row) {
1449
+ if (v == null || v === "") continue;
1450
+ count += 1;
1451
+ if (typeof v === "number" && Number.isFinite(v)) {
1452
+ numCount += 1;
1453
+ sum += v;
1454
+ if (v < min) min = v;
1455
+ if (v > max) max = v;
1456
+ }
1457
+ }
1458
+ }
1459
+ if (count === 0) return null;
1460
+ return { count, numCount, sum, avg: numCount ? sum / numCount : 0, min, max };
1461
+ }
1462
+ function readZoom(api) {
1463
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1464
+ const ws = sheet?.getSheet?.();
1465
+ const z = ws?.getConfig?.().zoomRatio;
1466
+ return typeof z === "number" && z > 0 ? z : 1;
1467
+ }
1468
+ function fmt(n) {
1469
+ return Number(n.toFixed(10)).toLocaleString();
1470
+ }
1471
+ var BAR_STYLE3 = {
1472
+ display: "flex",
1473
+ alignItems: "center",
1474
+ justifyContent: "flex-end",
1475
+ gap: 16,
1476
+ height: 24,
1477
+ padding: "0 12px",
1478
+ borderTop: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.12))",
1479
+ background: "var(--cs-chrome-bg, #f8f9fa)",
1480
+ color: "var(--cs-chrome-muted, #4b5563)",
1481
+ flex: "0 0 auto",
1482
+ font: "inherit",
1483
+ fontSize: 12,
1484
+ userSelect: "none"
1485
+ };
1486
+ var ZOOM_GROUP_STYLE = {
1487
+ display: "flex",
1488
+ alignItems: "center",
1489
+ gap: 4
1490
+ };
1491
+ var ZOOM_BTN_STYLE = {
1492
+ width: 20,
1493
+ height: 18,
1494
+ border: "none",
1495
+ borderRadius: 4,
1496
+ background: "transparent",
1497
+ color: "var(--cs-chrome-fg, #201f1e)",
1498
+ cursor: "pointer",
1499
+ fontSize: 14,
1500
+ lineHeight: 1,
1501
+ padding: 0
1502
+ };
1503
+ var ZOOM_LEVEL_STYLE = {
1504
+ minWidth: 40,
1505
+ height: 18,
1506
+ border: "none",
1507
+ borderRadius: 4,
1508
+ background: "transparent",
1509
+ color: "var(--cs-chrome-muted, #4b5563)",
1510
+ cursor: "pointer",
1511
+ font: "inherit",
1512
+ fontSize: 12,
1513
+ textAlign: "center"
1514
+ };
1515
+ function StatusBar({ api }) {
1516
+ const [stats, setStats] = useState7(null);
1517
+ const [zoom, setZoom] = useState7(1);
1518
+ useEffect7(() => {
1519
+ if (!api) return;
1520
+ const refresh = () => {
1521
+ setStats(readStats(api));
1522
+ setZoom(readZoom(api));
1523
+ };
1524
+ refresh();
1525
+ const injector = api.univer._injector;
1526
+ const cmd = injector?.get(ICommandService4);
1527
+ const sub = cmd?.onCommandExecuted(() => refresh());
1528
+ return () => sub?.dispose();
1529
+ }, [api]);
1530
+ const setZoomRatio = (ratio) => {
1531
+ const wb = api?.univer.getActiveWorkbook();
1532
+ const sheet = wb?.getActiveSheet();
1533
+ if (!wb || !sheet) return;
1534
+ const clamped = Math.min(4, Math.max(0.1, ratio));
1535
+ void api?.executeCommand("sheet.operation.set-zoom-ratio", {
1536
+ unitId: wb.getId(),
1537
+ subUnitId: sheet.getSheetId(),
1538
+ zoomRatio: clamped
1539
+ });
1540
+ setZoom(clamped);
1541
+ };
1542
+ const zoomBy = (delta) => setZoomRatio(Math.round((zoom + delta) * 100) / 100);
1543
+ const resetZoom = () => setZoomRatio(1);
1544
+ return /* @__PURE__ */ jsxs7("div", { style: BAR_STYLE3, "data-testid": "casual-sheets-status-bar", children: [
1545
+ stats && /* @__PURE__ */ jsxs7(Fragment, { children: [
1546
+ stats.numCount > 0 && /* @__PURE__ */ jsxs7("span", { "data-stat": "average", children: [
1547
+ "Average: ",
1548
+ fmt(stats.avg)
1549
+ ] }),
1550
+ /* @__PURE__ */ jsxs7("span", { "data-stat": "count", children: [
1551
+ "Count: ",
1552
+ stats.count
1553
+ ] }),
1554
+ stats.numCount > 0 && /* @__PURE__ */ jsxs7(Fragment, { children: [
1555
+ /* @__PURE__ */ jsxs7("span", { "data-stat": "num-count", children: [
1556
+ "Numerical Count: ",
1557
+ stats.numCount
1558
+ ] }),
1559
+ /* @__PURE__ */ jsxs7("span", { "data-stat": "min", children: [
1560
+ "Min: ",
1561
+ fmt(stats.min)
1562
+ ] }),
1563
+ /* @__PURE__ */ jsxs7("span", { "data-stat": "max", children: [
1564
+ "Max: ",
1565
+ fmt(stats.max)
1566
+ ] }),
1567
+ /* @__PURE__ */ jsxs7("span", { "data-stat": "sum", children: [
1568
+ "Sum: ",
1569
+ fmt(stats.sum)
1570
+ ] })
1571
+ ] })
1572
+ ] }),
1573
+ /* @__PURE__ */ jsxs7("span", { style: ZOOM_GROUP_STYLE, "data-testid": "cs-zoom", children: [
1574
+ /* @__PURE__ */ jsx8(
1575
+ "button",
1576
+ {
1577
+ type: "button",
1578
+ style: ZOOM_BTN_STYLE,
1579
+ "aria-label": "Zoom out",
1580
+ title: "Zoom out",
1581
+ "data-testid": "cs-zoom-out",
1582
+ disabled: !api,
1583
+ onMouseDown: (e) => {
1584
+ e.preventDefault();
1585
+ zoomBy(-0.1);
1586
+ },
1587
+ children: "\u2212"
1588
+ }
1589
+ ),
1590
+ /* @__PURE__ */ jsxs7(
1591
+ "button",
1592
+ {
1593
+ type: "button",
1594
+ style: ZOOM_LEVEL_STYLE,
1595
+ "aria-label": "Reset zoom to 100%",
1596
+ title: "Reset zoom to 100%",
1597
+ "data-testid": "cs-zoom-level",
1598
+ disabled: !api,
1599
+ onMouseDown: (e) => {
1600
+ e.preventDefault();
1601
+ resetZoom();
1602
+ },
1603
+ children: [
1604
+ Math.round(zoom * 100),
1605
+ "%"
1606
+ ]
1607
+ }
1608
+ ),
1609
+ /* @__PURE__ */ jsx8(
1610
+ "button",
1611
+ {
1612
+ type: "button",
1613
+ style: ZOOM_BTN_STYLE,
1614
+ "aria-label": "Zoom in",
1615
+ title: "Zoom in",
1616
+ "data-testid": "cs-zoom-in",
1617
+ disabled: !api,
1618
+ onMouseDown: (e) => {
1619
+ e.preventDefault();
1620
+ zoomBy(0.1);
1621
+ },
1622
+ children: "+"
1623
+ }
1624
+ )
1625
+ ] })
1626
+ ] });
1627
+ }
1628
+
1629
+ // src/chrome/MenuBar.tsx
1630
+ import { useEffect as useEffect8, useRef as useRef6, useState as useState8 } from "react";
1631
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1632
+ var MENUS = [
1633
+ {
1634
+ id: "edit",
1635
+ label: "Edit",
1636
+ items: [
1637
+ { id: "undo", label: "Undo", command: "univer.command.undo", icon: "undo" },
1638
+ { id: "redo", label: "Redo", command: "univer.command.redo", icon: "redo" },
1639
+ { id: "cut", label: "Cut", command: "univer.command.cut", icon: "content_cut" },
1640
+ { id: "copy", label: "Copy", command: "univer.command.copy", icon: "content_copy" },
1641
+ { id: "paste", label: "Paste", command: "univer.command.paste", icon: "content_paste" }
1642
+ ]
1643
+ },
1644
+ {
1645
+ id: "insert",
1646
+ label: "Insert",
1647
+ items: [
1648
+ {
1649
+ id: "insert-row",
1650
+ label: "Insert row above",
1651
+ command: "sheet.command.insert-row-before",
1652
+ icon: "add_row_above"
1653
+ },
1654
+ {
1655
+ id: "insert-col",
1656
+ label: "Insert column left",
1657
+ command: "sheet.command.insert-col-before",
1658
+ icon: "add_column_left"
1659
+ },
1660
+ {
1661
+ id: "delete-row",
1662
+ label: "Delete row",
1663
+ command: "sheet.command.remove-row",
1664
+ icon: "delete"
1665
+ },
1666
+ {
1667
+ id: "delete-col",
1668
+ label: "Delete column",
1669
+ command: "sheet.command.remove-col",
1670
+ icon: "delete"
1671
+ }
1672
+ ]
1673
+ },
1674
+ {
1675
+ id: "format",
1676
+ label: "Format",
1677
+ items: [
1678
+ { id: "bold", label: "Bold", command: "sheet.command.set-range-bold", icon: "format_bold" },
1679
+ {
1680
+ id: "italic",
1681
+ label: "Italic",
1682
+ command: "sheet.command.set-range-italic",
1683
+ icon: "format_italic"
1684
+ },
1685
+ {
1686
+ id: "underline",
1687
+ label: "Underline",
1688
+ command: "sheet.command.set-range-underline",
1689
+ icon: "format_underlined"
1690
+ },
1691
+ {
1692
+ id: "wrap-text",
1693
+ label: "Wrap text",
1694
+ command: "sheet.command.set-text-wrap",
1695
+ icon: "wrap_text"
1696
+ },
1697
+ {
1698
+ id: "clear-format",
1699
+ label: "Clear formatting",
1700
+ command: "sheet.command.clear-selection-format",
1701
+ icon: "format_clear"
1702
+ }
1703
+ ]
1704
+ },
1705
+ {
1706
+ id: "data",
1707
+ label: "Data",
1708
+ items: [
1709
+ {
1710
+ id: "sort-asc",
1711
+ label: "Sort ascending",
1712
+ command: "sheet.command.sort-range-asc",
1713
+ icon: "arrow_upward"
1714
+ },
1715
+ {
1716
+ id: "sort-desc",
1717
+ label: "Sort descending",
1718
+ command: "sheet.command.sort-range-desc",
1719
+ icon: "arrow_downward"
1720
+ },
1721
+ {
1722
+ id: "toggle-filter",
1723
+ label: "Toggle filter",
1724
+ command: "sheet.command.smart-toggle-filter",
1725
+ icon: "filter_alt"
1726
+ }
1727
+ ]
1728
+ },
1729
+ {
1730
+ id: "view",
1731
+ label: "View",
1732
+ items: [
1733
+ {
1734
+ id: "freeze",
1735
+ label: "Freeze panes",
1736
+ command: "sheet.command.set-selection-frozen",
1737
+ icon: "table_view"
1738
+ },
1739
+ {
1740
+ id: "unfreeze",
1741
+ label: "Unfreeze panes",
1742
+ command: "sheet.command.cancel-frozen",
1743
+ icon: "grid_off"
1744
+ },
1745
+ {
1746
+ id: "toggle-gridlines",
1747
+ label: "Toggle gridlines",
1748
+ command: "sheet.command.toggle-gridlines",
1749
+ icon: "grid_on"
1750
+ }
1751
+ ]
1752
+ }
1753
+ ];
1754
+ var BAR_STYLE4 = {
1755
+ position: "relative",
1756
+ display: "flex",
1757
+ alignItems: "center",
1758
+ gap: 2,
1759
+ padding: "2px 6px",
1760
+ borderBottom: "1px solid var(--cs-chrome-border, #e6e9ee)",
1761
+ background: "var(--cs-chrome-bg, #eef1f5)",
1762
+ flex: "0 0 auto",
1763
+ userSelect: "none",
1764
+ font: "inherit",
1765
+ fontSize: 13
1766
+ };
1767
+ var MENU_BTN_STYLE = {
1768
+ height: 26,
1769
+ display: "inline-flex",
1770
+ alignItems: "center",
1771
+ padding: "0 10px",
1772
+ border: "none",
1773
+ borderRadius: 6,
1774
+ background: "transparent",
1775
+ cursor: "pointer",
1776
+ color: "var(--cs-chrome-fg, #201f1e)",
1777
+ font: "inherit",
1778
+ fontSize: 13
1779
+ };
1780
+ var DROPDOWN_STYLE = {
1781
+ position: "absolute",
1782
+ top: "100%",
1783
+ minWidth: 200,
1784
+ marginTop: 2,
1785
+ padding: 4,
1786
+ display: "flex",
1787
+ flexDirection: "column",
1788
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
1789
+ borderRadius: 8,
1790
+ background: "var(--cs-chrome-input-bg, #fff)",
1791
+ boxShadow: "0 6px 20px rgba(0,0,0,0.16)",
1792
+ zIndex: 1e3
1793
+ };
1794
+ var ITEM_STYLE3 = {
1795
+ display: "flex",
1796
+ alignItems: "center",
1797
+ gap: 10,
1798
+ width: "100%",
1799
+ height: 30,
1800
+ padding: "0 10px",
1801
+ border: "none",
1802
+ borderRadius: 6,
1803
+ background: "transparent",
1804
+ cursor: "pointer",
1805
+ color: "var(--cs-chrome-fg, #201f1e)",
1806
+ font: "inherit",
1807
+ fontSize: 13,
1808
+ textAlign: "left"
1809
+ };
1810
+ function MenuBar({ api }) {
1811
+ const [open, setOpen] = useState8(null);
1812
+ const rootRef = useRef6(null);
1813
+ useEffect8(() => {
1814
+ ensureChromeFonts();
1815
+ }, []);
1816
+ useEffect8(() => {
1817
+ if (open === null) return;
1818
+ const onKey = (e) => {
1819
+ if (e.key === "Escape") setOpen(null);
1820
+ };
1821
+ const onDown = (e) => {
1822
+ if (!rootRef.current?.contains(e.target)) setOpen(null);
1823
+ };
1824
+ document.addEventListener("keydown", onKey);
1825
+ document.addEventListener("pointerdown", onDown, true);
1826
+ return () => {
1827
+ document.removeEventListener("keydown", onKey);
1828
+ document.removeEventListener("pointerdown", onDown, true);
1829
+ };
1830
+ }, [open]);
1831
+ const run = (item) => {
1832
+ setOpen(null);
1833
+ void api?.executeCommand(item.command, item.params);
1834
+ };
1835
+ return /* @__PURE__ */ jsx9(
1836
+ "div",
1837
+ {
1838
+ ref: rootRef,
1839
+ style: BAR_STYLE4,
1840
+ "data-testid": "cs-menubar",
1841
+ role: "menubar",
1842
+ "aria-label": "Menu bar",
1843
+ children: MENUS.map((menu) => {
1844
+ const isOpen = open === menu.id;
1845
+ return /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, children: [
1846
+ /* @__PURE__ */ jsx9(
1847
+ "button",
1848
+ {
1849
+ type: "button",
1850
+ "data-menu": menu.id,
1851
+ "aria-haspopup": "menu",
1852
+ "aria-expanded": isOpen,
1853
+ style: {
1854
+ ...MENU_BTN_STYLE,
1855
+ background: isOpen ? "var(--cs-chrome-active, #e6f3f7)" : "transparent",
1856
+ color: isOpen ? "var(--cs-chrome-active-fg, #0e7490)" : MENU_BTN_STYLE.color
1857
+ },
1858
+ onMouseDown: (e) => {
1859
+ e.preventDefault();
1860
+ setOpen((cur) => cur === menu.id ? null : menu.id);
1861
+ },
1862
+ onMouseEnter: (e) => {
1863
+ if (open !== null && open !== menu.id) setOpen(menu.id);
1864
+ else if (!isOpen)
1865
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
1866
+ },
1867
+ onMouseLeave: (e) => {
1868
+ e.currentTarget.style.background = isOpen ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
1869
+ },
1870
+ children: menu.label
1871
+ }
1872
+ ),
1873
+ isOpen && /* @__PURE__ */ jsx9("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: menu.items.map((item) => /* @__PURE__ */ jsxs8(
1874
+ "button",
1875
+ {
1876
+ type: "button",
1877
+ role: "menuitem",
1878
+ "data-testid": `cs-menuitem-${item.id}`,
1879
+ disabled: !api,
1880
+ style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
1881
+ onMouseDown: (e) => {
1882
+ e.preventDefault();
1883
+ run(item);
1884
+ },
1885
+ onMouseEnter: (e) => {
1886
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
1887
+ },
1888
+ onMouseLeave: (e) => {
1889
+ e.currentTarget.style.background = "transparent";
1890
+ },
1891
+ children: [
1892
+ item.icon ? /* @__PURE__ */ jsx9(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ jsx9("span", { style: { width: 18 }, "aria-hidden": true }),
1893
+ /* @__PURE__ */ jsx9("span", { children: item.label })
1894
+ ]
1895
+ },
1896
+ item.id
1897
+ )) })
1898
+ ] }, menu.id);
1899
+ })
1900
+ }
1901
+ );
1902
+ }
1903
+
1904
+ // src/chrome/SheetTabs.tsx
1905
+ import { useEffect as useEffect9, useState as useState9 } from "react";
1906
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1907
+ var EMPTY = { tabs: [], activeId: null };
1908
+ var SHEET_LIST_MUTATIONS = /* @__PURE__ */ new Set([
1909
+ "sheet.mutation.insert-sheet",
1910
+ "sheet.mutation.remove-sheet",
1911
+ "sheet.mutation.set-worksheet-name",
1912
+ "sheet.mutation.set-worksheet-order",
1913
+ "sheet.mutation.set-worksheet-hidden"
1914
+ ]);
1915
+ function readSheets(api) {
1916
+ const wb = api.univer.getActiveWorkbook();
1917
+ if (!wb) return EMPTY;
1918
+ const tabs = wb.getSheets().map((s) => ({
1919
+ id: s.getSheetId(),
1920
+ name: s.getSheetName(),
1921
+ hidden: s.isSheetHidden?.() === true
1922
+ }));
1923
+ return { tabs, activeId: wb.getActiveSheet()?.getSheetId() ?? null };
1924
+ }
1925
+ var BAR_STYLE5 = {
1926
+ display: "flex",
1927
+ alignItems: "stretch",
1928
+ gap: 2,
1929
+ height: 30,
1930
+ flex: "0 0 auto",
1931
+ padding: "0 6px",
1932
+ borderTop: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.12))",
1933
+ background: "var(--cs-chrome-bg, #f8f9fa)",
1934
+ font: "inherit",
1935
+ fontSize: 13,
1936
+ overflowX: "auto",
1937
+ overflowY: "hidden"
1938
+ };
1939
+ var ADD_BTN_STYLE = {
1940
+ flex: "0 0 auto",
1941
+ width: 28,
1942
+ border: "none",
1943
+ background: "transparent",
1944
+ color: "var(--cs-chrome-fg, #201f1e)",
1945
+ cursor: "pointer",
1946
+ fontSize: 18,
1947
+ lineHeight: 1,
1948
+ borderRadius: 4
1949
+ };
1950
+ var RENAME_INPUT_STYLE = {
1951
+ height: 22,
1952
+ alignSelf: "center",
1953
+ padding: "0 6px",
1954
+ border: "1px solid var(--cs-chrome-active-fg, #0e7490)",
1955
+ borderRadius: 4,
1956
+ background: "var(--cs-chrome-input-bg, #fff)",
1957
+ color: "var(--cs-chrome-fg, #201f1e)",
1958
+ font: "inherit",
1959
+ fontSize: 13,
1960
+ width: 120
1961
+ };
1962
+ var MENU_STYLE2 = {
1963
+ position: "fixed",
1964
+ zIndex: 1e3,
1965
+ minWidth: 140,
1966
+ padding: 4,
1967
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
1968
+ borderRadius: 8,
1969
+ background: "var(--cs-chrome-input-bg, #fff)",
1970
+ boxShadow: "0 6px 20px rgba(0,0,0,0.16)"
1971
+ };
1972
+ var MENU_ITEM_STYLE2 = {
1973
+ display: "block",
1974
+ width: "100%",
1975
+ textAlign: "left",
1976
+ height: 28,
1977
+ padding: "0 10px",
1978
+ border: "none",
1979
+ borderRadius: 6,
1980
+ background: "transparent",
1981
+ color: "var(--cs-chrome-fg, #201f1e)",
1982
+ font: "inherit",
1983
+ fontSize: 13,
1984
+ cursor: "pointer"
1985
+ };
1986
+ function SheetTabs({ api }) {
1987
+ const [{ tabs, activeId }, setSnapshot] = useState9(EMPTY);
1988
+ const [renaming, setRenaming] = useState9(null);
1989
+ const [draft, setDraft] = useState9("");
1990
+ const [menu, setMenu] = useState9(null);
1991
+ useEffect9(() => {
1992
+ if (!api) return;
1993
+ const refresh = () => setSnapshot(readSheets(api));
1994
+ refresh();
1995
+ const f = api.univer;
1996
+ const subs = [];
1997
+ try {
1998
+ subs.push(
1999
+ f.addEvent(f.Event.SheetCreated, refresh),
2000
+ f.addEvent(f.Event.SheetDeleted, refresh),
2001
+ f.addEvent(f.Event.SheetNameChanged, refresh),
2002
+ f.addEvent(f.Event.ActiveSheetChanged, refresh),
2003
+ f.addEvent(f.Event.SheetMoved, refresh),
2004
+ // Mutation-level fallback — catches collab/replay-driven changes that
2005
+ // don't emit the higher-level facade events.
2006
+ f.addEvent(f.Event.CommandExecuted, (e) => {
2007
+ if (e?.id && SHEET_LIST_MUTATIONS.has(e.id)) refresh();
2008
+ })
2009
+ );
2010
+ if (typeof f.onUniverSheetCreated === "function") {
2011
+ subs.push(f.onUniverSheetCreated(() => queueMicrotask(refresh)));
2012
+ }
2013
+ } catch {
2014
+ }
2015
+ return () => {
2016
+ for (const s of subs) s?.dispose?.();
2017
+ };
2018
+ }, [api]);
2019
+ useEffect9(() => {
2020
+ if (!menu) return;
2021
+ const close = () => setMenu(null);
2022
+ window.addEventListener("pointerdown", close);
2023
+ window.addEventListener("keydown", close);
2024
+ return () => {
2025
+ window.removeEventListener("pointerdown", close);
2026
+ window.removeEventListener("keydown", close);
2027
+ };
2028
+ }, [menu]);
2029
+ if (!api) {
2030
+ return /* @__PURE__ */ jsx10("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs" });
2031
+ }
2032
+ const visible = tabs.filter((t) => !t.hidden);
2033
+ const wb = () => api.univer.getActiveWorkbook();
2034
+ const switchTo = (id) => {
2035
+ if (id === activeId) return;
2036
+ wb()?.setActiveSheet(id);
2037
+ };
2038
+ const addSheet = () => {
2039
+ const created = wb()?.insertSheet?.();
2040
+ created?.activate?.();
2041
+ };
2042
+ const startRename = (tab) => {
2043
+ setMenu(null);
2044
+ setRenaming(tab.id);
2045
+ setDraft(tab.name);
2046
+ };
2047
+ const commitRename = () => {
2048
+ const id = renaming;
2049
+ setRenaming(null);
2050
+ if (!id) return;
2051
+ const name = draft.trim();
2052
+ if (!name) return;
2053
+ const sheet = wb()?.getSheetBySheetId?.(id);
2054
+ if (sheet && sheet.getSheetName?.() !== name) sheet.setName?.(name);
2055
+ };
2056
+ const deleteSheet = (id) => {
2057
+ setMenu(null);
2058
+ if (visible.length <= 1) return;
2059
+ wb()?.deleteSheet?.(id);
2060
+ };
2061
+ const onRenameKey = (e) => {
2062
+ if (e.key === "Enter") {
2063
+ e.preventDefault();
2064
+ commitRename();
2065
+ } else if (e.key === "Escape") {
2066
+ e.preventDefault();
2067
+ setRenaming(null);
2068
+ }
2069
+ };
2070
+ return /* @__PURE__ */ jsxs9("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs", role: "tablist", children: [
2071
+ visible.map((tab) => {
2072
+ const active = tab.id === activeId;
2073
+ if (renaming === tab.id) {
2074
+ return /* @__PURE__ */ jsx10(
2075
+ "input",
2076
+ {
2077
+ autoFocus: true,
2078
+ style: RENAME_INPUT_STYLE,
2079
+ value: draft,
2080
+ "data-testid": "cs-tab-rename-input",
2081
+ onChange: (e) => setDraft(e.target.value),
2082
+ onKeyDown: onRenameKey,
2083
+ onBlur: commitRename
2084
+ },
2085
+ tab.id
2086
+ );
2087
+ }
2088
+ return /* @__PURE__ */ jsx10(
2089
+ "button",
2090
+ {
2091
+ type: "button",
2092
+ role: "tab",
2093
+ "aria-selected": active,
2094
+ "data-testid": `cs-tab-${tab.id}`,
2095
+ "data-active": active || void 0,
2096
+ title: tab.name,
2097
+ style: {
2098
+ flex: "0 0 auto",
2099
+ maxWidth: 160,
2100
+ padding: "0 12px",
2101
+ border: "none",
2102
+ borderTop: active ? "2px solid var(--cs-chrome-active-fg, #0e7490)" : "2px solid transparent",
2103
+ background: active ? "var(--cs-chrome-input-bg, #fff)" : "transparent",
2104
+ color: active ? "var(--cs-chrome-active-fg, #0e7490)" : "var(--cs-chrome-fg, #201f1e)",
2105
+ fontWeight: active ? 600 : 400,
2106
+ font: "inherit",
2107
+ fontSize: 13,
2108
+ cursor: "pointer",
2109
+ whiteSpace: "nowrap",
2110
+ overflow: "hidden",
2111
+ textOverflow: "ellipsis"
2112
+ },
2113
+ onMouseDown: (e) => {
2114
+ e.preventDefault();
2115
+ switchTo(tab.id);
2116
+ },
2117
+ onDoubleClick: () => startRename(tab),
2118
+ onContextMenu: (e) => {
2119
+ e.preventDefault();
2120
+ setMenu({ x: e.clientX, y: e.clientY, sheetId: tab.id });
2121
+ },
2122
+ children: tab.name
2123
+ },
2124
+ tab.id
2125
+ );
2126
+ }),
2127
+ /* @__PURE__ */ jsx10(
2128
+ "button",
2129
+ {
2130
+ type: "button",
2131
+ style: ADD_BTN_STYLE,
2132
+ "aria-label": "Add sheet",
2133
+ title: "Add sheet",
2134
+ "data-testid": "cs-tab-add",
2135
+ onMouseDown: (e) => {
2136
+ e.preventDefault();
2137
+ addSheet();
2138
+ },
2139
+ children: "+"
2140
+ }
2141
+ ),
2142
+ menu && /* @__PURE__ */ jsxs9(
2143
+ "div",
2144
+ {
2145
+ style: { ...MENU_STYLE2, left: menu.x, top: menu.y },
2146
+ "data-testid": "cs-tab-menu",
2147
+ role: "menu",
2148
+ onPointerDown: (e) => e.stopPropagation(),
2149
+ children: [
2150
+ /* @__PURE__ */ jsx10(
2151
+ "button",
2152
+ {
2153
+ type: "button",
2154
+ role: "menuitem",
2155
+ style: MENU_ITEM_STYLE2,
2156
+ "data-testid": "cs-tab-menu-rename",
2157
+ onMouseDown: (e) => {
2158
+ e.preventDefault();
2159
+ const tab = tabs.find((t) => t.id === menu.sheetId);
2160
+ if (tab) startRename(tab);
2161
+ },
2162
+ children: "Rename"
2163
+ }
2164
+ ),
2165
+ /* @__PURE__ */ jsx10(
2166
+ "button",
2167
+ {
2168
+ type: "button",
2169
+ role: "menuitem",
2170
+ style: {
2171
+ ...MENU_ITEM_STYLE2,
2172
+ opacity: visible.length <= 1 ? 0.5 : 1,
2173
+ cursor: visible.length <= 1 ? "not-allowed" : "pointer"
2174
+ },
2175
+ "data-testid": "cs-tab-menu-delete",
2176
+ onMouseDown: (e) => {
2177
+ e.preventDefault();
2178
+ deleteSheet(menu.sheetId);
2179
+ },
2180
+ children: "Delete"
2181
+ }
2182
+ )
2183
+ ]
2184
+ }
2185
+ )
2186
+ ] });
2187
+ }
2188
+
2189
+ // src/chrome/FindReplace.tsx
2190
+ import {
2191
+ useEffect as useEffect10,
2192
+ useMemo as useMemo2,
2193
+ useRef as useRef7,
2194
+ useState as useState10
2195
+ } from "react";
2196
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2197
+ function findMatches(api, query, matchCase) {
2198
+ if (!query) return [];
2199
+ const snap = api.getSnapshot();
2200
+ const sheetId = api.getSelection()?.sheetId ?? api.univer.getActiveWorkbook()?.getActiveSheet()?.getSheetId();
2201
+ const sheet = sheetId ? snap?.sheets?.[sheetId] : void 0;
2202
+ const cellData = sheet?.cellData;
2203
+ if (!cellData) return [];
2204
+ const needle = matchCase ? query : query.toLowerCase();
2205
+ const out = [];
2206
+ for (const rk of Object.keys(cellData).sort((a, b) => Number(a) - Number(b))) {
2207
+ const rowObj = cellData[rk];
2208
+ if (!rowObj) continue;
2209
+ for (const ck of Object.keys(rowObj).sort((a, b) => Number(a) - Number(b))) {
2210
+ const cell = rowObj[ck];
2211
+ if (!cell) continue;
2212
+ const hay = `${cell.v ?? ""}${cell.f ? ` ${cell.f}` : ""}`;
2213
+ const cmp = matchCase ? hay : hay.toLowerCase();
2214
+ if (cmp.includes(needle)) out.push({ row: Number(rk), col: Number(ck) });
2215
+ }
2216
+ }
2217
+ return out;
2218
+ }
2219
+ var WRAP_STYLE = {
2220
+ position: "absolute",
2221
+ top: 8,
2222
+ right: 12,
2223
+ zIndex: 1100,
2224
+ width: 320,
2225
+ padding: 10,
2226
+ borderRadius: 8,
2227
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
2228
+ background: "var(--cs-chrome-input-bg, #fff)",
2229
+ boxShadow: "0 8px 28px rgba(0,0,0,0.18)",
2230
+ font: "inherit",
2231
+ fontSize: 13,
2232
+ color: "var(--cs-chrome-fg, #201f1e)"
2233
+ };
2234
+ var ROW_STYLE4 = { display: "flex", alignItems: "center", gap: 6, marginBottom: 6 };
2235
+ var INPUT_STYLE2 = {
2236
+ flex: "1 1 auto",
2237
+ height: 26,
2238
+ padding: "0 8px",
2239
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
2240
+ borderRadius: 4,
2241
+ background: "var(--cs-chrome-input-bg, #fff)",
2242
+ color: "var(--cs-chrome-fg, #201f1e)",
2243
+ font: "inherit",
2244
+ fontSize: 13,
2245
+ boxSizing: "border-box"
2246
+ };
2247
+ var BTN_STYLE5 = {
2248
+ height: 26,
2249
+ padding: "0 10px",
2250
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
2251
+ borderRadius: 4,
2252
+ background: "var(--cs-chrome-bg, #f3f5f8)",
2253
+ color: "var(--cs-chrome-fg, #201f1e)",
2254
+ font: "inherit",
2255
+ fontSize: 12,
2256
+ cursor: "pointer"
2257
+ };
2258
+ var ICON_BTN_STYLE = {
2259
+ width: 26,
2260
+ height: 26,
2261
+ display: "inline-flex",
2262
+ alignItems: "center",
2263
+ justifyContent: "center",
2264
+ border: "none",
2265
+ borderRadius: 4,
2266
+ background: "transparent",
2267
+ color: "var(--cs-chrome-fg, #201f1e)",
2268
+ cursor: "pointer",
2269
+ padding: 0
2270
+ };
2271
+ function FindReplace({ api }) {
2272
+ const [open, setOpen] = useState10(false);
2273
+ const [showReplace, setShowReplace] = useState10(false);
2274
+ const [query, setQuery] = useState10("");
2275
+ const [replaceText, setReplaceText] = useState10("");
2276
+ const [matchCase, setMatchCase] = useState10(false);
2277
+ const [idx, setIdx] = useState10(0);
2278
+ const inputRef = useRef7(null);
2279
+ useEffect10(() => {
2280
+ const onKey = (e) => {
2281
+ const mod = e.metaKey || e.ctrlKey;
2282
+ if (mod && (e.key === "f" || e.key === "F")) {
2283
+ e.preventDefault();
2284
+ setShowReplace(false);
2285
+ setOpen(true);
2286
+ requestAnimationFrame(() => inputRef.current?.focus());
2287
+ } else if (mod && (e.key === "h" || e.key === "H")) {
2288
+ e.preventDefault();
2289
+ setShowReplace(true);
2290
+ setOpen(true);
2291
+ requestAnimationFrame(() => inputRef.current?.focus());
2292
+ }
2293
+ };
2294
+ document.addEventListener("keydown", onKey);
2295
+ return () => document.removeEventListener("keydown", onKey);
2296
+ }, []);
2297
+ const matches = useMemo2(
2298
+ () => api && open && query ? findMatches(api, query, matchCase) : [],
2299
+ [api, open, query, matchCase]
2300
+ );
2301
+ useEffect10(() => {
2302
+ setIdx((i) => matches.length === 0 ? 0 : Math.min(i, matches.length - 1));
2303
+ }, [matches.length]);
2304
+ if (!open || !api) return null;
2305
+ const go = (delta) => {
2306
+ if (matches.length === 0) return;
2307
+ const next = (idx + delta + matches.length) % matches.length;
2308
+ setIdx(next);
2309
+ activate(matches[next]);
2310
+ };
2311
+ const activate = (m) => {
2312
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2313
+ sheet?.getRange(m.row, m.col)?.activate?.();
2314
+ };
2315
+ const replaceOne = () => {
2316
+ if (matches.length === 0) return;
2317
+ const m = matches[Math.min(idx, matches.length - 1)];
2318
+ replaceAt(m);
2319
+ };
2320
+ const replaceAt = (m) => {
2321
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2322
+ if (!sheet) return;
2323
+ const cur = String(sheet.getRange(m.row, m.col).getValue?.() ?? "");
2324
+ const next = replaceInString(cur, query, replaceText, matchCase);
2325
+ if (next !== cur) sheet.getRange(m.row, m.col).setValue(next);
2326
+ };
2327
+ const replaceAll = () => {
2328
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2329
+ if (!sheet) return;
2330
+ for (const m of matches) {
2331
+ const cur = String(sheet.getRange(m.row, m.col).getValue?.() ?? "");
2332
+ const next = replaceInString(cur, query, replaceText, matchCase);
2333
+ if (next !== cur) sheet.getRange(m.row, m.col).setValue(next);
2334
+ }
2335
+ };
2336
+ const onInputKey = (e) => {
2337
+ if (e.key === "Enter") {
2338
+ e.preventDefault();
2339
+ go(e.shiftKey ? -1 : 1);
2340
+ } else if (e.key === "Escape") {
2341
+ e.preventDefault();
2342
+ setOpen(false);
2343
+ }
2344
+ };
2345
+ const count = matches.length;
2346
+ const position = count === 0 ? 0 : Math.min(idx, count - 1) + 1;
2347
+ return /* @__PURE__ */ jsxs10(
2348
+ "div",
2349
+ {
2350
+ style: WRAP_STYLE,
2351
+ "data-testid": "cs-find-replace",
2352
+ role: "dialog",
2353
+ "aria-label": "Find and replace",
2354
+ children: [
2355
+ /* @__PURE__ */ jsxs10("div", { style: ROW_STYLE4, children: [
2356
+ /* @__PURE__ */ jsx11(
2357
+ "input",
2358
+ {
2359
+ ref: inputRef,
2360
+ type: "text",
2361
+ "aria-label": "Find",
2362
+ placeholder: "Find",
2363
+ "data-testid": "cs-find-input",
2364
+ style: INPUT_STYLE2,
2365
+ value: query,
2366
+ onChange: (e) => setQuery(e.target.value),
2367
+ onKeyDown: onInputKey
2368
+ }
2369
+ ),
2370
+ /* @__PURE__ */ jsxs10(
2371
+ "span",
2372
+ {
2373
+ style: { minWidth: 54, textAlign: "center", color: "var(--cs-chrome-muted, #6b7280)" },
2374
+ "data-testid": "cs-find-count",
2375
+ children: [
2376
+ position,
2377
+ "/",
2378
+ count
2379
+ ]
2380
+ }
2381
+ ),
2382
+ /* @__PURE__ */ jsx11(
2383
+ "button",
2384
+ {
2385
+ type: "button",
2386
+ style: ICON_BTN_STYLE,
2387
+ "aria-label": "Previous match",
2388
+ "data-testid": "cs-find-prev",
2389
+ onMouseDown: (e) => {
2390
+ e.preventDefault();
2391
+ go(-1);
2392
+ },
2393
+ children: /* @__PURE__ */ jsx11(Icon, { name: "keyboard_arrow_up", size: 18 })
2394
+ }
2395
+ ),
2396
+ /* @__PURE__ */ jsx11(
2397
+ "button",
2398
+ {
2399
+ type: "button",
2400
+ style: ICON_BTN_STYLE,
2401
+ "aria-label": "Next match",
2402
+ "data-testid": "cs-find-next",
2403
+ onMouseDown: (e) => {
2404
+ e.preventDefault();
2405
+ go(1);
2406
+ },
2407
+ children: /* @__PURE__ */ jsx11(Icon, { name: "keyboard_arrow_down", size: 18 })
2408
+ }
2409
+ ),
2410
+ /* @__PURE__ */ jsx11(
2411
+ "button",
2412
+ {
2413
+ type: "button",
2414
+ style: ICON_BTN_STYLE,
2415
+ "aria-label": "Close",
2416
+ "data-testid": "cs-find-close",
2417
+ onMouseDown: (e) => {
2418
+ e.preventDefault();
2419
+ setOpen(false);
2420
+ },
2421
+ children: /* @__PURE__ */ jsx11(Icon, { name: "close", size: 18 })
2422
+ }
2423
+ )
2424
+ ] }),
2425
+ showReplace && /* @__PURE__ */ jsxs10("div", { style: ROW_STYLE4, children: [
2426
+ /* @__PURE__ */ jsx11(
2427
+ "input",
2428
+ {
2429
+ type: "text",
2430
+ "aria-label": "Replace with",
2431
+ placeholder: "Replace with",
2432
+ "data-testid": "cs-replace-input",
2433
+ style: INPUT_STYLE2,
2434
+ value: replaceText,
2435
+ onChange: (e) => setReplaceText(e.target.value)
2436
+ }
2437
+ ),
2438
+ /* @__PURE__ */ jsx11(
2439
+ "button",
2440
+ {
2441
+ type: "button",
2442
+ style: BTN_STYLE5,
2443
+ "data-testid": "cs-replace-one",
2444
+ onMouseDown: (e) => {
2445
+ e.preventDefault();
2446
+ replaceOne();
2447
+ },
2448
+ children: "Replace"
2449
+ }
2450
+ ),
2451
+ /* @__PURE__ */ jsx11(
2452
+ "button",
2453
+ {
2454
+ type: "button",
2455
+ style: BTN_STYLE5,
2456
+ "data-testid": "cs-replace-all",
2457
+ onMouseDown: (e) => {
2458
+ e.preventDefault();
2459
+ replaceAll();
2460
+ },
2461
+ children: "All"
2462
+ }
2463
+ )
2464
+ ] }),
2465
+ /* @__PURE__ */ jsxs10("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
2466
+ /* @__PURE__ */ jsxs10("label", { style: { display: "inline-flex", alignItems: "center", gap: 4, cursor: "pointer" }, children: [
2467
+ /* @__PURE__ */ jsx11(
2468
+ "input",
2469
+ {
2470
+ type: "checkbox",
2471
+ "data-testid": "cs-find-match-case",
2472
+ checked: matchCase,
2473
+ onChange: (e) => setMatchCase(e.target.checked)
2474
+ }
2475
+ ),
2476
+ "Match case"
2477
+ ] }),
2478
+ !showReplace && /* @__PURE__ */ jsx11(
2479
+ "button",
2480
+ {
2481
+ type: "button",
2482
+ style: {
2483
+ ...BTN_STYLE5,
2484
+ marginLeft: "auto",
2485
+ border: "none",
2486
+ background: "transparent",
2487
+ color: "var(--cs-chrome-active-fg, #0e7490)"
2488
+ },
2489
+ "data-testid": "cs-find-toggle-replace",
2490
+ onMouseDown: (e) => {
2491
+ e.preventDefault();
2492
+ setShowReplace(true);
2493
+ },
2494
+ children: "Replace\u2026"
2495
+ }
2496
+ )
2497
+ ] })
2498
+ ]
2499
+ }
2500
+ );
2501
+ }
2502
+ function replaceInString(text, query, replacement, matchCase) {
2503
+ if (!query) return text;
2504
+ if (matchCase) return text.split(query).join(replacement);
2505
+ const lower = text.toLowerCase();
2506
+ const needle = query.toLowerCase();
2507
+ let out = "";
2508
+ let i = 0;
2509
+ while (i < text.length) {
2510
+ if (lower.startsWith(needle, i)) {
2511
+ out += replacement;
2512
+ i += needle.length;
2513
+ } else {
2514
+ out += text[i];
2515
+ i += 1;
2516
+ }
2517
+ }
2518
+ return out;
2519
+ }
2520
+
2521
+ // src/chrome/ChromeTop.tsx
2522
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2523
+ function ChromeTop({ api }) {
2524
+ return /* @__PURE__ */ jsxs11(Fragment2, { children: [
2525
+ /* @__PURE__ */ jsx12(MenuBar, { api }),
2526
+ /* @__PURE__ */ jsx12(Toolbar, { api }),
2527
+ /* @__PURE__ */ jsx12(FormulaBar, { api })
2528
+ ] });
2529
+ }
2530
+
2531
+ // src/chrome/ChromeBottom.tsx
2532
+ import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
2533
+ function ChromeBottom({ api }) {
2534
+ return /* @__PURE__ */ jsxs12(Fragment3, { children: [
2535
+ /* @__PURE__ */ jsx13(SheetTabs, { api }),
2536
+ /* @__PURE__ */ jsx13(StatusBar, { api }),
2537
+ /* @__PURE__ */ jsx13(FindReplace, { api })
2538
+ ] });
2539
+ }
2540
+ export {
2541
+ AutoSumPicker,
2542
+ BordersPicker,
2543
+ ChromeBottom,
2544
+ ChromeTop,
2545
+ ColorPicker,
2546
+ FindReplace,
2547
+ FormulaBar,
2548
+ Icon,
2549
+ MenuBar,
2550
+ NameBox,
2551
+ SheetTabs,
2552
+ StatusBar,
2553
+ Toolbar,
2554
+ ensureChromeFonts
2555
+ };
2556
+ //# sourceMappingURL=chrome.js.map