@gui-chat-plugin/spreadsheet 2.0.0 → 3.0.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 (64) hide show
  1. package/README.md +33 -4
  2. package/dist/core/dispatch.d.ts +8 -0
  3. package/dist/core/index.d.ts +4 -2
  4. package/dist/core/paths.d.ts +13 -0
  5. package/dist/core/plugin.d.ts +7 -3
  6. package/dist/core/sheetGuards.d.ts +5 -0
  7. package/dist/core/types.d.ts +25 -3
  8. package/dist/core.cjs +1 -1
  9. package/dist/core.js +2 -2
  10. package/dist/engine/calculator.d.ts +15 -3
  11. package/dist/engine/cellBuilder.d.ts +44 -0
  12. package/dist/engine/cellEmpty.d.ts +13 -0
  13. package/dist/engine/cellFormatting.d.ts +25 -0
  14. package/dist/engine/coerce-boolean.d.ts +7 -0
  15. package/dist/engine/condition.d.ts +49 -0
  16. package/dist/engine/date-locale.d.ts +12 -0
  17. package/dist/engine/date-parser.d.ts +7 -1
  18. package/dist/engine/date-utils.d.ts +7 -4
  19. package/dist/engine/datedif.d.ts +13 -0
  20. package/dist/engine/evaluator.d.ts +44 -1
  21. package/dist/engine/financial-math.d.ts +28 -0
  22. package/dist/engine/formatter.d.ts +9 -0
  23. package/dist/engine/formulaError.d.ts +37 -0
  24. package/dist/engine/formulaRefs.d.ts +37 -0
  25. package/dist/engine/functions/lookup-math.d.ts +19 -0
  26. package/dist/engine/functions/statistical-math.d.ts +41 -0
  27. package/dist/engine/functions/text.d.ts +17 -1
  28. package/dist/engine/guards.d.ts +10 -0
  29. package/dist/engine/index.d.ts +11 -0
  30. package/dist/engine/jsonCellLocator.d.ts +27 -0
  31. package/dist/engine/math-ops.d.ts +41 -0
  32. package/dist/engine/numericCoercion.d.ts +38 -0
  33. package/dist/engine/parser.d.ts +0 -24
  34. package/dist/engine/registry.d.ts +30 -2
  35. package/dist/engine/responseDecoder.d.ts +40 -0
  36. package/dist/engine/spreadsheet-errors.d.ts +42 -0
  37. package/dist/engine/textFormat.d.ts +32 -0
  38. package/dist/engine/translateFormula.d.ts +37 -0
  39. package/dist/engine/types.d.ts +17 -3
  40. package/dist/index.cjs +1 -1
  41. package/dist/index.js +2 -2
  42. package/dist/lang/de.d.ts +3 -0
  43. package/dist/lang/en.d.ts +3 -0
  44. package/dist/lang/es.d.ts +3 -0
  45. package/dist/lang/fr.d.ts +3 -0
  46. package/dist/lang/index.d.ts +1 -0
  47. package/dist/lang/ja.d.ts +3 -0
  48. package/dist/lang/ko.d.ts +3 -0
  49. package/dist/lang/messages.d.ts +20 -0
  50. package/dist/lang/ptBR.d.ts +3 -0
  51. package/dist/lang/zh.d.ts +3 -0
  52. package/dist/samples-BzI0GdUj.cjs +1 -0
  53. package/dist/{samples-sKAnErVb.js → samples-Cylf7cMw.js} +110 -17
  54. package/dist/style.css +1 -1
  55. package/dist/vue/View.vue.d.ts +1 -1
  56. package/dist/vue/cellHighlights.d.ts +42 -0
  57. package/dist/vue/htmlText.d.ts +4 -0
  58. package/dist/vue/index.d.ts +6 -4
  59. package/dist/vue/keyboardNav.d.ts +9 -0
  60. package/dist/vue/sheetSession.d.ts +24 -0
  61. package/dist/vue.cjs +72 -72
  62. package/dist/vue.js +7023 -6575
  63. package/package.json +16 -15
  64. package/dist/samples-ClV76j10.cjs +0 -1
@@ -1,7 +1,8 @@
1
+ import { ARTIFACTS_ROOT as e } from "gui-chat-protocol";
1
2
  //#region src/core/definition.ts
