@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
package/dist/index.js CHANGED
@@ -4133,116 +4133,6 @@ var Cd = Kt(
4133
4133
  );
4134
4134
  da.displayName = "Slider";
4135
4135
  var Ed = Kt(
4136
- [
4137
- "w-full px-3 py-2",
4138
- "border border-input rounded-lg",
4139
- "bg-background text-foreground",
4140
- "placeholder:text-muted-foreground",
4141
- "transition-colors duration-200",
4142
- "focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent",
4143
- "disabled:cursor-not-allowed disabled:opacity-50"
4144
- ],
4145
- {
4146
- variants: {
4147
- size: {
4148
- sm: "h-8 text-sm",
4149
- md: "h-10 text-base",
4150
- lg: "h-12 text-lg"
4151
- },
4152
- hasError: {
4153
- true: "border-destructive focus:ring-destructive",
4154
- false: ""
4155
- }
4156
- },
4157
- defaultVariants: {
4158
- size: "md",
4159
- hasError: !1
4160
- }
4161
- }
4162
- ), vo = B.forwardRef(
4163
- ({
4164
- className: e,
4165
- size: t,
4166
- hasError: r,
4167
- error: n,
4168
- helperText: s,
4169
- label: i,
4170
- hideLabel: o,
4171
- required: l,
4172
- disabled: a,
4173
- id: d,
4174
- "aria-describedby": c,
4175
- ...u
4176
- }, f) => {
4177
- const p = B.useId(), h = d || p, b = `${h}-error`, v = `${h}-helper`, w = [
4178
- n ? b : null,
4179
- s && !n ? v : null,
4180
- c
4181
- ].filter(Boolean).join(" ");
4182
- return /* @__PURE__ */ g(
4183
- "div",
4184
- {
4185
- "data-slot": "input-wrapper",
4186
- className: ue("flex flex-col gap-1.5", a && "opacity-50"),
4187
- children: [
4188
- i && /* @__PURE__ */ g(
4189
- "label",
4190
- {
4191
- "data-slot": "input-label",
4192
- htmlFor: h,
4193
- className: ue(
4194
- "text-foreground text-sm font-medium",
4195
- o && "sr-only"
4196
- ),
4197
- children: [
4198
- i,
4199
- l && /* @__PURE__ */ m("span", { className: "text-destructive ml-1", "aria-hidden": "true", children: "*" })
4200
- ]
4201
- }
4202
- ),
4203
- /* @__PURE__ */ m(
4204
- "input",
4205
- {
4206
- "data-slot": "input",
4207
- id: h,
4208
- ref: f,
4209
- className: ue(
4210
- Ed({ size: t, hasError: r || !!n }),
4211
- e
4212
- ),
4213
- "aria-invalid": r || !!n,
4214
- "aria-describedby": w || void 0,
4215
- required: l,
4216
- disabled: a,
4217
- ...u
4218
- }
4219
- ),
4220
- n && /* @__PURE__ */ m(
4221
- "p",
4222
- {
4223
- id: b,
4224
- "data-slot": "input-error",
4225
- className: "text-destructive-700 dark:text-destructive-400 text-sm",
4226
- role: "alert",
4227
- children: n
4228
- }
4229
- ),
4230
- s && !n && /* @__PURE__ */ m(
4231
- "p",
4232
- {
4233
- id: v,
4234
- "data-slot": "input-helper",
4235
- className: "text-muted-foreground text-sm",
4236
- children: s
4237
- }
4238
- )
4239
- ]
4240
- }
4241
- );
4242
- }
4243
- );
4244
- vo.displayName = "Input";
4245
- var Td = Kt(
4246
4136
  [
4247
4137
  "shrink-0 appearance-none",
4248
4138
  "border-2 border-input rounded",
@@ -4300,19 +4190,19 @@ var Td = Kt(
4300
4190
  disabled: a,
4301
4191
  "aria-describedby": [n ? h : null, i ? b : null].filter(Boolean).join(" ") || void 0,
4302
4192
  "aria-invalid": !!i,
4303
- className: ue("peer", Td({ size: t }), e),
4193
+ className: ue("peer", Ed({ size: t }), e),
4304
4194
  ...d
4305
4195
  }
4306
4196
  ),
4307
4197
  /* @__PURE__ */ m(
4308
- Id,
4198
+ Td,
4309
4199
  {
4310
4200
  size: t,
4311
4201
  className: "pointer-events-none absolute text-white opacity-0 transition-opacity peer-checked:opacity-100"
4312
4202
  }
4313
4203
  ),
4314
4204
  /* @__PURE__ */ m(
4315
- _d,
4205
+ Id,
4316
4206
  {
4317
4207
  size: t,
4318
4208
  className: "pointer-events-none absolute text-white opacity-0 transition-opacity peer-indeterminate:opacity-100"
@@ -4373,7 +4263,7 @@ var Td = Kt(
4373
4263
  }
4374
4264
  );
4375
4265
  Yn.displayName = "Checkbox";
4376
- function Id({ size: e, className: t }) {
4266
+ function Td({ size: e, className: t }) {
4377
4267
  const r = {
4378
4268
  sm: 10,
4379
4269
  md: 12,
@@ -4397,7 +4287,7 @@ function Id({ size: e, className: t }) {
4397
4287
  }
4398
4288
  );
4399
4289
  }
4400
- function _d({ size: e, className: t }) {
4290
+ function Id({ size: e, className: t }) {
4401
4291
  const r = {
4402
4292
  sm: 10,
4403
4293
  md: 12,
@@ -4421,6 +4311,116 @@ function _d({ size: e, className: t }) {
4421
4311
  }
4422
4312
  );
4423
4313
  }
4314
+ var _d = Kt(
4315
+ [
4316
+ "w-full px-3 py-2",
4317
+ "border border-input rounded-lg",
4318
+ "bg-background text-foreground",
4319
+ "placeholder:text-muted-foreground",
4320
+ "transition-colors duration-200",
4321
+ "focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent",
4322
+ "disabled:cursor-not-allowed disabled:opacity-50"
4323
+ ],
4324
+ {
4325
+ variants: {
4326
+ size: {
4327
+ sm: "h-8 text-sm",
4328
+ md: "h-10 text-base",
4329
+ lg: "h-12 text-lg"
4330
+ },
4331
+ hasError: {
4332
+ true: "border-destructive focus:ring-destructive",
4333
+ false: ""
4334
+ }
4335
+ },
4336
+ defaultVariants: {
4337
+ size: "md",
4338
+ hasError: !1
4339
+ }
4340
+ }
4341
+ ), vo = B.forwardRef(
4342
+ ({
4343
+ className: e,
4344
+ size: t,
4345
+ hasError: r,
4346
+ error: n,
4347
+ helperText: s,
4348
+ label: i,
4349
+ hideLabel: o,
4350
+ required: l,
4351
+ disabled: a,
4352
+ id: d,
4353
+ "aria-describedby": c,
4354
+ ...u
4355
+ }, f) => {
4356
+ const p = B.useId(), h = d || p, b = `${h}-error`, v = `${h}-helper`, w = [
4357
+ n ? b : null,
4358
+ s && !n ? v : null,
4359
+ c
4360
+ ].filter(Boolean).join(" ");
4361
+ return /* @__PURE__ */ g(
4362
+ "div",
4363
+ {
4364
+ "data-slot": "input-wrapper",
4365
+ className: ue("flex flex-col gap-1.5", a && "opacity-50"),
4366
+ children: [
4367
+ i && /* @__PURE__ */ g(
4368
+ "label",
4369
+ {
4370
+ "data-slot": "input-label",
4371
+ htmlFor: h,
4372
+ className: ue(
4373
+ "text-foreground text-sm font-medium",
4374
+ o && "sr-only"
4375
+ ),
4376
+ children: [
4377
+ i,
4378
+ l && /* @__PURE__ */ m("span", { className: "text-destructive ml-1", "aria-hidden": "true", children: "*" })
4379
+ ]
4380
+ }
4381
+ ),
4382
+ /* @__PURE__ */ m(
4383
+ "input",
4384
+ {
4385
+ "data-slot": "input",
4386
+ id: h,
4387
+ ref: f,
4388
+ className: ue(
4389
+ _d({ size: t, hasError: r || !!n }),
4390
+ e
4391
+ ),
4392
+ "aria-invalid": r || !!n,
4393
+ "aria-describedby": w || void 0,
4394
+ required: l,
4395
+ disabled: a,
4396
+ ...u
4397
+ }
4398
+ ),
4399
+ n && /* @__PURE__ */ m(
4400
+ "p",
4401
+ {
4402
+ id: b,
4403
+ "data-slot": "input-error",
4404
+ className: "text-destructive-700 dark:text-destructive-400 text-sm",
4405
+ role: "alert",
4406
+ children: n
4407
+ }
4408
+ ),
4409
+ s && !n && /* @__PURE__ */ m(
4410
+ "p",
4411
+ {
4412
+ id: v,
4413
+ "data-slot": "input-helper",
4414
+ className: "text-muted-foreground text-sm",
4415
+ children: s
4416
+ }
4417
+ )
4418
+ ]
4419
+ }
4420
+ );
4421
+ }
4422
+ );
4423
+ vo.displayName = "Input";
4424
4424
  function Ad(e) {
4425
4425
  if (!e) return e;
4426
4426
  if (e.startsWith("+")) {
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { type FormDefinition } from '@esheet/core';
3
+ import { type BuilderTools, type FieldSummary } from './builder-tools.js';
4
+ export { FormStoreContext, UIContext, useFormStore, useUI, } from '@esheet/fields';
5
+ export declare const InstanceIdContext: React.Context<string>;
6
+ /** Hook to access the per-instance ID for unique DOM element IDs. */
7
+ export declare function useInstanceId(): string;
8
+ export type { BuilderTools, FieldSummary };
9
+ export interface EsheetBuilderProps {
10
+ /** Initial form definition to load. Also accepts SurveyJS, MCP elicitation, or FHIR Questionnaire schemas, which are auto-converted. */
11
+ definition?: FormDefinition | Record<string, unknown>;
12
+ /** Callback fired when the form definition changes. */
13
+ onChange?: (definition: FormDefinition) => void;
14
+ /**
15
+ * Called once after the builder mounts, providing a narrow `BuilderTools`
16
+ * facade for MCP / AI tool integrations. Not intended for general developer use —
17
+ * everything a developer needs is available through the builder's own UI and props.
18
+ */
19
+ onBuilderToolsReady?: (tools: BuilderTools) => void;
20
+ /** Whether drag-and-drop reordering is enabled (default: true). When false, field reordering is disabled entirely — no fallback UI (e.g. arrow buttons) is shown. */
21
+ dragEnabled?: boolean;
22
+ /** Additional CSS class name. */
23
+ className?: string;
24
+ /** Optional content rendered below the header (e.g. custom status/debug panels). */
25
+ children?: React.ReactNode;
26
+ /**
27
+ * Enable touch-optimized mode with larger touch targets in preview mode.
28
+ * - `true`: Always enable touch mode
29
+ * - `false`: Never enable touch mode (CSS media query still applies)
30
+ * - `'auto'`: Enable based on viewport width (<980px) via JavaScript
31
+ * - `undefined`: Rely on CSS media query only (default)
32
+ */
33
+ touchMode?: boolean | 'auto';
34
+ /** Called when touch mode changes (via auto-detection or programmatic toggle). */
35
+ onTouchModeChange?: (enabled: boolean) => void;
36
+ }
37
+ export interface EsheetBuilderHandle {
38
+ /** Returns true if touch mode is currently enabled */
39
+ isTouchModeEnabled: () => boolean;
40
+ /** Toggle touch mode on/off. Only works when touchMode prop is 'auto' or undefined. */
41
+ setTouchMode: (enabled: boolean) => void;
42
+ /** Reset to auto-detection mode (clears manual override). Only works when touchMode='auto'. */
43
+ resetTouchMode: () => void;
44
+ }
45
+ export declare const EsheetBuilder: React.ForwardRefExoticComponent<EsheetBuilderProps & React.RefAttributes<EsheetBuilderHandle>>;
46
+ //# sourceMappingURL=EsheetBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EsheetBuilder.d.ts","sourceRoot":"","sources":["../../src/lib/EsheetBuilder.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAGL,KAAK,cAAc,EAGpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AA2B5B,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,KAAK,GACN,MAAM,gBAAgB,CAAC;AACxB,eAAO,MAAM,iBAAiB,uBAAkC,CAAC;AAEjE,qEAAqE;AACrE,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAGD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AAM3C,MAAM,WAAW,kBAAkB;IACjC,wIAAwI;IACxI,UAAU,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtD,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACpD,qKAAqK;IACrK,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,kBAAkB,EAAE,MAAM,OAAO,CAAC;IAClC,uFAAuF;IACvF,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,+FAA+F;IAC/F,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAMD,eAAO,MAAM,aAAa,gGAuMxB,CAAC"}
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { useSyncExternalStore } from 'react';
4
+ import { createFormStore, createUIStore, } from '@esheet/core';
5
+ import { createBuilderTools, } from './builder-tools.js';
6
+ import { FormStoreContext, UIContext, useTouchMode } from '@esheet/fields';
7
+ import { convertSurveyJS, isSurveyJSSchema, importFromMcp, isMcpElicitationRequest, importFromFhir, isFhirQuestionnaire, } from '@esheet/adapters';
8
+ import { Canvas } from './components/Canvas.js';
9
+ import { ToolPanel } from './components/ToolPanel.js';
10
+ import { EditPanel } from './components/edit-panel/EditPanel.js';
11
+ import { BuilderHeader } from './components/BuilderHeader.js';
12
+ import { CodeView } from './components/CodeView.js';
13
+ import { PlusIcon } from './icons.js';
14
+ import { ensureDefaultFieldComponentsRegistered } from './register-defaults.js';
15
+ import { MobileBottomDrawer } from './components/MobileBottomDrawer.js';
16
+ import { Switch } from '@mieweb/ui';
17
+ // ---------------------------------------------------------------------------
18
+ // Contexts
19
+ // ---------------------------------------------------------------------------
20
+ export { FormStoreContext, UIContext, useFormStore, useUI, } from '@esheet/fields';
21
+ export const InstanceIdContext = React.createContext('');
22
+ /** Hook to access the per-instance ID for unique DOM element IDs. */
23
+ export function useInstanceId() {
24
+ return React.useContext(InstanceIdContext);
25
+ }
26
+ // ---------------------------------------------------------------------------
27
+ // Component
28
+ // ---------------------------------------------------------------------------
29
+ export const EsheetBuilder = React.forwardRef(function EsheetBuilder({ definition, onChange, onBuilderToolsReady, dragEnabled = true, className = '', children, touchMode: touchModeProp, onTouchModeChange, }, ref) {
30
+ ensureDefaultFieldComponentsRegistered();
31
+ const formRef = React.useRef(null);
32
+ const uiRef = React.useRef(null);
33
+ if (!formRef.current) {
34
+ let resolved;
35
+ if (isFhirQuestionnaire(definition)) {
36
+ resolved = importFromFhir(definition);
37
+ }
38
+ else if (isSurveyJSSchema(definition)) {
39
+ resolved = convertSurveyJS(definition);
40
+ }
41
+ else if (isMcpElicitationRequest(definition)) {
42
+ const mcpReq = definition;
43
+ if (mcpReq.params.mode !== 'url') {
44
+ resolved = importFromMcp(mcpReq.params.requestedSchema, { mcpId: mcpReq.id, mcpMessage: mcpReq.params.message });
45
+ }
46
+ }
47
+ else {
48
+ resolved = definition;
49
+ }
50
+ formRef.current = createFormStore(resolved);
51
+ }
52
+ if (!uiRef.current) {
53
+ uiRef.current = createUIStore();
54
+ }
55
+ const form = formRef.current;
56
+ const ui = uiRef.current;
57
+ // Stable per-instance ID for unique DOM element IDs
58
+ const instanceId = React.useId();
59
+ // Subscribe to mode for conditional rendering
60
+ const mode = useSyncExternalStore((cb) => ui.subscribe(cb), () => ui.getState().mode, () => ui.getState().mode);
61
+ const selectedFieldId = useSyncExternalStore((cb) => ui.subscribe(cb), () => ui.getState().selectedFieldId, () => ui.getState().selectedFieldId);
62
+ const editModalOpen = useSyncExternalStore((cb) => ui.subscribe(cb), () => ui.getState().editModalOpen, () => ui.getState().editModalOpen);
63
+ const [toolsModalOpen, setToolsModalOpen] = React.useState(false);
64
+ // Touch mode state using shared hook
65
+ const { isTouchEnabled, isManualOverride, setTouchMode, resetTouchMode } = useTouchMode({ mode: touchModeProp, onChange: onTouchModeChange });
66
+ // Expose ref API for touch mode
67
+ React.useImperativeHandle(ref, () => ({
68
+ isTouchModeEnabled: () => isTouchEnabled,
69
+ setTouchMode,
70
+ resetTouchMode,
71
+ }), [isTouchEnabled, setTouchMode, resetTouchMode]);
72
+ React.useEffect(() => {
73
+ if (mode !== 'build') {
74
+ setToolsModalOpen(false);
75
+ ui.getState().setEditModalOpen(false);
76
+ }
77
+ }, [mode, ui]);
78
+ React.useEffect(() => {
79
+ if (!selectedFieldId && editModalOpen) {
80
+ ui.getState().setEditModalOpen(false);
81
+ }
82
+ }, [selectedFieldId, editModalOpen, ui]);
83
+ // Subscribe to form changes and forward to onChange.
84
+ React.useEffect(() => {
85
+ if (!onChange)
86
+ return;
87
+ return form.subscribe(() => {
88
+ onChange(form.getState().hydrateDefinition());
89
+ });
90
+ }, [form, onChange]);
91
+ // Expose a narrow BuilderTools facade to MCP / AI tool callers once on mount.
92
+ React.useEffect(() => {
93
+ if (onBuilderToolsReady)
94
+ onBuilderToolsReady(createBuilderTools(form));
95
+ // eslint-disable-next-line react-hooks/exhaustive-deps
96
+ }, [form]);
97
+ // Apply disabled class when user explicitly disabled touch mode (prevents CSS media query)
98
+ const applyTouchDisabledClass = isManualOverride && !isTouchEnabled;
99
+ // Build root class string
100
+ const rootClasses = [
101
+ 'ms-builder-root',
102
+ isTouchEnabled && 'esheet-touch-active',
103
+ applyTouchDisabledClass && 'touch-mode-disabled',
104
+ 'ms:flex ms:h-full ms:flex-1 ms:min-h-0 ms:max-h-full ms:w-full ms:min-w-0 ms:max-w-[1440px] ms:mx-auto ms:flex-col ms:gap-2',
105
+ 'ms:overflow-x-hidden ms:bg-msbackground ms:text-mstext',
106
+ className,
107
+ ]
108
+ .filter(Boolean)
109
+ .join(' ');
110
+ return (_jsx(FormStoreContext.Provider, { value: form, children: _jsx(UIContext.Provider, { value: ui, children: _jsx(InstanceIdContext.Provider, { value: instanceId, children: _jsxs("div", { className: rootClasses, children: [_jsx("div", { className: "ms:sticky ms:top-0 ms:z-40 ms:bg-msbackground", children: _jsx(BuilderHeader, {}) }), children, mode === 'build' && (_jsxs("div", { className: "builder-layout ms:grid ms:min-w-0 ms:grid-cols-1 ms:lg:grid-cols-[18rem_minmax(0,1fr)_340px] ms:gap-3", children: [_jsx("aside", { className: "panel-tools-wrap panel-tools ms:hidden ms:lg:flex ms:self-start ms:min-h-0 ms:max-h-[calc(100dvh-12.5rem)] ms:overflow-y-auto ms:flex-col ms:rounded-lg ms:border ms:border-msborder ms:bg-mssurface", children: _jsx(ToolPanel, {}) }), _jsxs("main", { className: "panel-canvas ms:min-w-0 ms:self-start ms:max-h-[calc(100dvh-12.5rem)] ms:overflow-hidden ms:flex ms:flex-col ms:rounded-lg ms:border ms:border-msborder ms:bg-mssurface", children: [_jsx(Canvas, { form: form, ui: ui, dragEnabled: dragEnabled }), _jsx("div", { className: "ms:lg:hidden ms:sticky ms:bottom-0 ms:z-20 ms:pt-2 ms:pb-3 ms:flex ms:justify-center ms:pointer-events-none", children: _jsxs("button", { type: "button", onClick: () => setToolsModalOpen(true), className: "ms:pointer-events-auto ms:inline-flex ms:items-center ms:gap-1.5 ms:px-3.5 ms:py-2 ms:rounded-full ms:bg-mssurface/95 ms:backdrop-blur-sm ms:text-mstext ms:text-sm ms:font-semibold ms:border ms:border-msprimary/35 ms:shadow-lg ms:shadow-msprimary/10 ms:outline-none ms:focus:outline-none ms:hover:bg-mssurface ms:hover:border-msprimary/50 ms:hover:shadow-xl ms:hover:shadow-msprimary/15 ms:transition-all", "aria-label": "Open add field tools", children: [_jsx(PlusIcon, { className: "ms:w-3.5 ms:h-3.5 ms:text-msprimary" }), _jsx("span", { children: "Add field" })] }) })] }), _jsx("aside", { className: "panel-editor-wrap panel-editor ms:hidden ms:lg:flex ms:self-start ms:min-h-0 ms:max-h-[calc(100dvh-12.5rem)] ms:overflow-y-auto ms:flex-col ms:rounded-lg ms:border ms:border-msborder ms:bg-mssurface", children: _jsx(EditPanel, {}) }), _jsx(MobileBottomDrawer, { title: "Add Field", open: toolsModalOpen, onClose: () => setToolsModalOpen(false), children: _jsx(ToolPanel, {}) }), _jsx(MobileBottomDrawer, { title: "Edit Field", open: editModalOpen && !!selectedFieldId, onClose: () => ui.getState().setEditModalOpen(false), children: _jsx(EditPanel, {}) })] })), mode === 'code' && (_jsx("div", { className: "code-layout ms:flex ms:h-[calc(100dvh-12.5rem)] ms:min-h-0 ms:min-w-0 ms:overflow-hidden ms:rounded-lg ms:border ms:border-msborder ms:bg-mssurface", children: _jsx(CodeView, { form: form, ui: ui }) })), mode === 'preview' && (_jsxs("div", { className: "preview-layout ms:flex-1 ms:min-h-0 ms:min-w-0 ms:w-full ms:max-w-2xl ms:mx-auto ms:p-4 ms:max-h-[calc(100dvh-12.5rem)] ms:overflow-y-auto", children: [_jsx("div", { className: "ms:flex ms:items-center ms:justify-end ms:mb-3", children: _jsx(Switch, { size: "sm", checked: isTouchEnabled, onCheckedChange: setTouchMode, label: "Touch Mode" }) }), _jsx(Canvas, { form: form, ui: ui, dragEnabled: false })] }))] }) }) }) }));
111
+ });
@@ -0,0 +1,107 @@
1
+ import type { AddFieldOptions, FieldType, FieldTypeMeta, FormStore } from '@esheet/core';
2
+ /** Summary of a single field, suitable for AI context. */
3
+ export interface FieldSummary {
4
+ id: string;
5
+ fieldType: string;
6
+ question: string | undefined;
7
+ required: boolean;
8
+ options: {
9
+ id: string;
10
+ value: string;
11
+ }[];
12
+ rows: {
13
+ id: string;
14
+ value: string;
15
+ }[];
16
+ columns: {
17
+ id: string;
18
+ value: string;
19
+ }[];
20
+ /** Which tool family to use: 'add_option' for option-based fields, 'add_row / add_column' for matrix fields. */
21
+ editWith: string;
22
+ /** True if this field has conditional logic rules attached. Use get_field to inspect them. */
23
+ hasRules: boolean;
24
+ /** Required value format for structured input types (date, datetime-local, month, time). Use this exact format when calling fill_field. */
25
+ valueFormat?: string;
26
+ /** True if this field already has a response value. False means it is still empty and needs to be filled. */
27
+ hasValue: boolean;
28
+ /** Section child fields (only present when fieldType === 'section'). */
29
+ children?: FieldSummary[];
30
+ }
31
+ /**
32
+ * Narrow interface exposed to MCP / AI tools via `onBuilderToolsReady`.
33
+ * Only the operations an external AI agent needs — no raw store internals.
34
+ */
35
+ export interface BuilderTools {
36
+ /** Add a new field. Returns the generated ID, or null for unknown type. */
37
+ addField: (fieldType: FieldType, opts?: AddFieldOptions) => string | null;
38
+ /** Patch a field's properties. Returns false if the field was not found. */
39
+ updateField: (fieldId: string, patch: Record<string, unknown>) => boolean;
40
+ /** Remove a field. Returns false if not found. */
41
+ removeField: (fieldId: string) => boolean;
42
+ /** Find a field ID by exact ID or partial question match. */
43
+ resolveFieldId: (fieldId?: string, fieldQuestion?: string) => string | undefined;
44
+ /** Replace the entire form with a new definition. */
45
+ resetForm: (definition: {
46
+ id?: string;
47
+ fields: Record<string, unknown>[];
48
+ }) => void;
49
+ /** Generate a form from a list of plain question descriptors. */
50
+ generateForm: (questions: {
51
+ question: string;
52
+ fieldType?: string;
53
+ required?: boolean;
54
+ inputType?: string;
55
+ options?: string[];
56
+ }[]) => string;
57
+ /** Snapshot of the current form suitable for AI context. */
58
+ getFormSummary: () => {
59
+ formId: string;
60
+ fieldCount: number;
61
+ fields: FieldSummary[];
62
+ };
63
+ /** Set a response value for a field (for testing in preview mode). Returns true on success, false if field not found, or an error string if the value format is invalid. */
64
+ fillField: (fieldId: string, value: unknown) => boolean | string;
65
+ /** Clear all responses. */
66
+ clearResponses: () => void;
67
+ /** Get current responses keyed by field ID. */
68
+ getResponses: () => Record<string, unknown>;
69
+ /** Granular option mutations (radio, check, dropdown, etc.). */
70
+ option: {
71
+ add: (fieldId: string, value?: string) => string | null;
72
+ update: (fieldId: string, optionId: string, value: string) => boolean;
73
+ remove: (fieldId: string, optionId: string) => boolean;
74
+ };
75
+ /** Granular row mutations (matrix fields). */
76
+ row: {
77
+ add: (fieldId: string, value?: string) => string | null;
78
+ update: (fieldId: string, rowId: string, value: string) => boolean;
79
+ remove: (fieldId: string, rowId: string) => boolean;
80
+ };
81
+ /** Granular column mutations (matrix fields). */
82
+ column: {
83
+ add: (fieldId: string, value?: string) => string | null;
84
+ update: (fieldId: string, columnId: string, value: string) => boolean;
85
+ remove: (fieldId: string, columnId: string) => boolean;
86
+ };
87
+ /** Move a field to a new index. */
88
+ moveField: (fieldId: string, toIndex: number, toParentId?: string | null) => boolean;
89
+ /** Get a single field node by ID. */
90
+ getField: (fieldId: string) => import('@esheet/core').FieldNode | undefined;
91
+ /** List all registered field type keys and their labels. */
92
+ getFieldTypes: () => {
93
+ key: string;
94
+ label: string;
95
+ category: string;
96
+ hasOptions: boolean;
97
+ hasMatrix: boolean;
98
+ }[];
99
+ /** Get full spec for a single field type (properties, capabilities, defaults). */
100
+ getFieldSpec: (fieldType: string) => FieldTypeMeta | undefined;
101
+ /** Export the full form definition tree (inverse of loadDefinition). */
102
+ getDefinition: () => import('@esheet/core').FormDefinition;
103
+ /** Update the form's top-level ID without replacing fields. */
104
+ setFormId: (id: string) => void;
105
+ }
106
+ export declare function createBuilderTools(form: FormStore): BuilderTools;
107
+ //# sourceMappingURL=builder-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder-tools.d.ts","sourceRoot":"","sources":["../../src/lib/builder-tools.ts"],"names":[],"mappings":"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"}