@esheet/builder 0.0.4-0 → 0.0.4-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 (82) hide show
  1. package/dist/index.d.ts +16 -417
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +115 -115
  4. package/dist/lib/EsheetBuilder.d.ts +46 -0
  5. package/dist/lib/EsheetBuilder.d.ts.map +1 -0
  6. package/dist/lib/EsheetBuilder.js +111 -0
  7. package/dist/lib/builder-tools.d.ts +107 -0
  8. package/dist/lib/builder-tools.d.ts.map +1 -0
  9. package/dist/lib/builder-tools.js +321 -0
  10. package/dist/lib/components/BuilderHeader.d.ts +7 -0
  11. package/dist/lib/components/BuilderHeader.d.ts.map +1 -0
  12. package/dist/lib/components/BuilderHeader.js +390 -0
  13. package/dist/lib/components/Canvas.d.ts +12 -0
  14. package/dist/lib/components/Canvas.d.ts.map +1 -0
  15. package/dist/lib/components/Canvas.js +288 -0
  16. package/dist/lib/components/CodeView.d.ts +12 -0
  17. package/dist/lib/components/CodeView.d.ts.map +1 -0
  18. package/dist/lib/components/CodeView.js +197 -0
  19. package/dist/lib/components/FeedbackModal.d.ts +23 -0
  20. package/dist/lib/components/FeedbackModal.d.ts.map +1 -0
  21. package/dist/lib/components/FeedbackModal.js +22 -0
  22. package/dist/lib/components/FieldWrapper.d.ts +53 -0
  23. package/dist/lib/components/FieldWrapper.d.ts.map +1 -0
  24. package/dist/lib/components/FieldWrapper.js +152 -0
  25. package/dist/lib/components/MobileBottomDrawer.d.ts +9 -0
  26. package/dist/lib/components/MobileBottomDrawer.d.ts.map +1 -0
  27. package/dist/lib/components/MobileBottomDrawer.js +6 -0
  28. package/dist/lib/components/ToolPanel.d.ts +11 -0
  29. package/dist/lib/components/ToolPanel.d.ts.map +1 -0
  30. package/dist/lib/components/ToolPanel.js +117 -0
  31. package/dist/lib/components/edit-panel/CommonEditor.d.ts +16 -0
  32. package/dist/lib/components/edit-panel/CommonEditor.d.ts.map +1 -0
  33. package/dist/lib/components/edit-panel/CommonEditor.js +16 -0
  34. package/dist/lib/components/edit-panel/DraftIdEditor.d.ts +16 -0
  35. package/dist/lib/components/edit-panel/DraftIdEditor.d.ts.map +1 -0
  36. package/dist/lib/components/edit-panel/DraftIdEditor.js +32 -0
  37. package/dist/lib/components/edit-panel/EditPanel.d.ts +10 -0
  38. package/dist/lib/components/edit-panel/EditPanel.d.ts.map +1 -0
  39. package/dist/lib/components/edit-panel/EditPanel.js +156 -0
  40. package/dist/lib/components/edit-panel/InputTypeEditor.d.ts +16 -0
  41. package/dist/lib/components/edit-panel/InputTypeEditor.d.ts.map +1 -0
  42. package/dist/lib/components/edit-panel/InputTypeEditor.js +32 -0
  43. package/dist/lib/components/edit-panel/LogicEditor.d.ts +15 -0
  44. package/dist/lib/components/edit-panel/LogicEditor.d.ts.map +1 -0
  45. package/dist/lib/components/edit-panel/LogicEditor.js +392 -0
  46. package/dist/lib/components/edit-panel/MatrixEditor.d.ts +12 -0
  47. package/dist/lib/components/edit-panel/MatrixEditor.d.ts.map +1 -0
  48. package/dist/lib/components/edit-panel/MatrixEditor.js +32 -0
  49. package/dist/lib/components/edit-panel/OptionListEditor.d.ts +14 -0
  50. package/dist/lib/components/edit-panel/OptionListEditor.d.ts.map +1 -0
  51. package/dist/lib/components/edit-panel/OptionListEditor.js +27 -0
  52. package/dist/lib/hooks/useFormApi.d.ts +55 -0
  53. package/dist/lib/hooks/useFormApi.d.ts.map +1 -0
  54. package/dist/lib/hooks/useFormApi.js +80 -0
  55. package/dist/lib/hooks/useUiApi.d.ts +22 -0
  56. package/dist/lib/hooks/useUiApi.d.ts.map +1 -0
  57. package/dist/lib/hooks/useUiApi.js +37 -0
  58. package/dist/lib/hooks/useVisibleRootIds.d.ts +11 -0
  59. package/dist/lib/hooks/useVisibleRootIds.d.ts.map +1 -0
  60. package/dist/lib/hooks/useVisibleRootIds.js +32 -0
  61. package/dist/lib/icons.d.ts +31 -0
  62. package/dist/lib/icons.d.ts.map +1 -0
  63. package/dist/lib/icons.js +47 -0
  64. package/dist/lib/mcp/index.d.ts +7 -0
  65. package/dist/lib/mcp/index.d.ts.map +1 -0
  66. package/dist/lib/mcp/index.js +4 -0
  67. package/dist/lib/mcp/system-prompt.d.ts +2 -0
  68. package/dist/lib/mcp/system-prompt.d.ts.map +1 -0
  69. package/dist/lib/mcp/system-prompt.js +9 -0
  70. package/dist/lib/mcp/tool-definitions.d.ts +10 -0
  71. package/dist/lib/mcp/tool-definitions.d.ts.map +1 -0
  72. package/dist/lib/mcp/tool-definitions.js +436 -0
  73. package/dist/lib/mcp/tool-executor.d.ts +5 -0
  74. package/dist/lib/mcp/tool-executor.d.ts.map +1 -0
  75. package/dist/lib/mcp/tool-executor.js +482 -0
  76. package/dist/lib/mcp/useBuilderToolBridge.d.ts +31 -0
  77. package/dist/lib/mcp/useBuilderToolBridge.d.ts.map +1 -0
  78. package/dist/lib/mcp/useBuilderToolBridge.js +56 -0
  79. package/dist/lib/register-defaults.d.ts +2 -0
  80. package/dist/lib/register-defaults.d.ts.map +1 -0
  81. package/dist/lib/register-defaults.js +37 -0
  82. package/package.json +4 -4