2
- var e = "presentSpreadsheet", t = {
3
+ var t = "presentSpreadsheet", n = {
3
4
  type: "function",
4
- name: e,
5
+ name: t,
5
6
  description: "Display an Excel-like spreadsheet with formulas and calculations.",
6
7
  parameters: {
7
8
  type: "object",
@@ -31,7 +32,11 @@ var e = "presentSpreadsheet", t = {
31
32
  description: "Cell object with value and optional format. If value is a string starting with '=', it's treated as a formula.",
32
33
  properties: {
33
34
  v: {
34
- oneOf: [{ type: "string" }, { type: "number" }],
35
+ oneOf: [
36
+ { type: "string" },
37
+ { type: "number" },
38
+ { type: "boolean" }
39
+ ],
35
40
  description: "Cell value. Can be text, number, date, or formula (string starting with '='). Examples: 'Revenue', 1500000, '01/15/2025', '=SUM(A1:A10)', '=B2-TODAY()'. Date strings like '01/15/2025' are automatically parsed to date serial numbers."
36
41
  },
37
42
  f: {
@@ -50,29 +55,117 @@ var e = "presentSpreadsheet", t = {
50
55
  },
51
56
  required: ["title", "sheets"]
52
57
  }
53
- }, n = `Use ${e} whenever the user needs a spreadsheet-style table, multi-step math, or dynamic what-if analysis—do not summarize in text. Build LIVE sheets where every cell is an object {"v": value, "f": format}. For formulas, set "v" to a string starting with "=" (e.g., {"v": "=B2*1.05", "f": "$#,##0.00"}). For dates, use date strings like "01/15/2025" or date formulas like "=TODAY()" or "=DATE(2025,1,15)". The spreadsheet auto-parses common date formats (MM/DD/YYYY, YYYY-MM-DD, DD-MMM-YYYY) into date serial numbers for calculations. Date arithmetic works: "=B2-TODAY()" calculates days between dates. Never pre-calculate; let the spreadsheet compute using cell refs, functions (SUM, AVERAGE, IF, TODAY, DATE, DATEDIF, etc.), and arithmetic. Standard formats: "$#,##0.00" currency, "#,##0" integer, "0.00%" percent, "0.00" decimal, "MM/DD/YYYY" date, "DD-MMM-YYYY" date, "YYYY-MM-DD" ISO date. Format is optional for plain text/numbers.`, r = async (e, t) => {
54
- let { title: n } = t, { sheets: r } = t;
55
- if (typeof r == "string") try {
56
- r = JSON.parse(r), console.warn("Sheets was provided as a string and has been parsed to an array");
58
+ }, r = `Use ${t} whenever the user needs a spreadsheet-style table, multi-step math, or dynamic what-if analysis—do not summarize in text. Build LIVE sheets where every cell is an object {"v": value, "f": format}. For formulas, set "v" to a string starting with "=" (e.g., {"v": "=B2*1.05", "f": "$#,##0.00"}). For dates, use date strings like "01/15/2025" or date formulas like "=TODAY()" or "=DATE(2025,1,15)". The spreadsheet auto-parses common date formats (MM/DD/YYYY, YYYY-MM-DD, DD-MMM-YYYY) into date serial numbers for calculations. Date arithmetic works: "=B2-TODAY()" calculates days between dates. Never pre-calculate; let the spreadsheet compute using cell refs, functions (SUM, AVERAGE, IF, TODAY, DATE, DATEDIF, etc.), and arithmetic. Standard formats: "$#,##0.00" currency, "#,##0" integer, "0.00%" percent, "0.00" decimal, "MM/DD/YYYY" date, "DD-MMM-YYYY" date, "YYYY-MM-DD" ISO date. Format is optional for plain text/numbers.`;
59
+ //#endregion
60
+ //#region src/engine/guards.ts
61
+ function i(e) {
62
+ return typeof e == "object" && !!e && !Array.isArray(e);
63
+ }
64
+ function a(e) {
65
+ return typeof e == "object" && !!e;
66
+ }
67
+ var o = (e, t) => i(e) && typeof e[t] == "string";
68
+ function s(e, t) {
69
+ return e instanceof Error ? e.message : o(e, "details") && e.details ? e.details : o(e, "message") && e.message ? e.message : t === void 0 ? String(e) : t;
70
+ }
71
+ //#endregion
72
+ //#region src/core/paths.ts
73
+ var c = "spreadsheets", l = ".json", u = 16, d = 2, f = (e) => `${e.getUTCFullYear()}/${String(e.getUTCMonth() + 1).padStart(d, "0")}`, p = () => globalThis.crypto.randomUUID().split("-").join("").slice(0, u);
74
+ function m(t = /* @__PURE__ */ new Date(), n = p()) {
75
+ let r = `${c}/${f(t)}/${n}${l}`;
76
+ return {
77
+ relPath: r,
78
+ filePath: `${e}/${r}`
79
+ };
80
+ }
81
+ var h = /* @__PURE__ */ new Set([
82
+ "",
83
+ ".",
84
+ ".."
85
+ ]);
86
+ function g(t) {
87
+ return typeof t != "string" || !t.startsWith(`${e}/${c}/`) || !t.endsWith(l) || t.includes("\\") ? !1 : !t.split("/").some((e) => h.has(e));
88
+ }
89
+ var _ = (t) => t.slice(e.length + 1), v = (e) => typeof e == "string" || typeof e == "number" || typeof e == "boolean", y = (e) => e === null || v(e) || i(e) && (e.v === void 0 || e.v === null || v(e.v)) && (e.f === void 0 || typeof e.f == "string"), b = (e) => Array.isArray(e) && e.every(y), x = (e) => i(e) && typeof e.name == "string" && Array.isArray(e.data) && e.data.every(b);
90
+ function S(e) {
91
+ return Array.isArray(e) && e.every(x) ? e : null;
92
+ }
93
+ //#endregion
94
+ //#region src/core/dispatch.ts
95
+ function C(e) {
96
+ return !i(e) || typeof e.path != "string" ? !1 : e.kind === "loadSpreadsheet" || e.kind === "saveSpreadsheet" && Array.isArray(e.sheets) && e.sheets.every(x);
97
+ }
98
+ var w = (e) => i(e) && typeof e.read == "function" && typeof e.write == "function";
99
+ function T(e) {
100
+ if (!i(e) || !i(e.files)) return null;
101
+ let t = e.files.artifacts;
102
+ return w(t) ? t : null;
103
+ }
104
+ async function E(e, t) {
105
+ let n = await e.read(_(t)), r = S(JSON.parse(n));
106
+ if (!r) throw Error(`${t} does not hold an array of sheets`);
107
+ return {
108
+ message: `Loaded spreadsheet ${t}`,
109
+ data: { sheets: r }
110
+ };
111
+ }
112
+ async function D(e, t) {
113
+ if (!g(t.path)) throw Error("path must be a .json file under artifacts/spreadsheets/");
114
+ return t.kind === "loadSpreadsheet" ? E(e, t.path) : (await e.write(_(t.path), JSON.stringify(t.sheets)), {
115
+ message: `Saved spreadsheet ${t.path}`,
116
+ data: { sheets: t.path }
117
+ });
118
+ }
119
+ //#endregion
120
+ //#region src/core/plugin.ts
121
+ function O(e) {
122
+ if (typeof e != "string") return e;
123
+ try {
124
+ let t = JSON.parse(e);
125
+ return console.warn("Sheets was provided as a string and has been parsed to an array"), t;
57
126
  } catch (e) {
58
127
  throw Error(`Invalid sheets format: sheets must be an array, not a string. Parse error: ${e instanceof Error ? e.message : String(e)}`);
59
128
  }
60
- if (!Array.isArray(r) || r.length === 0) throw Error("At least one sheet is required. Sheets must be an array of sheet objects.");
61
- for (let e of r) if (!e.name || !e.data || e.data.length === 0) throw Error(`Invalid sheet: ${e.name || "unnamed"}. Each sheet must have a name and data array.`);
62
- return {
129
+ }
130
+ function k(e) {
131
+ if (!Array.isArray(e) || e.length === 0) throw Error("At least one sheet is required. Sheets must be an array of sheet objects.");
132
+ e.forEach((e) => {
133
+ if (!e.name || !e.data || e.data.length === 0) throw Error(`Invalid sheet: ${e.name || "unnamed"}. Each sheet must have a name and data array.`);
134
+ });
135
+ }
136
+ var A = async (e, t) => {
137
+ let { title: n } = t, r = O(t.sheets);
138
+ k(r);
139
+ let i = "Acknowledge that the spreadsheet has been created and is displayed to the user.", a = T(e);
140
+ if (!a) return {
63
141
  message: `Created spreadsheet: ${n}`,
64
142
  title: n,
65
143
  data: { sheets: r },
66
- instructions: "Acknowledge that the spreadsheet has been created and is displayed to the user."
144
+ instructions: i
145
+ };
146
+ let { relPath: o, filePath: s } = m();
147
+ return await a.write(o, JSON.stringify(r)), {
148
+ message: `Created spreadsheet: ${n}. Saved to ${s}; the user can edit cells there, so read that file for the current values.`,
149
+ title: n,
150
+ data: { sheets: s },
151
+ instructions: i
67
152
  };
68
- }, i = {
69
- toolDefinition: t,
70
- execute: r,
153
+ };
154
+ async function j(e, t) {
155
+ if (!("kind" in t)) return A(e, t);
156
+ let n = T(e);
157
+ if (!n) throw Error("This host does not provide files.artifacts, so spreadsheets cannot be loaded or saved");
158
+ if (!C(t)) throw Error("Invalid spreadsheet dispatch");
159
+ return D(n, t);
160
+ }
161
+ var M = {
162
+ toolDefinition: n,
163
+ execute: j,
71
164
  generatingMessage: "Creating spreadsheet...",
72
165
  waitingMessage: "Tell the user that the spreadsheet was created and will be presented shortly.",
73
166
  isEnabled: () => !0,
74
- systemPrompt: n
75
- }, a = [
167
+ systemPrompt: r
168
+ }, N = [
76
169
  {
77
170
  name: "Simple Budget",
78
171
  args: {
@@ -417,4 +510,4 @@ var e = "presentSpreadsheet", t = {
417
510
  }
418
511
  ];
419
512
  //#endregion
420
- export { t as a, n as i, r as n, e as o, i as r, a as t };
513
+ export { C as a, s as c, r as d, n as f, D as i, a as l, A as n, S as o, t as p, M as r, g as s, N as t, i as u };
package/dist/style.css CHANGED
@@ -1,3 +1,3 @@
1
1
  /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-900:oklch(39.3% .095 152.535);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-white:#fff;--spacing:.25rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.contents{display:contents}.flex{display:flex}.table{display:table}.h-\[600px\]{height:600px}.max-h-64{max-height:calc(var(--spacing) * 64)}.min-h-full{min-height:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-\[200px\]{max-width:200px}.flex-shrink{flex-shrink:1}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-gray-200{border-color:var(--color-gray-200)}.border-green-200{border-color:var(--color-green-200)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-white{background-color:var(--color-white)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-8{padding:calc(var(--spacing) * 8)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-2{padding-block:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-800{color:var(--color-gray-800)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:border-green-300:hover{border-color:var(--color-green-300)}.hover\:bg-green-200:hover{background-color:var(--color-green-200)}}@media (prefers-color-scheme:dark){.dark\:bg-green-900{background-color:var(--color-green-900)}.dark\:text-gray-200{color:var(--color-gray-200)}.dark\:text-gray-400{color:var(--color-gray-400)}.dark\:text-green-300{color:var(--color-green-300)}}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}.spreadsheet-container[data-v-46c4c1c7]{background:#fff;flex-direction:column;width:100%;height:100%;display:flex}.spreadsheet-content-wrapper[data-v-46c4c1c7]{flex:1;min-height:0;overflow-y:auto}.header[data-v-46c4c1c7]{justify-content:space-between;align-items:center;margin-bottom:1em;display:flex}.title[data-v-46c4c1c7]{margin:0;font-size:2em;font-weight:700}.button-group[data-v-46c4c1c7]{gap:.5em;display:flex}.download-btn[data-v-46c4c1c7]{color:#fff;cursor:pointer;border:none;border-radius:4px;align-items:center;gap:.5em;padding:.5em 1em;font-size:.9em;transition:background-color .2s;display:flex}.excel-btn[data-v-46c4c1c7]{background-color:#217346}.excel-btn[data-v-46c4c1c7]:hover{background-color:#1e6a3f}.excel-btn[data-v-46c4c1c7]:active{background-color:#1a5c36}.download-btn .material-icons[data-v-46c4c1c7]{font-size:1.2em}.sheet-tabs[data-v-46c4c1c7]{border-bottom:2px solid #e0e0e0;gap:.25em;margin-bottom:1em;display:flex}.sheet-tab[data-v-46c4c1c7]{cursor:pointer;color:#666;background:#f5f5f5;border:none;border-top-left-radius:4px;border-top-right-radius:4px;padding:.5em 1em;font-size:.9em;transition:background-color .2s}.sheet-tab[data-v-46c4c1c7]:hover{background:#e8e8e8}.sheet-tab.active[data-v-46c4c1c7]{color:#333;background:#fff;border-bottom:2px solid #fff;margin-bottom:-2px;font-weight:500}.table-container[data-v-46c4c1c7]{background:#fff;overflow-x:auto}.table-container[data-v-46c4c1c7] table{border-collapse:collapse;width:100%;font-family:Segoe UI,Arial,sans-serif;font-size:.9em}.table-container[data-v-46c4c1c7] td,.table-container[data-v-46c4c1c7] th{text-align:left;border:1px solid #d0d0d0;padding:.5em .75em}.table-container[data-v-46c4c1c7] th{color:#333;background-color:#f5f5f5;font-weight:600}.table-container[data-v-46c4c1c7] tr:nth-child(2n){background-color:#fafafa}.table-container[data-v-46c4c1c7] tr:hover{background-color:#f0f0f0}.table-container[data-v-46c4c1c7] .cell-editing{outline-offset:-2px;background-color:#e8f5e9!important;outline:2px solid #217346!important}.table-container[data-v-46c4c1c7] .cell-referenced{outline-offset:-2px;background-color:#fff3e0!important;outline:2px solid #ff9800!important}.error[data-v-46c4c1c7]{color:#c62828;background:#ffebee;border-radius:4px;margin:1em 0;padding:1em}.spreadsheet-source[data-v-46c4c1c7]{background:#f5f5f5;border-top:1px solid #e0e0e0;flex-shrink:0;padding:.5rem;font-family:monospace;font-size:.85rem}.spreadsheet-source summary[data-v-46c4c1c7]{cursor:pointer;-webkit-user-select:none;user-select:none;color:#333;background:#e8e8e8;border-radius:4px;padding:.5rem;font-weight:500}.spreadsheet-source[open] summary[data-v-46c4c1c7]{margin-bottom:.5rem}.spreadsheet-source summary[data-v-46c4c1c7]:hover{background:#d8d8d8}.spreadsheet-editor[data-v-46c4c1c7]{color:#333;resize:vertical;background:#fff;border:1px solid #ccc;border-radius:4px;width:100%;height:40vh;margin-bottom:.5rem;padding:1rem;font-family:Courier New,monospace;font-size:.9rem;line-height:1.5}.spreadsheet-editor[data-v-46c4c1c7]:focus{border-color:#217346;outline:none;box-shadow:0 0 0 2px #2173461a}.apply-btn[data-v-46c4c1c7]{color:#fff;cursor:pointer;background:#217346;border:none;border-radius:4px;padding:.5rem 1rem;font-size:.9rem;font-weight:500;transition:background .2s}.apply-btn[data-v-46c4c1c7]:hover{background:#1e6a3f}.apply-btn[data-v-46c4c1c7]:active{background:#1a5c36}.apply-btn[data-v-46c4c1c7]:disabled{color:#666;cursor:not-allowed;opacity:.6;background:#ccc}.apply-btn[data-v-46c4c1c7]:disabled:hover{background:#ccc}.mini-editor-panel[data-v-46c4c1c7]{background:#f8f8f8;border-top:1px solid #d0d0d0;flex-shrink:0}.mini-editor-content[data-v-46c4c1c7]{align-items:center;gap:.5rem;padding:.5rem 1rem;display:flex}.cell-ref[data-v-46c4c1c7]{color:#217346;min-width:2rem;font-family:monospace;font-size:.85rem;font-weight:600}.radio-group[data-v-46c4c1c7]{border-right:1px solid #d0d0d0;gap:.75rem;padding-right:.75rem;display:flex}.radio-option[data-v-46c4c1c7]{cursor:pointer;color:#555;align-items:center;gap:.25rem;font-size:.85rem;display:flex}.radio-option input[type=radio][data-v-46c4c1c7]{cursor:pointer;width:.9rem;height:.9rem}.form-input[data-v-46c4c1c7]{border:1px solid #ccc;border-radius:3px;flex:1;min-width:120px;padding:.4rem .6rem;font-family:inherit;font-size:.85rem;transition:border-color .2s}.form-input[data-v-46c4c1c7]:focus{border-color:#217346;outline:none;box-shadow:0 0 0 2px #2173461a}.form-input[data-v-46c4c1c7]::placeholder{color:#999;font-size:.8rem}.save-btn[data-v-46c4c1c7],.cancel-btn[data-v-46c4c1c7]{cursor:pointer;border:none;border-radius:3px;padding:.4rem .8rem;font-size:.85rem;font-weight:500;transition:background .2s}.save-btn[data-v-46c4c1c7]{color:#fff;background:#217346}.save-btn[data-v-46c4c1c7]:hover{background:#1e6a3f}.cancel-btn[data-v-46c4c1c7]{color:#666;background:0 0;padding:.4rem;font-size:1.2rem;line-height:1}.cancel-btn[data-v-46c4c1c7]:hover{color:#333;background:#e0e0e0}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-700:oklch(52.7% .154 150.069);--color-green-900:oklch(39.3% .095 152.535);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-white:#fff;--spacing:.25rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.row-2{grid-row:2}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.inline{display:inline}.table{display:table}.h-8{height:calc(var(--spacing) * 8)}.h-\[600px\]{height:600px}.max-h-64{max-height:calc(var(--spacing) * 64)}.min-h-full{min-height:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-\[200px\]{max-width:200px}.flex-shrink{flex-shrink:1}.border-collapse{border-collapse:collapse}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-gray-200{border-color:var(--color-gray-200)}.border-green-200{border-color:var(--color-green-200)}.bg-\[\#217346\]{background-color:#217346}.bg-gray-100{background-color:var(--color-gray-100)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-white{background-color:var(--color-white)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-8{padding:calc(var(--spacing) * 8)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-2{padding-block:calc(var(--spacing) * 2)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.break-words{overflow-wrap:break-word}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-800{color:var(--color-gray-800)}.text-green-700{color:var(--color-green-700)}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:border-green-300:hover{border-color:var(--color-green-300)}.hover\:bg-\[\#1e6a3f\]:hover{background-color:#1e6a3f}.hover\:bg-green-200:hover{background-color:var(--color-green-200)}}.active\:bg-\[\#1a5c36\]:active{background-color:#1a5c36}@media (prefers-color-scheme:dark){.dark\:bg-green-900{background-color:var(--color-green-900)}.dark\:text-gray-200{color:var(--color-gray-200)}.dark\:text-gray-400{color:var(--color-gray-400)}}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}.spreadsheet-container[data-v-9281886a]{background:#fff;flex-direction:column;width:100%;height:100%;display:flex}.spreadsheet-content-wrapper[data-v-9281886a]{flex:1;min-height:0;overflow-y:auto}.header[data-v-9281886a]{justify-content:space-between;align-items:center;margin-bottom:1em;display:flex}.title[data-v-9281886a]{margin:0;font-size:2em;font-weight:700}.sheet-tabs[data-v-9281886a]{border-bottom:2px solid #e0e0e0;gap:.25em;margin-bottom:1em;display:flex}.sheet-tab[data-v-9281886a]{cursor:pointer;color:#666;background:#f5f5f5;border:none;border-top-left-radius:4px;border-top-right-radius:4px;padding:.5em 1em;font-size:.9em;transition:background-color .2s}.sheet-tab[data-v-9281886a]:hover{background:#e8e8e8}.sheet-tab.active[data-v-9281886a]{color:#333;background:#fff;border-bottom:2px solid #fff;margin-bottom:-2px;font-weight:500}.table-container[data-v-9281886a]{background:#fff;overflow-x:auto}.table-container[data-v-9281886a] table{border-collapse:collapse;width:100%;font-family:Segoe UI,Arial,sans-serif;font-size:.9em}.table-container[data-v-9281886a] td,.table-container[data-v-9281886a] th{text-align:left;border:1px solid #d0d0d0;padding:.5em .75em}.table-container[data-v-9281886a] th{color:#333;background-color:#f5f5f5;font-weight:600}.table-container[data-v-9281886a] tr:nth-child(2n){background-color:#fafafa}.table-container[data-v-9281886a] tr:hover{background-color:#f0f0f0}.table-container[data-v-9281886a] .cell-editing{outline-offset:-2px;background-color:#e8f5e9!important;outline:2px solid #217346!important}.table-container[data-v-9281886a] .cell-referenced{outline-offset:-2px;background-color:#fff3e0!important;outline:2px solid #ff9800!important}.error[data-v-9281886a]{color:#c62828;background:#ffebee;border-radius:4px;margin:1em 0;padding:1em}.spreadsheet-source[data-v-9281886a]{background:#f5f5f5;border-top:1px solid #e0e0e0;flex-shrink:0;padding:.5rem;font-family:Consolas,MS Gothic,BIZ UDGothic,monospace;font-size:.85rem}.spreadsheet-source summary[data-v-9281886a]{cursor:pointer;-webkit-user-select:none;user-select:none;color:#333;background:#e8e8e8;border-radius:4px;padding:.5rem;font-weight:500}.spreadsheet-source[open] summary[data-v-9281886a]{margin-bottom:.5rem}.spreadsheet-source summary[data-v-9281886a]:hover{background:#d8d8d8}.spreadsheet-editor[data-v-9281886a]{color:#333;resize:vertical;background:#fff;border:1px solid #ccc;border-radius:4px;width:100%;height:40vh;margin-bottom:.5rem;padding:1rem;font-family:Courier New,MS Gothic,BIZ UDGothic,monospace;font-size:.9rem;line-height:1.5}.spreadsheet-editor[data-v-9281886a]:focus{border-color:#217346;outline:none;box-shadow:0 0 0 2px #2173461a}.apply-btn[data-v-9281886a]{color:#fff;cursor:pointer;background:#217346;border:none;border-radius:4px;padding:.5rem 1rem;font-size:.9rem;font-weight:500;transition:background .2s}.apply-btn[data-v-9281886a]:hover{background:#1e6a3f}.apply-btn[data-v-9281886a]:active{background:#1a5c36}.apply-btn[data-v-9281886a]:disabled{color:#666;cursor:not-allowed;opacity:.6;background:#ccc}.apply-btn[data-v-9281886a]:disabled:hover{background:#ccc}.mini-editor-panel[data-v-9281886a]{background:#f8f8f8;border-top:1px solid #d0d0d0;flex-shrink:0}.mini-editor-content[data-v-9281886a]{align-items:center;gap:.5rem;padding:.5rem 1rem;display:flex}.cell-ref[data-v-9281886a]{color:#217346;min-width:2rem;font-family:Consolas,MS Gothic,BIZ UDGothic,monospace;font-size:.85rem;font-weight:600}.radio-group[data-v-9281886a]{border-right:1px solid #d0d0d0;gap:.75rem;padding-right:.75rem;display:flex}.radio-option[data-v-9281886a]{cursor:pointer;color:#555;align-items:center;gap:.25rem;font-size:.85rem;display:flex}.radio-option input[type=radio][data-v-9281886a]{cursor:pointer;width:.9rem;height:.9rem}.form-input[data-v-9281886a]{border:1px solid #ccc;border-radius:3px;flex:1;min-width:120px;padding:.4rem .6rem;font-family:inherit;font-size:.85rem;transition:border-color .2s}.form-input[data-v-9281886a]:focus{border-color:#217346;outline:none;box-shadow:0 0 0 2px #2173461a}.form-input[data-v-9281886a]::placeholder{color:#999;font-size:.8rem}.save-btn[data-v-9281886a],.cancel-btn[data-v-9281886a]{cursor:pointer;border:none;border-radius:3px;padding:.4rem .8rem;font-size:.85rem;font-weight:500;transition:background .2s}.save-btn[data-v-9281886a]{color:#fff;background:#217346}.save-btn[data-v-9281886a]:hover{background:#1e6a3f}.cancel-btn[data-v-9281886a]{color:#666;background:0 0;padding:.4rem;font-size:1.2rem;line-height:1}.cancel-btn[data-v-9281886a]:hover{color:#333;background:#e0e0e0}
3
3
  /*$vite$:1*/
@@ -1,4 +1,4 @@
1
- import type { ToolResult } from "gui-chat-protocol";
1
+ import { type ToolResult } from "gui-chat-protocol/vue";
2
2
  import type { SpreadsheetToolData } from "../core/types";
3
3
  import "../engine/functions";
4
4
  type __VLS_Props = {
@@ -0,0 +1,42 @@
1
+ /**
2
+ * DOM helpers for the mini-editor cell-highlight pass. Extracted from
3
+ * the post-flush watch in `src/vue/View.vue`, which
4
+ * had a cognitive complexity of 30 driven by four levels of nested
5
+ * optional chaining + loops.
6
+ *
7
+ * These helpers are side-effectful by nature (they add/remove CSS
8
+ * classes on DOM nodes) but each one is small enough that its
9
+ * behaviour is obvious. Unit-testable with a minimal mock DOM; see
10
+ * `tests/vue/test_cellHighlights.ts`.
11
+ */
12
+ /** Minimal DOM surface the helpers need. Defined here so tests can
13
+ * pass plain objects without pulling in jsdom. */
14
+ export interface HighlightableElement {
15
+ classList: {
16
+ add: (cls: string) => void;
17
+ remove: (cls: string) => void;
18
+ };
19
+ }
20
+ export interface HighlightableRow {
21
+ querySelectorAll: (selector: string) => ArrayLike<HighlightableElement>;
22
+ }
23
+ export interface HighlightableTable {
24
+ querySelectorAll: (selector: string) => ArrayLike<HighlightableRow>;
25
+ }
26
+ export interface HighlightableContainer {
27
+ querySelector: ((selector: "#spreadsheet-table") => HighlightableTable | null) & ((selector: string) => HighlightableElement | null);
28
+ querySelectorAll: (selector: string) => ArrayLike<HighlightableElement> & Iterable<HighlightableElement>;
29
+ }
30
+ export interface CellCoord {
31
+ row: number;
32
+ col: number;
33
+ }
34
+ /** Remove both kinds of highlight classes from the container. */
35
+ export declare function clearCellHighlights(container: HighlightableContainer | null | undefined): void;
36
+ /** Add `className` to the <td> at (row, col) of the given table.
37
+ * No-op if the row or cell doesn't exist. */
38
+ export declare function highlightCell(table: HighlightableTable | null | undefined, coord: CellCoord, className: string): void;
39
+ /** Apply the editing cell + referenced cells highlights. Looks up
40
+ * the #spreadsheet-table inside the container and no-ops if the
41
+ * table hasn't rendered yet. */
42
+ export declare function applyCellHighlights(container: HighlightableContainer | null | undefined, editingCell: CellCoord | null, references: readonly CellCoord[]): void;
@@ -0,0 +1,4 @@
1
+ export declare const escapeHtml: (text: string) => string;
2
+ /** A link in a cell that should open in a new tab rather than navigate the
3
+ * host app away: http(s) on another origin. */
4
+ export declare function isCrossOriginHttpUrl(href: string, currentOrigin: string): boolean;
@@ -6,18 +6,20 @@
6
6
  */
7
7
  import "../style.css";
8
8
  import type { ToolPlugin } from "gui-chat-protocol/vue";
9
- import type { SpreadsheetToolData, SpreadsheetArgs } from "../core/types";
9
+ import type { SpreadsheetToolData, SpreadsheetArgs, SpreadsheetDispatchArgs } from "../core/types";
10
10
  import View from "./View.vue";
11
11
  import Preview from "./Preview.vue";
12
12
  /**
13
13
  * Spreadsheet plugin instance with Vue components
14
14
  */
15
- export declare const plugin: ToolPlugin<SpreadsheetToolData, never, SpreadsheetArgs>;
16
- export type { SpreadsheetCell, SpreadsheetSheet, SpreadsheetToolData, SpreadsheetArgs, } from "../core/types";
15
+ export declare const plugin: ToolPlugin<SpreadsheetToolData, never, SpreadsheetArgs | SpreadsheetDispatchArgs>;
16
+ export type { SpreadsheetCell, SpreadsheetCellValue, StorableCell, SpreadsheetSheet, SpreadsheetToolData, SpreadsheetArgs, SpreadsheetDispatchArgs, } from "../core/types";
17
17
  export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, executeSpreadsheet, pluginCore, } from "../core/plugin";
18
+ export { executeSpreadsheetDispatch, isSpreadsheetDispatchArgs } from "../core/dispatch";
19
+ export { isSpreadsheetArtifactPath } from "../core/paths";
18
20
  export { samples } from "../core/samples";
19
21
  export { View, Preview };
20
22
  declare const _default: {
21
- plugin: ToolPlugin<SpreadsheetToolData, never, SpreadsheetArgs, import("gui-chat-protocol").InputHandler, Record<string, unknown>>;
23
+ plugin: ToolPlugin<SpreadsheetToolData, never, SpreadsheetArgs | SpreadsheetDispatchArgs, import("gui-chat-protocol").InputHandler, Record<string, unknown>>;
22
24
  };
23
25
  export default _default;
@@ -0,0 +1,9 @@
1
+ export interface CellPosition {
2
+ row: number;
3
+ col: number;
4
+ }
5
+ export interface SheetLike {
6
+ data?: unknown[][];
7
+ }
8
+ export declare function getArrowKeyOffset(key: string, row: number, col: number): CellPosition | null;
9
+ export declare function isWithinSheetBounds(sheet: SheetLike | null | undefined, row: number, col: number): boolean;
@@ -0,0 +1,24 @@
1
+ import type { SpreadsheetSheet } from "../core/types";
2
+ export interface SheetSessionHost {
3
+ loadFromHost(path: string): Promise<SpreadsheetSheet[]>;
4
+ saveToHost(path: string, sheets: SpreadsheetSheet[]): Promise<unknown>;
5
+ }
6
+ export type LoadOutcome = {
7
+ kind: "sheets";
8
+ sheets: SpreadsheetSheet[];
9
+ } | {
10
+ kind: "notSheets";
11
+ } | {
12
+ kind: "noHost";
13
+ path: string;
14
+ } | {
15
+ kind: "loadFailed";
16
+ error: unknown;
17
+ };
18
+ export interface SheetSession {
19
+ /** Resolves to null when a newer `load` started before this one finished. */
20
+ load(raw: unknown): Promise<LoadOutcome | null>;
21
+ /** Waits for every earlier save, so the last edit made is the last written. */
22
+ save(path: string, sheets: SpreadsheetSheet[]): Promise<void>;
23
+ }
24
+ export declare function createSheetSession(host: SheetSessionHost | null): SheetSession;