react-os-shell 0.2.68 → 0.3.1

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 (69) hide show
  1. package/README.md +35 -15
  2. package/dist/{Browser-UGZQMWKW.js → Browser-H5KDP5OH.js} +3 -3
  3. package/dist/{Browser-UGZQMWKW.js.map → Browser-H5KDP5OH.js.map} +1 -1
  4. package/dist/{Calculator-3ZXNXWDH.js → Calculator-QQ7NF53Q.js} +4 -4
  5. package/dist/{Calculator-3ZXNXWDH.js.map → Calculator-QQ7NF53Q.js.map} +1 -1
  6. package/dist/{Calendar-ON4AQ54T.js → Calendar-J6L7FGHS.js} +175 -108
  7. package/dist/Calendar-J6L7FGHS.js.map +1 -0
  8. package/dist/{CurrencyConverter-ACTLK72N.js → CurrencyConverter-YHOGBUPH.js} +4 -4
  9. package/dist/{CurrencyConverter-ACTLK72N.js.map → CurrencyConverter-YHOGBUPH.js.map} +1 -1
  10. package/dist/{Documents-STDXQ7I4.js → Documents-6DYALASM.js} +3 -3
  11. package/dist/{Documents-STDXQ7I4.js.map → Documents-6DYALASM.js.map} +1 -1
  12. package/dist/Email-U2U5Z4DL.js +475 -0
  13. package/dist/Email-U2U5Z4DL.js.map +1 -0
  14. package/dist/Files-T62M4V5I.js +11 -0
  15. package/dist/{Files-ASKLEUNU.js.map → Files-T62M4V5I.js.map} +1 -1
  16. package/dist/{Minesweeper-CZNIO75H.js → Minesweeper-S2JHXYLX.js} +3 -3
  17. package/dist/{Minesweeper-CZNIO75H.js.map → Minesweeper-S2JHXYLX.js.map} +1 -1
  18. package/dist/{Notepad-ZYYH4ZXN.js → Notepad-2YF7X3XO.js} +3 -3
  19. package/dist/{Notepad-ZYYH4ZXN.js.map → Notepad-2YF7X3XO.js.map} +1 -1
  20. package/dist/{PomodoroTimer-2MNIEAUM.js → PomodoroTimer-3J7Z3NVQ.js} +4 -4
  21. package/dist/{PomodoroTimer-2MNIEAUM.js.map → PomodoroTimer-3J7Z3NVQ.js.map} +1 -1
  22. package/dist/Preview-WM6ZP5PZ.js +8 -0
  23. package/dist/{Preview-U72UL74H.js.map → Preview-WM6ZP5PZ.js.map} +1 -1
  24. package/dist/Spreadsheet-ZIE2SXAF.js +6 -0
  25. package/dist/{Spreadsheet-T7Y7PRD6.js.map → Spreadsheet-ZIE2SXAF.js.map} +1 -1
  26. package/dist/{TodoList-7JZ2SLDI.js → TodoList-QGXCDEIE.js} +18 -204
  27. package/dist/TodoList-QGXCDEIE.js.map +1 -0
  28. package/dist/{Weather-DYCTCB6T.js → Weather-2GFPSZ5V.js} +4 -4
  29. package/dist/{Weather-DYCTCB6T.js.map → Weather-2GFPSZ5V.js.map} +1 -1
  30. package/dist/{WorldClock-XL4OMFOY.js → WorldClock-P4JR5I6X.js} +4 -4
  31. package/dist/{WorldClock-XL4OMFOY.js.map → WorldClock-P4JR5I6X.js.map} +1 -1
  32. package/dist/apps/index.d.ts +2 -5
  33. package/dist/apps/index.js +23 -26
  34. package/dist/apps/index.js.map +1 -1
  35. package/dist/chunk-57B3WALN.js +114 -0
  36. package/dist/chunk-57B3WALN.js.map +1 -0
  37. package/dist/{chunk-4SHZ7BZO.js → chunk-62FC2FHC.js} +92 -21
  38. package/dist/chunk-62FC2FHC.js.map +1 -0
  39. package/dist/{chunk-FXAOT23O.js → chunk-ATQVRDDQ.js} +3 -3
  40. package/dist/{chunk-FXAOT23O.js.map → chunk-ATQVRDDQ.js.map} +1 -1
  41. package/dist/{chunk-GP4Y3VCB.js → chunk-KMGWSDEI.js} +480 -4
  42. package/dist/chunk-KMGWSDEI.js.map +1 -0
  43. package/dist/{chunk-SU6XVJND.js → chunk-O6FJZAFM.js} +3 -3
  44. package/dist/{chunk-SU6XVJND.js.map → chunk-O6FJZAFM.js.map} +1 -1
  45. package/dist/{chunk-YL47AVBA.js → chunk-SEV7UXGN.js} +4 -4
  46. package/dist/{chunk-YL47AVBA.js.map → chunk-SEV7UXGN.js.map} +1 -1
  47. package/dist/{chunk-L2AFKNSQ.js → chunk-ZBRFMK3E.js} +4 -4
  48. package/dist/{chunk-L2AFKNSQ.js.map → chunk-ZBRFMK3E.js.map} +1 -1
  49. package/dist/index.d.ts +55 -1
  50. package/dist/index.js +241 -135
  51. package/dist/index.js.map +1 -1
  52. package/package.json +12 -6
  53. package/dist/Calendar-ON4AQ54T.js.map +0 -1
  54. package/dist/Email-5WL3TWC6.js +0 -1883
  55. package/dist/Email-5WL3TWC6.js.map +0 -1
  56. package/dist/Files-ASKLEUNU.js +0 -12
  57. package/dist/GeminiChat-XTEBZIVK.js +0 -184
  58. package/dist/GeminiChat-XTEBZIVK.js.map +0 -1
  59. package/dist/Preview-U72UL74H.js +0 -8
  60. package/dist/Spreadsheet-T7Y7PRD6.js +0 -7
  61. package/dist/TodoList-7JZ2SLDI.js.map +0 -1
  62. package/dist/chunk-4SHZ7BZO.js.map +0 -1
  63. package/dist/chunk-5VXRBUEH.js +0 -104
  64. package/dist/chunk-5VXRBUEH.js.map +0 -1
  65. package/dist/chunk-GP4Y3VCB.js.map +0 -1
  66. package/dist/chunk-MUXDKEOC.js +0 -485
  67. package/dist/chunk-MUXDKEOC.js.map +0 -1
  68. package/dist/chunk-MVWEL34Y.js +0 -209
  69. package/dist/chunk-MVWEL34Y.js.map +0 -1
