@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,482 @@
1
+ export function executeToolCall(toolName, args, tools) {
2
+ switch (toolName) {
3
+ case 'create_field':
4
+ return createField(args, tools);
5
+ case 'update_field':
6
+ return updateField(args, tools);
7
+ case 'delete_field':
8
+ return deleteField(args, tools);
9
+ case 'reset_form':
10
+ return resetForm(args, tools);
11
+ case 'generate_form':
12
+ return generateForm(args, tools);
13
+ case 'get_form_summary':
14
+ return getFormSummaryOptimized(tools);
15
+ case 'fill_field':
16
+ return fillField(args, tools);
17
+ case 'clear_responses':
18
+ tools.clearResponses();
19
+ return 'Responses cleared';
20
+ case 'get_responses':
21
+ return tools.getResponses();
22
+ case 'add_option':
23
+ return addOption(args, tools);
24
+ case 'update_option':
25
+ return updateOption(args, tools);
26
+ case 'remove_option':
27
+ return removeOption(args, tools);
28
+ case 'get_field':
29
+ return getFieldDetail(args, tools);
30
+ case 'move_field':
31
+ return moveField(args, tools);
32
+ case 'add_row':
33
+ return addRow(args, tools);
34
+ case 'update_row':
35
+ return updateRow(args, tools);
36
+ case 'remove_row':
37
+ return removeRow(args, tools);
38
+ case 'add_column':
39
+ return addColumn(args, tools);
40
+ case 'update_column':
41
+ return updateColumn(args, tools);
42
+ case 'remove_column':
43
+ return removeColumn(args, tools);
44
+ case 'get_field_types':
45
+ return { fieldTypes: tools.getFieldTypes() };
46
+ case 'get_field_spec':
47
+ return getFieldSpec(args, tools);
48
+ case 'get_definition':
49
+ return tools.getDefinition();
50
+ case 'set_form_id': {
51
+ const id = String(args['id'] ?? '');
52
+ if (!id)
53
+ return 'Error: id is required';
54
+ tools.setFormId(id);
55
+ return `Form ID set to "${id}"`;
56
+ }
57
+ case 'add_field_rule':
58
+ return addFieldRule(args, tools);
59
+ case 'add_expression_rule':
60
+ return addExpressionRule(args, tools);
61
+ case 'remove_rule':
62
+ return removeRule(args, tools);
63
+ default:
64
+ return `Unknown tool: ${toolName}`;
65
+ }
66
+ }
67
+ const PLACEHOLDER_IDS = new Set(['q1', 'q2', 'q3']);
68
+ function createField(args, tools) {
69
+ // Auto-clear placeholder fields on first AI-created field.
70
+ const { fields } = tools.getFormSummary();
71
+ if (fields.length > 0 && fields.every((f) => PLACEHOLDER_IDS.has(f.id))) {
72
+ tools.resetForm({ id: 'form-1', fields: [] });
73
+ }
74
+ const fieldType = args.fieldType;
75
+ const patch = { question: args.question };
76
+ if (args.required !== undefined)
77
+ patch.required = args.required;
78
+ // Suppress auto-generated placeholder rows/columns on matrix fields so the
79
+ // AI doesn't have to remove them — it will add exactly what it needs.
80
+ if (fieldType === 'singlematrix' || fieldType === 'multimatrix') {
81
+ patch.rows = [];
82
+ patch.columns = [];
83
+ }
84
+ if (args.options) {
85
+ const opts = args.options;
86
+ patch.options = opts.map((o, i) => {
87
+ if (typeof o === 'string')
88
+ return { id: `o${i + 1}`, value: o };
89
+ const obj = o;
90
+ return { id: obj.id ?? `o${i + 1}`, value: obj.value ?? '' };
91
+ });
92
+ }
93
+ if (args.properties && typeof args.properties === 'object')
94
+ Object.assign(patch, args.properties);
95
+ let opts = { patch };
96
+ if (args.parentId) {
97
+ opts = { ...opts, parentId: args.parentId };
98
+ }
99
+ if (args.afterFieldId) {
100
+ const { fields } = tools.getFormSummary();
101
+ const afterIdx = fields.findIndex((f) => f.id === args.afterFieldId);
102
+ if (afterIdx >= 0)
103
+ opts = { ...opts, index: afterIdx + 1 };
104
+ }
105
+ const newId = tools.addField(fieldType, opts);
106
+ if (!newId)
107
+ return `Unknown field type: ${fieldType}`;
108
+ return `Created field "${args.question}" with ID: ${newId}`;
109
+ }
110
+ /** Optimized get_form_summary: condenses options/rows/columns to counts to reduce token usage. */
111
+ function getFormSummaryOptimized(tools) {
112
+ const summary = tools.getFormSummary();
113
+ // Condense field details: replace full option arrays with counts
114
+ const condensedFields = summary.fields.map((f) => ({
115
+ id: f.id,
116
+ fieldType: f.fieldType,
117
+ question: f.question,
118
+ required: f.required,
119
+ optionCount: f.options?.length ?? 0,
120
+ rowCount: f.rows?.length ?? 0,
121
+ columnCount: f.columns?.length ?? 0,
122
+ editWith: f.editWith,
123
+ hasRules: f.hasRules,
124
+ hasValue: f.hasValue,
125
+ ...(f.valueFormat ? { valueFormat: f.valueFormat } : {}),
126
+ }));
127
+ return {
128
+ formId: summary.formId,
129
+ fieldCount: summary.fieldCount,
130
+ fields: condensedFields,
131
+ hint: 'Use get_field for full option/row/column details',
132
+ };
133
+ }
134
+ function fillField(args, tools) {
135
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
136
+ if (!fieldId)
137
+ return `Field not found: ${args.fieldId ?? args.fieldQuestion}`;
138
+ const result = tools.fillField(fieldId, args.value);
139
+ if (typeof result === 'string')
140
+ return result; // format validation error
141
+ if (!result)
142
+ return `Field not found: ${fieldId}`;
143
+ const summary = tools.getFormSummary();
144
+ const field = summary.fields.find((f) => f.id === fieldId);
145
+ const label = field?.question ?? fieldId;
146
+ // Return only unfilled fields with full details (token optimization)
147
+ const filledFields = summary.fields.filter((f) => f.hasValue);
148
+ const unfilledFields = summary.fields.filter((f) => !f.hasValue);
149
+ return {
150
+ result: `Filled "${label}" with ${JSON.stringify(args.value)}`,
151
+ filledCount: filledFields.length,
152
+ unfilledFields,
153
+ };
154
+ }
155
+ function generateForm(args, tools) {
156
+ const questions = args.questions;
157
+ if (!Array.isArray(questions) || questions.length === 0)
158
+ return `Missing 'questions' array — provide an array of { question, fieldType?, required?, inputType? }`;
159
+ return tools.generateForm(questions.slice(0, 5));
160
+ }
161
+ function resetForm(_args, tools) {
162
+ tools.resetForm({ id: 'form-1', fields: [] });
163
+ return 'Form cleared';
164
+ }
165
+ function updateField(args, tools) {
166
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
167
+ if (!fieldId)
168
+ return `Field not found: ${args.fieldId ?? args.fieldQuestion}`;
169
+ let updates = args.updates;
170
+ // Fallback: AI sometimes puts field props at the top level instead of inside `updates`.
171
+ // Collect any known field properties from top-level args.
172
+ if (updates === undefined || updates === null) {
173
+ const FIELD_PROPS = [
174
+ 'question',
175
+ 'required',
176
+ 'inputType',
177
+ 'unit',
178
+ 'min',
179
+ 'max',
180
+ 'step',
181
+ 'options',
182
+ 'placeholder',
183
+ ];
184
+ const collected = {};
185
+ for (const key of FIELD_PROPS) {
186
+ if (args[key] !== undefined)
187
+ collected[key] = args[key];
188
+ }
189
+ if (Object.keys(collected).length > 0) {
190
+ updates = collected;
191
+ }
192
+ else {
193
+ return `Missing 'updates' object. Pass the properties to change as an object, e.g. { "question": "New text" }`;
194
+ }
195
+ }
196
+ if (typeof updates === 'string') {
197
+ try {
198
+ updates = JSON.parse(updates);
199
+ }
200
+ catch {
201
+ return `Invalid updates format: ${updates}`;
202
+ }
203
+ }
204
+ // For options, use surgical add/remove instead of replacing the whole array.
205
+ if (Array.isArray(updates['options'])) {
206
+ const newOptions = updates['options'];
207
+ const current = tools.getField(fieldId);
208
+ const currentOptions = current?.definition?.options ?? [];
209
+ // Remove options not in the new list.
210
+ for (const existing of currentOptions) {
211
+ const kept = newOptions.some((o) => (typeof o === 'string' ? o : o.value) ===
212
+ existing.value || o.id === existing.id);
213
+ if (!kept)
214
+ tools.option.remove(fieldId, existing.id);
215
+ }
216
+ // Add options not already present.
217
+ for (const o of newOptions) {
218
+ const value = typeof o === 'string' ? o : o.value ?? '';
219
+ const alreadyExists = currentOptions.some((ex) => ex.value === value);
220
+ if (!alreadyExists)
221
+ tools.option.add(fieldId, value);
222
+ }
223
+ // Strip options from the patch so updateField doesn't clobber the store.
224
+ const { options: _options, ...rest } = updates;
225
+ void _options;
226
+ updates = rest;
227
+ }
228
+ if (Object.keys(updates).length === 0)
229
+ return `Updated field ${fieldId}`;
230
+ if ('rows' in updates || 'columns' in updates)
231
+ return `Cannot set rows/columns via update_field. Use add_row/add_column to add items one at a time.`;
232
+ const ok = tools.updateField(fieldId, updates);
233
+ return ok ? `Updated field ${fieldId}` : `Field not found: ${fieldId}`;
234
+ }
235
+ function deleteField(args, tools) {
236
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
237
+ if (!fieldId)
238
+ return `Field not found: ${args.fieldId ?? args.fieldQuestion}`;
239
+ const ok = tools.removeField(fieldId);
240
+ return ok ? `Deleted field ${fieldId}` : `Field not found: ${fieldId}`;
241
+ }
242
+ function getFieldDetail(args, tools) {
243
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
244
+ if (!fieldId)
245
+ return `Field not found: ${args.fieldId ?? args.fieldQuestion}`;
246
+ const node = tools.getField(fieldId);
247
+ if (!node)
248
+ return `Field not found: ${fieldId}`;
249
+ const def = node.definition;
250
+ return { id: fieldId, ...def };
251
+ }
252
+ function moveField(args, tools) {
253
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
254
+ if (!fieldId)
255
+ return `Field not found: ${args.fieldId ?? args.fieldQuestion}`;
256
+ const toIndex = args.toIndex;
257
+ if (typeof toIndex !== 'number')
258
+ return `Missing 'toIndex' (number)`;
259
+ const ok = tools.moveField(fieldId, toIndex, args.toParentId ?? null);
260
+ return ok
261
+ ? `Moved field ${fieldId} to index ${toIndex}`
262
+ : `Failed to move field ${fieldId}`;
263
+ }
264
+ function addRow(args, tools) {
265
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
266
+ if (!fieldId)
267
+ return `Field not found`;
268
+ const fieldType = getFieldType(fieldId, tools);
269
+ if (fieldType && !MATRIX_TYPES.has(fieldType))
270
+ return `Field '${fieldId}' is a ${fieldType} field — rows only apply to singlematrix/multimatrix. Use add_option for option-based fields.`;
271
+ const rowId = tools.row.add(fieldId, args.value);
272
+ return rowId ? `Added row ${rowId}` : `Field not found or not a matrix field`;
273
+ }
274
+ function updateRow(args, tools) {
275
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
276
+ if (!fieldId)
277
+ return `Field not found`;
278
+ let rowId = args.rowId;
279
+ if (!rowId && args.value) {
280
+ const node = tools.getField(fieldId);
281
+ const rows = node?.definition?.rows ?? [];
282
+ const match = rows.find((r) => r.value === args.currentValue);
283
+ if (match)
284
+ rowId = match.id;
285
+ }
286
+ if (!rowId)
287
+ return `Missing 'rowId'`;
288
+ const ok = tools.row.update(fieldId, rowId, args.value);
289
+ return ok ? `Updated row ${rowId}` : `Row not found`;
290
+ }
291
+ function removeRow(args, tools) {
292
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
293
+ if (!fieldId)
294
+ return `Field not found`;
295
+ const ok = tools.row.remove(fieldId, args.rowId);
296
+ return ok ? `Removed row ${args.rowId}` : `Row not found`;
297
+ }
298
+ function addColumn(args, tools) {
299
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
300
+ if (!fieldId)
301
+ return `Field not found`;
302
+ const fieldType = getFieldType(fieldId, tools);
303
+ if (fieldType && !MATRIX_TYPES.has(fieldType))
304
+ return `Field '${fieldId}' is a ${fieldType} field — columns only apply to singlematrix/multimatrix fields.`;
305
+ const colId = tools.column.add(fieldId, args.value);
306
+ return colId
307
+ ? `Added column ${colId}`
308
+ : `Field not found or not a matrix field`;
309
+ }
310
+ function updateColumn(args, tools) {
311
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
312
+ if (!fieldId)
313
+ return `Field not found`;
314
+ let columnId = args.columnId;
315
+ if (!columnId && args.value) {
316
+ const node = tools.getField(fieldId);
317
+ const cols = node?.definition?.columns ?? [];
318
+ const match = cols.find((c) => c.value === args.currentValue);
319
+ if (match)
320
+ columnId = match.id;
321
+ }
322
+ if (!columnId)
323
+ return `Missing 'columnId'`;
324
+ const ok = tools.column.update(fieldId, columnId, args.value);
325
+ return ok ? `Updated column ${columnId}` : `Column not found`;
326
+ }
327
+ function removeColumn(args, tools) {
328
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
329
+ if (!fieldId)
330
+ return `Field not found`;
331
+ const ok = tools.column.remove(fieldId, args.columnId);
332
+ return ok ? `Removed column ${args.columnId}` : `Column not found`;
333
+ }
334
+ function getFieldSpec(args, tools) {
335
+ const fieldType = args.fieldType;
336
+ if (!fieldType)
337
+ return `Missing 'fieldType'`;
338
+ const spec = tools.getFieldSpec(fieldType);
339
+ if (!spec)
340
+ return `Unknown field type: ${fieldType}`;
341
+ return spec;
342
+ }
343
+ const MATRIX_TYPES = new Set(['singlematrix', 'multimatrix']);
344
+ const OPTION_TYPES = new Set([
345
+ 'radio',
346
+ 'check',
347
+ 'boolean',
348
+ 'dropdown',
349
+ 'multiselectdropdown',
350
+ 'rating',
351
+ 'ranking',
352
+ 'slider',
353
+ 'multitext',
354
+ ]);
355
+ function getFieldType(fieldId, tools) {
356
+ const node = tools.getField(fieldId);
357
+ return node?.definition?.fieldType;
358
+ }
359
+ function addOption(args, tools) {
360
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
361
+ if (!fieldId)
362
+ return `Field not found`;
363
+ const fieldType = getFieldType(fieldId, tools);
364
+ if (fieldType && MATRIX_TYPES.has(fieldType))
365
+ return `'${fieldId}' is a ${fieldType} field — use add_row/add_column, not add_option.`;
366
+ if (fieldType && !OPTION_TYPES.has(fieldType))
367
+ return `'${fieldId}' is a ${fieldType} field — it does not support options.`;
368
+ const optId = tools.option.add(fieldId, args.value);
369
+ return optId
370
+ ? `Added option ${optId}`
371
+ : `Field not found or not an option field`;
372
+ }
373
+ function updateOption(args, tools) {
374
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
375
+ if (!fieldId)
376
+ return `Field not found`;
377
+ let optionId = args.optionId;
378
+ // Allow resolving by current label if id not provided
379
+ if (!optionId && args.currentValue) {
380
+ const node = tools.getField(fieldId);
381
+ const opts = node?.definition?.options ?? [];
382
+ const match = opts.find((o) => o.value === args.currentValue);
383
+ if (match)
384
+ optionId = match.id;
385
+ }
386
+ if (!optionId)
387
+ return `Missing 'optionId' or 'currentValue' to identify option`;
388
+ const ok = tools.option.update(fieldId, optionId, args.value);
389
+ return ok ? `Updated option ${optionId}` : `Option not found`;
390
+ }
391
+ function removeOption(args, tools) {
392
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
393
+ if (!fieldId)
394
+ return `Field not found`;
395
+ let optionId = args.optionId;
396
+ if (!optionId && args.currentValue) {
397
+ const node = tools.getField(fieldId);
398
+ const opts = node?.definition?.options ?? [];
399
+ const match = opts.find((o) => o.value === args.currentValue);
400
+ if (match)
401
+ optionId = match.id;
402
+ }
403
+ if (!optionId)
404
+ return `Missing 'optionId' or 'currentValue' to identify option`;
405
+ const ok = tools.option.remove(fieldId, optionId);
406
+ return ok ? `Removed option ${optionId}` : `Option not found`;
407
+ }
408
+ function addFieldRule(args, tools) {
409
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
410
+ if (!fieldId)
411
+ return `Field not found`;
412
+ const effect = args.effect;
413
+ const logic = args.logic ?? 'AND';
414
+ const conditions = args.conditions;
415
+ if (!effect)
416
+ return `Missing 'effect' (visible|enable|required)`;
417
+ if (!Array.isArray(conditions) || conditions.length === 0)
418
+ return `Missing 'conditions' array`;
419
+ const node = tools.getField(fieldId);
420
+ if (!node)
421
+ return `Field not found: ${fieldId}`;
422
+ const mappedConditions = conditions.map((c) => ({
423
+ conditionType: 'field',
424
+ targetId: c['targetId'],
425
+ operator: c['operator'],
426
+ ...(c['expected'] !== undefined && { expected: c['expected'] }),
427
+ ...(c['propertyAccessor'] !== undefined && {
428
+ propertyAccessor: c['propertyAccessor'],
429
+ }),
430
+ }));
431
+ const existing = node.definition.rules ?? [];
432
+ const newRule = { effect, logic, conditions: mappedConditions };
433
+ const ok = tools.updateField(fieldId, { rules: [...existing, newRule] });
434
+ return ok
435
+ ? `Added ${effect} field rule (index ${existing.length}) to field ${fieldId}`
436
+ : `Failed to update field ${fieldId}`;
437
+ }
438
+ function addExpressionRule(args, tools) {
439
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
440
+ if (!fieldId)
441
+ return `Field not found`;
442
+ const effect = args.effect;
443
+ const expression = args.expression;
444
+ if (!effect)
445
+ return `Missing 'effect' (visible|enable|required)`;
446
+ if (!expression?.trim())
447
+ return `Missing 'expression'`;
448
+ const node = tools.getField(fieldId);
449
+ if (!node)
450
+ return `Field not found: ${fieldId}`;
451
+ const existing = node.definition.rules ?? [];
452
+ const newRule = {
453
+ effect,
454
+ logic: 'AND',
455
+ conditions: [
456
+ { conditionType: 'expression', expression: expression.trim() },
457
+ ],
458
+ };
459
+ const ok = tools.updateField(fieldId, { rules: [...existing, newRule] });
460
+ return ok
461
+ ? `Added ${effect} expression rule (index ${existing.length}) to field ${fieldId}`
462
+ : `Failed to update field ${fieldId}`;
463
+ }
464
+ function removeRule(args, tools) {
465
+ const fieldId = tools.resolveFieldId(args.fieldId, args.fieldQuestion);
466
+ if (!fieldId)
467
+ return `Field not found`;
468
+ const ruleIndex = args.ruleIndex;
469
+ if (typeof ruleIndex !== 'number')
470
+ return `Missing 'ruleIndex' (number)`;
471
+ const node = tools.getField(fieldId);
472
+ if (!node)
473
+ return `Field not found: ${fieldId}`;
474
+ const existing = node.definition.rules ?? [];
475
+ if (ruleIndex < 0 || ruleIndex >= existing.length)
476
+ return `Rule index ${ruleIndex} out of range (field has ${existing.length} rule(s))`;
477
+ const updated = existing.filter((_, i) => i !== ruleIndex);
478
+ const ok = tools.updateField(fieldId, { rules: updated });
479
+ return ok
480
+ ? `Removed rule ${ruleIndex} from field ${fieldId}`
481
+ : `Failed to update field ${fieldId}`;
482
+ }
@@ -0,0 +1,31 @@
1
+ import type { BuilderTools } from '../builder-tools.js';
2
+ export interface UseBuilderMcpToolHandlerOptions {
3
+ /**
4
+ * The DOM element to listen on for tool-call events.
5
+ * Pass `document` to listen globally, or a specific element to scope it.
6
+ */
7
+ target?: EventTarget;
8
+ /**
9
+ * The event name to listen for, e.g. `'ozwell-tool-call'`.
10
+ */
11
+ eventName: string;
12
+ }
13
+ /**
14
+ * Listens for AI tool-call events on a target element and dispatches them to
15
+ * the builder's MCP tool executor. Returns the `onBuilderToolsReady` callback
16
+ * to pass to `<EsheetBuilder>`.
17
+ *
18
+ * @example – default (listens on document)
19
+ * const onBuilderToolsReady = useBuilderMcpToolHandler();
20
+ * <EsheetBuilder onBuilderToolsReady={onBuilderToolsReady} />
21
+ *
22
+ * @example – scoped to a container element
23
+ * const ref = React.useRef<HTMLDivElement>(null);
24
+ * const onBuilderToolsReady = useBuilderMcpToolHandler({ target: ref.current ?? undefined });
25
+ * <div ref={ref}><EsheetBuilder onBuilderToolsReady={onBuilderToolsReady} /></div>
26
+ *
27
+ * @example – custom event name
28
+ * const onBuilderToolsReady = useBuilderMcpToolHandler({ eventName: 'my-ai-tool-call' });
29
+ */
30
+ export declare function useBuilderMcpToolHandler(options: UseBuilderMcpToolHandlerOptions): (tools: BuilderTools) => void;
31
+ //# sourceMappingURL=useBuilderToolBridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBuilderToolBridge.d.ts","sourceRoot":"","sources":["../../../src/lib/mcp/useBuilderToolBridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAgD/B"}
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import { executeToolCall } from './tool-executor.js';
3
+ /**
4
+ * Listens for AI tool-call events on a target element and dispatches them to
5
+ * the builder's MCP tool executor. Returns the `onBuilderToolsReady` callback
6
+ * to pass to `<EsheetBuilder>`.
7
+ *
8
+ * @example – default (listens on document)
9
+ * const onBuilderToolsReady = useBuilderMcpToolHandler();
10
+ * <EsheetBuilder onBuilderToolsReady={onBuilderToolsReady} />
11
+ *
12
+ * @example – scoped to a container element
13
+ * const ref = React.useRef<HTMLDivElement>(null);
14
+ * const onBuilderToolsReady = useBuilderMcpToolHandler({ target: ref.current ?? undefined });
15
+ * <div ref={ref}><EsheetBuilder onBuilderToolsReady={onBuilderToolsReady} /></div>
16
+ *
17
+ * @example – custom event name
18
+ * const onBuilderToolsReady = useBuilderMcpToolHandler({ eventName: 'my-ai-tool-call' });
19
+ */
20
+ export function useBuilderMcpToolHandler(options) {
21
+ const { target, eventName } = options;
22
+ const toolsRef = React.useRef(null);
23
+ const onBuilderToolsReady = React.useCallback((tools) => {
24
+ toolsRef.current = tools;
25
+ }, []);
26
+ React.useEffect(() => {
27
+ const el = target ?? document;
28
+ function onToolCall(e) {
29
+ const { name: rawName, arguments: args, respond, } = e.detail;
30
+ // Strip 'postMessage:' prefix if present (added by Ozwell widget internally)
31
+ const name = rawName.replace(/^postMessage:/, '');
32
+ if (!toolsRef.current) {
33
+ respond({ success: false, message: 'Builder not ready' });
34
+ return;
35
+ }
36
+ const toResponse = (r) => typeof r === 'string' ? { result: r } : r;
37
+ try {
38
+ const result = executeToolCall(name, args, toolsRef.current);
39
+ if (result instanceof Promise) {
40
+ result
41
+ .then((r) => respond(toResponse(r)))
42
+ .catch((err) => respond({ error: String(err) }));
43
+ }
44
+ else {
45
+ respond(toResponse(result));
46
+ }
47
+ }
48
+ catch (err) {
49
+ respond({ error: String(err) });
50
+ }
51
+ }
52
+ el.addEventListener(eventName, onToolCall);
53
+ return () => el.removeEventListener(eventName, onToolCall);
54
+ }, [target, eventName]);
55
+ return onBuilderToolsReady;
56
+ }
@@ -0,0 +1,2 @@
1
+ export declare function ensureDefaultFieldComponentsRegistered(): void;
2
+ //# sourceMappingURL=register-defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-defaults.d.ts","sourceRoot":"","sources":["../../src/lib/register-defaults.ts"],"names":[],"mappings":"AAkCA,wBAAgB,sCAAsC,IAAI,IAAI,CA0B7D"}
@@ -0,0 +1,37 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Built-in field component registration
3
+ // ---------------------------------------------------------------------------
4
+ // Registers built-in field components for basic field types.
5
+ // Explicitly invoked from EsheetBuilder at runtime via
6
+ // ensureDefaultFieldComponentsRegistered().
7
+ // Safe to call multiple times; registration is idempotent.
8
+ // ---------------------------------------------------------------------------
9
+ import { registerFieldComponents } from '@esheet/fields';
10
+ import { TextField, LongTextField, MultiTextField, RadioField, CheckField, BooleanField, DropdownField, MultiSelectDropdownField, RatingField, RankingField, SliderField, SingleMatrixField, MultiMatrixField, SectionField, SignatureField, DiagramField, ImageField, HtmlField, DisplayField, } from '@esheet/fields';
11
+ let defaultFieldComponentsRegistered = false;
12
+ export function ensureDefaultFieldComponentsRegistered() {
13
+ if (defaultFieldComponentsRegistered)
14
+ return;
15
+ registerFieldComponents({
16
+ text: TextField,
17
+ longtext: LongTextField,
18
+ multitext: MultiTextField,
19
+ radio: RadioField,
20
+ check: CheckField,
21
+ boolean: BooleanField,
22
+ dropdown: DropdownField,
23
+ multiselectdropdown: MultiSelectDropdownField,
24
+ rating: RatingField,
25
+ ranking: RankingField,
26
+ slider: SliderField,
27
+ singlematrix: SingleMatrixField,
28
+ multimatrix: MultiMatrixField,
29
+ section: SectionField,
30
+ signature: SignatureField,
31
+ diagram: DiagramField,
32
+ image: ImageField,
33
+ html: HtmlField,
34
+ display: DisplayField,
35
+ });
36
+ defaultFieldComponentsRegistered = true;
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esheet/builder",
3
- "version": "0.0.4-0",
3
+ "version": "0.0.4-1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mieweb/eSheet"
@@ -38,9 +38,9 @@
38
38
  ]
39
39
  },
40
40
  "dependencies": {
41
- "@esheet/adapters": "0.0.3",
42
- "@esheet/core": "0.0.3",
43
- "@esheet/fields": "0.0.3",
41
+ "@esheet/adapters": "0.0.4-1",
42
+ "@esheet/core": "0.0.4-1",
43
+ "@esheet/fields": "0.0.4-1",
44
44
  "@mieweb/ui": "^0.6.1",
45
45
  "@monaco-editor/react": "^4.7.0",
46
46
  "js-yaml": "^4.1.1",