@hachej/boring-generated-pane 0.1.60 → 0.1.65
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/README.md +3 -3
- package/dist/chunk-PELWQS23.js +149 -0
- package/dist/chunk-PELWQS23.js.map +1 -0
- package/dist/front/index.d.ts +22 -35
- package/dist/front/index.js +51 -46
- package/dist/front/index.js.map +1 -1
- package/dist/server/index.d.ts +16 -0
- package/dist/server/index.js +53 -0
- package/dist/server/index.js.map +1 -0
- package/dist/shared/index.d.ts +40 -2
- package/dist/shared/index.js +11 -3
- package/package.json +19 -14
- package/skills/generated-pane-authoring/SKILL.md +2 -2
- package/dist/chunk-4IMMGGBQ.js +0 -62
- package/dist/chunk-4IMMGGBQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -24,8 +24,8 @@ This plugin wraps Vercel Labs `json-render` so agents can write safe JSON pane s
|
|
|
24
24
|
|
|
25
25
|
- Agents generate JSON specs.
|
|
26
26
|
- Plugins define allowed components and prop schemas.
|
|
27
|
-
- Unknown components/props are rejected by the
|
|
28
|
-
- Actions
|
|
27
|
+
- Unknown components/props are rejected by the generated-pane vocabulary/catalog.
|
|
28
|
+
- Actions are out of scope for generated JSON specs; no generated JavaScript is executed.
|
|
29
29
|
|
|
30
30
|
## Eval
|
|
31
31
|
|
|
@@ -39,4 +39,4 @@ The eval checks that the agent writes a `panes/*.pane.json` file and the runner
|
|
|
39
39
|
|
|
40
40
|
## Extending
|
|
41
41
|
|
|
42
|
-
Use `defineGeneratedPaneProfile` to add app/plugin-specific components, then render with `GeneratedPaneRenderer` or a profile-specific pane.
|
|
42
|
+
Use `defineGeneratedPaneVocabulary` in shared code plus `defineGeneratedPaneProfile` in front code to add app/plugin-specific components, then render with `GeneratedPaneRenderer` or a profile-specific pane.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/shared/index.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var GENERATED_PANE_DIAGNOSTIC_CODES = {
|
|
4
|
+
invalidRoot: "generated-pane.invalid_root",
|
|
5
|
+
invalidElements: "generated-pane.invalid_elements",
|
|
6
|
+
missingElement: "generated-pane.missing_element",
|
|
7
|
+
elementCycle: "generated-pane.element_cycle",
|
|
8
|
+
unknownComponent: "generated-pane.unknown_component",
|
|
9
|
+
invalidProps: "generated-pane.invalid_props",
|
|
10
|
+
unsupportedProfile: "generated-pane.unsupported_profile"
|
|
11
|
+
};
|
|
12
|
+
function defineGeneratedPaneVocabulary(vocabulary) {
|
|
13
|
+
return vocabulary;
|
|
14
|
+
}
|
|
15
|
+
var baseGeneratedPaneVocabulary = defineGeneratedPaneVocabulary({
|
|
16
|
+
id: "base",
|
|
17
|
+
label: "Generated Pane",
|
|
18
|
+
components: {
|
|
19
|
+
Card: {
|
|
20
|
+
description: "A bordered content card with an optional title and description.",
|
|
21
|
+
slots: ["default"],
|
|
22
|
+
props: z.object({ title: z.string().optional(), description: z.string().optional() })
|
|
23
|
+
},
|
|
24
|
+
Stack: {
|
|
25
|
+
description: "Vertical stack for grouping child elements.",
|
|
26
|
+
slots: ["default"],
|
|
27
|
+
props: z.object({ gap: z.enum(["sm", "md", "lg"]).optional() })
|
|
28
|
+
},
|
|
29
|
+
Grid: {
|
|
30
|
+
description: "Responsive grid layout for child elements.",
|
|
31
|
+
slots: ["default"],
|
|
32
|
+
props: z.object({ columns: z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]).optional() })
|
|
33
|
+
},
|
|
34
|
+
Text: {
|
|
35
|
+
description: "Plain text block.",
|
|
36
|
+
props: z.object({ text: z.string(), tone: z.enum(["default", "muted"]).optional() })
|
|
37
|
+
},
|
|
38
|
+
Badge: {
|
|
39
|
+
description: "Small status badge.",
|
|
40
|
+
props: z.object({ label: z.string(), variant: z.enum(["default", "secondary", "outline"]).optional() })
|
|
41
|
+
},
|
|
42
|
+
Alert: {
|
|
43
|
+
description: "Notice block for warnings, status, or context.",
|
|
44
|
+
props: z.object({ title: z.string(), description: z.string().optional() })
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
function isRecord(value) {
|
|
49
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
function isStringArray(value) {
|
|
52
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
53
|
+
}
|
|
54
|
+
function diagnostic(code, message, options = {}) {
|
|
55
|
+
const { severity = "error", ...rest } = options;
|
|
56
|
+
return { severity, code, message, ...rest };
|
|
57
|
+
}
|
|
58
|
+
function schemaPath(path) {
|
|
59
|
+
return path.length ? path.map(String).join(".") : void 0;
|
|
60
|
+
}
|
|
61
|
+
function validateGeneratedPaneSpec(value, vocabulary = baseGeneratedPaneVocabulary) {
|
|
62
|
+
const diagnostics = [];
|
|
63
|
+
if (!isRecord(value)) {
|
|
64
|
+
return { spec: null, diagnostics: [diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, "generated pane spec must be an object")] };
|
|
65
|
+
}
|
|
66
|
+
if (value.kind !== "boring.generated-pane") diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, 'generated pane spec kind must be "boring.generated-pane"', { path: "kind" }));
|
|
67
|
+
if (value.version !== 1) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, "generated pane spec version must be 1", { path: "version" }));
|
|
68
|
+
if (value.profile !== void 0 && typeof value.profile !== "string") diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, "generated pane profile must be a string", { path: "profile" }));
|
|
69
|
+
if (value.title !== void 0 && typeof value.title !== "string") diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, "generated pane title must be a string", { path: "title" }));
|
|
70
|
+
if (value.description !== void 0 && typeof value.description !== "string") diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, "generated pane description must be a string", { path: "description" }));
|
|
71
|
+
if (typeof value.root !== "string" || value.root.length === 0) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, "generated pane root must be a string", { path: "root" }));
|
|
72
|
+
if (value.queries !== void 0 && !isRecord(value.queries)) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, "generated pane queries must be an object", { path: "queries" }));
|
|
73
|
+
if (!isRecord(value.elements)) {
|
|
74
|
+
diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, "generated pane elements must be an object", { path: "elements" }));
|
|
75
|
+
} else {
|
|
76
|
+
for (const [id, element] of Object.entries(value.elements)) {
|
|
77
|
+
if (!isRecord(element)) {
|
|
78
|
+
diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, `element ${id} must be an object`, { elementId: id, path: `elements.${id}` }));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (typeof element.type !== "string" || element.type.length === 0) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, `element ${id} must include type`, { elementId: id, path: `elements.${id}.type` }));
|
|
82
|
+
if (element.props !== void 0 && !isRecord(element.props)) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, `element ${id}.props must be an object`, { elementId: id, path: `elements.${id}.props` }));
|
|
83
|
+
if (element.children !== void 0 && !isStringArray(element.children)) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, `element ${id}.children must be an array of ids`, { elementId: id, path: `elements.${id}.children` }));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (isRecord(value.elements) && typeof value.root === "string") validateGraph(value.root, value.elements, diagnostics);
|
|
87
|
+
const fatal = diagnostics.some((item) => item.code === GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot || item.code === GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements || item.code === GENERATED_PANE_DIAGNOSTIC_CODES.missingElement || item.code === GENERATED_PANE_DIAGNOSTIC_CODES.elementCycle);
|
|
88
|
+
if (fatal) return { spec: null, diagnostics };
|
|
89
|
+
const spec = value;
|
|
90
|
+
const declaredProfile = spec.profile ?? "base";
|
|
91
|
+
if (declaredProfile !== vocabulary.id) {
|
|
92
|
+
diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.unsupportedProfile, `generated pane profile ${declaredProfile} is not supported by active vocabulary ${vocabulary.id}`, { path: "profile" }));
|
|
93
|
+
}
|
|
94
|
+
for (const [id, element] of Object.entries(spec.elements)) {
|
|
95
|
+
const entry = vocabulary.components[element.type];
|
|
96
|
+
if (!entry) {
|
|
97
|
+
diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.unknownComponent, `component ${id} has unsupported type ${element.type}`, { elementId: id, path: `elements.${id}.type` }));
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const parsed = entry.props.safeParse(element.props ?? {});
|
|
101
|
+
if (!parsed.success) {
|
|
102
|
+
for (const issue of parsed.error.issues) {
|
|
103
|
+
const propPath = schemaPath(issue.path);
|
|
104
|
+
diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidProps, `component ${id}.props${propPath ? `.${propPath}` : ""}: ${issue.message}`, { elementId: id, path: propPath ? `elements.${id}.props.${propPath}` : `elements.${id}.props` }));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const check of vocabulary.diagnostics ?? []) diagnostics.push(...check(spec));
|
|
109
|
+
return { spec, diagnostics };
|
|
110
|
+
}
|
|
111
|
+
function parseGeneratedPaneSpec(value, vocabulary = baseGeneratedPaneVocabulary) {
|
|
112
|
+
const result = validateGeneratedPaneSpec(value, vocabulary);
|
|
113
|
+
return {
|
|
114
|
+
spec: result.diagnostics.some((item) => item.severity === "error") ? null : result.spec,
|
|
115
|
+
errors: result.diagnostics.filter((item) => item.severity === "error").map((item) => item.message)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function validateGraph(root, elements, diagnostics) {
|
|
119
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
120
|
+
const visited = /* @__PURE__ */ new Set();
|
|
121
|
+
const visit = (id, path) => {
|
|
122
|
+
if (visiting.has(id)) {
|
|
123
|
+
diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.elementCycle, `generated pane element cycle: ${[...path, id].join(" -> ")}`, { elementId: id, path: `elements.${id}` }));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (visited.has(id)) return;
|
|
127
|
+
const element = elements[id];
|
|
128
|
+
if (!isRecord(element)) {
|
|
129
|
+
diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.missingElement, `element ${id} is referenced but not defined`, { elementId: id, path: `elements.${id}` }));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
visiting.add(id);
|
|
133
|
+
if (isStringArray(element.children)) {
|
|
134
|
+
for (const child of element.children) visit(child, [...path, id]);
|
|
135
|
+
}
|
|
136
|
+
visiting.delete(id);
|
|
137
|
+
visited.add(id);
|
|
138
|
+
};
|
|
139
|
+
visit(root, []);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export {
|
|
143
|
+
GENERATED_PANE_DIAGNOSTIC_CODES,
|
|
144
|
+
defineGeneratedPaneVocabulary,
|
|
145
|
+
baseGeneratedPaneVocabulary,
|
|
146
|
+
validateGeneratedPaneSpec,
|
|
147
|
+
parseGeneratedPaneSpec
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=chunk-PELWQS23.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/index.ts"],"sourcesContent":["import { z } from \"zod\"\n\nexport interface GeneratedPaneElementSpec {\n type: string\n props?: Record<string, unknown>\n children?: string[]\n}\n\nexport interface GeneratedPaneSpec {\n kind: \"boring.generated-pane\"\n version: 1\n profile?: string\n title?: string\n description?: string\n root: string\n elements: Record<string, GeneratedPaneElementSpec>\n queries?: Record<string, unknown>\n}\n\nexport type GeneratedPaneDiagnosticSeverity = \"error\" | \"warning\" | \"info\"\n\nexport const GENERATED_PANE_DIAGNOSTIC_CODES = {\n invalidRoot: \"generated-pane.invalid_root\",\n invalidElements: \"generated-pane.invalid_elements\",\n missingElement: \"generated-pane.missing_element\",\n elementCycle: \"generated-pane.element_cycle\",\n unknownComponent: \"generated-pane.unknown_component\",\n invalidProps: \"generated-pane.invalid_props\",\n unsupportedProfile: \"generated-pane.unsupported_profile\",\n} as const\n\nexport type GeneratedPaneDiagnosticCode = typeof GENERATED_PANE_DIAGNOSTIC_CODES[keyof typeof GENERATED_PANE_DIAGNOSTIC_CODES]\n\nexport interface GeneratedPaneDiagnostic {\n severity: GeneratedPaneDiagnosticSeverity\n code: GeneratedPaneDiagnosticCode\n message: string\n elementId?: string\n path?: string\n}\n\nexport interface GeneratedPaneComponentVocabularyEntry {\n description: string\n props: z.ZodTypeAny\n slots?: string[]\n}\n\nexport interface GeneratedPaneVocabulary {\n id: string\n label: string\n components: Record<string, GeneratedPaneComponentVocabularyEntry>\n diagnostics?: Array<(spec: GeneratedPaneSpec) => GeneratedPaneDiagnostic[]>\n}\n\nexport interface GeneratedPaneValidationResult {\n spec: GeneratedPaneSpec | null\n errors: string[]\n}\n\nexport interface GeneratedPaneDiagnosticsResult {\n spec: GeneratedPaneSpec | null\n diagnostics: GeneratedPaneDiagnostic[]\n}\n\nexport function defineGeneratedPaneVocabulary(vocabulary: GeneratedPaneVocabulary): GeneratedPaneVocabulary {\n return vocabulary\n}\n\nexport const baseGeneratedPaneVocabulary = defineGeneratedPaneVocabulary({\n id: \"base\",\n label: \"Generated Pane\",\n components: {\n Card: {\n description: \"A bordered content card with an optional title and description.\",\n slots: [\"default\"],\n props: z.object({ title: z.string().optional(), description: z.string().optional() }),\n },\n Stack: {\n description: \"Vertical stack for grouping child elements.\",\n slots: [\"default\"],\n props: z.object({ gap: z.enum([\"sm\", \"md\", \"lg\"]).optional() }),\n },\n Grid: {\n description: \"Responsive grid layout for child elements.\",\n slots: [\"default\"],\n props: z.object({ columns: z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]).optional() }),\n },\n Text: {\n description: \"Plain text block.\",\n props: z.object({ text: z.string(), tone: z.enum([\"default\", \"muted\"]).optional() }),\n },\n Badge: {\n description: \"Small status badge.\",\n props: z.object({ label: z.string(), variant: z.enum([\"default\", \"secondary\", \"outline\"]).optional() }),\n },\n Alert: {\n description: \"Notice block for warnings, status, or context.\",\n props: z.object({ title: z.string(), description: z.string().optional() }),\n },\n },\n})\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\")\n}\n\nfunction diagnostic(code: GeneratedPaneDiagnosticCode, message: string, options: Omit<GeneratedPaneDiagnostic, \"severity\" | \"code\" | \"message\"> & { severity?: GeneratedPaneDiagnosticSeverity } = {}): GeneratedPaneDiagnostic {\n const { severity = \"error\", ...rest } = options\n return { severity, code, message, ...rest }\n}\n\nfunction schemaPath(path: PropertyKey[]): string | undefined {\n return path.length ? path.map(String).join(\".\") : undefined\n}\n\nexport function validateGeneratedPaneSpec(value: unknown, vocabulary: GeneratedPaneVocabulary = baseGeneratedPaneVocabulary): GeneratedPaneDiagnosticsResult {\n const diagnostics: GeneratedPaneDiagnostic[] = []\n if (!isRecord(value)) {\n return { spec: null, diagnostics: [diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, \"generated pane spec must be an object\")] }\n }\n\n if (value.kind !== \"boring.generated-pane\") diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, 'generated pane spec kind must be \"boring.generated-pane\"', { path: \"kind\" }))\n if (value.version !== 1) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, \"generated pane spec version must be 1\", { path: \"version\" }))\n if (value.profile !== undefined && typeof value.profile !== \"string\") diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, \"generated pane profile must be a string\", { path: \"profile\" }))\n if (value.title !== undefined && typeof value.title !== \"string\") diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, \"generated pane title must be a string\", { path: \"title\" }))\n if (value.description !== undefined && typeof value.description !== \"string\") diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, \"generated pane description must be a string\", { path: \"description\" }))\n if (typeof value.root !== \"string\" || value.root.length === 0) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, \"generated pane root must be a string\", { path: \"root\" }))\n if (value.queries !== undefined && !isRecord(value.queries)) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot, \"generated pane queries must be an object\", { path: \"queries\" }))\n\n if (!isRecord(value.elements)) {\n diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, \"generated pane elements must be an object\", { path: \"elements\" }))\n } else {\n for (const [id, element] of Object.entries(value.elements)) {\n if (!isRecord(element)) {\n diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, `element ${id} must be an object`, { elementId: id, path: `elements.${id}` }))\n continue\n }\n if (typeof element.type !== \"string\" || element.type.length === 0) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, `element ${id} must include type`, { elementId: id, path: `elements.${id}.type` }))\n if (element.props !== undefined && !isRecord(element.props)) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, `element ${id}.props must be an object`, { elementId: id, path: `elements.${id}.props` }))\n if (element.children !== undefined && !isStringArray(element.children)) diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements, `element ${id}.children must be an array of ids`, { elementId: id, path: `elements.${id}.children` }))\n }\n }\n\n if (isRecord(value.elements) && typeof value.root === \"string\") validateGraph(value.root, value.elements, diagnostics)\n\n const fatal = diagnostics.some((item) => item.code === GENERATED_PANE_DIAGNOSTIC_CODES.invalidRoot || item.code === GENERATED_PANE_DIAGNOSTIC_CODES.invalidElements || item.code === GENERATED_PANE_DIAGNOSTIC_CODES.missingElement || item.code === GENERATED_PANE_DIAGNOSTIC_CODES.elementCycle)\n if (fatal) return { spec: null, diagnostics }\n\n const spec = value as unknown as GeneratedPaneSpec\n const declaredProfile = spec.profile ?? \"base\"\n if (declaredProfile !== vocabulary.id) {\n diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.unsupportedProfile, `generated pane profile ${declaredProfile} is not supported by active vocabulary ${vocabulary.id}`, { path: \"profile\" }))\n }\n\n for (const [id, element] of Object.entries(spec.elements)) {\n const entry = vocabulary.components[element.type]\n if (!entry) {\n diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.unknownComponent, `component ${id} has unsupported type ${element.type}`, { elementId: id, path: `elements.${id}.type` }))\n continue\n }\n const parsed = entry.props.safeParse(element.props ?? {})\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n const propPath = schemaPath(issue.path)\n diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.invalidProps, `component ${id}.props${propPath ? `.${propPath}` : \"\"}: ${issue.message}`, { elementId: id, path: propPath ? `elements.${id}.props.${propPath}` : `elements.${id}.props` }))\n }\n }\n }\n\n for (const check of vocabulary.diagnostics ?? []) diagnostics.push(...check(spec))\n return { spec, diagnostics }\n}\n\nexport function parseGeneratedPaneSpec(value: unknown, vocabulary: GeneratedPaneVocabulary = baseGeneratedPaneVocabulary): GeneratedPaneValidationResult {\n const result = validateGeneratedPaneSpec(value, vocabulary)\n return {\n spec: result.diagnostics.some((item) => item.severity === \"error\") ? null : result.spec,\n errors: result.diagnostics.filter((item) => item.severity === \"error\").map((item) => item.message),\n }\n}\n\nfunction validateGraph(root: string, elements: Record<string, unknown>, diagnostics: GeneratedPaneDiagnostic[]): void {\n const visiting = new Set<string>()\n const visited = new Set<string>()\n const visit = (id: string, path: string[]): void => {\n if (visiting.has(id)) {\n diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.elementCycle, `generated pane element cycle: ${[...path, id].join(\" -> \")}`, { elementId: id, path: `elements.${id}` }))\n return\n }\n if (visited.has(id)) return\n const element = elements[id]\n if (!isRecord(element)) {\n diagnostics.push(diagnostic(GENERATED_PANE_DIAGNOSTIC_CODES.missingElement, `element ${id} is referenced but not defined`, { elementId: id, path: `elements.${id}` }))\n return\n }\n visiting.add(id)\n if (isStringArray(element.children)) {\n for (const child of element.children) visit(child, [...path, id])\n }\n visiting.delete(id)\n visited.add(id)\n }\n visit(root, [])\n}\n"],"mappings":"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","names":[]}
|
package/dist/front/index.d.ts
CHANGED
|
@@ -1,33 +1,25 @@
|
|
|
1
1
|
import * as _hachej_boring_workspace_plugin from '@hachej/boring-workspace/plugin';
|
|
2
2
|
import { PaneProps, BoringFrontSurfaceResolverRegistration } from '@hachej/boring-workspace/plugin';
|
|
3
|
-
import * as
|
|
4
|
-
import { GeneratedPaneSpec } from '../shared/index.js';
|
|
5
|
-
export { GeneratedPaneElementSpec, GeneratedPaneValidationResult, parseGeneratedPaneSpec } from '../shared/index.js';
|
|
6
|
-
import * as _json_render_core from '@json-render/core';
|
|
3
|
+
import * as react from 'react';
|
|
7
4
|
import { ReactNode } from 'react';
|
|
8
|
-
import {
|
|
5
|
+
import { GeneratedPaneComponentVocabularyEntry, GeneratedPaneVocabulary, GeneratedPaneSpec } from '../shared/index.js';
|
|
6
|
+
export { GENERATED_PANE_DIAGNOSTIC_CODES, GeneratedPaneDiagnostic, GeneratedPaneDiagnosticCode, GeneratedPaneDiagnosticsResult, GeneratedPaneElementSpec, GeneratedPaneValidationResult, baseGeneratedPaneVocabulary, defineGeneratedPaneVocabulary, parseGeneratedPaneSpec, validateGeneratedPaneSpec } from '../shared/index.js';
|
|
7
|
+
import * as zod from 'zod';
|
|
8
|
+
import * as zod_v4_core from 'zod/v4/core';
|
|
9
|
+
import * as _json_render_core from '@json-render/core';
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
slots?: string[];
|
|
14
|
-
description: string;
|
|
15
|
-
component: (props: {
|
|
16
|
-
props: Record<string, unknown>;
|
|
17
|
-
children?: ReactNode;
|
|
18
|
-
emit: (event: string) => void;
|
|
19
|
-
}) => ReactNode;
|
|
11
|
+
interface GeneratedPaneComponentProps {
|
|
12
|
+
props: Record<string, unknown>;
|
|
13
|
+
children?: ReactNode;
|
|
20
14
|
}
|
|
21
|
-
interface
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
interface GeneratedPaneComponentBinding {
|
|
16
|
+
component: (props: GeneratedPaneComponentProps) => ReactNode;
|
|
17
|
+
}
|
|
18
|
+
interface GeneratedPaneComponentDefinition extends GeneratedPaneComponentVocabularyEntry, GeneratedPaneComponentBinding {
|
|
25
19
|
}
|
|
26
20
|
interface GeneratedPaneProfile {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
components: Record<string, GeneratedPaneComponentDefinition>;
|
|
30
|
-
actions?: Record<string, GeneratedPaneActionDefinition>;
|
|
21
|
+
vocabulary: GeneratedPaneVocabulary;
|
|
22
|
+
components: Record<string, GeneratedPaneComponentBinding>;
|
|
31
23
|
}
|
|
32
24
|
declare function defineGeneratedPaneProfile(profile: GeneratedPaneProfile): GeneratedPaneProfile;
|
|
33
25
|
declare const baseGeneratedPaneProfile: GeneratedPaneProfile;
|
|
@@ -57,22 +49,17 @@ declare function createGeneratedPaneCatalog(profile: GeneratedPaneProfile): _jso
|
|
|
57
49
|
}, {
|
|
58
50
|
components: {
|
|
59
51
|
[k: string]: {
|
|
60
|
-
props:
|
|
52
|
+
props: zod.ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>;
|
|
61
53
|
slots: string[];
|
|
62
54
|
description: string;
|
|
63
55
|
};
|
|
64
56
|
};
|
|
65
|
-
actions: {
|
|
66
|
-
[k: string]: {
|
|
67
|
-
params: z.ZodObject<Record<string, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>, z.core.$strip> | undefined;
|
|
68
|
-
description: string;
|
|
69
|
-
};
|
|
70
|
-
};
|
|
57
|
+
actions: {};
|
|
71
58
|
}>;
|
|
72
59
|
declare function GeneratedPaneRenderer({ spec, profile }: {
|
|
73
60
|
spec: GeneratedPaneSpec;
|
|
74
61
|
profile?: GeneratedPaneProfile;
|
|
75
|
-
}):
|
|
62
|
+
}): react.JSX.Element;
|
|
76
63
|
|
|
77
64
|
interface GeneratedPanePaneParams {
|
|
78
65
|
path?: string;
|
|
@@ -80,7 +67,7 @@ interface GeneratedPanePaneParams {
|
|
|
80
67
|
}
|
|
81
68
|
declare function GeneratedPanePane({ params, profile }: PaneProps<GeneratedPanePaneParams> & {
|
|
82
69
|
profile?: GeneratedPaneProfile;
|
|
83
|
-
}):
|
|
70
|
+
}): react.JSX.Element;
|
|
84
71
|
|
|
85
72
|
interface GeneratedPaneExplorerConfig {
|
|
86
73
|
title?: string;
|
|
@@ -92,12 +79,12 @@ interface GeneratedPaneExplorerConfig {
|
|
|
92
79
|
}
|
|
93
80
|
declare function createGeneratedPaneExplorerPane(config?: GeneratedPaneExplorerConfig): (props: PaneProps<{
|
|
94
81
|
searchQuery?: string;
|
|
95
|
-
}>) =>
|
|
82
|
+
}>) => react.JSX.Element;
|
|
96
83
|
declare function GeneratedPaneExplorerPane({ params, containerApi, config }: PaneProps<{
|
|
97
84
|
searchQuery?: string;
|
|
98
85
|
}> & {
|
|
99
86
|
config?: GeneratedPaneExplorerConfig;
|
|
100
|
-
}):
|
|
87
|
+
}): react.JSX.Element;
|
|
101
88
|
|
|
102
89
|
declare const GENERATED_PANE_PANEL_ID = "generated-pane.panel";
|
|
103
90
|
declare const GENERATED_PANE_EXPLORER_LEFT_TAB_ID = "generated-pane.explorer";
|
|
@@ -107,4 +94,4 @@ declare const generatedPaneSurfaceResolver: BoringFrontSurfaceResolverRegistrati
|
|
|
107
94
|
|
|
108
95
|
declare const generatedPanePlugin: _hachej_boring_workspace_plugin.BoringFrontFactoryWithId;
|
|
109
96
|
|
|
110
|
-
export { GENERATED_PANE_EXPLORER_LEFT_TAB_ID, GENERATED_PANE_PANEL_ID, type
|
|
97
|
+
export { GENERATED_PANE_EXPLORER_LEFT_TAB_ID, GENERATED_PANE_PANEL_ID, type GeneratedPaneComponentBinding, type GeneratedPaneComponentDefinition, type GeneratedPaneComponentProps, type GeneratedPaneExplorerConfig, GeneratedPaneExplorerPane, GeneratedPanePane, type GeneratedPanePaneParams, type GeneratedPaneProfile, GeneratedPaneRenderer, GeneratedPaneSpec, GeneratedPaneVocabulary, baseGeneratedPaneProfile, createGeneratedPaneCatalog, createGeneratedPaneExplorerPane, generatedPanePlugin as default, defineGeneratedPaneProfile, generatedPanePlugin, generatedPaneSurfaceResolver, isGeneratedPanePath, mergeGeneratedPaneProfiles };
|
package/dist/front/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
GENERATED_PANE_DIAGNOSTIC_CODES,
|
|
3
|
+
baseGeneratedPaneVocabulary,
|
|
4
|
+
defineGeneratedPaneVocabulary,
|
|
5
|
+
parseGeneratedPaneSpec,
|
|
6
|
+
validateGeneratedPaneSpec
|
|
7
|
+
} from "../chunk-PELWQS23.js";
|
|
4
8
|
|
|
5
9
|
// src/front/index.ts
|
|
6
10
|
import { PanelTop as PanelTop2 } from "lucide-react";
|
|
@@ -12,23 +16,25 @@ import { EmptyState, Toolbar, ToolbarGroup, Badge as Badge2 } from "@hachej/bori
|
|
|
12
16
|
import { useApiBaseUrl, useWorkspaceRequestId } from "@hachej/boring-workspace";
|
|
13
17
|
|
|
14
18
|
// src/front/catalog.tsx
|
|
19
|
+
import { useMemo } from "react";
|
|
15
20
|
import { defineCatalog } from "@json-render/core";
|
|
16
21
|
import { defineRegistry, JSONUIProvider, Renderer } from "@json-render/react";
|
|
17
22
|
import { schema } from "@json-render/react/schema";
|
|
18
|
-
import {
|
|
19
|
-
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from "@hachej/boring-ui-kit";
|
|
23
|
+
import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from "@hachej/boring-ui-kit";
|
|
20
24
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
25
|
function defineGeneratedPaneProfile(profile) {
|
|
26
|
+
const componentKeys = new Set(Object.keys(profile.components));
|
|
27
|
+
const vocabularyKeys = new Set(Object.keys(profile.vocabulary.components));
|
|
28
|
+
const bindingWithoutVocabulary = [...componentKeys].filter((key) => !vocabularyKeys.has(key));
|
|
29
|
+
const vocabularyWithoutBinding = [...vocabularyKeys].filter((key) => !componentKeys.has(key));
|
|
30
|
+
if (bindingWithoutVocabulary.length > 0) throw new Error(`generated pane profile ${profile.vocabulary.id} has render bindings without vocabulary entries: ${bindingWithoutVocabulary.join(", ")}`);
|
|
31
|
+
if (vocabularyWithoutBinding.length > 0) throw new Error(`generated pane profile ${profile.vocabulary.id} has vocabulary entries without render bindings: ${vocabularyWithoutBinding.join(", ")}`);
|
|
22
32
|
return profile;
|
|
23
33
|
}
|
|
24
34
|
var baseGeneratedPaneProfile = defineGeneratedPaneProfile({
|
|
25
|
-
|
|
26
|
-
label: "Generated Pane",
|
|
35
|
+
vocabulary: baseGeneratedPaneVocabulary,
|
|
27
36
|
components: {
|
|
28
37
|
Card: {
|
|
29
|
-
description: "A bordered content card with an optional title and description.",
|
|
30
|
-
slots: ["default"],
|
|
31
|
-
props: z.object({ title: z.string().optional(), description: z.string().optional() }),
|
|
32
38
|
component: ({ props, children }) => /* @__PURE__ */ jsxs(Card, { children: [
|
|
33
39
|
props.title || props.description ? /* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
34
40
|
typeof props.title === "string" ? /* @__PURE__ */ jsx(CardTitle, { children: props.title }) : null,
|
|
@@ -38,76 +44,70 @@ var baseGeneratedPaneProfile = defineGeneratedPaneProfile({
|
|
|
38
44
|
] })
|
|
39
45
|
},
|
|
40
46
|
Stack: {
|
|
41
|
-
description: "Vertical stack for grouping child elements.",
|
|
42
|
-
slots: ["default"],
|
|
43
|
-
props: z.object({ gap: z.enum(["sm", "md", "lg"]).optional() }),
|
|
44
47
|
component: ({ props, children }) => /* @__PURE__ */ jsx("div", { className: props.gap === "sm" ? "space-y-2" : props.gap === "lg" ? "space-y-6" : "space-y-4", children })
|
|
45
48
|
},
|
|
46
49
|
Grid: {
|
|
47
|
-
description: "Responsive grid layout for child elements.",
|
|
48
|
-
slots: ["default"],
|
|
49
|
-
props: z.object({ columns: z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]).optional() }),
|
|
50
50
|
component: ({ props, children }) => {
|
|
51
51
|
const columns = props.columns === 3 ? "lg:grid-cols-3" : props.columns === 4 ? "lg:grid-cols-4" : props.columns === 1 ? "grid-cols-1" : "lg:grid-cols-2";
|
|
52
52
|
return /* @__PURE__ */ jsx("div", { className: `grid gap-4 ${columns}`, children });
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
Text: {
|
|
56
|
-
description: "Plain text block.",
|
|
57
|
-
props: z.object({ text: z.string(), tone: z.enum(["default", "muted"]).optional() }),
|
|
58
56
|
component: ({ props }) => /* @__PURE__ */ jsx("p", { className: props.tone === "muted" ? "text-sm text-muted-foreground" : "text-sm text-foreground", children: String(props.text) })
|
|
59
57
|
},
|
|
60
58
|
Badge: {
|
|
61
|
-
description: "Small status badge.",
|
|
62
|
-
props: z.object({ label: z.string(), variant: z.enum(["default", "secondary", "outline"]).optional() }),
|
|
63
59
|
component: ({ props }) => /* @__PURE__ */ jsx(Badge, { variant: props.variant ?? "secondary", children: String(props.label) })
|
|
64
60
|
},
|
|
65
61
|
Alert: {
|
|
66
|
-
description: "Notice block for warnings, status, or context.",
|
|
67
|
-
props: z.object({ title: z.string(), description: z.string().optional() }),
|
|
68
62
|
component: ({ props }) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-muted/40 p-3 text-sm", children: [
|
|
69
63
|
/* @__PURE__ */ jsx("div", { className: "font-medium text-foreground", children: String(props.title) }),
|
|
70
64
|
typeof props.description === "string" ? /* @__PURE__ */ jsx("div", { className: "mt-1 text-muted-foreground", children: props.description }) : null
|
|
71
65
|
] })
|
|
72
|
-
},
|
|
73
|
-
Button: {
|
|
74
|
-
description: "Button that emits its press event to a configured action.",
|
|
75
|
-
props: z.object({ label: z.string() }),
|
|
76
|
-
component: ({ props, emit }) => /* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => emit("press"), children: String(props.label) })
|
|
77
66
|
}
|
|
78
67
|
}
|
|
79
68
|
});
|
|
80
69
|
function mergeGeneratedPaneProfiles(...profiles) {
|
|
81
70
|
const [first, ...rest] = profiles;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
const mergedVocabularyComponents = Object.assign({}, ...profiles.map((profile) => profile.vocabulary.components));
|
|
72
|
+
const mergedBindings = Object.assign({}, ...profiles.map((profile) => profile.components));
|
|
73
|
+
const finalProfile = rest.at(-1) ?? first;
|
|
74
|
+
return defineGeneratedPaneProfile({
|
|
75
|
+
vocabulary: {
|
|
76
|
+
id: finalProfile?.vocabulary.id ?? "base",
|
|
77
|
+
label: finalProfile?.vocabulary.label ?? "Generated Pane",
|
|
78
|
+
components: Object.fromEntries(Object.entries(mergedVocabularyComponents).filter(([key]) => key in mergedBindings)),
|
|
79
|
+
diagnostics: profiles.flatMap((profile) => profile.vocabulary.diagnostics ?? [])
|
|
80
|
+
},
|
|
81
|
+
components: Object.fromEntries(Object.entries(mergedBindings).filter(([key]) => key in mergedVocabularyComponents))
|
|
82
|
+
});
|
|
88
83
|
}
|
|
89
84
|
function createGeneratedPaneCatalog(profile) {
|
|
90
85
|
return defineCatalog(schema, {
|
|
91
|
-
components: Object.fromEntries(Object.entries(profile.components).map(([name, definition]) => [name, {
|
|
86
|
+
components: Object.fromEntries(Object.entries(profile.vocabulary.components).map(([name, definition]) => [name, {
|
|
92
87
|
props: definition.props,
|
|
93
88
|
slots: definition.slots ?? [],
|
|
94
89
|
description: definition.description
|
|
95
90
|
}])),
|
|
96
|
-
actions:
|
|
97
|
-
params: definition.params,
|
|
98
|
-
description: definition.description
|
|
99
|
-
}]))
|
|
91
|
+
actions: {}
|
|
100
92
|
});
|
|
101
93
|
}
|
|
102
94
|
function GeneratedPaneRenderer({ spec, profile }) {
|
|
103
|
-
const activeProfile = profile ? mergeGeneratedPaneProfiles(baseGeneratedPaneProfile, profile) : baseGeneratedPaneProfile;
|
|
104
|
-
const catalog = createGeneratedPaneCatalog(activeProfile);
|
|
105
|
-
const normalizedElements = Object.fromEntries(Object.entries(spec.elements).map(([id, element]) => [id, {
|
|
95
|
+
const activeProfile = useMemo(() => profile ? mergeGeneratedPaneProfiles(baseGeneratedPaneProfile, profile) : baseGeneratedPaneProfile, [profile]);
|
|
96
|
+
const catalog = useMemo(() => createGeneratedPaneCatalog(activeProfile), [activeProfile]);
|
|
97
|
+
const normalizedElements = useMemo(() => Object.fromEntries(Object.entries(spec.elements).map(([id, element]) => [id, {
|
|
106
98
|
...element,
|
|
107
99
|
props: element.props ?? {},
|
|
108
100
|
children: element.children ?? [],
|
|
109
101
|
visible: "visible" in element ? element.visible : true
|
|
110
|
-
}]));
|
|
102
|
+
}])), [spec.elements]);
|
|
103
|
+
const diagnostics = useMemo(() => validateGeneratedPaneSpec(spec, activeProfile.vocabulary).diagnostics, [activeProfile.vocabulary, spec]);
|
|
104
|
+
const errors = diagnostics.filter((item) => item.severity === "error");
|
|
105
|
+
if (errors.length > 0) {
|
|
106
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: [
|
|
107
|
+
"Invalid generated pane spec: ",
|
|
108
|
+
errors.slice(0, 3).map((issue) => issue.message).join(" \u2022 ")
|
|
109
|
+
] });
|
|
110
|
+
}
|
|
111
111
|
const validation = catalog.validate({ root: spec.root, elements: normalizedElements });
|
|
112
112
|
if (!validation.success) {
|
|
113
113
|
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: [
|
|
@@ -119,7 +119,7 @@ function GeneratedPaneRenderer({ spec, profile }) {
|
|
|
119
119
|
return /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: "Invalid generated pane spec" });
|
|
120
120
|
}
|
|
121
121
|
const { registry } = defineRegistry(catalog, {
|
|
122
|
-
components: Object.fromEntries(Object.entries(activeProfile.components).map(([name, definition]) => [name, ({ props, children
|
|
122
|
+
components: Object.fromEntries(Object.entries(activeProfile.components).map(([name, definition]) => [name, ({ props, children }) => definition.component({ props: props && typeof props === "object" && !Array.isArray(props) ? props : {}, children })])),
|
|
123
123
|
actions: {}
|
|
124
124
|
});
|
|
125
125
|
return /* @__PURE__ */ jsx(JSONUIProvider, { registry, handlers: {}, children: /* @__PURE__ */ jsx(Renderer, { spec: validation.data, registry }) });
|
|
@@ -153,7 +153,8 @@ function GeneratedPanePane({ params, profile }) {
|
|
|
153
153
|
}, [apiBaseUrl, params?.path, params?.spec, workspaceId]);
|
|
154
154
|
if (loadedFile.loading) return /* @__PURE__ */ jsx2(PaneState, { title: "Loading generated pane", description: params?.path });
|
|
155
155
|
if (loadedFile.error) return /* @__PURE__ */ jsx2(PaneState, { title: "Could not load generated pane", description: loadedFile.error });
|
|
156
|
-
const
|
|
156
|
+
const activeProfile = profile ? mergeGeneratedPaneProfiles(baseGeneratedPaneProfile, profile) : baseGeneratedPaneProfile;
|
|
157
|
+
const parsed = parseGeneratedPaneSpec(params?.spec ?? loadedFile.spec, activeProfile.vocabulary);
|
|
157
158
|
if (!parsed.spec) return /* @__PURE__ */ jsx2(PaneState, { title: "Invalid generated pane spec", description: parsed.errors.slice(0, 5).join(" \u2022 ") });
|
|
158
159
|
return /* @__PURE__ */ jsxs2("div", { className: "flex h-full min-h-0 min-w-0 flex-col bg-background text-foreground", children: [
|
|
159
160
|
/* @__PURE__ */ jsx2(Toolbar, { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ jsxs2(ToolbarGroup, { children: [
|
|
@@ -172,7 +173,7 @@ function PaneState({ title, description }) {
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
// src/front/GeneratedPaneExplorerPane.tsx
|
|
175
|
-
import { useEffect as useEffect2, useMemo, useState as useState2 } from "react";
|
|
176
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
176
177
|
import { FileJson2, PanelTop, RefreshCw } from "lucide-react";
|
|
177
178
|
import { Badge as Badge3, EmptyState as EmptyState2, IconButton } from "@hachej/boring-ui-kit";
|
|
178
179
|
import { useApiBaseUrl as useApiBaseUrl2, useWorkspaceRequestId as useWorkspaceRequestId2 } from "@hachej/boring-workspace";
|
|
@@ -230,7 +231,7 @@ function GeneratedPaneExplorerPane({ params, containerApi, config = {} }) {
|
|
|
230
231
|
});
|
|
231
232
|
return () => controller.abort();
|
|
232
233
|
}, [apiBaseUrl, patterns.join("\n"), refreshKey, workspaceId]);
|
|
233
|
-
const paths =
|
|
234
|
+
const paths = useMemo2(
|
|
234
235
|
() => state.paths.filter((path) => matchesQuery(path, params?.searchQuery)),
|
|
235
236
|
[params?.searchQuery, state.paths]
|
|
236
237
|
);
|
|
@@ -361,20 +362,24 @@ var generatedPanePlugin = definePlugin({
|
|
|
361
362
|
});
|
|
362
363
|
var front_default = generatedPanePlugin;
|
|
363
364
|
export {
|
|
365
|
+
GENERATED_PANE_DIAGNOSTIC_CODES,
|
|
364
366
|
GENERATED_PANE_EXPLORER_LEFT_TAB_ID,
|
|
365
367
|
GENERATED_PANE_PANEL_ID,
|
|
366
368
|
GeneratedPaneExplorerPane,
|
|
367
369
|
GeneratedPanePane,
|
|
368
370
|
GeneratedPaneRenderer,
|
|
369
371
|
baseGeneratedPaneProfile,
|
|
372
|
+
baseGeneratedPaneVocabulary,
|
|
370
373
|
createGeneratedPaneCatalog,
|
|
371
374
|
createGeneratedPaneExplorerPane,
|
|
372
375
|
front_default as default,
|
|
373
376
|
defineGeneratedPaneProfile,
|
|
377
|
+
defineGeneratedPaneVocabulary,
|
|
374
378
|
generatedPanePlugin,
|
|
375
379
|
generatedPaneSurfaceResolver,
|
|
376
380
|
isGeneratedPanePath,
|
|
377
381
|
mergeGeneratedPaneProfiles,
|
|
378
|
-
parseGeneratedPaneSpec
|
|
382
|
+
parseGeneratedPaneSpec,
|
|
383
|
+
validateGeneratedPaneSpec
|
|
379
384
|
};
|
|
380
385
|
//# sourceMappingURL=index.js.map
|
package/dist/front/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/front/index.ts","../../src/front/GeneratedPanePane.tsx","../../src/front/catalog.tsx","../../src/front/GeneratedPaneExplorerPane.tsx","../../src/front/constants.ts","../../src/front/surfaceResolver.ts"],"sourcesContent":["import { PanelTop } from \"lucide-react\"\nimport { definePlugin } from \"@hachej/boring-workspace/plugin\"\nimport { GeneratedPanePane } from \"./GeneratedPanePane\"\nimport { createGeneratedPaneExplorerPane, GeneratedPaneExplorerPane } from \"./GeneratedPaneExplorerPane\"\nimport { GENERATED_PANE_EXPLORER_LEFT_TAB_ID, GENERATED_PANE_PANEL_ID } from \"./constants\"\nimport { generatedPaneSurfaceResolver } from \"./surfaceResolver\"\n\nexport { GeneratedPanePane }\nexport type { GeneratedPanePaneParams } from \"./GeneratedPanePane\"\nexport { GeneratedPaneExplorerPane, createGeneratedPaneExplorerPane }\nexport type { GeneratedPaneExplorerConfig } from \"./GeneratedPaneExplorerPane\"\nexport {\n GeneratedPaneRenderer,\n baseGeneratedPaneProfile,\n createGeneratedPaneCatalog,\n defineGeneratedPaneProfile,\n mergeGeneratedPaneProfiles,\n} from \"./catalog\"\nexport type {\n GeneratedPaneActionDefinition,\n GeneratedPaneActionHandler,\n GeneratedPaneComponentDefinition,\n GeneratedPaneProfile,\n} from \"./catalog\"\nexport type { GeneratedPaneElementSpec, GeneratedPaneSpec, GeneratedPaneValidationResult } from \"../shared\"\nexport { parseGeneratedPaneSpec } from \"../shared\"\nexport { GENERATED_PANE_EXPLORER_LEFT_TAB_ID, GENERATED_PANE_PANEL_ID } from \"./constants\"\nexport { generatedPaneSurfaceResolver, isGeneratedPanePath } from \"./surfaceResolver\"\n\nexport const generatedPanePlugin = definePlugin({\n id: \"generated-pane\",\n label: \"Generated Pane\",\n panels: [\n {\n id: GENERATED_PANE_PANEL_ID,\n label: \"Generated Pane\",\n icon: PanelTop,\n component: GeneratedPanePane,\n supportsFullPage: true,\n },\n ],\n leftTabs: [\n {\n id: GENERATED_PANE_EXPLORER_LEFT_TAB_ID,\n title: \"Panes\",\n panelId: GENERATED_PANE_EXPLORER_LEFT_TAB_ID,\n icon: PanelTop,\n component: createGeneratedPaneExplorerPane({\n title: \"Panes\",\n patterns: [\"**/*.pane.json\"],\n panelId: GENERATED_PANE_PANEL_ID,\n itemLabel: \"Pane\",\n emptyDescription: \"Create panes/*.pane.json files to list generated panes here.\",\n }),\n chromeless: true,\n },\n ],\n surfaceResolvers: [generatedPaneSurfaceResolver],\n commands: [\n {\n id: \"generated-pane.open\",\n title: \"Open Generated Pane\",\n panelId: GENERATED_PANE_PANEL_ID,\n keywords: [\"json-render\", \"generated\", \"custom pane\", \"pane\"],\n },\n ],\n})\n\nexport default generatedPanePlugin\n","import { useEffect, useState } from \"react\"\nimport { EmptyState, Toolbar, ToolbarGroup, Badge } from \"@hachej/boring-ui-kit\"\nimport { useApiBaseUrl, useWorkspaceRequestId } from \"@hachej/boring-workspace\"\nimport type { PaneProps } from \"@hachej/boring-workspace/plugin\"\nimport { parseGeneratedPaneSpec, type GeneratedPaneSpec } from \"../shared\"\nimport { GeneratedPaneRenderer, type GeneratedPaneProfile } from \"./catalog\"\n\nexport interface GeneratedPanePaneParams {\n path?: string\n spec?: GeneratedPaneSpec\n}\n\ninterface LoadedPaneFile {\n spec: unknown\n error?: string\n loading: boolean\n}\n\nexport function GeneratedPanePane({ params, profile }: PaneProps<GeneratedPanePaneParams> & { profile?: GeneratedPaneProfile }) {\n const apiBaseUrl = useApiBaseUrl()\n const workspaceId = useWorkspaceRequestId()\n const [loadedFile, setLoadedFile] = useState<LoadedPaneFile>({ spec: null, loading: false })\n\n useEffect(() => {\n if (!params?.path || params.spec) {\n setLoadedFile({ spec: null, loading: false })\n return\n }\n const controller = new AbortController()\n setLoadedFile({ spec: null, loading: true })\n void fetch(`${apiBaseUrl}/api/v1/files/raw?path=${encodeURIComponent(params.path)}`, {\n signal: controller.signal,\n credentials: \"include\",\n headers: workspaceId ? { \"x-boring-workspace-id\": workspaceId } : {},\n })\n .then(async (response) => {\n if (!response.ok) throw new Error(`Failed to load ${params.path}: HTTP ${response.status}`)\n return await response.text()\n })\n .then((text) => setLoadedFile({ spec: JSON.parse(text), loading: false }))\n .catch((error) => {\n if (controller.signal.aborted) return\n setLoadedFile({ spec: null, loading: false, error: error instanceof Error ? error.message : String(error) })\n })\n return () => controller.abort()\n }, [apiBaseUrl, params?.path, params?.spec, workspaceId])\n\n if (loadedFile.loading) return <PaneState title=\"Loading generated pane\" description={params?.path} />\n if (loadedFile.error) return <PaneState title=\"Could not load generated pane\" description={loadedFile.error} />\n\n const parsed = parseGeneratedPaneSpec(params?.spec ?? loadedFile.spec)\n if (!parsed.spec) return <PaneState title=\"Invalid generated pane spec\" description={parsed.errors.slice(0, 5).join(\" • \")} />\n\n return (\n <div className=\"flex h-full min-h-0 min-w-0 flex-col bg-background text-foreground\">\n <Toolbar className=\"border-b border-border px-3 py-2\">\n <ToolbarGroup>\n <Badge variant=\"secondary\">Generated Pane</Badge>\n {parsed.spec.profile ? <Badge variant=\"outline\">{parsed.spec.profile}</Badge> : null}\n </ToolbarGroup>\n </Toolbar>\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto p-4\">\n {parsed.spec.title ? <h1 className=\"mb-1 text-2xl font-semibold tracking-tight\">{parsed.spec.title}</h1> : null}\n {parsed.spec.description ? <p className=\"mb-4 text-sm text-muted-foreground\">{parsed.spec.description}</p> : null}\n <GeneratedPaneRenderer spec={parsed.spec} profile={profile} />\n </div>\n </div>\n )\n}\n\nfunction PaneState({ title, description }: { title: string; description?: string }) {\n return (\n <div className=\"flex h-full min-h-0 min-w-0 items-center justify-center bg-background p-6 text-foreground\">\n <EmptyState title={title} description={description} />\n </div>\n )\n}\n","import type { ReactNode } from \"react\"\nimport { defineCatalog } from \"@json-render/core\"\nimport { defineRegistry, JSONUIProvider, Renderer } from \"@json-render/react\"\nimport { schema } from \"@json-render/react/schema\"\nimport { z } from \"zod\"\nimport { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@hachej/boring-ui-kit\"\nimport type { GeneratedPaneSpec } from \"../shared\"\n\nexport type GeneratedPaneActionHandler = (params: Record<string, unknown>) => void | Promise<void>\n\nexport interface GeneratedPaneComponentDefinition {\n props: z.ZodObject<Record<string, z.ZodType>>\n slots?: string[]\n description: string\n component: (props: { props: Record<string, unknown>; children?: ReactNode; emit: (event: string) => void }) => ReactNode\n}\n\nexport interface GeneratedPaneActionDefinition {\n description: string\n params?: z.ZodObject<Record<string, z.ZodType>>\n handler?: GeneratedPaneActionHandler\n}\n\nexport interface GeneratedPaneProfile {\n id: string\n label: string\n components: Record<string, GeneratedPaneComponentDefinition>\n actions?: Record<string, GeneratedPaneActionDefinition>\n}\n\nexport function defineGeneratedPaneProfile(profile: GeneratedPaneProfile): GeneratedPaneProfile {\n return profile\n}\n\nexport const baseGeneratedPaneProfile = defineGeneratedPaneProfile({\n id: \"base\",\n label: \"Generated Pane\",\n components: {\n Card: {\n description: \"A bordered content card with an optional title and description.\",\n slots: [\"default\"],\n props: z.object({ title: z.string().optional(), description: z.string().optional() }),\n component: ({ props, children }) => (\n <Card>\n {props.title || props.description ? (\n <CardHeader>\n {typeof props.title === \"string\" ? <CardTitle>{props.title}</CardTitle> : null}\n {typeof props.description === \"string\" ? <CardDescription>{props.description}</CardDescription> : null}\n </CardHeader>\n ) : null}\n <CardContent>{children}</CardContent>\n </Card>\n ),\n },\n Stack: {\n description: \"Vertical stack for grouping child elements.\",\n slots: [\"default\"],\n props: z.object({ gap: z.enum([\"sm\", \"md\", \"lg\"]).optional() }),\n component: ({ props, children }) => <div className={props.gap === \"sm\" ? \"space-y-2\" : props.gap === \"lg\" ? \"space-y-6\" : \"space-y-4\"}>{children}</div>,\n },\n Grid: {\n description: \"Responsive grid layout for child elements.\",\n slots: [\"default\"],\n props: z.object({ columns: z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]).optional() }),\n component: ({ props, children }) => {\n const columns = props.columns === 3 ? \"lg:grid-cols-3\" : props.columns === 4 ? \"lg:grid-cols-4\" : props.columns === 1 ? \"grid-cols-1\" : \"lg:grid-cols-2\"\n return <div className={`grid gap-4 ${columns}`}>{children}</div>\n },\n },\n Text: {\n description: \"Plain text block.\",\n props: z.object({ text: z.string(), tone: z.enum([\"default\", \"muted\"]).optional() }),\n component: ({ props }) => <p className={props.tone === \"muted\" ? \"text-sm text-muted-foreground\" : \"text-sm text-foreground\"}>{String(props.text)}</p>,\n },\n Badge: {\n description: \"Small status badge.\",\n props: z.object({ label: z.string(), variant: z.enum([\"default\", \"secondary\", \"outline\"]).optional() }),\n component: ({ props }) => <Badge variant={(props.variant as \"default\" | \"secondary\" | \"outline\" | undefined) ?? \"secondary\"}>{String(props.label)}</Badge>,\n },\n Alert: {\n description: \"Notice block for warnings, status, or context.\",\n props: z.object({ title: z.string(), description: z.string().optional() }),\n component: ({ props }) => (\n <div className=\"rounded-lg border border-border bg-muted/40 p-3 text-sm\">\n <div className=\"font-medium text-foreground\">{String(props.title)}</div>\n {typeof props.description === \"string\" ? <div className=\"mt-1 text-muted-foreground\">{props.description}</div> : null}\n </div>\n ),\n },\n Button: {\n description: \"Button that emits its press event to a configured action.\",\n props: z.object({ label: z.string() }),\n component: ({ props, emit }) => <Button size=\"sm\" onClick={() => emit(\"press\")}>{String(props.label)}</Button>,\n },\n },\n})\n\nexport function mergeGeneratedPaneProfiles(...profiles: GeneratedPaneProfile[]): GeneratedPaneProfile {\n const [first, ...rest] = profiles\n return {\n id: first?.id ?? \"generated-pane\",\n label: first?.label ?? \"Generated Pane\",\n components: Object.assign({}, ...profiles.map((profile) => profile.components)),\n actions: Object.assign({}, ...profiles.map((profile) => profile.actions ?? {})),\n }\n}\n\nexport function createGeneratedPaneCatalog(profile: GeneratedPaneProfile) {\n return defineCatalog(schema, {\n components: Object.fromEntries(Object.entries(profile.components).map(([name, definition]) => [name, {\n props: definition.props,\n slots: definition.slots ?? [],\n description: definition.description,\n }])),\n actions: Object.fromEntries(Object.entries(profile.actions ?? {}).map(([name, definition]) => [name, {\n params: definition.params,\n description: definition.description,\n }])),\n })\n}\n\nexport function GeneratedPaneRenderer({ spec, profile }: { spec: GeneratedPaneSpec; profile?: GeneratedPaneProfile }) {\n const activeProfile = profile ? mergeGeneratedPaneProfiles(baseGeneratedPaneProfile, profile) : baseGeneratedPaneProfile\n const catalog = createGeneratedPaneCatalog(activeProfile)\n const normalizedElements = Object.fromEntries(Object.entries(spec.elements).map(([id, element]) => [id, {\n ...element,\n props: element.props ?? {},\n children: element.children ?? [],\n visible: \"visible\" in element ? element.visible : true,\n }]))\n const validation = catalog.validate({ root: spec.root, elements: normalizedElements })\n if (!validation.success) {\n return <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive\">Invalid generated pane spec: {validation.error?.issues.slice(0, 3).map((issue) => issue.message).join(\" • \")}</div>\n }\n if (!validation.data) {\n return <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive\">Invalid generated pane spec</div>\n }\n const { registry } = defineRegistry(catalog, {\n components: Object.fromEntries(Object.entries(activeProfile.components).map(([name, definition]) => [name, ({ props, children, emit }: { props: Record<string, unknown>; children?: ReactNode; emit: (event: string) => void }) => definition.component({ props, children, emit })])),\n actions: {},\n })\n return (\n <JSONUIProvider registry={registry} handlers={{}}>\n <Renderer spec={validation.data as never} registry={registry} />\n </JSONUIProvider>\n )\n}\n","import { useEffect, useMemo, useState } from \"react\"\nimport { FileJson2, PanelTop, RefreshCw } from \"lucide-react\"\nimport { Badge, EmptyState, IconButton } from \"@hachej/boring-ui-kit\"\nimport { useApiBaseUrl, useWorkspaceRequestId } from \"@hachej/boring-workspace\"\nimport type { PaneProps } from \"@hachej/boring-workspace/plugin\"\nimport { GENERATED_PANE_PANEL_ID } from \"./constants\"\n\nexport interface GeneratedPaneExplorerConfig {\n title?: string\n patterns?: string[]\n panelId?: string\n itemLabel?: string\n emptyTitle?: string\n emptyDescription?: string\n}\n\ninterface PaneSearchState {\n loading: boolean\n error?: string\n paths: string[]\n}\n\ninterface PaneSearchResponse {\n results?: string[]\n}\n\nconst DEFAULT_PATTERNS = [\"**/*.pane.json\"]\n\nfunction titleFromPath(path: string): string {\n const file = path.split(\"/\").pop() ?? path\n return file\n .replace(/\\.(pane|dashboard)\\.json$/i, \"\")\n .replace(/[-_]+/g, \" \")\n}\n\nfunction matchesQuery(path: string, query: string | undefined): boolean {\n const value = query?.trim().toLowerCase()\n if (!value) return true\n return path.toLowerCase().includes(value) || titleFromPath(path).toLowerCase().includes(value)\n}\n\nfunction dedupeAndSort(values: string[]): string[] {\n return [...new Set(values)].sort((a, b) => a.localeCompare(b))\n}\n\nexport function createGeneratedPaneExplorerPane(config: GeneratedPaneExplorerConfig = {}) {\n return function GeneratedPaneExplorerPaneWithConfig(props: PaneProps<{ searchQuery?: string }>) {\n return <GeneratedPaneExplorerPane {...props} config={config} />\n }\n}\n\nexport function GeneratedPaneExplorerPane({ params, containerApi, config = {} }: PaneProps<{ searchQuery?: string }> & { config?: GeneratedPaneExplorerConfig }) {\n const apiBaseUrl = useApiBaseUrl()\n const workspaceId = useWorkspaceRequestId()\n const patterns = config.patterns?.length ? config.patterns : DEFAULT_PATTERNS\n const panelId = config.panelId ?? GENERATED_PANE_PANEL_ID\n const title = config.title ?? \"Panes\"\n const itemLabel = config.itemLabel ?? \"Generated pane\"\n const [state, setState] = useState<PaneSearchState>({ loading: true, paths: [] })\n const [refreshKey, setRefreshKey] = useState(0)\n\n useEffect(() => {\n const controller = new AbortController()\n setState((prev) => ({ ...prev, loading: true, error: undefined }))\n void Promise.all(patterns.map(async (pattern) => {\n const response = await fetch(`${apiBaseUrl}/api/v1/files/search?q=${encodeURIComponent(pattern)}&limit=500`, {\n signal: controller.signal,\n credentials: \"include\",\n headers: workspaceId ? { \"x-boring-workspace-id\": workspaceId } : {},\n })\n if (!response.ok) throw new Error(`Pane search failed for ${pattern} with HTTP ${response.status}`)\n const body = await response.json() as PaneSearchResponse\n return body.results ?? []\n }))\n .then((groups) => {\n setState({ loading: false, paths: dedupeAndSort(groups.flat()) })\n })\n .catch((error) => {\n if (controller.signal.aborted) return\n setState({ loading: false, paths: [], error: error instanceof Error ? error.message : String(error) })\n })\n return () => controller.abort()\n }, [apiBaseUrl, patterns.join(\"\\n\"), refreshKey, workspaceId])\n\n const paths = useMemo(\n () => state.paths.filter((path) => matchesQuery(path, params?.searchQuery)),\n [params?.searchQuery, state.paths],\n )\n\n const openPane = (path: string) => {\n containerApi.addPanel({\n id: `${panelId}:${path}`,\n component: panelId,\n title: titleFromPath(path),\n params: { path },\n })\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col text-sm\">\n <div className=\"flex items-center justify-between border-b border-border/60 px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <PanelTop className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"truncate font-medium\">{title}</span>\n <Badge variant=\"secondary\">{state.paths.length}</Badge>\n </div>\n <IconButton\n type=\"button\"\n aria-label={`Refresh ${title.toLowerCase()}`}\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={() => setRefreshKey((value) => value + 1)}\n disabled={state.loading}\n >\n <RefreshCw className={`h-3.5 w-3.5 ${state.loading ? \"animate-spin\" : \"\"}`} />\n </IconButton>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-auto p-2\">\n {state.error ? (\n <EmptyState title={`Could not list ${title.toLowerCase()}`} description={state.error} />\n ) : state.loading ? (\n <div className=\"px-2 py-3 text-xs text-muted-foreground\">Scanning {title.toLowerCase()}…</div>\n ) : paths.length === 0 ? (\n <EmptyState\n title={config.emptyTitle ?? `No ${title.toLowerCase()} found`}\n description={config.emptyDescription ?? `Create ${patterns.join(\" or \")} files to list them here.`}\n />\n ) : (\n <div className=\"space-y-1\">\n {paths.map((path) => (\n <button\n key={path}\n type=\"button\"\n onClick={() => openPane(path)}\n className=\"flex w-full min-w-0 items-start gap-2 rounded-lg px-2 py-2 text-left hover:bg-background/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40\"\n >\n <FileJson2 className=\"mt-0.5 h-4 w-4 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-[13px] font-medium text-foreground\">{titleFromPath(path)}</span>\n <span className=\"block truncate text-[11px] text-muted-foreground\">{path}</span>\n </span>\n <Badge variant=\"outline\" className=\"mt-0.5 shrink-0 text-[10px]\">{itemLabel}</Badge>\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n )\n}\n","export const GENERATED_PANE_PANEL_ID = \"generated-pane.panel\"\nexport const GENERATED_PANE_EXPLORER_LEFT_TAB_ID = \"generated-pane.explorer\"\n","import {\n WORKSPACE_OPEN_PATH_SURFACE_KIND,\n type BoringFrontSurfaceResolverRegistration,\n} from \"@hachej/boring-workspace/plugin\"\nimport { GENERATED_PANE_PANEL_ID } from \"./constants\"\n\nexport function isGeneratedPanePath(path: string): boolean {\n return /(^|\\/)panes\\/[^/].*\\.pane\\.json$/i.test(path) || /\\.pane\\.json$/i.test(path)\n}\n\nfunction titleFromPath(path: string): string {\n const file = path.split(\"/\").pop() ?? path\n return file.replace(/\\.pane\\.json$/i, \"\").replace(/[-_]+/g, \" \")\n}\n\nexport const generatedPaneSurfaceResolver: BoringFrontSurfaceResolverRegistration = {\n id: \"generated-pane.open-path\",\n kind: WORKSPACE_OPEN_PATH_SURFACE_KIND,\n source: \"app\",\n resolve: (request) => {\n if (request.kind !== WORKSPACE_OPEN_PATH_SURFACE_KIND) return null\n const target = String(request.target ?? \"\")\n if (!isGeneratedPanePath(target)) return null\n return {\n component: GENERATED_PANE_PANEL_ID,\n title: titleFromPath(target),\n params: { path: target },\n score: 110,\n }\n },\n}\n"],"mappings":"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","names":["PanelTop","Badge","jsx","jsxs","Badge","useEffect","useState","Badge","EmptyState","useApiBaseUrl","useWorkspaceRequestId","jsx","jsxs","useApiBaseUrl","useWorkspaceRequestId","useState","useEffect","Badge","EmptyState","titleFromPath","PanelTop"]}
|
|
1
|
+
{"version":3,"sources":["../../src/front/index.ts","../../src/front/GeneratedPanePane.tsx","../../src/front/catalog.tsx","../../src/front/GeneratedPaneExplorerPane.tsx","../../src/front/constants.ts","../../src/front/surfaceResolver.ts"],"sourcesContent":["import { PanelTop } from \"lucide-react\"\nimport { definePlugin } from \"@hachej/boring-workspace/plugin\"\nimport { GeneratedPanePane } from \"./GeneratedPanePane\"\nimport { createGeneratedPaneExplorerPane, GeneratedPaneExplorerPane } from \"./GeneratedPaneExplorerPane\"\nimport { GENERATED_PANE_EXPLORER_LEFT_TAB_ID, GENERATED_PANE_PANEL_ID } from \"./constants\"\nimport { generatedPaneSurfaceResolver } from \"./surfaceResolver\"\n\nexport { GeneratedPanePane }\nexport type { GeneratedPanePaneParams } from \"./GeneratedPanePane\"\nexport { GeneratedPaneExplorerPane, createGeneratedPaneExplorerPane }\nexport type { GeneratedPaneExplorerConfig } from \"./GeneratedPaneExplorerPane\"\nexport {\n GeneratedPaneRenderer,\n baseGeneratedPaneProfile,\n createGeneratedPaneCatalog,\n defineGeneratedPaneProfile,\n mergeGeneratedPaneProfiles,\n} from \"./catalog\"\nexport type {\n GeneratedPaneComponentBinding,\n GeneratedPaneComponentDefinition,\n GeneratedPaneComponentProps,\n GeneratedPaneProfile,\n} from \"./catalog\"\nexport type {\n GeneratedPaneDiagnostic,\n GeneratedPaneDiagnosticCode,\n GeneratedPaneDiagnosticsResult,\n GeneratedPaneElementSpec,\n GeneratedPaneSpec,\n GeneratedPaneValidationResult,\n GeneratedPaneVocabulary,\n} from \"../shared\"\nexport { GENERATED_PANE_DIAGNOSTIC_CODES, baseGeneratedPaneVocabulary, defineGeneratedPaneVocabulary, parseGeneratedPaneSpec, validateGeneratedPaneSpec } from \"../shared\"\nexport { GENERATED_PANE_EXPLORER_LEFT_TAB_ID, GENERATED_PANE_PANEL_ID } from \"./constants\"\nexport { generatedPaneSurfaceResolver, isGeneratedPanePath } from \"./surfaceResolver\"\n\nexport const generatedPanePlugin = definePlugin({\n id: \"generated-pane\",\n label: \"Generated Pane\",\n panels: [\n {\n id: GENERATED_PANE_PANEL_ID,\n label: \"Generated Pane\",\n icon: PanelTop,\n component: GeneratedPanePane,\n supportsFullPage: true,\n },\n ],\n leftTabs: [\n {\n id: GENERATED_PANE_EXPLORER_LEFT_TAB_ID,\n title: \"Panes\",\n panelId: GENERATED_PANE_EXPLORER_LEFT_TAB_ID,\n icon: PanelTop,\n component: createGeneratedPaneExplorerPane({\n title: \"Panes\",\n patterns: [\"**/*.pane.json\"],\n panelId: GENERATED_PANE_PANEL_ID,\n itemLabel: \"Pane\",\n emptyDescription: \"Create panes/*.pane.json files to list generated panes here.\",\n }),\n chromeless: true,\n },\n ],\n surfaceResolvers: [generatedPaneSurfaceResolver],\n commands: [\n {\n id: \"generated-pane.open\",\n title: \"Open Generated Pane\",\n panelId: GENERATED_PANE_PANEL_ID,\n keywords: [\"json-render\", \"generated\", \"custom pane\", \"pane\"],\n },\n ],\n})\n\nexport default generatedPanePlugin\n","import { useEffect, useState } from \"react\"\nimport { EmptyState, Toolbar, ToolbarGroup, Badge } from \"@hachej/boring-ui-kit\"\nimport { useApiBaseUrl, useWorkspaceRequestId } from \"@hachej/boring-workspace\"\nimport type { PaneProps } from \"@hachej/boring-workspace/plugin\"\nimport { parseGeneratedPaneSpec, type GeneratedPaneSpec } from \"../shared\"\nimport { GeneratedPaneRenderer, baseGeneratedPaneProfile, mergeGeneratedPaneProfiles, type GeneratedPaneProfile } from \"./catalog\"\n\nexport interface GeneratedPanePaneParams {\n path?: string\n spec?: GeneratedPaneSpec\n}\n\ninterface LoadedPaneFile {\n spec: unknown\n error?: string\n loading: boolean\n}\n\nexport function GeneratedPanePane({ params, profile }: PaneProps<GeneratedPanePaneParams> & { profile?: GeneratedPaneProfile }) {\n const apiBaseUrl = useApiBaseUrl()\n const workspaceId = useWorkspaceRequestId()\n const [loadedFile, setLoadedFile] = useState<LoadedPaneFile>({ spec: null, loading: false })\n\n useEffect(() => {\n if (!params?.path || params.spec) {\n setLoadedFile({ spec: null, loading: false })\n return\n }\n const controller = new AbortController()\n setLoadedFile({ spec: null, loading: true })\n void fetch(`${apiBaseUrl}/api/v1/files/raw?path=${encodeURIComponent(params.path)}`, {\n signal: controller.signal,\n credentials: \"include\",\n headers: workspaceId ? { \"x-boring-workspace-id\": workspaceId } : {},\n })\n .then(async (response) => {\n if (!response.ok) throw new Error(`Failed to load ${params.path}: HTTP ${response.status}`)\n return await response.text()\n })\n .then((text) => setLoadedFile({ spec: JSON.parse(text), loading: false }))\n .catch((error) => {\n if (controller.signal.aborted) return\n setLoadedFile({ spec: null, loading: false, error: error instanceof Error ? error.message : String(error) })\n })\n return () => controller.abort()\n }, [apiBaseUrl, params?.path, params?.spec, workspaceId])\n\n if (loadedFile.loading) return <PaneState title=\"Loading generated pane\" description={params?.path} />\n if (loadedFile.error) return <PaneState title=\"Could not load generated pane\" description={loadedFile.error} />\n\n const activeProfile = profile ? mergeGeneratedPaneProfiles(baseGeneratedPaneProfile, profile) : baseGeneratedPaneProfile\n const parsed = parseGeneratedPaneSpec(params?.spec ?? loadedFile.spec, activeProfile.vocabulary)\n if (!parsed.spec) return <PaneState title=\"Invalid generated pane spec\" description={parsed.errors.slice(0, 5).join(\" • \")} />\n\n return (\n <div className=\"flex h-full min-h-0 min-w-0 flex-col bg-background text-foreground\">\n <Toolbar className=\"border-b border-border px-3 py-2\">\n <ToolbarGroup>\n <Badge variant=\"secondary\">Generated Pane</Badge>\n {parsed.spec.profile ? <Badge variant=\"outline\">{parsed.spec.profile}</Badge> : null}\n </ToolbarGroup>\n </Toolbar>\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto p-4\">\n {parsed.spec.title ? <h1 className=\"mb-1 text-2xl font-semibold tracking-tight\">{parsed.spec.title}</h1> : null}\n {parsed.spec.description ? <p className=\"mb-4 text-sm text-muted-foreground\">{parsed.spec.description}</p> : null}\n <GeneratedPaneRenderer spec={parsed.spec} profile={profile} />\n </div>\n </div>\n )\n}\n\nfunction PaneState({ title, description }: { title: string; description?: string }) {\n return (\n <div className=\"flex h-full min-h-0 min-w-0 items-center justify-center bg-background p-6 text-foreground\">\n <EmptyState title={title} description={description} />\n </div>\n )\n}\n","import { useMemo, type ReactNode } from \"react\"\nimport { defineCatalog } from \"@json-render/core\"\nimport { defineRegistry, JSONUIProvider, Renderer } from \"@json-render/react\"\nimport { schema } from \"@json-render/react/schema\"\nimport { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@hachej/boring-ui-kit\"\nimport {\n baseGeneratedPaneVocabulary,\n validateGeneratedPaneSpec,\n type GeneratedPaneComponentVocabularyEntry,\n type GeneratedPaneSpec,\n type GeneratedPaneVocabulary,\n} from \"../shared\"\n\nexport interface GeneratedPaneComponentProps {\n props: Record<string, unknown>\n children?: ReactNode\n}\n\nexport interface GeneratedPaneComponentBinding {\n component: (props: GeneratedPaneComponentProps) => ReactNode\n}\n\nexport interface GeneratedPaneComponentDefinition extends GeneratedPaneComponentVocabularyEntry, GeneratedPaneComponentBinding {}\n\nexport interface GeneratedPaneProfile {\n vocabulary: GeneratedPaneVocabulary\n components: Record<string, GeneratedPaneComponentBinding>\n}\n\nexport function defineGeneratedPaneProfile(profile: GeneratedPaneProfile): GeneratedPaneProfile {\n const componentKeys = new Set(Object.keys(profile.components))\n const vocabularyKeys = new Set(Object.keys(profile.vocabulary.components))\n const bindingWithoutVocabulary = [...componentKeys].filter((key) => !vocabularyKeys.has(key))\n const vocabularyWithoutBinding = [...vocabularyKeys].filter((key) => !componentKeys.has(key))\n if (bindingWithoutVocabulary.length > 0) throw new Error(`generated pane profile ${profile.vocabulary.id} has render bindings without vocabulary entries: ${bindingWithoutVocabulary.join(\", \")}`)\n if (vocabularyWithoutBinding.length > 0) throw new Error(`generated pane profile ${profile.vocabulary.id} has vocabulary entries without render bindings: ${vocabularyWithoutBinding.join(\", \")}`)\n return profile\n}\n\nexport function defineLegacyGeneratedPaneProfile(profile: { id: string; label: string; components: Record<string, GeneratedPaneComponentDefinition> }): GeneratedPaneProfile {\n return defineGeneratedPaneProfile({\n vocabulary: {\n id: profile.id,\n label: profile.label,\n components: Object.fromEntries(Object.entries(profile.components).map(([key, value]) => [key, { description: value.description, props: value.props, slots: value.slots }])),\n },\n components: Object.fromEntries(Object.entries(profile.components).map(([key, value]) => [key, { component: value.component }])),\n })\n}\n\nexport const baseGeneratedPaneProfile = defineGeneratedPaneProfile({\n vocabulary: baseGeneratedPaneVocabulary,\n components: {\n Card: {\n component: ({ props, children }) => (\n <Card>\n {props.title || props.description ? (\n <CardHeader>\n {typeof props.title === \"string\" ? <CardTitle>{props.title}</CardTitle> : null}\n {typeof props.description === \"string\" ? <CardDescription>{props.description}</CardDescription> : null}\n </CardHeader>\n ) : null}\n <CardContent>{children}</CardContent>\n </Card>\n ),\n },\n Stack: {\n component: ({ props, children }) => <div className={props.gap === \"sm\" ? \"space-y-2\" : props.gap === \"lg\" ? \"space-y-6\" : \"space-y-4\"}>{children}</div>,\n },\n Grid: {\n component: ({ props, children }) => {\n const columns = props.columns === 3 ? \"lg:grid-cols-3\" : props.columns === 4 ? \"lg:grid-cols-4\" : props.columns === 1 ? \"grid-cols-1\" : \"lg:grid-cols-2\"\n return <div className={`grid gap-4 ${columns}`}>{children}</div>\n },\n },\n Text: {\n component: ({ props }) => <p className={props.tone === \"muted\" ? \"text-sm text-muted-foreground\" : \"text-sm text-foreground\"}>{String(props.text)}</p>,\n },\n Badge: {\n component: ({ props }) => <Badge variant={(props.variant as \"default\" | \"secondary\" | \"outline\" | undefined) ?? \"secondary\"}>{String(props.label)}</Badge>,\n },\n Alert: {\n component: ({ props }) => (\n <div className=\"rounded-lg border border-border bg-muted/40 p-3 text-sm\">\n <div className=\"font-medium text-foreground\">{String(props.title)}</div>\n {typeof props.description === \"string\" ? <div className=\"mt-1 text-muted-foreground\">{props.description}</div> : null}\n </div>\n ),\n },\n },\n})\n\nexport function mergeGeneratedPaneProfiles(...profiles: GeneratedPaneProfile[]): GeneratedPaneProfile {\n const [first, ...rest] = profiles\n const mergedVocabularyComponents: Record<string, GeneratedPaneComponentVocabularyEntry> = Object.assign({}, ...profiles.map((profile) => profile.vocabulary.components))\n const mergedBindings: Record<string, GeneratedPaneComponentBinding> = Object.assign({}, ...profiles.map((profile) => profile.components))\n const finalProfile = rest.at(-1) ?? first\n return defineGeneratedPaneProfile({\n vocabulary: {\n id: finalProfile?.vocabulary.id ?? \"base\",\n label: finalProfile?.vocabulary.label ?? \"Generated Pane\",\n components: Object.fromEntries(Object.entries(mergedVocabularyComponents).filter(([key]) => key in mergedBindings)),\n diagnostics: profiles.flatMap((profile) => profile.vocabulary.diagnostics ?? []),\n },\n components: Object.fromEntries(Object.entries(mergedBindings).filter(([key]) => key in mergedVocabularyComponents)),\n })\n}\n\nexport function createGeneratedPaneCatalog(profile: GeneratedPaneProfile) {\n return defineCatalog(schema, {\n components: Object.fromEntries(Object.entries(profile.vocabulary.components).map(([name, definition]) => [name, {\n props: definition.props,\n slots: definition.slots ?? [],\n description: definition.description,\n }])),\n actions: {},\n })\n}\n\nexport function GeneratedPaneRenderer({ spec, profile }: { spec: GeneratedPaneSpec; profile?: GeneratedPaneProfile }) {\n const activeProfile = useMemo(() => profile ? mergeGeneratedPaneProfiles(baseGeneratedPaneProfile, profile) : baseGeneratedPaneProfile, [profile])\n const catalog = useMemo(() => createGeneratedPaneCatalog(activeProfile), [activeProfile])\n const normalizedElements = useMemo(() => Object.fromEntries(Object.entries(spec.elements).map(([id, element]) => [id, {\n ...element,\n props: element.props ?? {},\n children: element.children ?? [],\n visible: \"visible\" in element ? element.visible : true,\n }])), [spec.elements])\n const diagnostics = useMemo(() => validateGeneratedPaneSpec(spec, activeProfile.vocabulary).diagnostics, [activeProfile.vocabulary, spec])\n const errors = diagnostics.filter((item) => item.severity === \"error\")\n if (errors.length > 0) {\n return <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive\">Invalid generated pane spec: {errors.slice(0, 3).map((issue) => issue.message).join(\" • \")}</div>\n }\n const validation = catalog.validate({ root: spec.root, elements: normalizedElements })\n if (!validation.success) {\n return <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive\">Invalid generated pane spec: {validation.error?.issues.slice(0, 3).map((issue) => issue.message).join(\" • \")}</div>\n }\n if (!validation.data) {\n return <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive\">Invalid generated pane spec</div>\n }\n const { registry } = defineRegistry(catalog, {\n components: Object.fromEntries(Object.entries(activeProfile.components).map(([name, definition]) => [name, ({ props, children }: { props: unknown; children?: ReactNode }) => definition.component({ props: props && typeof props === \"object\" && !Array.isArray(props) ? props as Record<string, unknown> : {}, children })])) as never,\n actions: {},\n })\n return (\n <JSONUIProvider registry={registry} handlers={{}}>\n <Renderer spec={validation.data as never} registry={registry} />\n </JSONUIProvider>\n )\n}\n","import { useEffect, useMemo, useState } from \"react\"\nimport { FileJson2, PanelTop, RefreshCw } from \"lucide-react\"\nimport { Badge, EmptyState, IconButton } from \"@hachej/boring-ui-kit\"\nimport { useApiBaseUrl, useWorkspaceRequestId } from \"@hachej/boring-workspace\"\nimport type { PaneProps } from \"@hachej/boring-workspace/plugin\"\nimport { GENERATED_PANE_PANEL_ID } from \"./constants\"\n\nexport interface GeneratedPaneExplorerConfig {\n title?: string\n patterns?: string[]\n panelId?: string\n itemLabel?: string\n emptyTitle?: string\n emptyDescription?: string\n}\n\ninterface PaneSearchState {\n loading: boolean\n error?: string\n paths: string[]\n}\n\ninterface PaneSearchResponse {\n results?: string[]\n}\n\nconst DEFAULT_PATTERNS = [\"**/*.pane.json\"]\n\nfunction titleFromPath(path: string): string {\n const file = path.split(\"/\").pop() ?? path\n return file\n .replace(/\\.(pane|dashboard)\\.json$/i, \"\")\n .replace(/[-_]+/g, \" \")\n}\n\nfunction matchesQuery(path: string, query: string | undefined): boolean {\n const value = query?.trim().toLowerCase()\n if (!value) return true\n return path.toLowerCase().includes(value) || titleFromPath(path).toLowerCase().includes(value)\n}\n\nfunction dedupeAndSort(values: string[]): string[] {\n return [...new Set(values)].sort((a, b) => a.localeCompare(b))\n}\n\nexport function createGeneratedPaneExplorerPane(config: GeneratedPaneExplorerConfig = {}) {\n return function GeneratedPaneExplorerPaneWithConfig(props: PaneProps<{ searchQuery?: string }>) {\n return <GeneratedPaneExplorerPane {...props} config={config} />\n }\n}\n\nexport function GeneratedPaneExplorerPane({ params, containerApi, config = {} }: PaneProps<{ searchQuery?: string }> & { config?: GeneratedPaneExplorerConfig }) {\n const apiBaseUrl = useApiBaseUrl()\n const workspaceId = useWorkspaceRequestId()\n const patterns = config.patterns?.length ? config.patterns : DEFAULT_PATTERNS\n const panelId = config.panelId ?? GENERATED_PANE_PANEL_ID\n const title = config.title ?? \"Panes\"\n const itemLabel = config.itemLabel ?? \"Generated pane\"\n const [state, setState] = useState<PaneSearchState>({ loading: true, paths: [] })\n const [refreshKey, setRefreshKey] = useState(0)\n\n useEffect(() => {\n const controller = new AbortController()\n setState((prev) => ({ ...prev, loading: true, error: undefined }))\n void Promise.all(patterns.map(async (pattern) => {\n const response = await fetch(`${apiBaseUrl}/api/v1/files/search?q=${encodeURIComponent(pattern)}&limit=500`, {\n signal: controller.signal,\n credentials: \"include\",\n headers: workspaceId ? { \"x-boring-workspace-id\": workspaceId } : {},\n })\n if (!response.ok) throw new Error(`Pane search failed for ${pattern} with HTTP ${response.status}`)\n const body = await response.json() as PaneSearchResponse\n return body.results ?? []\n }))\n .then((groups) => {\n setState({ loading: false, paths: dedupeAndSort(groups.flat()) })\n })\n .catch((error) => {\n if (controller.signal.aborted) return\n setState({ loading: false, paths: [], error: error instanceof Error ? error.message : String(error) })\n })\n return () => controller.abort()\n }, [apiBaseUrl, patterns.join(\"\\n\"), refreshKey, workspaceId])\n\n const paths = useMemo(\n () => state.paths.filter((path) => matchesQuery(path, params?.searchQuery)),\n [params?.searchQuery, state.paths],\n )\n\n const openPane = (path: string) => {\n containerApi.addPanel({\n id: `${panelId}:${path}`,\n component: panelId,\n title: titleFromPath(path),\n params: { path },\n })\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col text-sm\">\n <div className=\"flex items-center justify-between border-b border-border/60 px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <PanelTop className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"truncate font-medium\">{title}</span>\n <Badge variant=\"secondary\">{state.paths.length}</Badge>\n </div>\n <IconButton\n type=\"button\"\n aria-label={`Refresh ${title.toLowerCase()}`}\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={() => setRefreshKey((value) => value + 1)}\n disabled={state.loading}\n >\n <RefreshCw className={`h-3.5 w-3.5 ${state.loading ? \"animate-spin\" : \"\"}`} />\n </IconButton>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-auto p-2\">\n {state.error ? (\n <EmptyState title={`Could not list ${title.toLowerCase()}`} description={state.error} />\n ) : state.loading ? (\n <div className=\"px-2 py-3 text-xs text-muted-foreground\">Scanning {title.toLowerCase()}…</div>\n ) : paths.length === 0 ? (\n <EmptyState\n title={config.emptyTitle ?? `No ${title.toLowerCase()} found`}\n description={config.emptyDescription ?? `Create ${patterns.join(\" or \")} files to list them here.`}\n />\n ) : (\n <div className=\"space-y-1\">\n {paths.map((path) => (\n <button\n key={path}\n type=\"button\"\n onClick={() => openPane(path)}\n className=\"flex w-full min-w-0 items-start gap-2 rounded-lg px-2 py-2 text-left hover:bg-background/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40\"\n >\n <FileJson2 className=\"mt-0.5 h-4 w-4 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-[13px] font-medium text-foreground\">{titleFromPath(path)}</span>\n <span className=\"block truncate text-[11px] text-muted-foreground\">{path}</span>\n </span>\n <Badge variant=\"outline\" className=\"mt-0.5 shrink-0 text-[10px]\">{itemLabel}</Badge>\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n )\n}\n","export const GENERATED_PANE_PANEL_ID = \"generated-pane.panel\"\nexport const GENERATED_PANE_EXPLORER_LEFT_TAB_ID = \"generated-pane.explorer\"\n","import {\n WORKSPACE_OPEN_PATH_SURFACE_KIND,\n type BoringFrontSurfaceResolverRegistration,\n} from \"@hachej/boring-workspace/plugin\"\nimport { GENERATED_PANE_PANEL_ID } from \"./constants\"\n\nexport function isGeneratedPanePath(path: string): boolean {\n return /(^|\\/)panes\\/[^/].*\\.pane\\.json$/i.test(path) || /\\.pane\\.json$/i.test(path)\n}\n\nfunction titleFromPath(path: string): string {\n const file = path.split(\"/\").pop() ?? path\n return file.replace(/\\.pane\\.json$/i, \"\").replace(/[-_]+/g, \" \")\n}\n\nexport const generatedPaneSurfaceResolver: BoringFrontSurfaceResolverRegistration = {\n id: \"generated-pane.open-path\",\n kind: WORKSPACE_OPEN_PATH_SURFACE_KIND,\n source: \"app\",\n resolve: (request) => {\n if (request.kind !== WORKSPACE_OPEN_PATH_SURFACE_KIND) return null\n const target = String(request.target ?? \"\")\n if (!isGeneratedPanePath(target)) return null\n return {\n component: GENERATED_PANE_PANEL_ID,\n title: titleFromPath(target),\n params: { path: target },\n score: 110,\n }\n },\n}\n"],"mappings":"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","names":["PanelTop","Badge","jsx","jsxs","Badge","useEffect","useMemo","useState","Badge","EmptyState","useApiBaseUrl","useWorkspaceRequestId","jsx","jsxs","useApiBaseUrl","useWorkspaceRequestId","useState","useEffect","useMemo","Badge","EmptyState","titleFromPath","PanelTop"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { WorkspaceServerPlugin } from '@hachej/boring-workspace/server';
|
|
2
|
+
import { GeneratedPaneDiagnostic } from '../shared/index.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
declare const GENERATED_PANE_VALIDATE_OP = "generated-pane.v1.validate";
|
|
6
|
+
interface GeneratedPaneValidateInput {
|
|
7
|
+
spec?: unknown;
|
|
8
|
+
}
|
|
9
|
+
interface GeneratedPaneValidateOutput {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
diagnostics: GeneratedPaneDiagnostic[];
|
|
12
|
+
}
|
|
13
|
+
declare function createGeneratedPaneServerPlugin(): WorkspaceServerPlugin;
|
|
14
|
+
declare function defaultGeneratedPaneServerPlugin(): WorkspaceServerPlugin;
|
|
15
|
+
|
|
16
|
+
export { GENERATED_PANE_VALIDATE_OP, type GeneratedPaneValidateInput, type GeneratedPaneValidateOutput, createGeneratedPaneServerPlugin, defaultGeneratedPaneServerPlugin as default };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
validateGeneratedPaneSpec
|
|
3
|
+
} from "../chunk-PELWQS23.js";
|
|
4
|
+
|
|
5
|
+
// src/server/index.ts
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { defineServerPlugin, defineTrustedDomainBridgeHandler } from "@hachej/boring-workspace/server";
|
|
8
|
+
var GENERATED_PANE_VALIDATE_OP = "generated-pane.v1.validate";
|
|
9
|
+
function contribution(entry) {
|
|
10
|
+
return {
|
|
11
|
+
definition: entry.definition,
|
|
12
|
+
handler: entry.handler
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function assertValidateInput(input) {
|
|
16
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("generated-pane validation input must be an object");
|
|
17
|
+
if (!("spec" in input)) throw new Error("generated-pane validation requires spec");
|
|
18
|
+
}
|
|
19
|
+
function createGeneratedPaneServerPlugin() {
|
|
20
|
+
const validateGeneratedPane = defineTrustedDomainBridgeHandler({
|
|
21
|
+
op: GENERATED_PANE_VALIDATE_OP,
|
|
22
|
+
version: 1,
|
|
23
|
+
owner: "generated-pane",
|
|
24
|
+
callerClassesAllowed: ["browser", "runtime", "server"],
|
|
25
|
+
requiredCapabilities: ["generated-pane:validate"],
|
|
26
|
+
inputSchema: z.object({ spec: z.unknown() }),
|
|
27
|
+
outputSchema: { type: "object" },
|
|
28
|
+
maxInputBytes: 2 * 1024 * 1024,
|
|
29
|
+
maxOutputBytes: 512 * 1024,
|
|
30
|
+
timeoutMs: 1e4,
|
|
31
|
+
idempotencyPolicy: "none",
|
|
32
|
+
handler: async ({ input }) => {
|
|
33
|
+
assertValidateInput(input);
|
|
34
|
+
const result = validateGeneratedPaneSpec(input.spec);
|
|
35
|
+
return { ok: result.diagnostics.every((item) => item.severity !== "error"), diagnostics: result.diagnostics };
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return defineServerPlugin({
|
|
39
|
+
id: "generated-pane",
|
|
40
|
+
label: "Generated Pane",
|
|
41
|
+
workspaceBridgeHandlers: [contribution(validateGeneratedPane)],
|
|
42
|
+
systemPrompt: "Use generated-pane.v1.validate through WorkspaceBridge for base generated pane specs. Profile-specific panes must use their owning plugin validate op."
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function defaultGeneratedPaneServerPlugin() {
|
|
46
|
+
return createGeneratedPaneServerPlugin();
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
GENERATED_PANE_VALIDATE_OP,
|
|
50
|
+
createGeneratedPaneServerPlugin,
|
|
51
|
+
defaultGeneratedPaneServerPlugin as default
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/index.ts"],"sourcesContent":["import { z } from \"zod\"\nimport { defineServerPlugin, defineTrustedDomainBridgeHandler, type WorkspaceBridgeHandler, type WorkspaceBridgeHandlerContribution, type WorkspaceServerPlugin } from \"@hachej/boring-workspace/server\"\nimport { validateGeneratedPaneSpec, type GeneratedPaneDiagnostic } from \"../shared\"\n\nexport const GENERATED_PANE_VALIDATE_OP = \"generated-pane.v1.validate\"\n\nexport interface GeneratedPaneValidateInput {\n spec?: unknown\n}\n\nexport interface GeneratedPaneValidateOutput {\n ok: boolean\n diagnostics: GeneratedPaneDiagnostic[]\n}\n\nfunction contribution<TInput, TOutput>(entry: ReturnType<typeof defineTrustedDomainBridgeHandler<TInput, TOutput>>): WorkspaceBridgeHandlerContribution {\n return {\n definition: entry.definition,\n handler: entry.handler as WorkspaceBridgeHandler,\n }\n}\n\nfunction assertValidateInput(input: unknown): asserts input is GeneratedPaneValidateInput {\n if (!input || typeof input !== \"object\" || Array.isArray(input)) throw new Error(\"generated-pane validation input must be an object\")\n if (!(\"spec\" in input)) throw new Error(\"generated-pane validation requires spec\")\n}\n\nexport function createGeneratedPaneServerPlugin(): WorkspaceServerPlugin {\n const validateGeneratedPane = defineTrustedDomainBridgeHandler<GeneratedPaneValidateInput, GeneratedPaneValidateOutput>({\n op: GENERATED_PANE_VALIDATE_OP,\n version: 1,\n owner: \"generated-pane\",\n callerClassesAllowed: [\"browser\", \"runtime\", \"server\"],\n requiredCapabilities: [\"generated-pane:validate\"],\n inputSchema: z.object({ spec: z.unknown() }),\n outputSchema: { type: \"object\" },\n maxInputBytes: 2 * 1024 * 1024,\n maxOutputBytes: 512 * 1024,\n timeoutMs: 10_000,\n idempotencyPolicy: \"none\",\n handler: async ({ input }) => {\n assertValidateInput(input)\n const result = validateGeneratedPaneSpec(input.spec)\n return { ok: result.diagnostics.every((item) => item.severity !== \"error\"), diagnostics: result.diagnostics }\n },\n })\n\n return defineServerPlugin({\n id: \"generated-pane\",\n label: \"Generated Pane\",\n workspaceBridgeHandlers: [contribution(validateGeneratedPane)],\n systemPrompt: \"Use generated-pane.v1.validate through WorkspaceBridge for base generated pane specs. Profile-specific panes must use their owning plugin validate op.\",\n })\n}\n\nexport default function defaultGeneratedPaneServerPlugin(): WorkspaceServerPlugin {\n return createGeneratedPaneServerPlugin()\n}\n"],"mappings":";;;;;AAAA,SAAS,SAAS;AAClB,SAAS,oBAAoB,wCAA0I;AAGhK,IAAM,6BAA6B;AAW1C,SAAS,aAA8B,OAAiH;AACtJ,SAAO;AAAA,IACL,YAAY,MAAM;AAAA,IAClB,SAAS,MAAM;AAAA,EACjB;AACF;AAEA,SAAS,oBAAoB,OAA6D;AACxF,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,OAAM,IAAI,MAAM,mDAAmD;AACpI,MAAI,EAAE,UAAU,OAAQ,OAAM,IAAI,MAAM,yCAAyC;AACnF;AAEO,SAAS,kCAAyD;AACvE,QAAM,wBAAwB,iCAA0F;AAAA,IACtH,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,OAAO;AAAA,IACP,sBAAsB,CAAC,WAAW,WAAW,QAAQ;AAAA,IACrD,sBAAsB,CAAC,yBAAyB;AAAA,IAChD,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAAA,IAC3C,cAAc,EAAE,MAAM,SAAS;AAAA,IAC/B,eAAe,IAAI,OAAO;AAAA,IAC1B,gBAAgB,MAAM;AAAA,IACtB,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,SAAS,OAAO,EAAE,MAAM,MAAM;AAC5B,0BAAoB,KAAK;AACzB,YAAM,SAAS,0BAA0B,MAAM,IAAI;AACnD,aAAO,EAAE,IAAI,OAAO,YAAY,MAAM,CAAC,SAAS,KAAK,aAAa,OAAO,GAAG,aAAa,OAAO,YAAY;AAAA,IAC9G;AAAA,EACF,CAAC;AAED,SAAO,mBAAmB;AAAA,IACxB,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,yBAAyB,CAAC,aAAa,qBAAqB,CAAC;AAAA,IAC7D,cAAc;AAAA,EAChB,CAAC;AACH;AAEe,SAAR,mCAA2E;AAChF,SAAO,gCAAgC;AACzC;","names":[]}
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
1
3
|
interface GeneratedPaneElementSpec {
|
|
2
4
|
type: string;
|
|
3
5
|
props?: Record<string, unknown>;
|
|
@@ -13,10 +15,46 @@ interface GeneratedPaneSpec {
|
|
|
13
15
|
elements: Record<string, GeneratedPaneElementSpec>;
|
|
14
16
|
queries?: Record<string, unknown>;
|
|
15
17
|
}
|
|
18
|
+
type GeneratedPaneDiagnosticSeverity = "error" | "warning" | "info";
|
|
19
|
+
declare const GENERATED_PANE_DIAGNOSTIC_CODES: {
|
|
20
|
+
readonly invalidRoot: "generated-pane.invalid_root";
|
|
21
|
+
readonly invalidElements: "generated-pane.invalid_elements";
|
|
22
|
+
readonly missingElement: "generated-pane.missing_element";
|
|
23
|
+
readonly elementCycle: "generated-pane.element_cycle";
|
|
24
|
+
readonly unknownComponent: "generated-pane.unknown_component";
|
|
25
|
+
readonly invalidProps: "generated-pane.invalid_props";
|
|
26
|
+
readonly unsupportedProfile: "generated-pane.unsupported_profile";
|
|
27
|
+
};
|
|
28
|
+
type GeneratedPaneDiagnosticCode = typeof GENERATED_PANE_DIAGNOSTIC_CODES[keyof typeof GENERATED_PANE_DIAGNOSTIC_CODES];
|
|
29
|
+
interface GeneratedPaneDiagnostic {
|
|
30
|
+
severity: GeneratedPaneDiagnosticSeverity;
|
|
31
|
+
code: GeneratedPaneDiagnosticCode;
|
|
32
|
+
message: string;
|
|
33
|
+
elementId?: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
}
|
|
36
|
+
interface GeneratedPaneComponentVocabularyEntry {
|
|
37
|
+
description: string;
|
|
38
|
+
props: z.ZodTypeAny;
|
|
39
|
+
slots?: string[];
|
|
40
|
+
}
|
|
41
|
+
interface GeneratedPaneVocabulary {
|
|
42
|
+
id: string;
|
|
43
|
+
label: string;
|
|
44
|
+
components: Record<string, GeneratedPaneComponentVocabularyEntry>;
|
|
45
|
+
diagnostics?: Array<(spec: GeneratedPaneSpec) => GeneratedPaneDiagnostic[]>;
|
|
46
|
+
}
|
|
16
47
|
interface GeneratedPaneValidationResult {
|
|
17
48
|
spec: GeneratedPaneSpec | null;
|
|
18
49
|
errors: string[];
|
|
19
50
|
}
|
|
20
|
-
|
|
51
|
+
interface GeneratedPaneDiagnosticsResult {
|
|
52
|
+
spec: GeneratedPaneSpec | null;
|
|
53
|
+
diagnostics: GeneratedPaneDiagnostic[];
|
|
54
|
+
}
|
|
55
|
+
declare function defineGeneratedPaneVocabulary(vocabulary: GeneratedPaneVocabulary): GeneratedPaneVocabulary;
|
|
56
|
+
declare const baseGeneratedPaneVocabulary: GeneratedPaneVocabulary;
|
|
57
|
+
declare function validateGeneratedPaneSpec(value: unknown, vocabulary?: GeneratedPaneVocabulary): GeneratedPaneDiagnosticsResult;
|
|
58
|
+
declare function parseGeneratedPaneSpec(value: unknown, vocabulary?: GeneratedPaneVocabulary): GeneratedPaneValidationResult;
|
|
21
59
|
|
|
22
|
-
export { type GeneratedPaneElementSpec, type GeneratedPaneSpec, type GeneratedPaneValidationResult, parseGeneratedPaneSpec };
|
|
60
|
+
export { GENERATED_PANE_DIAGNOSTIC_CODES, type GeneratedPaneComponentVocabularyEntry, type GeneratedPaneDiagnostic, type GeneratedPaneDiagnosticCode, type GeneratedPaneDiagnosticSeverity, type GeneratedPaneDiagnosticsResult, type GeneratedPaneElementSpec, type GeneratedPaneSpec, type GeneratedPaneValidationResult, type GeneratedPaneVocabulary, baseGeneratedPaneVocabulary, defineGeneratedPaneVocabulary, parseGeneratedPaneSpec, validateGeneratedPaneSpec };
|
package/dist/shared/index.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
GENERATED_PANE_DIAGNOSTIC_CODES,
|
|
3
|
+
baseGeneratedPaneVocabulary,
|
|
4
|
+
defineGeneratedPaneVocabulary,
|
|
5
|
+
parseGeneratedPaneSpec,
|
|
6
|
+
validateGeneratedPaneSpec
|
|
7
|
+
} from "../chunk-PELWQS23.js";
|
|
4
8
|
export {
|
|
5
|
-
|
|
9
|
+
GENERATED_PANE_DIAGNOSTIC_CODES,
|
|
10
|
+
baseGeneratedPaneVocabulary,
|
|
11
|
+
defineGeneratedPaneVocabulary,
|
|
12
|
+
parseGeneratedPaneSpec,
|
|
13
|
+
validateGeneratedPaneSpec
|
|
6
14
|
};
|
|
7
15
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-generated-pane",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.65",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"description": "Generated pane runtime for Boring workspace using json-render catalogs and safe
|
|
7
|
+
"description": "Generated pane runtime for Boring workspace using json-render catalogs and safe profile vocabularies.",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://github.com/hachej/boring-ui"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"id": "generated-pane",
|
|
15
15
|
"label": "Generated Pane",
|
|
16
16
|
"front": "dist/front/index.js",
|
|
17
|
-
"server":
|
|
17
|
+
"server": "dist/server/index.js"
|
|
18
18
|
},
|
|
19
19
|
"pi": {
|
|
20
20
|
"skills": [
|
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
"types": "./dist/front/index.d.ts",
|
|
40
40
|
"import": "./dist/front/index.js"
|
|
41
41
|
},
|
|
42
|
+
"./server": {
|
|
43
|
+
"types": "./dist/server/index.d.ts",
|
|
44
|
+
"import": "./dist/server/index.js",
|
|
45
|
+
"default": "./dist/server/index.js"
|
|
46
|
+
},
|
|
42
47
|
"./shared": {
|
|
43
48
|
"types": "./dist/shared/index.d.ts",
|
|
44
49
|
"import": "./dist/shared/index.js"
|
|
@@ -49,26 +54,26 @@
|
|
|
49
54
|
"peerDependencies": {
|
|
50
55
|
"react": "^18.0.0 || ^19.0.0",
|
|
51
56
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
52
|
-
"@hachej/boring-workspace": "0.1.
|
|
57
|
+
"@hachej/boring-workspace": "0.1.65"
|
|
53
58
|
},
|
|
54
59
|
"dependencies": {
|
|
55
60
|
"@json-render/core": "^0.19.0",
|
|
56
61
|
"@json-render/react": "^0.19.0",
|
|
57
|
-
"lucide-react": "^1.
|
|
62
|
+
"lucide-react": "^1.21.0",
|
|
58
63
|
"zod": "^4.3.6",
|
|
59
|
-
"@hachej/boring-ui-kit": "0.1.
|
|
64
|
+
"@hachej/boring-ui-kit": "0.1.65"
|
|
60
65
|
},
|
|
61
66
|
"devDependencies": {
|
|
62
|
-
"tsx": "^4.
|
|
63
|
-
"@types/react": "^19.
|
|
67
|
+
"tsx": "^4.22.4",
|
|
68
|
+
"@types/react": "^19.2.17",
|
|
64
69
|
"@types/react-dom": "^19.0.0",
|
|
65
|
-
"react": "^19.
|
|
66
|
-
"react-dom": "^19.
|
|
70
|
+
"react": "^19.2.7",
|
|
71
|
+
"react-dom": "^19.2.7",
|
|
67
72
|
"tsup": "^8.4.0",
|
|
68
|
-
"typescript": "~
|
|
69
|
-
"vitest": "^
|
|
70
|
-
"@hachej/boring-agent": "0.1.
|
|
71
|
-
"@hachej/boring-workspace": "0.1.
|
|
73
|
+
"typescript": "~6.0.3",
|
|
74
|
+
"vitest": "^4.1.9",
|
|
75
|
+
"@hachej/boring-agent": "0.1.65",
|
|
76
|
+
"@hachej/boring-workspace": "0.1.65"
|
|
72
77
|
},
|
|
73
78
|
"scripts": {
|
|
74
79
|
"build": "tsup",
|
|
@@ -26,9 +26,9 @@ Required root shape:
|
|
|
26
26
|
|
|
27
27
|
Rules:
|
|
28
28
|
|
|
29
|
-
- Do not generate React, JavaScript, functions, or inline event handlers.
|
|
29
|
+
- Do not generate React, JavaScript, functions, actions, or inline event handlers.
|
|
30
30
|
- Use only components documented by the active profile/catalog.
|
|
31
31
|
- Use only props documented for each component.
|
|
32
32
|
- Keep element ids stable and descriptive.
|
|
33
33
|
- Prefer a simple tree: one root layout, then cards/sections/widgets.
|
|
34
|
-
-
|
|
34
|
+
- For base `*.pane.json` specs, validate with WorkspaceBridge op `generated-pane.v1.validate` using `{ "spec": <json> }` when available. Profile-specific specs must use their owning plugin validate op.
|
package/dist/chunk-4IMMGGBQ.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// src/shared/index.ts
|
|
2
|
-
function isRecord(value) {
|
|
3
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
|
-
function isStringArray(value) {
|
|
6
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
7
|
-
}
|
|
8
|
-
function parseGeneratedPaneSpec(value) {
|
|
9
|
-
const errors = [];
|
|
10
|
-
if (!isRecord(value)) return { spec: null, errors: ["generated pane spec must be an object"] };
|
|
11
|
-
if (value.kind !== "boring.generated-pane") errors.push('generated pane spec kind must be "boring.generated-pane"');
|
|
12
|
-
if (value.version !== 1) errors.push("generated pane spec version must be 1");
|
|
13
|
-
if (value.profile !== void 0 && typeof value.profile !== "string") errors.push("generated pane profile must be a string");
|
|
14
|
-
if (value.title !== void 0 && typeof value.title !== "string") errors.push("generated pane title must be a string");
|
|
15
|
-
if (value.description !== void 0 && typeof value.description !== "string") errors.push("generated pane description must be a string");
|
|
16
|
-
if (typeof value.root !== "string" || value.root.length === 0) errors.push("generated pane root must be a string");
|
|
17
|
-
if (!isRecord(value.elements)) {
|
|
18
|
-
errors.push("generated pane elements must be an object");
|
|
19
|
-
} else {
|
|
20
|
-
for (const [id, element] of Object.entries(value.elements)) {
|
|
21
|
-
if (!isRecord(element)) {
|
|
22
|
-
errors.push(`element ${id} must be an object`);
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
if (typeof element.type !== "string" || element.type.length === 0) errors.push(`element ${id} must include type`);
|
|
26
|
-
if (element.props !== void 0 && !isRecord(element.props)) errors.push(`element ${id}.props must be an object`);
|
|
27
|
-
if (element.children !== void 0 && !isStringArray(element.children)) errors.push(`element ${id}.children must be an array of ids`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if (isRecord(value.elements) && typeof value.root === "string") validateAcyclic(value.root, value.elements, errors);
|
|
31
|
-
if (value.queries !== void 0 && !isRecord(value.queries)) errors.push("generated pane queries must be an object");
|
|
32
|
-
if (errors.length > 0) return { spec: null, errors };
|
|
33
|
-
return { spec: value, errors: [] };
|
|
34
|
-
}
|
|
35
|
-
function validateAcyclic(root, elements, errors) {
|
|
36
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
37
|
-
const visited = /* @__PURE__ */ new Set();
|
|
38
|
-
const visit = (id, path) => {
|
|
39
|
-
if (visiting.has(id)) {
|
|
40
|
-
errors.push(`generated pane element cycle: ${[...path, id].join(" -> ")}`);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
if (visited.has(id)) return;
|
|
44
|
-
const element = elements[id];
|
|
45
|
-
if (!isRecord(element)) {
|
|
46
|
-
errors.push(`element ${id} is referenced but not defined`);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
visiting.add(id);
|
|
50
|
-
if (isStringArray(element.children)) {
|
|
51
|
-
for (const child of element.children) visit(child, [...path, id]);
|
|
52
|
-
}
|
|
53
|
-
visiting.delete(id);
|
|
54
|
-
visited.add(id);
|
|
55
|
-
};
|
|
56
|
-
visit(root, []);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export {
|
|
60
|
-
parseGeneratedPaneSpec
|
|
61
|
-
};
|
|
62
|
-
//# sourceMappingURL=chunk-4IMMGGBQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/index.ts"],"sourcesContent":["export interface GeneratedPaneElementSpec {\n type: string\n props?: Record<string, unknown>\n children?: string[]\n}\n\nexport interface GeneratedPaneSpec {\n kind: \"boring.generated-pane\"\n version: 1\n profile?: string\n title?: string\n description?: string\n root: string\n elements: Record<string, GeneratedPaneElementSpec>\n queries?: Record<string, unknown>\n}\n\nexport interface GeneratedPaneValidationResult {\n spec: GeneratedPaneSpec | null\n errors: string[]\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\")\n}\n\nexport function parseGeneratedPaneSpec(value: unknown): GeneratedPaneValidationResult {\n const errors: string[] = []\n if (!isRecord(value)) return { spec: null, errors: [\"generated pane spec must be an object\"] }\n if (value.kind !== \"boring.generated-pane\") errors.push('generated pane spec kind must be \"boring.generated-pane\"')\n if (value.version !== 1) errors.push(\"generated pane spec version must be 1\")\n if (value.profile !== undefined && typeof value.profile !== \"string\") errors.push(\"generated pane profile must be a string\")\n if (value.title !== undefined && typeof value.title !== \"string\") errors.push(\"generated pane title must be a string\")\n if (value.description !== undefined && typeof value.description !== \"string\") errors.push(\"generated pane description must be a string\")\n if (typeof value.root !== \"string\" || value.root.length === 0) errors.push(\"generated pane root must be a string\")\n if (!isRecord(value.elements)) {\n errors.push(\"generated pane elements must be an object\")\n } else {\n for (const [id, element] of Object.entries(value.elements)) {\n if (!isRecord(element)) {\n errors.push(`element ${id} must be an object`)\n continue\n }\n if (typeof element.type !== \"string\" || element.type.length === 0) errors.push(`element ${id} must include type`)\n if (element.props !== undefined && !isRecord(element.props)) errors.push(`element ${id}.props must be an object`)\n if (element.children !== undefined && !isStringArray(element.children)) errors.push(`element ${id}.children must be an array of ids`)\n }\n }\n if (isRecord(value.elements) && typeof value.root === \"string\") validateAcyclic(value.root, value.elements, errors)\n if (value.queries !== undefined && !isRecord(value.queries)) errors.push(\"generated pane queries must be an object\")\n if (errors.length > 0) return { spec: null, errors }\n return { spec: value as unknown as GeneratedPaneSpec, errors: [] }\n}\n\nfunction validateAcyclic(root: string, elements: Record<string, unknown>, errors: string[]): void {\n const visiting = new Set<string>()\n const visited = new Set<string>()\n const visit = (id: string, path: string[]): void => {\n if (visiting.has(id)) {\n errors.push(`generated pane element cycle: ${[...path, id].join(\" -> \")}`)\n return\n }\n if (visited.has(id)) return\n const element = elements[id]\n if (!isRecord(element)) {\n errors.push(`element ${id} is referenced but not defined`)\n return\n }\n visiting.add(id)\n if (isStringArray(element.children)) {\n for (const child of element.children) visit(child, [...path, id])\n }\n visiting.delete(id)\n visited.add(id)\n }\n visit(root, [])\n}\n"],"mappings":";AAsBA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,cAAc,OAAmC;AACxD,SAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,CAAC,SAAS,OAAO,SAAS,QAAQ;AAC/E;AAEO,SAAS,uBAAuB,OAA+C;AACpF,QAAM,SAAmB,CAAC;AAC1B,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO,EAAE,MAAM,MAAM,QAAQ,CAAC,uCAAuC,EAAE;AAC7F,MAAI,MAAM,SAAS,wBAAyB,QAAO,KAAK,0DAA0D;AAClH,MAAI,MAAM,YAAY,EAAG,QAAO,KAAK,uCAAuC;AAC5E,MAAI,MAAM,YAAY,UAAa,OAAO,MAAM,YAAY,SAAU,QAAO,KAAK,yCAAyC;AAC3H,MAAI,MAAM,UAAU,UAAa,OAAO,MAAM,UAAU,SAAU,QAAO,KAAK,uCAAuC;AACrH,MAAI,MAAM,gBAAgB,UAAa,OAAO,MAAM,gBAAgB,SAAU,QAAO,KAAK,6CAA6C;AACvI,MAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,WAAW,EAAG,QAAO,KAAK,sCAAsC;AACjH,MAAI,CAAC,SAAS,MAAM,QAAQ,GAAG;AAC7B,WAAO,KAAK,2CAA2C;AAAA,EACzD,OAAO;AACL,eAAW,CAAC,IAAI,OAAO,KAAK,OAAO,QAAQ,MAAM,QAAQ,GAAG;AAC1D,UAAI,CAAC,SAAS,OAAO,GAAG;AACtB,eAAO,KAAK,WAAW,EAAE,oBAAoB;AAC7C;AAAA,MACF;AACA,UAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,WAAW,EAAG,QAAO,KAAK,WAAW,EAAE,oBAAoB;AAChH,UAAI,QAAQ,UAAU,UAAa,CAAC,SAAS,QAAQ,KAAK,EAAG,QAAO,KAAK,WAAW,EAAE,0BAA0B;AAChH,UAAI,QAAQ,aAAa,UAAa,CAAC,cAAc,QAAQ,QAAQ,EAAG,QAAO,KAAK,WAAW,EAAE,mCAAmC;AAAA,IACtI;AAAA,EACF;AACA,MAAI,SAAS,MAAM,QAAQ,KAAK,OAAO,MAAM,SAAS,SAAU,iBAAgB,MAAM,MAAM,MAAM,UAAU,MAAM;AAClH,MAAI,MAAM,YAAY,UAAa,CAAC,SAAS,MAAM,OAAO,EAAG,QAAO,KAAK,0CAA0C;AACnH,MAAI,OAAO,SAAS,EAAG,QAAO,EAAE,MAAM,MAAM,OAAO;AACnD,SAAO,EAAE,MAAM,OAAuC,QAAQ,CAAC,EAAE;AACnE;AAEA,SAAS,gBAAgB,MAAc,UAAmC,QAAwB;AAChG,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,QAAQ,CAAC,IAAY,SAAyB;AAClD,QAAI,SAAS,IAAI,EAAE,GAAG;AACpB,aAAO,KAAK,iCAAiC,CAAC,GAAG,MAAM,EAAE,EAAE,KAAK,MAAM,CAAC,EAAE;AACzE;AAAA,IACF;AACA,QAAI,QAAQ,IAAI,EAAE,EAAG;AACrB,UAAM,UAAU,SAAS,EAAE;AAC3B,QAAI,CAAC,SAAS,OAAO,GAAG;AACtB,aAAO,KAAK,WAAW,EAAE,gCAAgC;AACzD;AAAA,IACF;AACA,aAAS,IAAI,EAAE;AACf,QAAI,cAAc,QAAQ,QAAQ,GAAG;AACnC,iBAAW,SAAS,QAAQ,SAAU,OAAM,OAAO,CAAC,GAAG,MAAM,EAAE,CAAC;AAAA,IAClE;AACA,aAAS,OAAO,EAAE;AAClB,YAAQ,IAAI,EAAE;AAAA,EAChB;AACA,QAAM,MAAM,CAAC,CAAC;AAChB;","names":[]}
|