@widgentic/designer 0.1.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 (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -0
  3. package/dist/action-designer.d.ts +35 -0
  4. package/dist/action-designer.d.ts.map +1 -0
  5. package/dist/action-designer.js +294 -0
  6. package/dist/action-designer.js.map +1 -0
  7. package/dist/action-editor.d.ts +49 -0
  8. package/dist/action-editor.d.ts.map +1 -0
  9. package/dist/action-editor.js +515 -0
  10. package/dist/action-editor.js.map +1 -0
  11. package/dist/browser/widgentic-designer.js +433 -0
  12. package/dist/browser/widgentic-designer.js.map +7 -0
  13. package/dist/browser.d.ts +2 -0
  14. package/dist/browser.d.ts.map +1 -0
  15. package/dist/browser.js +12 -0
  16. package/dist/browser.js.map +1 -0
  17. package/dist/dom.d.ts +37 -0
  18. package/dist/dom.d.ts.map +1 -0
  19. package/dist/dom.js +387 -0
  20. package/dist/dom.js.map +1 -0
  21. package/dist/element.d.ts +12 -0
  22. package/dist/element.d.ts.map +1 -0
  23. package/dist/element.js +131 -0
  24. package/dist/element.js.map +1 -0
  25. package/dist/highlight.d.ts +30 -0
  26. package/dist/highlight.d.ts.map +1 -0
  27. package/dist/highlight.js +123 -0
  28. package/dist/highlight.js.map +1 -0
  29. package/dist/index.d.ts +30 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +21 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/internal.d.ts +6 -0
  34. package/dist/internal.d.ts.map +1 -0
  35. package/dist/internal.js +10 -0
  36. package/dist/internal.js.map +1 -0
  37. package/dist/io.d.ts +32 -0
  38. package/dist/io.d.ts.map +1 -0
  39. package/dist/io.js +155 -0
  40. package/dist/io.js.map +1 -0
  41. package/dist/json-tree-editor.d.ts +8 -0
  42. package/dist/json-tree-editor.d.ts.map +1 -0
  43. package/dist/json-tree-editor.js +186 -0
  44. package/dist/json-tree-editor.js.map +1 -0
  45. package/dist/panels.d.ts +13 -0
  46. package/dist/panels.d.ts.map +1 -0
  47. package/dist/panels.js +414 -0
  48. package/dist/panels.js.map +1 -0
  49. package/dist/preview.d.ts +40 -0
  50. package/dist/preview.d.ts.map +1 -0
  51. package/dist/preview.js +138 -0
  52. package/dist/preview.js.map +1 -0
  53. package/dist/record-editor.d.ts +11 -0
  54. package/dist/record-editor.d.ts.map +1 -0
  55. package/dist/record-editor.js +70 -0
  56. package/dist/record-editor.js.map +1 -0
  57. package/dist/schema-builder.d.ts +8 -0
  58. package/dist/schema-builder.d.ts.map +1 -0
  59. package/dist/schema-builder.js +235 -0
  60. package/dist/schema-builder.js.map +1 -0
  61. package/dist/schema-designer.d.ts +33 -0
  62. package/dist/schema-designer.d.ts.map +1 -0
  63. package/dist/schema-designer.js +251 -0
  64. package/dist/schema-designer.js.map +1 -0
  65. package/dist/schema-form.d.ts +8 -0
  66. package/dist/schema-form.d.ts.map +1 -0
  67. package/dist/schema-form.js +181 -0
  68. package/dist/schema-form.js.map +1 -0
  69. package/dist/schema-paths.d.ts +18 -0
  70. package/dist/schema-paths.d.ts.map +1 -0
  71. package/dist/schema-paths.js +75 -0
  72. package/dist/schema-paths.js.map +1 -0
  73. package/dist/seed.d.ts +18 -0
  74. package/dist/seed.d.ts.map +1 -0
  75. package/dist/seed.js +299 -0
  76. package/dist/seed.js.map +1 -0
  77. package/dist/shell.d.ts +59 -0
  78. package/dist/shell.d.ts.map +1 -0
  79. package/dist/shell.js +102 -0
  80. package/dist/shell.js.map +1 -0
  81. package/dist/store.d.ts +44 -0
  82. package/dist/store.d.ts.map +1 -0
  83. package/dist/store.js +71 -0
  84. package/dist/store.js.map +1 -0
  85. package/dist/styles-editor.d.ts +8 -0
  86. package/dist/styles-editor.d.ts.map +1 -0
  87. package/dist/styles-editor.js +100 -0
  88. package/dist/styles-editor.js.map +1 -0
  89. package/dist/template-panel.d.ts +19 -0
  90. package/dist/template-panel.d.ts.map +1 -0
  91. package/dist/template-panel.js +585 -0
  92. package/dist/template-panel.js.map +1 -0
  93. package/dist/theme-designer.d.ts +35 -0
  94. package/dist/theme-designer.d.ts.map +1 -0
  95. package/dist/theme-designer.js +336 -0
  96. package/dist/theme-designer.js.map +1 -0
  97. package/dist/theme-panel.d.ts +20 -0
  98. package/dist/theme-panel.d.ts.map +1 -0
  99. package/dist/theme-panel.js +96 -0
  100. package/dist/theme-panel.js.map +1 -0
  101. package/dist/validate.d.ts +57 -0
  102. package/dist/validate.d.ts.map +1 -0
  103. package/dist/validate.js +131 -0
  104. package/dist/validate.js.map +1 -0
  105. package/package.json +49 -0
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Generic JSON value editor: collapsible tree with in-place key/value
3
+ * editing, type switching, and add/remove — the NanoJSON/JSONTree
4
+ * interaction pattern, built in-house because the general libraries carry
5
+ * far more (and weigh far more) than the designer needs.
6
+ *
7
+ * Structural operations re-render the tree; text edits commit on change
8
+ * (blur/Enter) so typing is never interrupted.
9
+ */
10
+ import { h } from "./dom.js";
11
+ const TYPE_OPTIONS = ["string", "number", "boolean", "null", "object", "array"];
12
+ function typeOf(value) {
13
+ if (value === null)
14
+ return "null";
15
+ if (Array.isArray(value))
16
+ return "array";
17
+ return (typeof value);
18
+ }
19
+ function defaultFor(type, previous) {
20
+ switch (type) {
21
+ case "string":
22
+ return typeof previous === "number" || typeof previous === "boolean"
23
+ ? String(previous)
24
+ : "";
25
+ case "number": {
26
+ const parsed = Number(previous);
27
+ return Number.isFinite(parsed) ? parsed : 0;
28
+ }
29
+ case "boolean":
30
+ return previous === "true" ? true : Boolean(previous) && previous !== "false";
31
+ case "null":
32
+ return null;
33
+ case "object":
34
+ return {};
35
+ case "array":
36
+ return [];
37
+ }
38
+ }
39
+ function setAt(root, path, value, remove = false) {
40
+ if (path.length === 0)
41
+ return value;
42
+ const [step, ...rest] = path;
43
+ if (Array.isArray(root)) {
44
+ const copy = [...root];
45
+ if (rest.length === 0 && remove)
46
+ copy.splice(step, 1);
47
+ else
48
+ copy[step] = setAt(copy[step], rest, value, remove);
49
+ return copy;
50
+ }
51
+ if (typeof root === "object" && root !== null) {
52
+ const copy = { ...root };
53
+ if (rest.length === 0 && remove)
54
+ delete copy[step];
55
+ else
56
+ copy[step] = setAt(copy[step], rest, value, remove);
57
+ return copy;
58
+ }
59
+ return root;
60
+ }
61
+ function renameKey(root, path, from, to) {
62
+ const holder = path.reduce((node, step) => typeof node === "object" && node !== null
63
+ ? node[step]
64
+ : undefined, root);
65
+ if (typeof holder !== "object" || holder === null || Array.isArray(holder))
66
+ return root;
67
+ const entries = Object.entries(holder).map(([k, v]) => k === from ? [to, v] : [k, v]);
68
+ return setAt(root, path, Object.fromEntries(entries));
69
+ }
70
+ export function createJsonTreeEditor(initial, onChange) {
71
+ let current = initial;
72
+ const element = h("div", { class: "wgd-jsontree" });
73
+ function commit(next) {
74
+ current = next;
75
+ render();
76
+ onChange(current);
77
+ }
78
+ function commitText(path, type, text) {
79
+ const value = type === "number"
80
+ ? Number.isFinite(Number(text))
81
+ ? Number(text)
82
+ : 0
83
+ : text;
84
+ current = setAt(current, path, value);
85
+ onChange(current); // no re-render: in-place text edit
86
+ }
87
+ function typeSelect(value, path) {
88
+ const select = h("select", { class: "wgd-select wgd-jt-type" });
89
+ for (const option of TYPE_OPTIONS) {
90
+ select.append(h("option", { value: option }, [option]));
91
+ }
92
+ select.value = typeOf(value);
93
+ select.addEventListener("change", () => commit(setAt(current, path, defaultFor(select.value, value))));
94
+ return select;
95
+ }
96
+ function valueControl(value, path) {
97
+ const type = typeOf(value);
98
+ if (type === "boolean") {
99
+ const box = h("input", { type: "checkbox" });
100
+ box.checked = value;
101
+ box.addEventListener("change", () => commit(setAt(current, path, box.checked)));
102
+ return box;
103
+ }
104
+ if (type === "null")
105
+ return h("code", undefined, ["null"]);
106
+ if (type === "string" || type === "number") {
107
+ const input = h("input", { type: "text", class: "wgd-input" });
108
+ input.value = String(value);
109
+ input.addEventListener("change", () => commitText(path, type, input.value));
110
+ return input;
111
+ }
112
+ return h("span");
113
+ }
114
+ function entryRow(keyLabel, value, path, removable) {
115
+ const row = h("div", { class: "wgd-jt-row" }, [keyLabel, typeSelect(value, path)]);
116
+ const type = typeOf(value);
117
+ if (type !== "object" && type !== "array") {
118
+ row.append(valueControl(value, path));
119
+ }
120
+ if (removable) {
121
+ const remove = h("button", { class: "wgd-icon", type: "button", title: "Remove" }, ["✕"]);
122
+ remove.addEventListener("click", () => commit(setAt(current, path, undefined, true)));
123
+ row.append(remove);
124
+ }
125
+ const wrap = h("div", { class: "wgd-jt-entry" }, [row]);
126
+ if (type === "object" || type === "array") {
127
+ wrap.append(renderContainer(value, path));
128
+ }
129
+ return wrap;
130
+ }
131
+ function renderContainer(value, path) {
132
+ const body = h("div", { class: "wgd-jt-children" });
133
+ if (Array.isArray(value)) {
134
+ value.forEach((item, index) => {
135
+ body.append(entryRow(h("span", { class: "wgd-jt-key" }, [String(index)]), item, [...path, index], true));
136
+ });
137
+ const add = h("button", { class: "wgd-button wgd-add", type: "button" }, ["+ item"]);
138
+ add.addEventListener("click", () => commit(setAt(current, [...path, value.length], "")));
139
+ body.append(h("div", { class: "wgd-toolbar" }, [add]));
140
+ }
141
+ else if (typeof value === "object" && value !== null) {
142
+ for (const [key, item] of Object.entries(value)) {
143
+ const keyInput = h("input", {
144
+ type: "text",
145
+ class: "wgd-input wgd-jt-keyinput"
146
+ });
147
+ keyInput.value = key;
148
+ keyInput.addEventListener("change", () => commit(renameKey(current, path, key, keyInput.value)));
149
+ body.append(entryRow(keyInput, item, [...path, key], true));
150
+ }
151
+ const add = h("button", { class: "wgd-button wgd-add", type: "button" }, ["+ property"]);
152
+ add.addEventListener("click", () => {
153
+ const holder = value;
154
+ let name = "key";
155
+ for (let i = 1; name in holder; i++)
156
+ name = `key${i}`;
157
+ commit(setAt(current, [...path, name], ""));
158
+ });
159
+ body.append(h("div", { class: "wgd-toolbar" }, [add]));
160
+ }
161
+ const details = h("details", { class: "wgd-jt-branch", open: "" }, [
162
+ h("summary", { class: "wgd-jt-summary" }, [
163
+ Array.isArray(value)
164
+ ? `[ ${value.length} item${value.length === 1 ? "" : "s"} ]`
165
+ : `{ ${Object.keys(value).length} propert${Object.keys(value).length === 1 ? "y" : "ies"} }`
166
+ ]),
167
+ body
168
+ ]);
169
+ return details;
170
+ }
171
+ function render() {
172
+ element.replaceChildren(entryRow(h("span", { class: "wgd-jt-key" }, ["value"]), current, [], false));
173
+ }
174
+ render();
175
+ return {
176
+ element,
177
+ getValue: () => current,
178
+ setValue(value) {
179
+ if (element.contains(document.activeElement))
180
+ return;
181
+ current = value;
182
+ render();
183
+ }
184
+ };
185
+ }
186
+ //# sourceMappingURL=json-tree-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-tree-editor.js","sourceRoot":"","sources":["../src/json-tree-editor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ThemeEntry } from "@widgentic/core";
2
+ import type { DraftStore } from "./store.js";
3
+ import type { SchemaEntry } from "./schema-designer.js";
4
+ import type { TemplateActionContext } from "./template-panel.js";
5
+ /** Column layout: definition (exported) on the left, presentation on the right. */
6
+ export interface PanelColumns {
7
+ left: HTMLElement;
8
+ right: HTMLElement;
9
+ }
10
+ export declare function mountPanels(store: DraftStore, columns: PanelColumns, themes?: ThemeEntry[], schemas?: SchemaEntry[], actionContext?: TemplateActionContext): {
11
+ dispose(): void;
12
+ };
13
+ //# sourceMappingURL=panels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AASlD,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AA6BjE,mFAAmF;AACnF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;CACpB;AAqDD,wBAAgB,WAAW,CACzB,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,YAAY,EACrB,MAAM,GAAE,UAAU,EAAO,EACzB,OAAO,GAAE,WAAW,EAAO,EAC3B,aAAa,GAAE,qBAAqE,GACnF;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CA4crB"}
package/dist/panels.js ADDED
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Editor panels: plain modules over the draft store. Refresh discipline:
3
+ * every store change re-derives diagnostics and refreshes bound controls,
4
+ * but never overwrites the control the user is typing in
5
+ * (`document.activeElement` wins) — the store is the single model, the
6
+ * DOM is a projection.
7
+ */
8
+ import { errorMessage } from "./internal.js";
9
+ import { isPlainObject } from "@widgentic/core";
10
+ import { diagnosticLine, h, section, textArea, textField, requireChild } from "./dom.js";
11
+ import { attachJsonHighlight, repaintHighlight } from "./highlight.js";
12
+ import { mountIoPanel } from "./io.js";
13
+ import { createJsonTreeEditor } from "./json-tree-editor.js";
14
+ import { createSchemaBuilder } from "./schema-builder.js";
15
+ import { createRecordEditor } from "./record-editor.js";
16
+ import { createSchemaForm } from "./schema-form.js";
17
+ import { createStylesEditor } from "./styles-editor.js";
18
+ import { mountTemplatePanel } from "./template-panel.js";
19
+ import { createBindingEditor } from "./action-editor.js";
20
+ import { mountThemePanel } from "./theme-panel.js";
21
+ import { deriveDiagnostics, effectiveDataSchema } from "./validate.js";
22
+ /** Tabbed container: first tab visible, buttons switch panes. */
23
+ function tabs(entries) {
24
+ const buttons = entries.map((entry, index) => {
25
+ const button = h("button", { class: `wgd-button wgd-tab${index === 0 ? " wgd-tab-active" : ""}`, type: "button" }, [entry.label]);
26
+ button.addEventListener("click", () => {
27
+ entries.forEach((other, i) => {
28
+ other.element.hidden = i !== index;
29
+ buttons[i]?.classList.toggle("wgd-tab-active", i === index);
30
+ });
31
+ });
32
+ entry.element.hidden = index !== 0;
33
+ return button;
34
+ });
35
+ return h("div", { class: "wgd-tabs" }, [
36
+ h("div", { class: "wgd-row" }, buttons),
37
+ ...entries.map((entry) => entry.element)
38
+ ]);
39
+ }
40
+ /**
41
+ * JSON-valued textarea with parse gating: invalid JSON never reaches the
42
+ * store (last-valid-wins) — the parse error shows beside the field.
43
+ */
44
+ function jsonField(label, read, write, clear, store, refreshers, rows = 6) {
45
+ const parseError = diagnosticLine(undefined);
46
+ const initial = read(store.get());
47
+ const field = textArea(label, initial === undefined ? "" : JSON.stringify(initial, null, 2), (text) => {
48
+ if (text.trim() === "") {
49
+ parseError.hidden = true;
50
+ if (clear)
51
+ store.update((draft) => clear(draft));
52
+ return;
53
+ }
54
+ try {
55
+ const value = JSON.parse(text);
56
+ parseError.hidden = true;
57
+ parseError.textContent = "";
58
+ store.update((draft) => write(draft, value));
59
+ }
60
+ catch (error) {
61
+ parseError.hidden = false;
62
+ parseError.textContent = `Invalid JSON (draft keeps the last valid value): ${String(errorMessage(error))}`;
63
+ }
64
+ }, rows);
65
+ const area = requireChild(field, "textarea");
66
+ attachJsonHighlight(area);
67
+ refreshers.push((draft) => {
68
+ if (document.activeElement === area)
69
+ return;
70
+ const value = read(draft);
71
+ area.value = value === undefined ? "" : JSON.stringify(value, null, 2);
72
+ repaintHighlight(area); // programmatic writes fire no input event
73
+ });
74
+ return h("div", undefined, [field, parseError]);
75
+ }
76
+ export function mountPanels(store, columns, themes = [], schemas = [], actionContext = { actions: [], secretNames: [], schemas: [] }) {
77
+ const refreshers = [];
78
+ const disposers = [];
79
+ function bindText(label, read, write) {
80
+ const field = textField(label, read(store.get()), (value) => store.update((draft) => write(draft, value)));
81
+ const input = requireChild(field, "input");
82
+ refreshers.push((draft) => {
83
+ if (document.activeElement !== input)
84
+ input.value = read(draft);
85
+ });
86
+ return field;
87
+ }
88
+ // --- General: kind + prose descriptor fields (left) -------------------
89
+ const kindDiag = diagnosticLine(undefined);
90
+ // Hints: name→doc rows beside the parse-gated JSON pane — the same
91
+ // Tree/JSON pair as styles, scaled down to a flat record.
92
+ const hintsEditor = createRecordEditor(store.get().descriptor.hints, { key: "hint name", value: "what the hint does", add: "+ hint" }, (hints) => store.update((d) => {
93
+ if (hints === undefined) {
94
+ const { hints: _h, ...rest } = d.descriptor;
95
+ return { ...d, descriptor: rest };
96
+ }
97
+ return { ...d, descriptor: { ...d.descriptor, hints } };
98
+ }));
99
+ refreshers.push((draft) => hintsEditor.setValue(draft.descriptor.hints));
100
+ const generalPanel = section("General", [
101
+ bindText("Kind (id)", (d) => d.kind, (d, v) => ({ ...d, kind: v })),
102
+ kindDiag,
103
+ bindText("Description", (d) => d.descriptor.description ?? "", (d, v) => ({ ...d, descriptor: { ...d.descriptor, description: v } })),
104
+ bindText("Data shape (prose)", (d) => d.descriptor.dataShape ?? "", (d, v) => ({ ...d, descriptor: { ...d.descriptor, dataShape: v } })),
105
+ h("div", { class: "wgd-field" }, [
106
+ h("span", { class: "wgd-field-label" }, ["Hints"]),
107
+ tabs([
108
+ { label: "Tree", element: hintsEditor.element },
109
+ {
110
+ label: "JSON",
111
+ element: jsonField("", (d) => d.descriptor.hints, (d, v) => {
112
+ if (!isPlainObject(v))
113
+ return d;
114
+ const hints = Object.fromEntries(Object.entries(v).map(([k, doc]) => [k, String(doc)]));
115
+ return { ...d, descriptor: { ...d.descriptor, hints } };
116
+ }, (d) => {
117
+ const { hints: _hints, ...rest } = d.descriptor;
118
+ return { ...d, descriptor: rest };
119
+ }, store, refreshers, 3)
120
+ }
121
+ ])
122
+ ])
123
+ ]);
124
+ // --- Data schema (left, definition): inline editing OR a shared ref ----
125
+ // Two modes, mirroring the store's contract: an inline dataSchema, or a
126
+ // dataSchemaRef into the host-supplied shared schemas — never both.
127
+ const schemaBuilder = createSchemaBuilder(store.get().descriptor.dataSchema, (schema) => store.update((d) => {
128
+ if (schema === undefined) {
129
+ const { dataSchema: _s, ...rest } = d.descriptor;
130
+ return { ...d, descriptor: rest };
131
+ }
132
+ return { ...d, descriptor: { ...d.descriptor, dataSchema: schema } };
133
+ }));
134
+ refreshers.push((draft) => schemaBuilder.setValue(draft.descriptor.dataSchema));
135
+ const inlineEditors = tabs([
136
+ { label: "Builder", element: schemaBuilder.element },
137
+ {
138
+ label: "JSON",
139
+ element: jsonField("dataSchema (JSON-Schema subset: type/properties/required/items/enum/pattern)", (d) => d.descriptor.dataSchema, (d, v) => {
140
+ if (!isPlainObject(v))
141
+ return d;
142
+ return { ...d, descriptor: { ...d.descriptor, dataSchema: v } };
143
+ }, (d) => {
144
+ const { dataSchema: _s, ...rest } = d.descriptor;
145
+ return { ...d, descriptor: rest };
146
+ }, store, refreshers)
147
+ }
148
+ ]);
149
+ const modeSelect = h("select", { class: "wgd-select wgd-schema-mode" });
150
+ modeSelect.append(h("option", { value: "inline" }, ["define inline"]));
151
+ const sharedOption = h("option", { value: "shared" }, ["use shared"]);
152
+ if (schemas.length === 0)
153
+ sharedOption.disabled = true;
154
+ modeSelect.append(sharedOption);
155
+ const refSelect = h("select", { class: "wgd-select wgd-schema-ref" });
156
+ for (const entry of schemas) {
157
+ refSelect.append(h("option", { value: entry.name }, [entry.label ?? entry.name]));
158
+ }
159
+ refSelect.addEventListener("change", () => {
160
+ store.update((d) => ({
161
+ ...d,
162
+ descriptor: { ...d.descriptor, dataSchemaRef: refSelect.value }
163
+ }));
164
+ });
165
+ // The shared schema shows read-only: editing it from inside a widget
166
+ // draft would be edit-at-a-distance — that belongs to the schema section.
167
+ const sharedView = h("textarea", {
168
+ class: "wgd-textarea wgd-schema-shared",
169
+ rows: "10",
170
+ readonly: "",
171
+ spellcheck: "false"
172
+ });
173
+ const schemaRefDiag = diagnosticLine(undefined);
174
+ const sharedWrap = h("div", undefined, [
175
+ h("div", { class: "wgd-row" }, [
176
+ h("span", { class: "wgd-field-label" }, ["Shared schema"]),
177
+ refSelect
178
+ ]),
179
+ sharedView,
180
+ h("span", { class: "wgd-field-label" }, [
181
+ "Read-only here — edit shared schemas in the Data schemas section."
182
+ ])
183
+ ]);
184
+ modeSelect.addEventListener("change", () => {
185
+ if (modeSelect.value === "shared") {
186
+ const name = store.get().descriptor.dataSchemaRef ?? schemas[0]?.name ?? "";
187
+ store.update((d) => {
188
+ const { dataSchema: _s, ...rest } = d.descriptor;
189
+ return { ...d, descriptor: { ...rest, dataSchemaRef: name } };
190
+ });
191
+ }
192
+ else {
193
+ // Back to inline: seed with the resolved shared schema (a copy), so
194
+ // "customize from here" is one switch away.
195
+ const resolved = effectiveDataSchema(store.get(), schemas);
196
+ store.update((d) => {
197
+ const { dataSchemaRef: _r, ...rest } = d.descriptor;
198
+ return {
199
+ ...d,
200
+ descriptor: {
201
+ ...rest,
202
+ dataSchema: (resolved ?? { type: "object" })
203
+ }
204
+ };
205
+ });
206
+ }
207
+ });
208
+ function refreshSchemaMode(draft) {
209
+ const ref = draft.descriptor.dataSchemaRef;
210
+ const shared = typeof ref === "string";
211
+ modeSelect.value = shared ? "shared" : "inline";
212
+ inlineEditors.hidden = shared;
213
+ sharedWrap.hidden = !shared;
214
+ if (shared) {
215
+ if (refSelect.value !== ref)
216
+ refSelect.value = ref;
217
+ const resolved = schemas.find((entry) => entry.name === ref)?.schema;
218
+ sharedView.value =
219
+ resolved === undefined ? "" : JSON.stringify(resolved, null, 2);
220
+ repaintHighlight(sharedView); // programmatic writes fire no input event
221
+ }
222
+ }
223
+ refreshers.push(refreshSchemaMode);
224
+ refreshSchemaMode(store.get());
225
+ const schemaPanel = section("Data schema", [
226
+ h("div", { class: "wgd-row" }, [
227
+ h("span", { class: "wgd-field-label" }, ["Source"]),
228
+ modeSelect
229
+ ]),
230
+ schemaRefDiag,
231
+ inlineEditors,
232
+ sharedWrap
233
+ ], false);
234
+ // The highlight layer wraps the textarea in place, so attach after the
235
+ // section assembly gives it a parent.
236
+ attachJsonHighlight(sharedView);
237
+ /**
238
+ * Adaptive data editor: schema-driven form when a dataSchema exists,
239
+ * generic JSON tree otherwise — rebuilt when the schema changes; the raw
240
+ * JSON textarea remains as a sibling tab either way.
241
+ */
242
+ function adaptiveDataEditor(read, write) {
243
+ const hostEl = h("div");
244
+ let schemaKey = "";
245
+ let editor;
246
+ function rebuild(draft) {
247
+ const schema = effectiveDataSchema(draft, schemas);
248
+ const key = schema === undefined ? "" : JSON.stringify(schema);
249
+ if (editor !== undefined && key === schemaKey) {
250
+ editor.setValue(read(draft));
251
+ return;
252
+ }
253
+ schemaKey = key;
254
+ const onChange = (value) => store.update((d) => write(d, value));
255
+ editor =
256
+ schema !== undefined && isPlainObject(schema)
257
+ ? createSchemaForm(schema, read(draft), onChange)
258
+ : createJsonTreeEditor(read(draft), onChange);
259
+ hostEl.replaceChildren(editor.element);
260
+ }
261
+ rebuild(store.get());
262
+ refreshers.push(rebuild);
263
+ return hostEl;
264
+ }
265
+ // --- Sample data = the descriptor's dataExample (left, exported) -------
266
+ const exampleDiag = diagnosticLine(undefined);
267
+ const samplePanel = section("Sample data", [
268
+ h("span", { class: "wgd-field-label" }, [
269
+ "dataExample — ships with the widget as the example agents imitate"
270
+ ]),
271
+ tabs([
272
+ {
273
+ label: "Edit",
274
+ element: adaptiveDataEditor((d) => d.descriptor.dataExample, (d, v) => ({ ...d, descriptor: { ...d.descriptor, dataExample: v } }))
275
+ },
276
+ {
277
+ label: "JSON",
278
+ element: jsonField("dataExample (JSON)", (d) => d.descriptor.dataExample, (d, v) => ({ ...d, descriptor: { ...d.descriptor, dataExample: v } }), (d) => {
279
+ const { dataExample: _e, ...rest } = d.descriptor;
280
+ return { ...d, descriptor: rest };
281
+ }, store, refreshers)
282
+ }
283
+ ]),
284
+ exampleDiag
285
+ ], false);
286
+ // --- Data for preview (right, session-only) ----------------------------
287
+ const sampleDiag = diagnosticLine(undefined);
288
+ const previewDataPanel = section("Data for preview", [
289
+ h("span", { class: "wgd-field-label" }, [
290
+ "Overrides dataExample in the preview only (not exported)"
291
+ ]),
292
+ tabs([
293
+ {
294
+ label: "Edit",
295
+ element: adaptiveDataEditor((d) => d.sampleData, (d, v) => ({ ...d, sampleData: v }))
296
+ },
297
+ {
298
+ label: "JSON",
299
+ element: jsonField("Preview data (JSON)", (d) => d.sampleData, (d, v) => ({ ...d, sampleData: v }), (d) => {
300
+ const { sampleData: _s, ...rest } = d;
301
+ return rest;
302
+ }, store, refreshers, 4)
303
+ }
304
+ ]),
305
+ sampleDiag
306
+ ], false);
307
+ // --- Styles panel (right, presentation): Tree + JSON tabs --------------
308
+ // Two projections of the one draft value, exactly like the template's
309
+ // tree/JSON pair: the tree enforces the string-only two-level shape,
310
+ // the JSON pane stays parse-gated.
311
+ const stylesDiag = h("div");
312
+ const stylesEditor = createStylesEditor(store.get().descriptor.styles, (styles) => store.update((d) => {
313
+ if (styles === undefined) {
314
+ const { styles: _s, ...rest } = d.descriptor;
315
+ return { ...d, descriptor: rest };
316
+ }
317
+ return { ...d, descriptor: { ...d.descriptor, styles } };
318
+ }));
319
+ refreshers.push((draft) => stylesEditor.setValue(draft.descriptor.styles));
320
+ const stylesPanel = section("Styles (.wg- selectors, guarded like the server)", [
321
+ tabs([
322
+ { label: "Tree", element: stylesEditor.element },
323
+ {
324
+ label: "JSON",
325
+ // No legend — the section title already names the shape.
326
+ element: jsonField("", (d) => d.descriptor.styles, (d, v) => {
327
+ if (!isPlainObject(v))
328
+ return d;
329
+ return {
330
+ ...d,
331
+ descriptor: {
332
+ ...d.descriptor,
333
+ styles: v
334
+ }
335
+ };
336
+ }, (d) => {
337
+ const { styles: _s, ...rest } = d.descriptor;
338
+ return { ...d, descriptor: rest };
339
+ }, store, refreshers)
340
+ }
341
+ ]),
342
+ stylesDiag
343
+ ]);
344
+ const templatePanel = mountTemplatePanel(store, refreshers, { actionContext });
345
+ const themePanel = mountThemePanel(store, refreshers, themes);
346
+ // --- Load: the widget-level http GET action run once per instance -------
347
+ const loadDiag = diagnosticLine(undefined);
348
+ const loadEditor = createBindingEditor(store.get().load, {
349
+ ...actionContext,
350
+ loadOnly: true,
351
+ getDataSchema: () => effectiveDataSchema(store.get(), schemas)
352
+ }, (binding) => store.update((d) => {
353
+ if (binding === undefined) {
354
+ const { load: _l, ...rest } = d;
355
+ return rest;
356
+ }
357
+ return { ...d, load: binding };
358
+ }));
359
+ refreshers.push((draft) => {
360
+ if (!loadEditor.element.contains(document.activeElement))
361
+ loadEditor.setValue(draft.load);
362
+ });
363
+ // Same compact chrome as an element's binding editor in the tree.
364
+ const loadPanel = section("Load action (http GET, runs once when the widget first renders in an Apps host)", [h("div", { class: "wgd-compact wgd-load" }, [loadEditor.element]), loadDiag], false);
365
+ const ioPanel = mountIoPanel(store);
366
+ // Read-only marks the right column's EDITING sections; the theme panel
367
+ // (preview selector + token reference) stays live in that mode.
368
+ previewDataPanel.classList.add("wgd-edit-only");
369
+ stylesPanel.classList.add("wgd-edit-only");
370
+ // Left: the widget definition (what export produces). Right: presentation.
371
+ columns.left.append(generalPanel, schemaPanel, samplePanel, templatePanel.element, loadPanel, ioPanel.element);
372
+ columns.right.append(previewDataPanel, stylesPanel, themePanel.element);
373
+ disposers.push(templatePanel.dispose, themePanel.dispose, ioPanel.dispose);
374
+ // Diagnostics refresh: derive once per change here (cheap, pure) — and
375
+ // once immediately, so the tree/JSON projections render before any edit.
376
+ function applyDiagnostics(draft) {
377
+ const diagnostics = deriveDiagnostics(draft, { schemas, actions: actionContext.actions });
378
+ kindDiag.hidden = diagnostics.kind === undefined;
379
+ // Unknown refs are flagged in the template panel, at the element; the
380
+ // Load section only ever speaks about the load binding.
381
+ loadDiag.hidden = diagnostics.load === undefined;
382
+ loadDiag.textContent = diagnostics.load ?? "";
383
+ kindDiag.textContent = diagnostics.kind ?? "";
384
+ exampleDiag.hidden = diagnostics.example === undefined;
385
+ exampleDiag.textContent = diagnostics.example
386
+ ? `dataExample vs dataSchema: ${diagnostics.example.code} at ${diagnostics.example.path} — ${diagnostics.example.message}`
387
+ : "";
388
+ sampleDiag.hidden = diagnostics.sample === undefined;
389
+ sampleDiag.textContent = diagnostics.sample
390
+ ? `sample vs dataSchema: ${diagnostics.sample.code} at ${diagnostics.sample.path} — ${diagnostics.sample.message}`
391
+ : "";
392
+ schemaRefDiag.hidden = diagnostics.schemaRef === undefined;
393
+ schemaRefDiag.textContent = diagnostics.schemaRef ?? "";
394
+ themePanel.setDiagnostic(diagnostics.theme
395
+ ? `${diagnostics.theme.code}: ${diagnostics.theme.message}`
396
+ : undefined);
397
+ stylesDiag.replaceChildren(...diagnostics.styles.map((issue) => diagnosticLine(`${issue.selector}${issue.property ? ` → ${issue.property}` : ""}: ${issue.message}`)));
398
+ for (const refresh of refreshers)
399
+ refresh(draft);
400
+ templatePanel.refresh(draft, diagnostics);
401
+ }
402
+ const off = store.subscribe(applyDiagnostics);
403
+ disposers.push(off);
404
+ applyDiagnostics(store.get());
405
+ return {
406
+ dispose() {
407
+ for (const dispose of disposers)
408
+ dispose();
409
+ columns.left.replaceChildren();
410
+ columns.right.replaceChildren();
411
+ }
412
+ };
413
+ }
414
+ //# sourceMappingURL=panels.js.map