@@ -1,485 +0,0 @@
1
- import { EditableGrid } from './chunk-GP4Y3VCB.js';
2
- import { WindowTitle } from './chunk-4SHZ7BZO.js';
3
- import { useRef, useState, useEffect, useCallback } from 'react';
4
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
-
6
- var TITLE_DISPLAY_MAX = 24;
7
- function truncateForTitle(s) {
8
- return s.length > TITLE_DISPLAY_MAX ? `${s.slice(0, TITLE_DISPLAY_MAX - 1)}\u2026` : s;
9
- }
10
- var ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
11
- var DEFAULT_COLS = 10;
12
- var DEFAULT_ROWS = 30;
13
- function colLabel(i) {
14
- if (i < 26) return ALPHA[i];
15
- return ALPHA[Math.floor(i / 26) - 1] + ALPHA[i % 26];
16
- }
17
- function makeColumns(count) {
18
- return Array.from({ length: count }, (_, i) => ({
19
- key: `col_${i}`,
20
- title: colLabel(i),
21
- width: 100
22
- }));
23
- }
24
- function makeEmptyData(rows, cols) {
25
- return Array.from({ length: rows }, () => Array(cols).fill(""));
26
- }
27
- function newSheet(name) {
28
- return {
29
- id: crypto.randomUUID(),
30
- name,
31
- columns: makeColumns(DEFAULT_COLS),
32
- data: makeEmptyData(DEFAULT_ROWS, DEFAULT_COLS),
33
- cellStyles: {}
34
- };
35
- }
36
- function parseCSV(text) {
37
- return text.split("\n").map((line) => {
38
- if (line.includes(" ")) return line.split(" ").map((s) => s.trim());
39
- const parts = [];
40
- let current = "";
41
- let inQuotes = false;
42
- for (const ch of line) {
43
- if (ch === '"') {
44
- inQuotes = !inQuotes;
45
- continue;
46
- }
47
- if (ch === "," && !inQuotes) {
48
- parts.push(current.trim());
49
- current = "";
50
- continue;
51
- }
52
- current += ch;
53
- }
54
- parts.push(current.trim());
55
- return parts;
56
- }).filter((r) => r.some((c) => c.trim()));
57
- }
58
- function sheetFromCSV(text, sheetName) {
59
- const parsed = parseCSV(text);
60
- if (parsed.length === 0) return null;
61
- const maxCols = Math.max(DEFAULT_COLS, parsed.reduce((m, r) => Math.max(m, r.length), 0));
62
- const padded = parsed.map((r) => {
63
- while (r.length < maxCols) r.push("");
64
- return r;
65
- });
66
- while (padded.length < DEFAULT_ROWS) padded.push(Array(maxCols).fill(""));
67
- return { id: crypto.randomUUID(), name: sheetName, columns: makeColumns(maxCols), data: padded, cellStyles: {} };
68
- }
69
- var SPREADSHEET_EVENT_NAME = "react-os-shell:spreadsheet-preview";
70
- var pendingSpreadsheet = null;
71
- function setSpreadsheetPreview(data) {
72
- pendingSpreadsheet = data;
73
- if (typeof window !== "undefined") {
74
- window.dispatchEvent(new CustomEvent(SPREADSHEET_EVENT_NAME, { detail: data }));
75
- }
76
- }
77
- function Spreadsheet() {
78
- const containerRef = useRef(null);
79
- const initialPreview = (() => {
80
- const p = pendingSpreadsheet;
81
- pendingSpreadsheet = null;
82
- if (!p) return null;
83
- const sheet = sheetFromCSV(p.csv, "Sheet 1");
84
- if (!sheet) return null;
85
- const titleName = p.filename.replace(/\.(csv|tsv|txt)$/i, "");
86
- return { sheet, title: titleName };
87
- })();
88
- const [sheets, setSheets] = useState(initialPreview ? [initialPreview.sheet] : [newSheet("Sheet 1")]);
89
- const [activeIdx, setActiveIdx] = useState(0);
90
- const undoStackRef = useRef([]);
91
- const lastCommittedRef = useRef(sheets);
92
- const skipRecordRef = useRef(false);
93
- useEffect(() => {
94
- if (skipRecordRef.current) {
95
- skipRecordRef.current = false;
96
- } else if (lastCommittedRef.current !== sheets) {
97
- undoStackRef.current.push(lastCommittedRef.current);
98
- if (undoStackRef.current.length > 50) undoStackRef.current.shift();
99
- }
100
- lastCommittedRef.current = sheets;
101
- }, [sheets]);
102
- const undo = useCallback(() => {
103
- const prev = undoStackRef.current.pop();
104
- if (!prev) return;
105
- skipRecordRef.current = true;
106
- setSheets(prev);
107
- }, []);
108
- useEffect(() => {
109
- const handler = (e) => {
110
- if (!containerRef.current?.contains(document.activeElement) && !containerRef.current?.matches(":focus-within")) return;
111
- const isUndo = (e.ctrlKey || e.metaKey) && !e.shiftKey && (e.key === "z" || e.key === "Z");
112
- if (isUndo) {
113
- e.preventDefault();
114
- undo();
115
- }
116
- };
117
- window.addEventListener("keydown", handler);
118
- return () => window.removeEventListener("keydown", handler);
119
- }, [undo]);
120
- const [title, setTitle] = useState(initialPreview?.title ?? "Untitled");
121
- useEffect(() => {
122
- const handler = (e) => {
123
- const next = e.detail;
124
- const sheet = sheetFromCSV(next.csv, "Sheet 1");
125
- if (!sheet) return;
126
- setSheets([sheet]);
127
- setActiveIdx(0);
128
- setTitle(next.filename.replace(/\.(csv|tsv|txt)$/i, ""));
129
- };
130
- window.addEventListener(SPREADSHEET_EVENT_NAME, handler);
131
- return () => window.removeEventListener(SPREADSHEET_EVENT_NAME, handler);
132
- }, []);
133
- const [editingTitle, setEditingTitle] = useState(false);
134
- const [editingTab, setEditingTab] = useState(null);
135
- const [tabName, setTabName] = useState("");
136
- const fileRef = useRef(null);
137
- const [addColCount, setAddColCount] = useState("1");
138
- const [addRowCount, setAddRowCount] = useState("10");
139
- const active = sheets[activeIdx] || sheets[0];
140
- const data = active.data;
141
- const columns = active.columns;
142
- const cellStyles = active.cellStyles ?? {};
143
- const [focusedCell, setFocusedCell] = useState(null);
144
- const [selection, setSelection] = useState(null);
145
- const targetCells = useCallback(() => {
146
- if (selection) {
147
- const r1 = Math.min(selection.anchor.row, selection.end.row);
148
- const r2 = Math.max(selection.anchor.row, selection.end.row);
149
- const c1 = Math.min(selection.anchor.col, selection.end.col);
150
- const c2 = Math.max(selection.anchor.col, selection.end.col);
151
- const cells = [];
152
- for (let r = r1; r <= r2; r++) for (let c = c1; c <= c2; c++) cells.push({ row: r, col: c });
153
- return cells;
154
- }
155
- return focusedCell ? [focusedCell] : [];
156
- }, [selection, focusedCell]);
157
- const toggleCellStyle = useCallback((key, value) => {
158
- const cells = targetCells();
159
- if (cells.length === 0) return;
160
- setSheets((prev) => prev.map((s, i) => {
161
- if (i !== activeIdx) return s;
162
- const styles = { ...s.cellStyles ?? {} };
163
- const firstKey = `${cells[0].row}:${cells[0].col}`;
164
- const desired = value !== void 0 ? value : !(styles[firstKey] ?? {})[key];
165
- for (const { row, col } of cells) {
166
- const k = `${row}:${col}`;
167
- styles[k] = { ...styles[k] ?? {}, [key]: desired };
168
- }
169
- return { ...s, cellStyles: styles };
170
- }));
171
- }, [targetCells, activeIdx]);
172
- const headCell = selection ? { row: selection.anchor.row, col: selection.anchor.col } : focusedCell;
173
- const focusedStyle = headCell ? cellStyles[`${headCell.row}:${headCell.col}`] ?? {} : {};
174
- const hasTarget = !!headCell;
175
- const updateActiveSheet = useCallback((update) => {
176
- setSheets((prev) => prev.map((s, i) => i === activeIdx ? { ...s, ...update } : s));
177
- }, [activeIdx]);
178
- const handleChange = useCallback((newData) => {
179
- const maxCols = newData.reduce((m, r) => Math.max(m, r.length), 0);
180
- if (maxCols !== columns.length) {
181
- updateActiveSheet({ data: newData, columns: makeColumns(maxCols) });
182
- } else {
183
- updateActiveSheet({ data: newData });
184
- }
185
- }, [updateActiveSheet, columns.length]);
186
- const addSheet = () => {
187
- const name = `Sheet ${sheets.length + 1}`;
188
- setSheets((prev) => [...prev, newSheet(name)]);
189
- setActiveIdx(sheets.length);
190
- };
191
- const removeSheet = (idx) => {
192
- if (sheets.length <= 1) return;
193
- setSheets((prev) => prev.filter((_, i) => i !== idx));
194
- if (activeIdx >= idx && activeIdx > 0) setActiveIdx(activeIdx - 1);
195
- };
196
- const renameSheet = (idx, name) => {
197
- setSheets((prev) => prev.map((s, i) => i === idx ? { ...s, name } : s));
198
- setEditingTab(null);
199
- };
200
- const addColumns = (count) => {
201
- const newColCount = columns.length + count;
202
- updateActiveSheet({
203
- columns: makeColumns(newColCount),
204
- data: data.map((row) => [...row, ...Array(count).fill("")])
205
- });
206
- };
207
- const addRows = (count) => {
208
- updateActiveSheet({
209
- data: [...data, ...Array.from({ length: count }, () => Array(columns.length).fill(""))]
210
- });
211
- };
212
- const handleClear = () => {
213
- updateActiveSheet({
214
- columns: makeColumns(DEFAULT_COLS),
215
- data: makeEmptyData(DEFAULT_ROWS, DEFAULT_COLS)
216
- });
217
- };
218
- const exportCSV = () => {
219
- const csv = data.filter((row) => row.some((c) => c.trim())).map((row) => row.map((cell) => {
220
- if (cell.includes(",") || cell.includes('"') || cell.includes("\n"))
221
- return `"${cell.replace(/"/g, '""')}"`;
222
- return cell;
223
- }).join(",")).join("\n");
224
- const blob = new Blob([csv], { type: "text/csv" });
225
- const url = URL.createObjectURL(blob);
226
- const a = document.createElement("a");
227
- a.href = url;
228
- a.download = `${title || "spreadsheet"}.csv`;
229
- a.click();
230
- URL.revokeObjectURL(url);
231
- };
232
- const importFile = async (e) => {
233
- const file = e.target.files?.[0];
234
- if (!file) return;
235
- const name = file.name.replace(/\.(csv|tsv|txt|xlsx|xls|ods)$/i, "");
236
- if (/\.(xlsx|xls|ods)$/i.test(file.name)) {
237
- const byteArr = new Uint8Array(await file.arrayBuffer());
238
- const XLSX = await import('xlsx');
239
- const wb = XLSX.read(byteArr, { type: "array" });
240
- const newSheets = wb.SheetNames.map((sn) => {
241
- const rows = XLSX.utils.sheet_to_json(wb.Sheets[sn], { header: 1, defval: "" });
242
- const maxCols = Math.max(DEFAULT_COLS, rows.reduce((m, r) => Math.max(m, r.length), 0));
243
- const padded = rows.map((r) => {
244
- const nr = r.map((c) => String(c ?? ""));
245
- while (nr.length < maxCols) nr.push("");
246
- return nr;
247
- });
248
- while (padded.length < DEFAULT_ROWS) padded.push(Array(maxCols).fill(""));
249
- return { id: crypto.randomUUID(), name: sn, columns: makeColumns(maxCols), data: padded };
250
- });
251
- setSheets(newSheets);
252
- setActiveIdx(0);
253
- setTitle(name);
254
- } else {
255
- const text = await file.text();
256
- const parsed = parseCSV(text);
257
- if (parsed.length === 0) return;
258
- const maxCols = Math.max(DEFAULT_COLS, parsed.reduce((m, r) => Math.max(m, r.length), 0));
259
- const padded = parsed.map((r) => {
260
- while (r.length < maxCols) r.push("");
261
- return r;
262
- });
263
- while (padded.length < DEFAULT_ROWS) padded.push(Array(maxCols).fill(""));
264
- updateActiveSheet({ columns: makeColumns(maxCols), data: padded });
265
- setTitle(name);
266
- }
267
- if (fileRef.current) fileRef.current.value = "";
268
- };
269
- const allNums = [];
270
- data.forEach((row) => row.forEach((cell) => {
271
- const v = parseFloat(cell);
272
- if (!isNaN(v) && cell.trim()) allNums.push(v);
273
- }));
274
- const filledCount = data.reduce((c, row) => c + row.filter((cell) => cell.trim()).length, 0);
275
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "flex flex-col h-full", children: [
276
- /* @__PURE__ */ jsx(WindowTitle, { title: `${truncateForTitle(title || "Untitled")} - Spreadsheets` }),
277
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0", children: [
278
- editingTitle ? /* @__PURE__ */ jsx(
279
- "input",
280
- {
281
- type: "text",
282
- value: title,
283
- onChange: (e) => setTitle(e.target.value),
284
- onBlur: () => setEditingTitle(false),
285
- onKeyDown: (e) => {
286
- if (e.key === "Enter") setEditingTitle(false);
287
- },
288
- autoFocus: true,
289
- className: "text-sm font-medium text-gray-900 border border-gray-300 rounded px-2 py-0.5 w-40 focus:border-blue-500 focus:ring-blue-500"
290
- }
291
- ) : /* @__PURE__ */ jsx("button", { onClick: () => setEditingTitle(true), className: "text-sm font-medium text-gray-900 hover:text-blue-600 truncate max-w-[200px]", title: "Click to rename", children: title || "Untitled" }),
292
- /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-gray-300" }),
293
- /* @__PURE__ */ jsx("input", { ref: fileRef, type: "file", accept: ".csv,.tsv,.txt,.xlsx,.xls,.ods", onChange: importFile, className: "hidden" }),
294
- /* @__PURE__ */ jsx(
295
- "button",
296
- {
297
- onClick: () => fileRef.current?.click(),
298
- className: "text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",
299
- children: "Open"
300
- }
301
- ),
302
- /* @__PURE__ */ jsx(
303
- "button",
304
- {
305
- onClick: exportCSV,
306
- className: "text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",
307
- children: "Save CSV"
308
- }
309
- ),
310
- /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-gray-300" }),
311
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
312
- /* @__PURE__ */ jsx(
313
- "input",
314
- {
315
- type: "number",
316
- min: "1",
317
- max: "50",
318
- value: addColCount,
319
- onChange: (e) => setAddColCount(e.target.value),
320
- className: "w-10 text-xs text-center border border-gray-300 rounded px-1 py-0.5 focus:border-blue-500 focus:ring-blue-500"
321
- }
322
- ),
323
- /* @__PURE__ */ jsx(
324
- "button",
325
- {
326
- onClick: () => addColumns(parseInt(addColCount) || 1),
327
- className: "text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",
328
- children: "+ Col"
329
- }
330
- )
331
- ] }),
332
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
333
- /* @__PURE__ */ jsx(
334
- "input",
335
- {
336
- type: "number",
337
- min: "1",
338
- max: "500",
339
- value: addRowCount,
340
- onChange: (e) => setAddRowCount(e.target.value),
341
- className: "w-10 text-xs text-center border border-gray-300 rounded px-1 py-0.5 focus:border-blue-500 focus:ring-blue-500"
342
- }
343
- ),
344
- /* @__PURE__ */ jsx(
345
- "button",
346
- {
347
- onClick: () => addRows(parseInt(addRowCount) || 10),
348
- className: "text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",
349
- children: "+ Row"
350
- }
351
- )
352
- ] }),
353
- /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-gray-300" }),
354
- /* @__PURE__ */ jsx(
355
- "button",
356
- {
357
- onClick: handleClear,
358
- className: "text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",
359
- children: "Clear"
360
- }
361
- ),
362
- /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-gray-300" }),
363
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", title: hasTarget ? "" : "Click a cell first", children: [
364
- /* @__PURE__ */ jsx(
365
- "button",
366
- {
367
- onClick: () => toggleCellStyle("bold"),
368
- disabled: !hasTarget,
369
- className: `px-2 py-1 text-xs rounded transition-colors font-bold ${focusedStyle.bold ? "bg-blue-100 text-blue-700" : "text-gray-600 hover:bg-gray-200"} disabled:opacity-40 disabled:cursor-not-allowed`,
370
- children: "B"
371
- }
372
- ),
373
- /* @__PURE__ */ jsx(
374
- "button",
375
- {
376
- onClick: () => toggleCellStyle("italic"),
377
- disabled: !hasTarget,
378
- className: `px-2 py-1 text-xs rounded transition-colors italic ${focusedStyle.italic ? "bg-blue-100 text-blue-700" : "text-gray-600 hover:bg-gray-200"} disabled:opacity-40 disabled:cursor-not-allowed`,
379
- children: "I"
380
- }
381
- ),
382
- /* @__PURE__ */ jsx(
383
- "button",
384
- {
385
- onClick: () => toggleCellStyle("underline"),
386
- disabled: !hasTarget,
387
- className: `px-2 py-1 text-xs rounded transition-colors underline ${focusedStyle.underline ? "bg-blue-100 text-blue-700" : "text-gray-600 hover:bg-gray-200"} disabled:opacity-40 disabled:cursor-not-allowed`,
388
- children: "U"
389
- }
390
- ),
391
- /* @__PURE__ */ jsxs(
392
- "select",
393
- {
394
- value: focusedStyle.fontSize ?? "base",
395
- onChange: (e) => toggleCellStyle("fontSize", e.target.value),
396
- disabled: !hasTarget,
397
- className: "ml-1 text-xs border border-gray-300 rounded px-1 py-0.5 bg-white disabled:opacity-40 disabled:cursor-not-allowed",
398
- children: [
399
- /* @__PURE__ */ jsx("option", { value: "sm", children: "XS" }),
400
- /* @__PURE__ */ jsx("option", { value: "base", children: "S" }),
401
- /* @__PURE__ */ jsx("option", { value: "lg", children: "M" }),
402
- /* @__PURE__ */ jsx("option", { value: "xl", children: "L" })
403
- ]
404
- }
405
- )
406
- ] })
407
- ] }),
408
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsx(
409
- EditableGrid,
410
- {
411
- columns,
412
- data,
413
- onChange: handleChange,
414
- onColumnsChange: (newCols) => updateActiveSheet({ columns: newCols }),
415
- cellStyles,
416
- onFocusChange: setFocusedCell,
417
- onSelectionChange: setSelection,
418
- minRows: DEFAULT_ROWS,
419
- maxHeight: "100%"
420
- }
421
- ) }),
422
- /* @__PURE__ */ jsxs("div", { className: "flex items-center border-t border-gray-200 bg-gray-50 shrink-0", children: [
423
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 px-1 py-1 overflow-x-auto flex-1 min-w-0", children: [
424
- sheets.map((sheet, idx) => /* @__PURE__ */ jsx(
425
- "button",
426
- {
427
- onClick: () => setActiveIdx(idx),
428
- onDoubleClick: () => {
429
- setEditingTab(idx);
430
- setTabName(sheet.name);
431
- },
432
- onContextMenu: (e) => {
433
- e.preventDefault();
434
- if (sheets.length > 1) removeSheet(idx);
435
- },
436
- className: `px-3 py-1 text-xs font-medium rounded-b whitespace-nowrap transition-colors ${idx === activeIdx ? "bg-white text-blue-700 border border-t-0 border-gray-300 -mt-px relative z-10" : "text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`,
437
- children: editingTab === idx ? /* @__PURE__ */ jsx(
438
- "input",
439
- {
440
- type: "text",
441
- value: tabName,
442
- onChange: (e) => setTabName(e.target.value),
443
- onBlur: () => renameSheet(idx, tabName || sheet.name),
444
- onKeyDown: (e) => {
445
- if (e.key === "Enter") renameSheet(idx, tabName || sheet.name);
446
- if (e.key === "Escape") setEditingTab(null);
447
- },
448
- onClick: (e) => e.stopPropagation(),
449
- autoFocus: true,
450
- className: "w-20 text-xs border border-blue-400 rounded px-1 py-0 focus:ring-0 focus:outline-none"
451
- }
452
- ) : sheet.name
453
- },
454
- sheet.id
455
- )),
456
- /* @__PURE__ */ jsx("button", { onClick: addSheet, className: "px-2 py-1 text-xs text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded", title: "Add sheet", children: "+" })
457
- ] }),
458
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 px-3 py-1 text-xs text-gray-500 shrink-0 border-l border-gray-200", children: [
459
- /* @__PURE__ */ jsxs("span", { children: [
460
- filledCount,
461
- " cells"
462
- ] }),
463
- allNums.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
464
- /* @__PURE__ */ jsxs("span", { children: [
465
- "Sum: ",
466
- allNums.reduce((s, v) => s + v, 0).toLocaleString(void 0, { maximumFractionDigits: 2 })
467
- ] }),
468
- /* @__PURE__ */ jsxs("span", { children: [
469
- "Avg: ",
470
- (allNums.reduce((s, v) => s + v, 0) / allNums.length).toLocaleString(void 0, { maximumFractionDigits: 2 })
471
- ] })
472
- ] }),
473
- /* @__PURE__ */ jsxs("span", { children: [
474
- data.length,
475
- " \xD7 ",
476
- columns.length
477
- ] })
478
- ] })
479
- ] })
480
- ] });
481
- }
482
-
483
- export { Spreadsheet, setSpreadsheetPreview };
484
- //# sourceMappingURL=chunk-MUXDKEOC.js.map
485
- //# sourceMappingURL=chunk-MUXDKEOC.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/apps/Spreadsheet.tsx"],"names":[],"mappings":"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","file":"chunk-MUXDKEOC.js","sourcesContent":["import { useState, useCallback, useEffect, useRef } from 'react';\nimport EditableGrid from '../shell/EditableGrid';\nimport type { GridColumn, CellStyle } from '../shell/EditableGrid';\nimport { WindowTitle } from '../shell/Modal';\n\nconst TITLE_DISPLAY_MAX = 24;\nfunction truncateForTitle(s: string) {\n return s.length > TITLE_DISPLAY_MAX ? `${s.slice(0, TITLE_DISPLAY_MAX - 1)}…` : s;\n}\n\nconst ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\nconst DEFAULT_COLS = 10;\nconst DEFAULT_ROWS = 30;\n\nfunction colLabel(i: number): string {\n if (i < 26) return ALPHA[i];\n return ALPHA[Math.floor(i / 26) - 1] + ALPHA[i % 26];\n}\n\nfunction makeColumns(count: number): GridColumn[] {\n return Array.from({ length: count }, (_, i) => ({\n key: `col_${i}`,\n title: colLabel(i),\n width: 100,\n }));\n}\n\nfunction makeEmptyData(rows: number, cols: number): string[][] {\n return Array.from({ length: rows }, () => Array(cols).fill(''));\n}\n\ninterface Sheet {\n id: string;\n name: string;\n columns: GridColumn[];\n data: string[][];\n /** Per-cell text styling. Key: `${row}:${col}`. */\n cellStyles?: Record<string, CellStyle>;\n}\n\nfunction newSheet(name: string): Sheet {\n return {\n id: crypto.randomUUID(),\n name,\n columns: makeColumns(DEFAULT_COLS),\n data: makeEmptyData(DEFAULT_ROWS, DEFAULT_COLS),\n cellStyles: {},\n };\n}\n\nfunction parseCSV(text: string): string[][] {\n return text.split('\\n').map(line => {\n if (line.includes('\\t')) return line.split('\\t').map(s => s.trim());\n const parts: string[] = [];\n let current = '';\n let inQuotes = false;\n for (const ch of line) {\n if (ch === '\"') { inQuotes = !inQuotes; continue; }\n if (ch === ',' && !inQuotes) { parts.push(current.trim()); current = ''; continue; }\n current += ch;\n }\n parts.push(current.trim());\n return parts;\n }).filter(r => r.some(c => c.trim()));\n}\n\n/** Build a single padded sheet from CSV/TSV text. Shared by file import and\n * the `setSpreadsheetPreview` external-load path. */\nfunction sheetFromCSV(text: string, sheetName: string): Sheet | null {\n const parsed = parseCSV(text);\n if (parsed.length === 0) return null;\n const maxCols = Math.max(DEFAULT_COLS, parsed.reduce((m, r) => Math.max(m, r.length), 0));\n const padded = parsed.map(r => { while (r.length < maxCols) r.push(''); return r; });\n while (padded.length < DEFAULT_ROWS) padded.push(Array(maxCols).fill(''));\n return { id: crypto.randomUUID(), name: sheetName, columns: makeColumns(maxCols), data: padded, cellStyles: {} };\n}\n\nexport interface SpreadsheetPreviewData {\n /** CSV text (comma- or tab-separated). */\n csv: string;\n /** Display name; the title strips a trailing `.csv`/`.tsv`/`.txt`. */\n filename: string;\n}\n\nconst SPREADSHEET_EVENT_NAME = 'react-os-shell:spreadsheet-preview';\nlet pendingSpreadsheet: SpreadsheetPreviewData | null = null;\n\n/** Stage CSV content for the next Spreadsheet window mount, or swap into an open one. */\nexport function setSpreadsheetPreview(data: SpreadsheetPreviewData) {\n pendingSpreadsheet = data;\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent(SPREADSHEET_EVENT_NAME, { detail: data }));\n }\n}\n\nexport default function Spreadsheet() {\n const containerRef = useRef<HTMLDivElement>(null);\n // Drain any CSV staged via setSpreadsheetPreview before the window mounted.\n const initialPreview = (() => {\n const p = pendingSpreadsheet;\n pendingSpreadsheet = null;\n if (!p) return null;\n const sheet = sheetFromCSV(p.csv, 'Sheet 1');\n if (!sheet) return null;\n const titleName = p.filename.replace(/\\.(csv|tsv|txt)$/i, '');\n return { sheet, title: titleName };\n })();\n const [sheets, setSheets] = useState<Sheet[]>(initialPreview ? [initialPreview.sheet] : [newSheet('Sheet 1')]);\n const [activeIdx, setActiveIdx] = useState(0);\n\n // Undo history — push a snapshot whenever sheets change, except when the\n // change came from undo itself.\n const undoStackRef = useRef<Sheet[][]>([]);\n const lastCommittedRef = useRef<Sheet[]>(sheets);\n const skipRecordRef = useRef(false);\n useEffect(() => {\n if (skipRecordRef.current) {\n skipRecordRef.current = false;\n } else if (lastCommittedRef.current !== sheets) {\n undoStackRef.current.push(lastCommittedRef.current);\n if (undoStackRef.current.length > 50) undoStackRef.current.shift();\n }\n lastCommittedRef.current = sheets;\n }, [sheets]);\n const undo = useCallback(() => {\n const prev = undoStackRef.current.pop();\n if (!prev) return;\n skipRecordRef.current = true;\n setSheets(prev);\n }, []);\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if (!containerRef.current?.contains(document.activeElement) && !containerRef.current?.matches(':focus-within')) return;\n const isUndo = (e.ctrlKey || e.metaKey) && !e.shiftKey && (e.key === 'z' || e.key === 'Z');\n if (isUndo) {\n e.preventDefault();\n undo();\n }\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [undo]);\n const [title, setTitle] = useState(initialPreview?.title ?? 'Untitled');\n\n // Swap to new CSV if `setSpreadsheetPreview` is called while the window is open.\n useEffect(() => {\n const handler = (e: Event) => {\n const next = (e as CustomEvent<SpreadsheetPreviewData>).detail;\n const sheet = sheetFromCSV(next.csv, 'Sheet 1');\n if (!sheet) return;\n setSheets([sheet]);\n setActiveIdx(0);\n setTitle(next.filename.replace(/\\.(csv|tsv|txt)$/i, ''));\n };\n window.addEventListener(SPREADSHEET_EVENT_NAME, handler);\n return () => window.removeEventListener(SPREADSHEET_EVENT_NAME, handler);\n }, []);\n const [editingTitle, setEditingTitle] = useState(false);\n const [editingTab, setEditingTab] = useState<number | null>(null);\n const [tabName, setTabName] = useState('');\n const fileRef = useRef<HTMLInputElement>(null);\n const [addColCount, setAddColCount] = useState('1');\n const [addRowCount, setAddRowCount] = useState('10');\n\n const active = sheets[activeIdx] || sheets[0];\n const data = active.data;\n const columns = active.columns;\n const cellStyles = active.cellStyles ?? {};\n const [focusedCell, setFocusedCell] = useState<{ row: number; col: number } | null>(null);\n const [selection, setSelection] = useState<{ anchor: { row: number; col: number }; end: { row: number; col: number } } | null>(null);\n\n // Cells affected by toolbar actions: full selection rectangle if non-empty,\n // otherwise the single focused cell.\n const targetCells = useCallback((): { row: number; col: number }[] => {\n if (selection) {\n const r1 = Math.min(selection.anchor.row, selection.end.row);\n const r2 = Math.max(selection.anchor.row, selection.end.row);\n const c1 = Math.min(selection.anchor.col, selection.end.col);\n const c2 = Math.max(selection.anchor.col, selection.end.col);\n const cells: { row: number; col: number }[] = [];\n for (let r = r1; r <= r2; r++) for (let c = c1; c <= c2; c++) cells.push({ row: r, col: c });\n return cells;\n }\n return focusedCell ? [focusedCell] : [];\n }, [selection, focusedCell]);\n\n const toggleCellStyle = useCallback((key: keyof CellStyle, value?: any) => {\n const cells = targetCells();\n if (cells.length === 0) return;\n setSheets(prev => prev.map((s, i) => {\n if (i !== activeIdx) return s;\n const styles = { ...(s.cellStyles ?? {}) };\n // For toggles, derive the new state from the FIRST cell so the whole\n // selection ends up consistent (rather than each cell flipping\n // independently).\n const firstKey = `${cells[0].row}:${cells[0].col}`;\n const desired = value !== undefined ? value : !((styles[firstKey] ?? {})[key] as any);\n for (const { row, col } of cells) {\n const k = `${row}:${col}`;\n styles[k] = { ...(styles[k] ?? {}), [key]: desired };\n }\n return { ...s, cellStyles: styles };\n }));\n }, [targetCells, activeIdx]);\n\n const headCell = selection ? { row: selection.anchor.row, col: selection.anchor.col } : focusedCell;\n const focusedStyle: CellStyle = headCell ? (cellStyles[`${headCell.row}:${headCell.col}`] ?? {}) : {};\n const hasTarget = !!headCell;\n\n const updateActiveSheet = useCallback((update: Partial<Sheet>) => {\n setSheets(prev => prev.map((s, i) => i === activeIdx ? { ...s, ...update } : s));\n }, [activeIdx]);\n\n const handleChange = useCallback((newData: string[][]) => {\n // Sync column count if rows were inserted/deleted with different column counts\n const maxCols = newData.reduce((m, r) => Math.max(m, r.length), 0);\n if (maxCols !== columns.length) {\n updateActiveSheet({ data: newData, columns: makeColumns(maxCols) });\n } else {\n updateActiveSheet({ data: newData });\n }\n }, [updateActiveSheet, columns.length]);\n\n // Tab management\n const addSheet = () => {\n const name = `Sheet ${sheets.length + 1}`;\n setSheets(prev => [...prev, newSheet(name)]);\n setActiveIdx(sheets.length);\n };\n\n const removeSheet = (idx: number) => {\n if (sheets.length <= 1) return;\n setSheets(prev => prev.filter((_, i) => i !== idx));\n if (activeIdx >= idx && activeIdx > 0) setActiveIdx(activeIdx - 1);\n };\n\n const renameSheet = (idx: number, name: string) => {\n setSheets(prev => prev.map((s, i) => i === idx ? { ...s, name } : s));\n setEditingTab(null);\n };\n\n // Add columns to active sheet\n const addColumns = (count: number) => {\n const newColCount = columns.length + count;\n updateActiveSheet({\n columns: makeColumns(newColCount),\n data: data.map(row => [...row, ...Array(count).fill('')]),\n });\n };\n\n // Add rows to active sheet\n const addRows = (count: number) => {\n updateActiveSheet({\n data: [...data, ...Array.from({ length: count }, () => Array(columns.length).fill(''))],\n });\n };\n\n // Clear active sheet\n const handleClear = () => {\n updateActiveSheet({\n columns: makeColumns(DEFAULT_COLS),\n data: makeEmptyData(DEFAULT_ROWS, DEFAULT_COLS),\n });\n };\n\n // Export as CSV\n const exportCSV = () => {\n const csv = data\n .filter(row => row.some(c => c.trim()))\n .map(row => row.map(cell => {\n if (cell.includes(',') || cell.includes('\"') || cell.includes('\\n'))\n return `\"${cell.replace(/\"/g, '\"\"')}\"`;\n return cell;\n }).join(','))\n .join('\\n');\n const blob = new Blob([csv], { type: 'text/csv' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `${title || 'spreadsheet'}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n };\n\n // Import CSV / XLSX\n const importFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n const name = file.name.replace(/\\.(csv|tsv|txt|xlsx|xls|ods)$/i, '');\n\n if (/\\.(xlsx|xls|ods)$/i.test(file.name)) {\n // XLSX — dynamic import\n const byteArr = new Uint8Array(await file.arrayBuffer());\n const XLSX = await import('xlsx');\n const wb = XLSX.read(byteArr, { type: 'array' });\n const newSheets: Sheet[] = wb.SheetNames.map(sn => {\n const rows: string[][] = XLSX.utils.sheet_to_json(wb.Sheets[sn], { header: 1, defval: '' });\n const maxCols = Math.max(DEFAULT_COLS, rows.reduce((m, r) => Math.max(m, r.length), 0));\n const padded = rows.map(r => { const nr = r.map(c => String(c ?? '')); while (nr.length < maxCols) nr.push(''); return nr; });\n while (padded.length < DEFAULT_ROWS) padded.push(Array(maxCols).fill(''));\n return { id: crypto.randomUUID(), name: sn, columns: makeColumns(maxCols), data: padded };\n });\n setSheets(newSheets);\n setActiveIdx(0);\n setTitle(name);\n } else {\n // CSV/TSV\n const text = await file.text();\n const parsed = parseCSV(text);\n if (parsed.length === 0) return;\n const maxCols = Math.max(DEFAULT_COLS, parsed.reduce((m, r) => Math.max(m, r.length), 0));\n const padded = parsed.map(r => { while (r.length < maxCols) r.push(''); return r; });\n while (padded.length < DEFAULT_ROWS) padded.push(Array(maxCols).fill(''));\n updateActiveSheet({ columns: makeColumns(maxCols), data: padded });\n setTitle(name);\n }\n\n if (fileRef.current) fileRef.current.value = '';\n };\n\n // Stats\n const allNums: number[] = [];\n data.forEach(row => row.forEach(cell => {\n const v = parseFloat(cell);\n if (!isNaN(v) && cell.trim()) allNums.push(v);\n }));\n const filledCount = data.reduce((c, row) => c + row.filter(cell => cell.trim()).length, 0);\n\n return (\n <div ref={containerRef} className=\"flex flex-col h-full\">\n <WindowTitle title={`${truncateForTitle(title || 'Untitled')} - Spreadsheets`} />\n {/* Toolbar */}\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0\">\n {editingTitle ? (\n <input type=\"text\" value={title} onChange={e => setTitle(e.target.value)}\n onBlur={() => setEditingTitle(false)}\n onKeyDown={e => { if (e.key === 'Enter') setEditingTitle(false); }}\n autoFocus\n className=\"text-sm font-medium text-gray-900 border border-gray-300 rounded px-2 py-0.5 w-40 focus:border-blue-500 focus:ring-blue-500\" />\n ) : (\n <button onClick={() => setEditingTitle(true)} className=\"text-sm font-medium text-gray-900 hover:text-blue-600 truncate max-w-[200px]\" title=\"Click to rename\">\n {title || 'Untitled'}\n </button>\n )}\n\n <div className=\"h-4 w-px bg-gray-300\" />\n\n <input ref={fileRef} type=\"file\" accept=\".csv,.tsv,.txt,.xlsx,.xls,.ods\" onChange={importFile} className=\"hidden\" />\n <button onClick={() => fileRef.current?.click()}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n Open\n </button>\n <button onClick={exportCSV}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n Save CSV\n </button>\n\n <div className=\"h-4 w-px bg-gray-300\" />\n\n <div className=\"flex items-center gap-1\">\n <input type=\"number\" min=\"1\" max=\"50\" value={addColCount} onChange={e => setAddColCount(e.target.value)}\n className=\"w-10 text-xs text-center border border-gray-300 rounded px-1 py-0.5 focus:border-blue-500 focus:ring-blue-500\" />\n <button onClick={() => addColumns(parseInt(addColCount) || 1)}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n + Col\n </button>\n </div>\n <div className=\"flex items-center gap-1\">\n <input type=\"number\" min=\"1\" max=\"500\" value={addRowCount} onChange={e => setAddRowCount(e.target.value)}\n className=\"w-10 text-xs text-center border border-gray-300 rounded px-1 py-0.5 focus:border-blue-500 focus:ring-blue-500\" />\n <button onClick={() => addRows(parseInt(addRowCount) || 10)}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n + Row\n </button>\n </div>\n\n <div className=\"h-4 w-px bg-gray-300\" />\n\n <button onClick={handleClear}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n Clear\n </button>\n\n <div className=\"h-4 w-px bg-gray-300\" />\n\n {/* Font style panel — applies to the focused cell. */}\n <div className=\"flex items-center gap-0.5\" title={hasTarget ? '' : 'Click a cell first'}>\n <button onClick={() => toggleCellStyle('bold')} disabled={!hasTarget}\n className={`px-2 py-1 text-xs rounded transition-colors font-bold ${focusedStyle.bold ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-200'} disabled:opacity-40 disabled:cursor-not-allowed`}>\n B\n </button>\n <button onClick={() => toggleCellStyle('italic')} disabled={!hasTarget}\n className={`px-2 py-1 text-xs rounded transition-colors italic ${focusedStyle.italic ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-200'} disabled:opacity-40 disabled:cursor-not-allowed`}>\n I\n </button>\n <button onClick={() => toggleCellStyle('underline')} disabled={!hasTarget}\n className={`px-2 py-1 text-xs rounded transition-colors underline ${focusedStyle.underline ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-200'} disabled:opacity-40 disabled:cursor-not-allowed`}>\n U\n </button>\n <select\n value={focusedStyle.fontSize ?? 'base'}\n onChange={e => toggleCellStyle('fontSize', e.target.value)}\n disabled={!hasTarget}\n className=\"ml-1 text-xs border border-gray-300 rounded px-1 py-0.5 bg-white disabled:opacity-40 disabled:cursor-not-allowed\">\n <option value=\"sm\">XS</option>\n <option value=\"base\">S</option>\n <option value=\"lg\">M</option>\n <option value=\"xl\">L</option>\n </select>\n </div>\n </div>\n\n {/* Grid */}\n <div className=\"flex-1 min-h-0\">\n <EditableGrid\n columns={columns}\n data={data}\n onChange={handleChange}\n onColumnsChange={(newCols) => updateActiveSheet({ columns: newCols })}\n cellStyles={cellStyles}\n onFocusChange={setFocusedCell}\n onSelectionChange={setSelection}\n minRows={DEFAULT_ROWS}\n maxHeight=\"100%\"\n />\n </div>\n\n {/* Sheet tabs + status bar */}\n <div className=\"flex items-center border-t border-gray-200 bg-gray-50 shrink-0\">\n <div className=\"flex items-center gap-0.5 px-1 py-1 overflow-x-auto flex-1 min-w-0\">\n {sheets.map((sheet, idx) => (\n <button key={sheet.id}\n onClick={() => setActiveIdx(idx)}\n onDoubleClick={() => { setEditingTab(idx); setTabName(sheet.name); }}\n onContextMenu={e => { e.preventDefault(); if (sheets.length > 1) removeSheet(idx); }}\n className={`px-3 py-1 text-xs font-medium rounded-b whitespace-nowrap transition-colors ${\n idx === activeIdx\n ? 'bg-white text-blue-700 border border-t-0 border-gray-300 -mt-px relative z-10'\n : 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'\n }`}>\n {editingTab === idx ? (\n <input type=\"text\" value={tabName} onChange={e => setTabName(e.target.value)}\n onBlur={() => renameSheet(idx, tabName || sheet.name)}\n onKeyDown={e => { if (e.key === 'Enter') renameSheet(idx, tabName || sheet.name); if (e.key === 'Escape') setEditingTab(null); }}\n onClick={e => e.stopPropagation()}\n autoFocus\n className=\"w-20 text-xs border border-blue-400 rounded px-1 py-0 focus:ring-0 focus:outline-none\" />\n ) : sheet.name}\n </button>\n ))}\n <button onClick={addSheet} className=\"px-2 py-1 text-xs text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded\" title=\"Add sheet\">+</button>\n </div>\n\n <div className=\"flex items-center gap-4 px-3 py-1 text-xs text-gray-500 shrink-0 border-l border-gray-200\">\n <span>{filledCount} cells</span>\n {allNums.length > 0 && (\n <>\n <span>Sum: {allNums.reduce((s, v) => s + v, 0).toLocaleString(undefined, { maximumFractionDigits: 2 })}</span>\n <span>Avg: {(allNums.reduce((s, v) => s + v, 0) / allNums.length).toLocaleString(undefined, { maximumFractionDigits: 2 })}</span>\n </>\n )}\n <span>{data.length} × {columns.length}</span>\n </div>\n </div>\n </div>\n );\n}\n"]}