@@ -0,0 +1,321 @@
1
+ // ---------------------------------------------------------------------------
2
+ // BuilderTools — narrow facade for MCP / AI tool callers only.
3
+ // Normal developers interact with the builder entirely through its props/UI.
4
+ // ---------------------------------------------------------------------------
5
+ import { getFieldTypeMeta, getRegisteredFieldTypes, hydrateDefinition, } from '@esheet/core';
6
+ export function createBuilderTools(form) {
7
+ function resolveFieldId(fieldId, fieldQuestion) {
8
+ if (fieldId)
9
+ return fieldId;
10
+ if (fieldQuestion) {
11
+ const q = fieldQuestion.toLowerCase();
12
+ const { normalized } = form.getState();
13
+ for (const id of Object.keys(normalized.byId)) {
14
+ const question = normalized.byId[id].definition.question;
15
+ if (question?.toLowerCase().includes(q))
16
+ return id;
17
+ }
18
+ }
19
+ return undefined;
20
+ }
21
+ return {
22
+ resetForm: (definition) => form.getState().loadDefinition(definition),
23
+ generateForm: (questions) => {
24
+ const fields = questions.map((q, i) => ({
25
+ id: `f${i + 1}`,
26
+ fieldType: q.fieldType ?? 'text',
27
+ question: q.question,
28
+ ...(q.required !== undefined && { required: q.required }),
29
+ ...(q.inputType && { inputType: q.inputType }),
30
+ ...(q.options && {
31
+ options: q.options.map((v, j) => ({ id: `o${j + 1}`, value: v })),
32
+ }),
33
+ }));
34
+ form.getState().loadDefinition({
35
+ id: form.getState().formId,
36
+ fields: [],
37
+ });
38
+ form.getState().loadDefinition({
39
+ id: form.getState().formId,
40
+ fields,
41
+ });
42
+ const summary = fields
43
+ .map((f, i) => `${i + 1}. ${f.question} (${f.fieldType})`)
44
+ .join(', ');
45
+ return `Form generated with ${fields.length} field(s): ${summary}`;
46
+ },
47
+ addField: (fieldType, opts) => form.getState().addField(fieldType, opts),
48
+ updateField: (fieldId, patch) => form.getState().updateField(fieldId, patch),
49
+ removeField: (fieldId) => form.getState().removeField(fieldId),
50
+ resolveFieldId,
51
+ fillField: (fieldId, value) => {
52
+ const { normalized } = form.getState();
53
+ const node = normalized.byId[fieldId];
54
+ if (!node)
55
+ return false;
56
+ const def = node.definition;
57
+ function normalizeTextValue(v, inputType) {
58
+ if (!v)
59
+ return v;
60
+ switch (inputType) {
61
+ case 'date':
62
+ if (/^\d{4}-\d{2}-\d{2}$/.test(v))
63
+ return v;
64
+ if (/^\d{4}-\d{2}$/.test(v))
65
+ return `${v}-01`;
66
+ if (/^\d{4}$/.test(v))
67
+ return `${v}-01-01`;
68
+ return null;
69
+ case 'month':
70
+ if (/^\d{4}-\d{2}$/.test(v))
71
+ return v;
72
+ if (/^\d{4}-\d{2}-\d{2}$/.test(v))
73
+ return v.slice(0, 7);
74
+ return null;
75
+ case 'datetime-local': {
76
+ const n = v.replace(' ', 'T');
77
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(n))
78
+ return n;
79
+ if (/^\d{4}-\d{2}-\d{2}$/.test(n))
80
+ return `${n}T00:00`;
81
+ return null;
82
+ }
83
+ case 'time': {
84
+ if (/^\d{2}:\d{2}$/.test(v))
85
+ return v;
86
+ const m = v.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
87
+ if (m) {
88
+ let h = parseInt(m[1], 10);
89
+ if (m[3].toUpperCase() === 'AM' && h === 12)
90
+ h = 0;
91
+ if (m[3].toUpperCase() === 'PM' && h !== 12)
92
+ h += 12;
93
+ return `${String(h).padStart(2, '0')}:${m[2]}`;
94
+ }
95
+ return null;
96
+ }
97
+ default:
98
+ return v;
99
+ }
100
+ }
101
+ let response;
102
+ if (def.fieldType === 'singlematrix' &&
103
+ value !== null &&
104
+ typeof value === 'object') {
105
+ const rows = def.rows ?? [];
106
+ const columns = def.columns ?? [];
107
+ const existing = (form.getState().responses[fieldId]?.selected ??
108
+ {});
109
+ const selected = {
110
+ ...existing,
111
+ };
112
+ const entries = Array.isArray(value)
113
+ ? value.map((v, i) => [rows[i]?.value ?? String(i), v])
114
+ : Object.entries(value);
115
+ for (const [rowKey, colVal] of entries) {
116
+ const row = rows.find((r) => r.value.toLowerCase() === rowKey.toLowerCase() || r.id === rowKey);
117
+ if (!row)
118
+ continue;
119
+ const colStr = String(colVal);
120
+ const col = columns.find((c) => c.value.toLowerCase() === colStr.toLowerCase() ||
121
+ c.id === colStr) ?? columns[parseInt(colStr, 10)];
122
+ if (col)
123
+ selected[row.id] = { id: col.id, value: col.value };
124
+ }
125
+ response = { selected };
126
+ }
127
+ else if (def.fieldType === 'multimatrix' &&
128
+ value !== null &&
129
+ typeof value === 'object') {
130
+ const rows = def.rows ?? [];
131
+ const columns = def.columns ?? [];
132
+ const existing = (form.getState().responses[fieldId]?.selected ??
133
+ {});
134
+ const selected = {
135
+ ...existing,
136
+ };
137
+ const entries = Array.isArray(value)
138
+ ? value.map((v, i) => [rows[i]?.value ?? String(i), v])
139
+ : Object.entries(value);
140
+ for (const [rowKey, colVals] of entries) {
141
+ const row = rows.find((r) => r.value.toLowerCase() === rowKey.toLowerCase() || r.id === rowKey);
142
+ if (!row)
143
+ continue;
144
+ const colValArr = Array.isArray(colVals)
145
+ ? colVals
146
+ : [colVals];
147
+ const matched = colValArr
148
+ .map((v) => {
149
+ const s = String(v);
150
+ return columns.find((c) => c.value.toLowerCase() === s.toLowerCase() || c.id === s);
151
+ })
152
+ .filter((c) => c != null);
153
+ if (matched.length > 0)
154
+ selected[row.id] = matched;
155
+ }
156
+ response = { selected };
157
+ }
158
+ else if (['radio', 'dropdown', 'boolean', 'rating', 'slider'].includes(def.fieldType)) {
159
+ const opts = def.options ?? [];
160
+ const match = opts.find((o) => o.value.toLowerCase() === String(value).toLowerCase() ||
161
+ o.id === value);
162
+ const numVal = Number(value);
163
+ const numMatch = !match && !isNaN(numVal)
164
+ ? opts[numVal - 1] ?? opts[numVal]
165
+ : undefined;
166
+ const resolved = match ?? numMatch;
167
+ response = resolved
168
+ ? { selected: { id: resolved.id, value: resolved.value } }
169
+ : { selected: undefined };
170
+ }
171
+ else if (def.fieldType === 'check' ||
172
+ def.fieldType === 'multiselectdropdown') {
173
+ const opts = def.options ?? [];
174
+ const vals = Array.isArray(value) ? value : [value];
175
+ const matches = opts.filter((o) => vals.some((v) => o.value.toLowerCase() === String(v).toLowerCase() || o.id === v));
176
+ response = {
177
+ selected: matches.map((o) => ({ id: o.id, value: o.value })),
178
+ };
179
+ }
180
+ else if (def.fieldType === 'ranking') {
181
+ const opts = def.options ?? [];
182
+ const vals = Array.isArray(value) ? value : [value];
183
+ const ordered = vals
184
+ .map((v) => opts.find((o) => o.value.toLowerCase() === String(v).toLowerCase() || o.id === v))
185
+ .filter((o) => o != null);
186
+ const mentioned = new Set(ordered.map((o) => o.id));
187
+ const remaining = opts.filter((o) => !mentioned.has(o.id));
188
+ response = { selected: [...ordered, ...remaining] };
189
+ }
190
+ else if (def.fieldType === 'multitext') {
191
+ const opts = def.options ?? [];
192
+ const vals = Array.isArray(value)
193
+ ? value
194
+ : typeof value === 'object' && value !== null
195
+ ? Object.values(value)
196
+ : [value];
197
+ const multitextAnswers = {};
198
+ opts.forEach((opt, i) => {
199
+ if (vals[i] != null)
200
+ multitextAnswers[opt.id] = String(vals[i]);
201
+ });
202
+ response = { multitextAnswers };
203
+ }
204
+ else {
205
+ if (value == null) {
206
+ response = { answer: undefined };
207
+ }
208
+ else {
209
+ const normalized = normalizeTextValue(String(value), def.inputType);
210
+ if (normalized === null) {
211
+ const fmt = {
212
+ date: 'YYYY-MM-DD',
213
+ 'datetime-local': 'YYYY-MM-DDTHH:mm',
214
+ month: 'YYYY-MM',
215
+ time: 'HH:mm',
216
+ };
217
+ const expected = fmt[def.inputType ?? ''];
218
+ return expected
219
+ ? `Error: invalid value for inputType "${def.inputType}" — expected format ${expected}`
220
+ : `Error: invalid value for field`;
221
+ }
222
+ response = { answer: normalized };
223
+ }
224
+ }
225
+ form
226
+ .getState()
227
+ .setResponse(fieldId, { ...response, _ai: true });
228
+ return true;
229
+ },
230
+ clearResponses: () => form.getState().resetResponses(),
231
+ getResponses: () => form.getState().responses,
232
+ option: {
233
+ add: (fieldId, value) => form.getState().addOption(fieldId, value),
234
+ update: (fieldId, optionId, value) => form.getState().updateOption(fieldId, optionId, value),
235
+ remove: (fieldId, optionId) => form.getState().removeOption(fieldId, optionId),
236
+ },
237
+ row: {
238
+ add: (fieldId, value) => form.getState().addRow(fieldId, value),
239
+ update: (fieldId, rowId, value) => form.getState().updateRow(fieldId, rowId, value),
240
+ remove: (fieldId, rowId) => form.getState().removeRow(fieldId, rowId),
241
+ },
242
+ column: {
243
+ add: (fieldId, value) => form.getState().addColumn(fieldId, value),
244
+ update: (fieldId, columnId, value) => form.getState().updateColumn(fieldId, columnId, value),
245
+ remove: (fieldId, columnId) => form.getState().removeColumn(fieldId, columnId),
246
+ },
247
+ moveField: (fieldId, toIndex, toParentId) => form.getState().moveField(fieldId, toIndex, toParentId),
248
+ getField: (fieldId) => form.getState().getField(fieldId),
249
+ getFieldTypes: () => getRegisteredFieldTypes().map((key) => {
250
+ const meta = getFieldTypeMeta(key);
251
+ return {
252
+ key,
253
+ label: meta.label,
254
+ category: meta.category,
255
+ hasOptions: meta.hasOptions,
256
+ hasMatrix: meta.hasMatrix,
257
+ };
258
+ }),
259
+ getFieldSpec: (fieldType) => getFieldTypeMeta(fieldType),
260
+ getDefinition: () => {
261
+ const { normalized, formId } = form.getState();
262
+ return {
263
+ id: formId,
264
+ fields: hydrateDefinition(normalized),
265
+ };
266
+ },
267
+ setFormId: (id) => form.getState().setFormId(id),
268
+ getFormSummary: () => {
269
+ const { normalized, formId } = form.getState();
270
+ const INPUT_TYPE_FORMAT = {
271
+ date: 'YYYY-MM-DD',
272
+ 'datetime-local': 'YYYY-MM-DDTHH:mm',
273
+ month: 'YYYY-MM',
274
+ time: 'HH:mm',
275
+ };
276
+ function summarizeField(id) {
277
+ const node = normalized.byId[id];
278
+ const def = node.definition;
279
+ const isMatrix = def.fieldType === 'singlematrix' || def.fieldType === 'multimatrix';
280
+ const isSection = def.fieldType === 'section';
281
+ const base = {
282
+ id,
283
+ fieldType: def.fieldType,
284
+ question: isSection
285
+ ? def.title
286
+ : def.question,
287
+ required: def.required ?? false,
288
+ options: def.options ?? [],
289
+ rows: def.rows ?? [],
290
+ columns: def.columns ?? [],
291
+ editWith: isMatrix
292
+ ? 'add_row / add_column (NOT add_option)'
293
+ : 'add_option',
294
+ hasRules: (def.rules?.length ?? 0) > 0,
295
+ hasValue: (() => {
296
+ const r = form.getState().responses[id];
297
+ return (r != null &&
298
+ Object.values(r).some((v) => v != null &&
299
+ v !== '' &&
300
+ !(Array.isArray(v) && v.length === 0) &&
301
+ !(typeof v === 'object' &&
302
+ !Array.isArray(v) &&
303
+ Object.keys(v).length === 0)));
304
+ })(),
305
+ ...(def.inputType && INPUT_TYPE_FORMAT[def.inputType]
306
+ ? { valueFormat: INPUT_TYPE_FORMAT[def.inputType] }
307
+ : {}),
308
+ };
309
+ if (isSection && node.childIds.length > 0) {
310
+ return { ...base, children: node.childIds.map(summarizeField) };
311
+ }
312
+ return base;
313
+ }
314
+ return {
315
+ formId,
316
+ fieldCount: Object.keys(normalized.byId).length,
317
+ fields: normalized.rootIds.map(summarizeField),
318
+ };
319
+ },
320
+ };
321
+ }
@@ -0,0 +1,7 @@
1
+ export interface BuilderHeaderProps {
2
+ }
3
+ /**
4
+ * BuilderHeader — top bar with Build/Code/Preview mode toggle and Import/Export actions.
5
+ */
6
+ export declare function BuilderHeader(_props: BuilderHeaderProps): import("react/jsx-runtime").JSX.Element;
7
+ //# sourceMappingURL=BuilderHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BuilderHeader.d.ts","sourceRoot":"","sources":["../../../src/lib/components/BuilderHeader.tsx"],"names":[],"mappings":"AAuCA,MAAM,WAAW,kBAAkB;CAAG;AAmLtC;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,2CAgnBvD"}