@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.
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/dist/action-designer.d.ts +35 -0
- package/dist/action-designer.d.ts.map +1 -0
- package/dist/action-designer.js +294 -0
- package/dist/action-designer.js.map +1 -0
- package/dist/action-editor.d.ts +49 -0
- package/dist/action-editor.d.ts.map +1 -0
- package/dist/action-editor.js +515 -0
- package/dist/action-editor.js.map +1 -0
- package/dist/browser/widgentic-designer.js +433 -0
- package/dist/browser/widgentic-designer.js.map +7 -0
- package/dist/browser.d.ts +2 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +12 -0
- package/dist/browser.js.map +1 -0
- package/dist/dom.d.ts +37 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +387 -0
- package/dist/dom.js.map +1 -0
- package/dist/element.d.ts +12 -0
- package/dist/element.d.ts.map +1 -0
- package/dist/element.js +131 -0
- package/dist/element.js.map +1 -0
- package/dist/highlight.d.ts +30 -0
- package/dist/highlight.d.ts.map +1 -0
- package/dist/highlight.js +123 -0
- package/dist/highlight.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +6 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +10 -0
- package/dist/internal.js.map +1 -0
- package/dist/io.d.ts +32 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +155 -0
- package/dist/io.js.map +1 -0
- package/dist/json-tree-editor.d.ts +8 -0
- package/dist/json-tree-editor.d.ts.map +1 -0
- package/dist/json-tree-editor.js +186 -0
- package/dist/json-tree-editor.js.map +1 -0
- package/dist/panels.d.ts +13 -0
- package/dist/panels.d.ts.map +1 -0
- package/dist/panels.js +414 -0
- package/dist/panels.js.map +1 -0
- package/dist/preview.d.ts +40 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +138 -0
- package/dist/preview.js.map +1 -0
- package/dist/record-editor.d.ts +11 -0
- package/dist/record-editor.d.ts.map +1 -0
- package/dist/record-editor.js +70 -0
- package/dist/record-editor.js.map +1 -0
- package/dist/schema-builder.d.ts +8 -0
- package/dist/schema-builder.d.ts.map +1 -0
- package/dist/schema-builder.js +235 -0
- package/dist/schema-builder.js.map +1 -0
- package/dist/schema-designer.d.ts +33 -0
- package/dist/schema-designer.d.ts.map +1 -0
- package/dist/schema-designer.js +251 -0
- package/dist/schema-designer.js.map +1 -0
- package/dist/schema-form.d.ts +8 -0
- package/dist/schema-form.d.ts.map +1 -0
- package/dist/schema-form.js +181 -0
- package/dist/schema-form.js.map +1 -0
- package/dist/schema-paths.d.ts +18 -0
- package/dist/schema-paths.d.ts.map +1 -0
- package/dist/schema-paths.js +75 -0
- package/dist/schema-paths.js.map +1 -0
- package/dist/seed.d.ts +18 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +299 -0
- package/dist/seed.js.map +1 -0
- package/dist/shell.d.ts +59 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +102 -0
- package/dist/shell.js.map +1 -0
- package/dist/store.d.ts +44 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +71 -0
- package/dist/store.js.map +1 -0
- package/dist/styles-editor.d.ts +8 -0
- package/dist/styles-editor.d.ts.map +1 -0
- package/dist/styles-editor.js +100 -0
- package/dist/styles-editor.js.map +1 -0
- package/dist/template-panel.d.ts +19 -0
- package/dist/template-panel.d.ts.map +1 -0
- package/dist/template-panel.js +585 -0
- package/dist/template-panel.js.map +1 -0
- package/dist/theme-designer.d.ts +35 -0
- package/dist/theme-designer.d.ts.map +1 -0
- package/dist/theme-designer.js +336 -0
- package/dist/theme-designer.js.map +1 -0
- package/dist/theme-panel.d.ts +20 -0
- package/dist/theme-panel.d.ts.map +1 -0
- package/dist/theme-panel.js +96 -0
- package/dist/theme-panel.js.map +1 -0
- package/dist/validate.d.ts +57 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +131 -0
- package/dist/validate.js.map +1 -0
- 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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"}
|
package/dist/panels.d.ts
ADDED
|
@@ -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
|