feat: add plugin architecture

This commit is contained in:
Peter Steinberger
2026-01-11 12:11:12 +00:00
parent f2b8f7bd5b
commit cf0c72a557
37 changed files with 2408 additions and 8 deletions

View File

@@ -190,6 +190,45 @@ describe("config form renderer", () => {
expect(onPatch).toHaveBeenCalledWith(["slack"], {});
});
it("supports wildcard uiHints for map entries", () => {
const onPatch = vi.fn();
const container = document.createElement("div");
const schema = {
type: "object",
properties: {
plugins: {
type: "object",
properties: {
entries: {
type: "object",
additionalProperties: {
type: "object",
properties: {
enabled: { type: "boolean" },
},
},
},
},
},
},
};
const analysis = analyzeConfigSchema(schema);
render(
renderConfigForm({
schema: analysis.schema,
uiHints: {
"plugins.entries.*.enabled": { label: "Plugin Enabled" },
},
unsupportedPaths: analysis.unsupportedPaths,
value: { plugins: { entries: { "voice-call": { enabled: true } } } },
onPatch,
}),
container,
);
expect(container.textContent).toContain("Plugin Enabled");
});
it("flags unsupported unions", () => {
const schema = {
type: "object",

View File

@@ -388,7 +388,23 @@ function defaultValue(schema?: JsonSchema): unknown {
function hintForPath(path: Array<string | number>, hints: ConfigUiHints) {
const key = pathKey(path);
return hints[key];
const direct = hints[key];
if (direct) return direct;
const segments = key.split(".");
for (const [hintKey, hint] of Object.entries(hints)) {
if (!hintKey.includes("*")) continue;
const hintSegments = hintKey.split(".");
if (hintSegments.length !== segments.length) continue;
let match = true;
for (let i = 0; i < segments.length; i += 1) {
if (hintSegments[i] !== "*" && hintSegments[i] !== segments[i]) {
match = false;
break;
}
}
if (match) return hint;
}
return undefined;
}
function pathKey(path: Array<string | number>): string {