@braincrew-lab/langchain-canvas 0.2.0 → 0.3.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.
@@ -0,0 +1,636 @@
1
+ import { loadOptional } from './chunk-YZZSJJMQ.js';
2
+ import { useCanvasStore } from './chunk-ZLXAWRUP.js';
3
+ import { useT } from './chunk-QMOJEGRH.js';
4
+ import { lazy, useMemo, useState, useRef, useEffect, useCallback, Suspense } from 'react';
5
+ import '@fortune-sheet/react/dist/index.css';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+
8
+ // src/io/formula.ts
9
+ var EMPTY = /* @__PURE__ */ new Map();
10
+ async function computeFormulas(columns, rows) {
11
+ const formulaCells = [];
12
+ rows.forEach(
13
+ (row, dataIdx) => columns.forEach((col, c) => {
14
+ const v = row[col.key];
15
+ if (typeof v === "string" && v.startsWith("=")) formulaCells.push({ dataIdx, col: c, formula: v });
16
+ })
17
+ );
18
+ if (formulaCells.length === 0) return EMPTY;
19
+ const mod = await loadOptional("fast-formula-parser", () => import('fast-formula-parser'));
20
+ const FormulaParser = mod.default ?? mod;
21
+ const memo = /* @__PURE__ */ new Map();
22
+ const inProgress = /* @__PURE__ */ new Set();
23
+ const rawAt = (row, col) => {
24
+ const colIdx = col - 1;
25
+ if (row === 1) return columns[colIdx]?.label ?? columns[colIdx]?.key ?? null;
26
+ const dataRow = rows[row - 2];
27
+ const column = columns[colIdx];
28
+ if (!dataRow || !column) return null;
29
+ const v = dataRow[column.key];
30
+ return v ?? null;
31
+ };
32
+ const valueAt = (row, col) => {
33
+ const raw = rawAt(row, col);
34
+ if (typeof raw !== "string" || !raw.startsWith("=")) return raw ?? 0;
35
+ const key = `${row},${col}`;
36
+ const cached = memo.get(key);
37
+ if (cached !== void 0) return cached;
38
+ if (inProgress.has(key)) return 0;
39
+ inProgress.add(key);
40
+ const value = evaluate(raw, row, col);
41
+ inProgress.delete(key);
42
+ memo.set(key, value);
43
+ return value;
44
+ };
45
+ const parser = new FormulaParser({
46
+ onCell: ({ row, col }) => valueAt(row, col),
47
+ onRange: (ref) => {
48
+ const maxRow = Math.min(ref.to.row, rows.length + 1);
49
+ const maxCol = Math.min(ref.to.col, columns.length);
50
+ const grid = [];
51
+ for (let r = ref.from.row; r <= maxRow; r++) {
52
+ const line = [];
53
+ for (let c = ref.from.col; c <= maxCol; c++) line.push(valueAt(r, c));
54
+ grid.push(line);
55
+ }
56
+ return grid;
57
+ }
58
+ });
59
+ const evaluate = (formula, row, col) => {
60
+ try {
61
+ const result = parser.parse(formula.slice(1), { row, col });
62
+ if (result != null && typeof result === "object") return "#ERR";
63
+ return result ?? 0;
64
+ } catch {
65
+ return "#ERR";
66
+ }
67
+ };
68
+ const out = /* @__PURE__ */ new Map();
69
+ for (const { dataIdx, col, formula } of formulaCells) {
70
+ out.set(`${dataIdx + 1},${col}`, evaluate(formula, dataIdx + 2, col + 1));
71
+ }
72
+ return out;
73
+ }
74
+ var Workbook = lazy(() => import('@fortune-sheet/react').then((m) => ({ default: m.Workbook })));
75
+ var isFormula = (v) => typeof v === "string" && v.startsWith("=");
76
+ function toWorkbook(columns, rows, formulas) {
77
+ const celldata = [];
78
+ columns.forEach((col, c) => {
79
+ const label = col.label ?? col.key;
80
+ celldata.push({ r: 0, c, v: { v: label, m: String(label), bl: 1, bg: "#f3f4f6" } });
81
+ });
82
+ rows.forEach((row, r) => {
83
+ columns.forEach((col, c) => {
84
+ const val = row[col.key];
85
+ if (val === void 0 || val === null || val === "") return;
86
+ if (isFormula(val)) {
87
+ const computed = formulas.get(`${r + 1},${c}`);
88
+ const v = { f: val };
89
+ if (computed !== void 0) {
90
+ v.v = computed;
91
+ v.m = String(computed);
92
+ if (typeof computed === "number") v.ct = { fa: "General", t: "n" };
93
+ }
94
+ celldata.push({ r: r + 1, c, v });
95
+ return;
96
+ }
97
+ const numeric = typeof val === "number";
98
+ celldata.push({
99
+ r: r + 1,
100
+ c,
101
+ v: { v: val, m: String(val), ...numeric ? { ct: { fa: "General", t: "n" } } : {} }
102
+ });
103
+ });
104
+ });
105
+ const sample = Math.min(rows.length, 400);
106
+ const columnlen = {};
107
+ columns.forEach((col, c) => {
108
+ let widest = String(col.label ?? col.key).length;
109
+ for (let ri = 0; ri < sample; ri++) {
110
+ let v = rows[ri][col.key];
111
+ if (isFormula(v)) v = formulas.get(`${ri + 1},${c}`) ?? "";
112
+ if (v != null && v !== "") widest = Math.max(widest, String(v).length);
113
+ }
114
+ columnlen[c] = Math.min(360, Math.max(64, Math.round(widest * 8.5) + 18));
115
+ });
116
+ return [
117
+ {
118
+ name: "Sheet1",
119
+ id: "sheet1",
120
+ order: 0,
121
+ // Size the grid to the data plus a modest buffer — big enough to feel like a
122
+ // real sheet and to keep growing, small enough that the scrollbar stays
123
+ // proportional (a huge empty grid makes scrolling feel disconnected).
124
+ row: Math.max(rows.length + 40, 60),
125
+ column: Math.max(columns.length + 2, 8),
126
+ celldata,
127
+ // No frozen pane: a freeze split offsets the initial scroll and hides the
128
+ // first data rows behind the split line. A plain grid scrolls cleanly.
129
+ config: { rowlen: { 0: 28 }, columnlen }
130
+ }
131
+ ];
132
+ }
133
+ function normalizeSheets(sheets) {
134
+ return (sheets ?? []).map((sheet) => {
135
+ const matrix = sheet.data;
136
+ if (!Array.isArray(matrix)) return sheet;
137
+ const celldata = [];
138
+ matrix.forEach((row, r) => {
139
+ if (!Array.isArray(row)) return;
140
+ row.forEach((v, c) => {
141
+ if (v != null) celldata.push({ r, c, v });
142
+ });
143
+ });
144
+ const { data: _dropped, ...rest } = sheet;
145
+ return { ...rest, celldata };
146
+ });
147
+ }
148
+ function deriveColumns(rows) {
149
+ const keys = /* @__PURE__ */ new Set();
150
+ for (let i = 0; i < Math.min(rows.length, 50); i++) Object.keys(rows[i] ?? {}).forEach((k) => keys.add(k));
151
+ return [...keys].map((key) => ({ key }));
152
+ }
153
+ var EMPTY_FORMULAS = /* @__PURE__ */ new Map();
154
+ var NUMBER_FORMATS = [
155
+ { labelKey: "fmtGeneral", suffix: "", fa: "General" },
156
+ { labelKey: "fmtCurrency", suffix: " \u20A9#,##0", fa: "\u20A9#,##0" },
157
+ { labelKey: "fmtCurrency", suffix: " $#,##0.00", fa: "$#,##0.00" },
158
+ { labelKey: "fmtPercent", suffix: " 0.0%", fa: "0.0%" },
159
+ { labelKey: "fmtThousands", suffix: " #,##0", fa: "#,##0" },
160
+ { labelKey: "fmtDecimal", suffix: " 0.00", fa: "0.00" },
161
+ { labelKey: "fmtDate", suffix: " yyyy-mm-dd", fa: "yyyy-mm-dd" }
162
+ ];
163
+ var QUICK_FUNCTIONS = ["SUM", "AVERAGE", "COUNT", "MAX", "MIN"];
164
+ var colToLetters = (c) => {
165
+ let s = "";
166
+ for (let n = c; n >= 0; n = Math.floor(n / 26) - 1) s = String.fromCharCode(65 + n % 26) + s;
167
+ return s;
168
+ };
169
+ var toA1 = (r, c) => `${colToLetters(c)}${r + 1}`;
170
+ var plainFormula = (f) => {
171
+ if (!f.includes("<")) return f;
172
+ const div = document.createElement("div");
173
+ div.innerHTML = f;
174
+ return div.textContent ?? f;
175
+ };
176
+ var selectionHasMerge = (wb, sel) => {
177
+ let scanned = 0;
178
+ for (let r = sel.row[0]; r <= sel.row[1]; r++) {
179
+ for (let c = sel.column[0]; c <= sel.column[1]; c++) {
180
+ if (scanned++ >= 400) return false;
181
+ if (wb.getCellValue?.(r, c, { type: "mc" })) return true;
182
+ }
183
+ }
184
+ return false;
185
+ };
186
+ function AlignIcon({ mode }) {
187
+ return /* @__PURE__ */ jsx("svg", { width: "14", height: "12", viewBox: "0 0 14 12", "aria-hidden": true, focusable: "false", children: [0, 1, 2, 3].map((i) => {
188
+ const w = i % 2 ? 8.5 : 13;
189
+ const x = mode === "left" ? 0 : mode === "right" ? 14 - w : (14 - w) / 2;
190
+ return /* @__PURE__ */ jsx("rect", { x, y: i * 3.1, width: w, height: "1.7", rx: "0.85", fill: "currentColor" }, i);
191
+ }) });
192
+ }
193
+ function FillIcon() {
194
+ return /* @__PURE__ */ jsxs("svg", { width: "13", height: "12", viewBox: "0 0 13 12", "aria-hidden": true, focusable: "false", children: [
195
+ /* @__PURE__ */ jsx("path", { d: "M5.6 1.2 10 5.6a1.1 1.1 0 0 1 0 1.6L7.4 9.8a1.1 1.1 0 0 1-1.6 0L1.4 5.4a1.1 1.1 0 0 1 0-1.6L4 1.2a1.1 1.1 0 0 1 1.6 0Z", fill: "none", stroke: "currentColor", strokeWidth: "1.3" }),
196
+ /* @__PURE__ */ jsx("path", { d: "M12.6 9.4c0 .9-.55 1.6-1.25 1.6s-1.25-.7-1.25-1.6c0-.85 1.25-2.3 1.25-2.3s1.25 1.45 1.25 2.3Z", fill: "currentColor" })
197
+ ] });
198
+ }
199
+ function RibbonGroup({ label, children }) {
200
+ return /* @__PURE__ */ jsxs("div", { className: "cv-ribbon__group", role: "group", "aria-label": label, children: [
201
+ /* @__PURE__ */ jsx("div", { className: "cv-ribbon__items", children }),
202
+ /* @__PURE__ */ jsx("span", { className: "cv-ribbon__label", children: label })
203
+ ] });
204
+ }
205
+ function TableRenderer({ artifact }) {
206
+ const t = useT();
207
+ const rows = artifact.data.rows;
208
+ const columns = useMemo(
209
+ () => artifact.data.columns.length ? artifact.data.columns : deriveColumns(rows),
210
+ // eslint-disable-next-line react-hooks/exhaustive-deps
211
+ [artifact.id, artifact.version, artifact.data.columns.length, rows.length]
212
+ );
213
+ const [mounted, setMounted] = useState(false);
214
+ const rootRef = useRef(null);
215
+ useEffect(() => setMounted(true), []);
216
+ useEffect(() => {
217
+ const root = rootRef.current;
218
+ if (!root) return;
219
+ const onWheel = (e) => {
220
+ const y = root.querySelector(".luckysheet-scrollbar-y");
221
+ const x = root.querySelector(".luckysheet-scrollbar-x");
222
+ let moved = false;
223
+ if (y && e.deltaY && y.scrollHeight > y.clientHeight) {
224
+ const max = y.scrollHeight - y.clientHeight;
225
+ const next = Math.max(0, Math.min(max, y.scrollTop + e.deltaY));
226
+ if (next !== y.scrollTop) {
227
+ y.scrollTop = next;
228
+ moved = true;
229
+ }
230
+ }
231
+ if (x && e.deltaX && x.scrollWidth > x.clientWidth) {
232
+ const max = x.scrollWidth - x.clientWidth;
233
+ const next = Math.max(0, Math.min(max, x.scrollLeft + e.deltaX));
234
+ if (next !== x.scrollLeft) {
235
+ x.scrollLeft = next;
236
+ moved = true;
237
+ }
238
+ }
239
+ const horizontal = Math.abs(e.deltaX) > Math.abs(e.deltaY);
240
+ if (moved || horizontal && e.deltaX) {
241
+ e.preventDefault();
242
+ e.stopPropagation();
243
+ }
244
+ };
245
+ root.addEventListener("wheel", onWheel, { passive: false, capture: true });
246
+ return () => root.removeEventListener("wheel", onWheel, { capture: true });
247
+ }, [mounted]);
248
+ const dataKey = `${artifact.id}:v${artifact.version}:${columns.length}x${rows.length}`;
249
+ const hasFormulas = useMemo(
250
+ () => rows.slice(0, 400).some((row) => columns.some((col) => isFormula(row[col.key]))),
251
+ // eslint-disable-next-line react-hooks/exhaustive-deps
252
+ [dataKey]
253
+ );
254
+ const [formulas, setFormulas] = useState(EMPTY_FORMULAS);
255
+ const [formulasReady, setFormulasReady] = useState(!hasFormulas);
256
+ useEffect(() => {
257
+ if (!hasFormulas) {
258
+ setFormulas(EMPTY_FORMULAS);
259
+ setFormulasReady(true);
260
+ return;
261
+ }
262
+ let alive = true;
263
+ setFormulasReady(false);
264
+ computeFormulas(columns, rows).then((values) => {
265
+ if (!alive) return;
266
+ setFormulas(values);
267
+ setFormulasReady(true);
268
+ });
269
+ return () => {
270
+ alive = false;
271
+ };
272
+ }, [dataKey, hasFormulas]);
273
+ const hasSheet = !!artifact.data.sheet?.length;
274
+ const [sortCol, setSortCol] = useState("");
275
+ const [sortDir, setSortDir] = useState(1);
276
+ const [filter, setFilter] = useState("");
277
+ const [appliedFilter, setAppliedFilter] = useState("");
278
+ useEffect(() => {
279
+ const t2 = setTimeout(() => setAppliedFilter(filter), 300);
280
+ return () => clearTimeout(t2);
281
+ }, [filter]);
282
+ const viewRows = useMemo(() => {
283
+ let r = rows;
284
+ const q = appliedFilter.trim().toLowerCase();
285
+ if (q) r = r.filter((row) => columns.some((c) => String(row[c.key] ?? "").toLowerCase().includes(q)));
286
+ if (sortCol) {
287
+ r = [...r].sort((a, b) => {
288
+ const av = a[sortCol];
289
+ const bv = b[sortCol];
290
+ const cmp = typeof av === "number" && typeof bv === "number" ? av - bv : String(av ?? "").localeCompare(String(bv ?? ""));
291
+ return cmp * sortDir;
292
+ });
293
+ }
294
+ return r;
295
+ }, [rows, columns, appliedFilter, sortCol, sortDir]);
296
+ const viewActive = !!appliedFilter.trim() || !!sortCol;
297
+ const wbKey = `${dataKey}:${viewActive ? `view-s${sortCol}${sortDir}-f${appliedFilter}` : hasSheet ? "sheet" : "rows"}`;
298
+ const initialData = useMemo(
299
+ () => viewActive ? toWorkbook(columns, viewRows, formulas) : hasSheet ? artifact.data.sheet : toWorkbook(columns, rows, formulas),
300
+ // eslint-disable-next-line react-hooks/exhaustive-deps
301
+ [wbKey, formulasReady]
302
+ );
303
+ const applyEvent = useCanvasStore((s) => s.applyUserEvent);
304
+ const persistTimer = useRef(null);
305
+ const mountEcho = useRef(1);
306
+ const handleChange = useCallback(
307
+ (sheets) => {
308
+ if (mountEcho.current > 0) {
309
+ mountEcho.current--;
310
+ return;
311
+ }
312
+ if (persistTimer.current) clearTimeout(persistTimer.current);
313
+ persistTimer.current = setTimeout(() => {
314
+ applyEvent({
315
+ type: "canvas.patch",
316
+ id: artifact.id,
317
+ patch: { sheet: normalizeSheets(sheets) }
318
+ });
319
+ }, 400);
320
+ },
321
+ [applyEvent, artifact.id]
322
+ );
323
+ useEffect(() => () => {
324
+ if (persistTimer.current) clearTimeout(persistTimer.current);
325
+ }, []);
326
+ const wbRef = useRef(null);
327
+ const insert = (type) => {
328
+ const sel = wbRef.current?.getSelection?.();
329
+ const range = sel?.[0]?.[type] ?? [0, 0];
330
+ wbRef.current?.insertRowOrColumn(type, Math.max(0, range[1]), 1, "rightbottom");
331
+ };
332
+ const [selection, setSelection] = useState(null);
333
+ const [boldOn, setBoldOn] = useState(false);
334
+ const [fillColor, setFillColor] = useState("#fef3c7");
335
+ const [textColor, setTextColor] = useState("#111827");
336
+ const [selHasMerge, setSelHasMerge] = useState(false);
337
+ const [fxValue, setFxValue] = useState("");
338
+ const [fxDraft, setFxDraft] = useState(null);
339
+ const handleSelectionChange = useCallback((_sheetId, sel) => {
340
+ setSelection({ row: [...sel.row], column: [...sel.column] });
341
+ const wb = wbRef.current;
342
+ const [r, c] = [sel.row[0], sel.column[0]];
343
+ const bl = wb?.getCellValue?.(r, c, { type: "bl" });
344
+ setBoldOn(bl === 1 || bl === "1");
345
+ const f = wb?.getCellValue?.(r, c, { type: "f" });
346
+ const v = wb?.getCellValue?.(r, c);
347
+ setFxValue(typeof f === "string" && f ? plainFormula(f) : v == null ? "" : String(v));
348
+ setFxDraft(null);
349
+ setSelHasMerge(wb ? selectionHasMerge(wb, sel) : false);
350
+ }, []);
351
+ const workbookHooks = useMemo(() => ({ afterSelectionChange: handleSelectionChange }), [handleSelectionChange]);
352
+ const applyFormat = (attr, value) => {
353
+ const sel = wbRef.current?.getSelection?.();
354
+ if (!sel?.length) return;
355
+ const ranges = sel.map((s) => ({ row: [s.row[0], s.row[1]], column: [s.column[0], s.column[1]] }));
356
+ wbRef.current?.setCellFormatByRange(attr, value, ranges);
357
+ };
358
+ const toggleBold = () => {
359
+ applyFormat("bl", boldOn ? 0 : 1);
360
+ setBoldOn((b) => !b);
361
+ };
362
+ const applyNumberFormat = (fa) => {
363
+ const wb = wbRef.current;
364
+ const sel = wb?.getSelection?.();
365
+ if (!wb || !sel?.length) return;
366
+ const isDate = fa === "yyyy-mm-dd";
367
+ const calls = [];
368
+ sel.forEach((s) => {
369
+ for (let r = s.row[0]; r <= s.row[1]; r++) {
370
+ for (let c = s.column[0]; c <= s.column[1]; c++) {
371
+ const v = wb.getCellValue?.(r, c);
372
+ const numeric = v != null && v !== "" && Number.isFinite(Number(v));
373
+ const t2 = isDate ? "d" : fa === "General" ? numeric ? "n" : "g" : "n";
374
+ calls.push({ name: "setCellFormat", args: [r, c, "ct", { fa, t: t2 }] });
375
+ }
376
+ }
377
+ });
378
+ wb.batchCallApis(calls);
379
+ };
380
+ const mergeSelection = () => {
381
+ const sel = wbRef.current?.getSelection?.();
382
+ if (!sel?.length) return;
383
+ wbRef.current?.mergeCells(sel, "merge-all");
384
+ setSelHasMerge(true);
385
+ };
386
+ const unmergeSelection = () => {
387
+ const sel = wbRef.current?.getSelection?.();
388
+ if (!sel?.length) return;
389
+ wbRef.current?.cancelMerge(sel);
390
+ setSelHasMerge(false);
391
+ };
392
+ const insertQuickFormula = (fn) => {
393
+ const wb = wbRef.current;
394
+ const sel = wb?.getSelection?.()?.[0];
395
+ if (!wb || !sel) return;
396
+ const [r1, r2] = [sel.row[0], sel.row[1]];
397
+ const [c1, c2] = [sel.column[0], sel.column[1]];
398
+ const singleRow = r1 === r2;
399
+ const [tr, tc] = singleRow ? [r1, c2 + 1] : [r2 + 1, c1];
400
+ const sheet = wb.getSheet?.();
401
+ if (tr >= (sheet?.row ?? 0) || tc >= (sheet?.column ?? 0)) return;
402
+ wb.setCellValue(tr, tc, `=${fn}(${toA1(r1, c1)}:${toA1(r2, c2)})`);
403
+ };
404
+ const commitFormulaBar = () => {
405
+ const wb = wbRef.current;
406
+ if (!wb || !selection || fxDraft == null) return;
407
+ wb.setCellValue(selection.row[0], selection.column[0], fxDraft);
408
+ setFxValue(fxDraft);
409
+ setFxDraft(null);
410
+ };
411
+ const cleanStyling = () => {
412
+ const wb = wbRef.current;
413
+ const sheet = wb?.getSheet?.();
414
+ const sheetId = sheet?.id;
415
+ if (!wb || !sheet || !sheetId) return;
416
+ const ops = [];
417
+ sheet.celldata.forEach(({ r, c, v }) => {
418
+ if (!v || typeof v !== "object") return;
419
+ const cell = v;
420
+ if (cell.bg != null) ops.push({ op: "remove", id: sheetId, path: ["data", r, c, "bg"] });
421
+ if (cell.fc != null) ops.push({ op: "remove", id: sheetId, path: ["data", r, c, "fc"] });
422
+ const spans = cell.ct?.s;
423
+ if (!Array.isArray(spans)) return;
424
+ spans.forEach((span, i) => {
425
+ if (!span || typeof span !== "object") return;
426
+ const run = span;
427
+ if (run.bg != null) ops.push({ op: "remove", id: sheetId, path: ["data", r, c, "ct", "s", i, "bg"] });
428
+ if (run.fc != null) ops.push({ op: "remove", id: sheetId, path: ["data", r, c, "ct", "s", i, "fc"] });
429
+ });
430
+ });
431
+ if (ops.length) wb.applyOp(ops);
432
+ };
433
+ const [frozen, setFrozen] = useState(false);
434
+ const toggleFreeze = () => {
435
+ const wb = wbRef.current;
436
+ if (!wb) return;
437
+ if (frozen) {
438
+ const sheetId = wb.getSheet?.()?.id;
439
+ if (sheetId) wb.applyOp([{ op: "remove", id: sheetId, path: ["frozen"] }]);
440
+ } else {
441
+ wb.freeze("row", { row: 0, column: 0 });
442
+ }
443
+ setFrozen((f) => !f);
444
+ };
445
+ useEffect(() => {
446
+ mountEcho.current = 1;
447
+ setFrozen(!viewActive && hasSheet && !!artifact.data.sheet?.[0]?.frozen);
448
+ setSelection(null);
449
+ setBoldOn(false);
450
+ setSelHasMerge(false);
451
+ setFxValue("");
452
+ setFxDraft(null);
453
+ }, [wbKey]);
454
+ if (!mounted) {
455
+ return /* @__PURE__ */ jsx("div", { className: "cv-sheet cv-sheet--empty", children: t("loadingSheet") });
456
+ }
457
+ if (!hasSheet && columns.length === 0) {
458
+ return /* @__PURE__ */ jsx("div", { className: "cv-sheet cv-sheet--empty", children: t("waitingData") });
459
+ }
460
+ if (!hasSheet && !formulasReady) {
461
+ return /* @__PURE__ */ jsx("div", { className: "cv-sheet cv-sheet--empty", children: t("calculating") });
462
+ }
463
+ return /* @__PURE__ */ jsxs("div", { className: "cv-sheet-panel", children: [
464
+ /* @__PURE__ */ jsxs("div", { className: "cv-sheet-tools cv-ribbon", children: [
465
+ /* @__PURE__ */ jsxs(RibbonGroup, { label: t("groupInsert"), children: [
466
+ /* @__PURE__ */ jsx("button", { type: "button", className: "cv-ribbon__btn", onClick: () => insert("column"), children: t("addColumn") }),
467
+ /* @__PURE__ */ jsx("button", { type: "button", className: "cv-ribbon__btn", onClick: () => insert("row"), children: t("addRow") })
468
+ ] }),
469
+ /* @__PURE__ */ jsxs(RibbonGroup, { label: t("groupFont"), children: [
470
+ /* @__PURE__ */ jsx(
471
+ "button",
472
+ {
473
+ type: "button",
474
+ className: `cv-ribbon__btn cv-ribbon__btn--glyph cv-ribbon__bold${boldOn ? " is-on" : ""}`,
475
+ title: t("bold"),
476
+ "aria-pressed": boldOn,
477
+ disabled: !selection,
478
+ onClick: toggleBold,
479
+ children: t("boldGlyph")
480
+ }
481
+ ),
482
+ /* @__PURE__ */ jsxs("label", { className: `cv-ribbon__swatch${!selection ? " is-disabled" : ""}`, title: t("textColor"), children: [
483
+ /* @__PURE__ */ jsx("span", { className: "cv-ribbon__swatch-glyph", children: t("textGlyph") }),
484
+ /* @__PURE__ */ jsx("span", { className: "cv-ribbon__swatch-bar", style: { background: textColor } }),
485
+ /* @__PURE__ */ jsx(
486
+ "input",
487
+ {
488
+ type: "color",
489
+ className: "cv-sheet-tools__color cv-sheet-tools__color--text",
490
+ disabled: !selection,
491
+ value: textColor,
492
+ onChange: (e) => {
493
+ setTextColor(e.target.value);
494
+ applyFormat("fc", e.target.value);
495
+ }
496
+ }
497
+ )
498
+ ] }),
499
+ /* @__PURE__ */ jsxs("label", { className: `cv-ribbon__swatch${!selection ? " is-disabled" : ""}`, title: t("fillColor"), children: [
500
+ /* @__PURE__ */ jsx("span", { className: "cv-ribbon__swatch-glyph", children: /* @__PURE__ */ jsx(FillIcon, {}) }),
501
+ /* @__PURE__ */ jsx("span", { className: "cv-ribbon__swatch-bar", style: { background: fillColor } }),
502
+ /* @__PURE__ */ jsx(
503
+ "input",
504
+ {
505
+ type: "color",
506
+ className: "cv-sheet-tools__color cv-sheet-tools__color--fill",
507
+ disabled: !selection,
508
+ value: fillColor,
509
+ onChange: (e) => {
510
+ setFillColor(e.target.value);
511
+ applyFormat("bg", e.target.value);
512
+ }
513
+ }
514
+ )
515
+ ] })
516
+ ] }),
517
+ /* @__PURE__ */ jsxs(RibbonGroup, { label: t("groupAlign"), children: [
518
+ /* @__PURE__ */ jsx("button", { type: "button", className: "cv-ribbon__btn cv-ribbon__btn--icon cv-sheet-tools__align", title: t("alignLeft"), disabled: !selection, onClick: () => applyFormat("ht", 1), children: /* @__PURE__ */ jsx(AlignIcon, { mode: "left" }) }),
519
+ /* @__PURE__ */ jsx("button", { type: "button", className: "cv-ribbon__btn cv-ribbon__btn--icon", title: t("alignCenter"), disabled: !selection, onClick: () => applyFormat("ht", 0), children: /* @__PURE__ */ jsx(AlignIcon, { mode: "center" }) }),
520
+ /* @__PURE__ */ jsx("button", { type: "button", className: "cv-ribbon__btn cv-ribbon__btn--icon", title: t("alignRight"), disabled: !selection, onClick: () => applyFormat("ht", 2), children: /* @__PURE__ */ jsx(AlignIcon, { mode: "right" }) }),
521
+ /* @__PURE__ */ jsx("span", { className: "cv-ribbon__gap" }),
522
+ /* @__PURE__ */ jsx(
523
+ "button",
524
+ {
525
+ type: "button",
526
+ className: "cv-ribbon__btn cv-sheet-tools__merge",
527
+ title: t("mergeTip"),
528
+ disabled: !selection || selection.row[0] === selection.row[1] && selection.column[0] === selection.column[1],
529
+ onClick: mergeSelection,
530
+ children: t("mergeCells")
531
+ }
532
+ ),
533
+ /* @__PURE__ */ jsx("button", { type: "button", className: "cv-ribbon__btn cv-sheet-tools__unmerge", title: t("unmergeTip"), disabled: !selHasMerge, onClick: unmergeSelection, children: t("unmergeCells") })
534
+ ] }),
535
+ /* @__PURE__ */ jsx(RibbonGroup, { label: t("groupNumber"), children: /* @__PURE__ */ jsxs(
536
+ "select",
537
+ {
538
+ className: "cv-ribbon__select cv-sheet-tools__numfmt",
539
+ value: "",
540
+ title: t("numberFormatTip"),
541
+ disabled: !selection,
542
+ onChange: (e) => {
543
+ if (e.target.value) applyNumberFormat(e.target.value);
544
+ },
545
+ children: [
546
+ /* @__PURE__ */ jsx("option", { value: "", children: t("numberFormat") }),
547
+ NUMBER_FORMATS.map((f) => /* @__PURE__ */ jsx("option", { value: f.fa, children: t(f.labelKey) + f.suffix }, f.fa))
548
+ ]
549
+ }
550
+ ) }),
551
+ /* @__PURE__ */ jsxs(RibbonGroup, { label: t("groupEdit"), children: [
552
+ /* @__PURE__ */ jsxs(
553
+ "select",
554
+ {
555
+ className: "cv-ribbon__select cv-sheet-tools__fx",
556
+ value: "",
557
+ title: t("quickFunctionTip"),
558
+ disabled: !selection,
559
+ onChange: (e) => {
560
+ if (e.target.value) insertQuickFormula(e.target.value);
561
+ },
562
+ children: [
563
+ /* @__PURE__ */ jsx("option", { value: "", children: t("autoSum") }),
564
+ QUICK_FUNCTIONS.map((fn) => /* @__PURE__ */ jsx("option", { value: fn, children: fn }, fn))
565
+ ]
566
+ }
567
+ ),
568
+ columns.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
569
+ /* @__PURE__ */ jsxs(
570
+ "select",
571
+ {
572
+ className: "cv-ribbon__select cv-sheet-tools__sort",
573
+ value: sortCol,
574
+ title: t("sortByColumn"),
575
+ onChange: (e) => setSortCol(e.target.value),
576
+ children: [
577
+ /* @__PURE__ */ jsx("option", { value: "", children: t("sortBy") }),
578
+ columns.map((c) => /* @__PURE__ */ jsx("option", { value: c.key, children: c.label ?? c.key }, c.key))
579
+ ]
580
+ }
581
+ ),
582
+ sortCol && /* @__PURE__ */ jsx("button", { type: "button", className: "cv-ribbon__btn cv-ribbon__btn--icon", title: sortDir === 1 ? t("ascending") : t("descending"), onClick: () => setSortDir((d) => d === 1 ? -1 : 1), children: sortDir === 1 ? "\u25B2" : "\u25BC" }),
583
+ /* @__PURE__ */ jsx(
584
+ "input",
585
+ {
586
+ className: "cv-ribbon__input cv-sheet-tools__filter",
587
+ value: filter,
588
+ placeholder: t("filterRows"),
589
+ onChange: (e) => setFilter(e.target.value),
590
+ title: t("filterTip")
591
+ }
592
+ )
593
+ ] })
594
+ ] }),
595
+ /* @__PURE__ */ jsxs(RibbonGroup, { label: t("groupView"), children: [
596
+ /* @__PURE__ */ jsx(
597
+ "button",
598
+ {
599
+ type: "button",
600
+ className: `cv-ribbon__btn cv-sheet-tools__freeze${frozen ? " is-on" : ""}`,
601
+ title: frozen ? t("freezeOffTip") : t("freezeOnTip"),
602
+ "aria-pressed": frozen,
603
+ onClick: toggleFreeze,
604
+ children: t("freezeHeader")
605
+ }
606
+ ),
607
+ /* @__PURE__ */ jsx("button", { type: "button", className: "cv-ribbon__btn cv-sheet-tools__clean", title: t("cleanStylingTip"), onClick: cleanStyling, children: t("cleanStyling") })
608
+ ] })
609
+ ] }),
610
+ /* @__PURE__ */ jsxs("div", { className: "cv-sheet-fxbar", children: [
611
+ /* @__PURE__ */ jsx("span", { className: "cv-sheet-fxbar__cell", children: selection ? toA1(selection.row[0], selection.column[0]) : "\u2014" }),
612
+ /* @__PURE__ */ jsx(
613
+ "input",
614
+ {
615
+ className: "cv-sheet-fxbar__input",
616
+ value: fxDraft ?? fxValue,
617
+ placeholder: t("fxPlaceholder"),
618
+ disabled: !selection,
619
+ onChange: (e) => setFxDraft(e.target.value),
620
+ onKeyDown: (e) => {
621
+ if (e.key === "Enter") {
622
+ e.preventDefault();
623
+ commitFormulaBar();
624
+ } else if (e.key === "Escape") {
625
+ setFxDraft(null);
626
+ }
627
+ },
628
+ title: "Formula bar"
629
+ }
630
+ )
631
+ ] }),
632
+ /* @__PURE__ */ jsx("div", { className: "cv-sheet", ref: rootRef, children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "cv-sheet--empty", children: t("loading") }), children: /* @__PURE__ */ jsx(Workbook, { ref: wbRef, data: initialData, onChange: handleChange, hooks: workbookHooks }, wbKey) }) })
633
+ ] });
634
+ }
635
+
636
+ export { TableRenderer };
@@ -1,4 +1,4 @@
1
- import { useCanvasStore } from './chunk-S54GJDSJ.js';
1
+ import { useCanvasStore } from './chunk-ZLXAWRUP.js';
2
2
  import { useCallback } from 'react';
3
3
 
4
4
  function useArtifactPatch(id) {