@copilotkit/web-inspector 1.80.0 → 1.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/generative-ui-view.cjs +939 -0
- package/dist/components/generative-ui-view.cjs.map +1 -0
- package/dist/components/generative-ui-view.d.mts +3 -0
- package/dist/components/generative-ui-view.mjs +940 -0
- package/dist/components/generative-ui-view.mjs.map +1 -0
- package/dist/index.cjs +79 -155
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -23
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +7 -23
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +63 -139
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1257 -257
- package/dist/index.umd.js.map +1 -1
- package/dist/lib/inspector-nav.cjs +2 -1
- package/dist/lib/inspector-nav.cjs.map +1 -1
- package/dist/lib/inspector-nav.mjs +2 -1
- package/dist/lib/inspector-nav.mjs.map +1 -1
- package/dist/lib/json-block.cjs +139 -0
- package/dist/lib/json-block.cjs.map +1 -0
- package/dist/lib/json-block.d.cts +26 -0
- package/dist/lib/json-block.d.cts.map +1 -0
- package/dist/lib/json-block.d.mts +28 -0
- package/dist/lib/json-block.d.mts.map +1 -0
- package/dist/lib/json-block.mjs +134 -0
- package/dist/lib/json-block.mjs.map +1 -0
- package/dist/lib/telemetry.cjs.map +1 -1
- package/dist/lib/telemetry.mjs.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-nav.cjs","names":[],"sources":["../../src/lib/inspector-nav.ts"],"sourcesContent":["import type { InspectorLeafKey } from \"./telemetry.js\";\n\nexport type ShellGroupKey = \"home\" | \"workbench\" | \"inspect\";\n\nexport const INSPECTOR_GROUPS = {\n home: [\"home\", \"whats-new\"],\n workbench: [\"playground\", \"threads\", \"memories\"],\n inspect: [\n \"agents\",\n \"ag-ui-events\",\n \"frontend-tools\",\n \"capabilities\",\n \"agent-context\",\n ],\n} as const satisfies Record<ShellGroupKey, readonly InspectorLeafKey[]>;\n\nexport type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;\nexport type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];\n\nexport const INSPECTOR_MENU_KEYS: ReadonlyArray<MenuKey> = [\n ...INSPECTOR_GROUPS.home,\n ...INSPECTOR_GROUPS.workbench,\n ...INSPECTOR_GROUPS.inspect,\n];\n\nexport const INSPECTOR_NAV_SECTIONS: ReadonlyArray<{\n group: InspectorNavGroupKey;\n label: string | null;\n}> = [\n { group: \"home\", label: null },\n { group: \"workbench\", label: \"Workbench\" },\n { group: \"inspect\", label: \"Inspect\" },\n];\n\nexport const ICON_RAIL_MAX_WIDTH_PX = 720;\n\n/** Return whether persisted state names a known Inspector leaf. */\nexport function isInspectorMenuKey(value: unknown): value is MenuKey {\n return (\n typeof value === \"string\" &&\n INSPECTOR_MENU_KEYS.some((menuKey) => menuKey === value)\n );\n}\n\n/** Return the sidebar group that owns a leaf. */\nexport function getGroupForMenu(key: MenuKey): InspectorNavGroupKey {\n for (const group of Object.keys(INSPECTOR_GROUPS) as InspectorNavGroupKey[]) {\n if (INSPECTOR_GROUPS[group].some((menuKey) => menuKey === key)) {\n return group;\n }\n }\n\n return \"workbench\";\n}\n\n/** Return whether the sidebar should collapse to an icon rail. */\nexport function shouldUseIconRail(args: {\n dockedLeft: boolean;\n width: number;\n}): boolean {\n return args.dockedLeft || args.width < ICON_RAIL_MAX_WIDTH_PX;\n}\n"],"mappings":";;AAIA,MAAa,mBAAmB;CAC9B,MAAM,CAAC,QAAQ,YAAY;CAC3B,WAAW;EAAC;EAAc;EAAW;EAAW;CAChD,SAAS;EACP;EACA;EACA;EACA;EACA;EACD;CACF;AAKD,MAAa,sBAA8C;CACzD,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACrB;AAED,MAAa,yBAGR;CACH;EAAE,OAAO;EAAQ,OAAO;EAAM;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,MAAa,yBAAyB;;AAGtC,SAAgB,mBAAmB,OAAkC;AACnE,QACE,OAAO,UAAU,YACjB,oBAAoB,MAAM,YAAY,YAAY,MAAM;;;AAK5D,SAAgB,gBAAgB,KAAoC;AAClE,MAAK,MAAM,SAAS,OAAO,KAAK,iBAAiB,CAC/C,KAAI,iBAAiB,OAAO,MAAM,YAAY,YAAY,IAAI,CAC5D,QAAO;AAIX,QAAO;;;AAIT,SAAgB,kBAAkB,MAGtB;AACV,QAAO,KAAK,cAAc,KAAK,QAAQ"}
|
|
1
|
+
{"version":3,"file":"inspector-nav.cjs","names":[],"sources":["../../src/lib/inspector-nav.ts"],"sourcesContent":["import type { InspectorLeafKey } from \"./telemetry.js\";\n\nexport type ShellGroupKey = \"home\" | \"workbench\" | \"inspect\";\n\nexport const INSPECTOR_GROUPS = {\n home: [\"home\", \"whats-new\"],\n workbench: [\"playground\", \"threads\", \"memories\"],\n inspect: [\n \"agents\",\n \"ag-ui-events\",\n \"frontend-tools\",\n \"capabilities\",\n \"agent-context\",\n \"generative-ui\",\n ],\n} as const satisfies Record<ShellGroupKey, readonly InspectorLeafKey[]>;\n\nexport type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;\nexport type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];\n\nexport const INSPECTOR_MENU_KEYS: ReadonlyArray<MenuKey> = [\n ...INSPECTOR_GROUPS.home,\n ...INSPECTOR_GROUPS.workbench,\n ...INSPECTOR_GROUPS.inspect,\n];\n\nexport const INSPECTOR_NAV_SECTIONS: ReadonlyArray<{\n group: InspectorNavGroupKey;\n label: string | null;\n}> = [\n { group: \"home\", label: null },\n { group: \"workbench\", label: \"Workbench\" },\n { group: \"inspect\", label: \"Inspect\" },\n];\n\nexport const ICON_RAIL_MAX_WIDTH_PX = 720;\n\n/** Return whether persisted state names a known Inspector leaf. */\nexport function isInspectorMenuKey(value: unknown): value is MenuKey {\n return (\n typeof value === \"string\" &&\n INSPECTOR_MENU_KEYS.some((menuKey) => menuKey === value)\n );\n}\n\n/** Return the sidebar group that owns a leaf. */\nexport function getGroupForMenu(key: MenuKey): InspectorNavGroupKey {\n for (const group of Object.keys(INSPECTOR_GROUPS) as InspectorNavGroupKey[]) {\n if (INSPECTOR_GROUPS[group].some((menuKey) => menuKey === key)) {\n return group;\n }\n }\n\n return \"workbench\";\n}\n\n/** Return whether the sidebar should collapse to an icon rail. */\nexport function shouldUseIconRail(args: {\n dockedLeft: boolean;\n width: number;\n}): boolean {\n return args.dockedLeft || args.width < ICON_RAIL_MAX_WIDTH_PX;\n}\n"],"mappings":";;AAIA,MAAa,mBAAmB;CAC9B,MAAM,CAAC,QAAQ,YAAY;CAC3B,WAAW;EAAC;EAAc;EAAW;EAAW;CAChD,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACD;CACF;AAKD,MAAa,sBAA8C;CACzD,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACrB;AAED,MAAa,yBAGR;CACH;EAAE,OAAO;EAAQ,OAAO;EAAM;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,MAAa,yBAAyB;;AAGtC,SAAgB,mBAAmB,OAAkC;AACnE,QACE,OAAO,UAAU,YACjB,oBAAoB,MAAM,YAAY,YAAY,MAAM;;;AAK5D,SAAgB,gBAAgB,KAAoC;AAClE,MAAK,MAAM,SAAS,OAAO,KAAK,iBAAiB,CAC/C,KAAI,iBAAiB,OAAO,MAAM,YAAY,YAAY,IAAI,CAC5D,QAAO;AAIX,QAAO;;;AAIT,SAAgB,kBAAkB,MAGtB;AACV,QAAO,KAAK,cAAc,KAAK,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-nav.mjs","names":[],"sources":["../../src/lib/inspector-nav.ts"],"sourcesContent":["import type { InspectorLeafKey } from \"./telemetry.js\";\n\nexport type ShellGroupKey = \"home\" | \"workbench\" | \"inspect\";\n\nexport const INSPECTOR_GROUPS = {\n home: [\"home\", \"whats-new\"],\n workbench: [\"playground\", \"threads\", \"memories\"],\n inspect: [\n \"agents\",\n \"ag-ui-events\",\n \"frontend-tools\",\n \"capabilities\",\n \"agent-context\",\n ],\n} as const satisfies Record<ShellGroupKey, readonly InspectorLeafKey[]>;\n\nexport type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;\nexport type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];\n\nexport const INSPECTOR_MENU_KEYS: ReadonlyArray<MenuKey> = [\n ...INSPECTOR_GROUPS.home,\n ...INSPECTOR_GROUPS.workbench,\n ...INSPECTOR_GROUPS.inspect,\n];\n\nexport const INSPECTOR_NAV_SECTIONS: ReadonlyArray<{\n group: InspectorNavGroupKey;\n label: string | null;\n}> = [\n { group: \"home\", label: null },\n { group: \"workbench\", label: \"Workbench\" },\n { group: \"inspect\", label: \"Inspect\" },\n];\n\nexport const ICON_RAIL_MAX_WIDTH_PX = 720;\n\n/** Return whether persisted state names a known Inspector leaf. */\nexport function isInspectorMenuKey(value: unknown): value is MenuKey {\n return (\n typeof value === \"string\" &&\n INSPECTOR_MENU_KEYS.some((menuKey) => menuKey === value)\n );\n}\n\n/** Return the sidebar group that owns a leaf. */\nexport function getGroupForMenu(key: MenuKey): InspectorNavGroupKey {\n for (const group of Object.keys(INSPECTOR_GROUPS) as InspectorNavGroupKey[]) {\n if (INSPECTOR_GROUPS[group].some((menuKey) => menuKey === key)) {\n return group;\n }\n }\n\n return \"workbench\";\n}\n\n/** Return whether the sidebar should collapse to an icon rail. */\nexport function shouldUseIconRail(args: {\n dockedLeft: boolean;\n width: number;\n}): boolean {\n return args.dockedLeft || args.width < ICON_RAIL_MAX_WIDTH_PX;\n}\n"],"mappings":";AAIA,MAAa,mBAAmB;CAC9B,MAAM,CAAC,QAAQ,YAAY;CAC3B,WAAW;EAAC;EAAc;EAAW;EAAW;CAChD,SAAS;EACP;EACA;EACA;EACA;EACA;EACD;CACF;AAKD,MAAa,sBAA8C;CACzD,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACrB;AAED,MAAa,yBAGR;CACH;EAAE,OAAO;EAAQ,OAAO;EAAM;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,MAAa,yBAAyB;;AAGtC,SAAgB,mBAAmB,OAAkC;AACnE,QACE,OAAO,UAAU,YACjB,oBAAoB,MAAM,YAAY,YAAY,MAAM;;;AAK5D,SAAgB,gBAAgB,KAAoC;AAClE,MAAK,MAAM,SAAS,OAAO,KAAK,iBAAiB,CAC/C,KAAI,iBAAiB,OAAO,MAAM,YAAY,YAAY,IAAI,CAC5D,QAAO;AAIX,QAAO;;;AAIT,SAAgB,kBAAkB,MAGtB;AACV,QAAO,KAAK,cAAc,KAAK,QAAQ"}
|
|
1
|
+
{"version":3,"file":"inspector-nav.mjs","names":[],"sources":["../../src/lib/inspector-nav.ts"],"sourcesContent":["import type { InspectorLeafKey } from \"./telemetry.js\";\n\nexport type ShellGroupKey = \"home\" | \"workbench\" | \"inspect\";\n\nexport const INSPECTOR_GROUPS = {\n home: [\"home\", \"whats-new\"],\n workbench: [\"playground\", \"threads\", \"memories\"],\n inspect: [\n \"agents\",\n \"ag-ui-events\",\n \"frontend-tools\",\n \"capabilities\",\n \"agent-context\",\n \"generative-ui\",\n ],\n} as const satisfies Record<ShellGroupKey, readonly InspectorLeafKey[]>;\n\nexport type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;\nexport type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];\n\nexport const INSPECTOR_MENU_KEYS: ReadonlyArray<MenuKey> = [\n ...INSPECTOR_GROUPS.home,\n ...INSPECTOR_GROUPS.workbench,\n ...INSPECTOR_GROUPS.inspect,\n];\n\nexport const INSPECTOR_NAV_SECTIONS: ReadonlyArray<{\n group: InspectorNavGroupKey;\n label: string | null;\n}> = [\n { group: \"home\", label: null },\n { group: \"workbench\", label: \"Workbench\" },\n { group: \"inspect\", label: \"Inspect\" },\n];\n\nexport const ICON_RAIL_MAX_WIDTH_PX = 720;\n\n/** Return whether persisted state names a known Inspector leaf. */\nexport function isInspectorMenuKey(value: unknown): value is MenuKey {\n return (\n typeof value === \"string\" &&\n INSPECTOR_MENU_KEYS.some((menuKey) => menuKey === value)\n );\n}\n\n/** Return the sidebar group that owns a leaf. */\nexport function getGroupForMenu(key: MenuKey): InspectorNavGroupKey {\n for (const group of Object.keys(INSPECTOR_GROUPS) as InspectorNavGroupKey[]) {\n if (INSPECTOR_GROUPS[group].some((menuKey) => menuKey === key)) {\n return group;\n }\n }\n\n return \"workbench\";\n}\n\n/** Return whether the sidebar should collapse to an icon rail. */\nexport function shouldUseIconRail(args: {\n dockedLeft: boolean;\n width: number;\n}): boolean {\n return args.dockedLeft || args.width < ICON_RAIL_MAX_WIDTH_PX;\n}\n"],"mappings":";AAIA,MAAa,mBAAmB;CAC9B,MAAM,CAAC,QAAQ,YAAY;CAC3B,WAAW;EAAC;EAAc;EAAW;EAAW;CAChD,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACD;CACF;AAKD,MAAa,sBAA8C;CACzD,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACrB;AAED,MAAa,yBAGR;CACH;EAAE,OAAO;EAAQ,OAAO;EAAM;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,MAAa,yBAAyB;;AAGtC,SAAgB,mBAAmB,OAAkC;AACnE,QACE,OAAO,UAAU,YACjB,oBAAoB,MAAM,YAAY,YAAY,MAAM;;;AAK5D,SAAgB,gBAAgB,KAAoC;AAClE,MAAK,MAAM,SAAS,OAAO,KAAK,iBAAiB,CAC/C,KAAI,iBAAiB,OAAO,MAAM,YAAY,YAAY,IAAI,CAC5D,QAAO;AAIX,QAAO;;;AAIT,SAAgB,kBAAkB,MAGtB;AACV,QAAO,KAAK,cAAc,KAAK,QAAQ"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
let lit = require("lit");
|
|
2
|
+
let lit_directives_unsafe_html_js = require("lit/directives/unsafe-html.js");
|
|
3
|
+
|
|
4
|
+
//#region src/lib/json-block.ts
|
|
5
|
+
function escapeHtml(s) {
|
|
6
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
7
|
+
}
|
|
8
|
+
const highlightedJsonCache = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
function highlightedJson(obj) {
|
|
10
|
+
if (typeof obj === "object" && obj !== null) {
|
|
11
|
+
const cached = highlightedJsonCache.get(obj);
|
|
12
|
+
if (cached !== void 0) return cached;
|
|
13
|
+
}
|
|
14
|
+
const json = JSON.stringify(obj, null, 2);
|
|
15
|
+
if (!json) return "";
|
|
16
|
+
const parts = [];
|
|
17
|
+
let lastIndex = 0;
|
|
18
|
+
const re = /("(?:\\u[a-fA-F0-9]{4}|\\[^u]|[^\\"])*"(?:\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g;
|
|
19
|
+
let match;
|
|
20
|
+
while ((match = re.exec(json)) !== null) {
|
|
21
|
+
parts.push(escapeHtml(json.slice(lastIndex, match.index)));
|
|
22
|
+
const m = match[0];
|
|
23
|
+
let token = "num";
|
|
24
|
+
if (m.startsWith("\"")) token = m.trimEnd().endsWith(":") ? "key" : "str";
|
|
25
|
+
else if (m === "true" || m === "false") token = "bool";
|
|
26
|
+
else if (m === "null") token = "nil";
|
|
27
|
+
parts.push(`<span class="cpk-json-token cpk-json-token--${token}">${escapeHtml(m)}</span>`);
|
|
28
|
+
lastIndex = match.index + m.length;
|
|
29
|
+
}
|
|
30
|
+
parts.push(escapeHtml(json.slice(lastIndex)));
|
|
31
|
+
const result = parts.join("");
|
|
32
|
+
if (typeof obj === "object" && obj !== null) highlightedJsonCache.set(obj, result);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function coerceJsonValue(value) {
|
|
36
|
+
if (typeof value !== "string") return value;
|
|
37
|
+
const trimmed = value.trim();
|
|
38
|
+
if (trimmed.length === 0) return value;
|
|
39
|
+
if (!(trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]"))) return value;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(trimmed);
|
|
42
|
+
} catch {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
let inspectorJsonRenderer = null;
|
|
47
|
+
let inspectorJsonSlotCount = 0;
|
|
48
|
+
const JSON_BLOCK_TAG = "cpk-json-block";
|
|
49
|
+
/**
|
|
50
|
+
* Replaces the inspector's built-in JSON blocks (tool arguments and results,
|
|
51
|
+
* state, events, and context) with a host renderer. Pass `null` to restore the
|
|
52
|
+
* built-in blocks. Set it before the inspector renders: a block that is
|
|
53
|
+
* already on screen changes at the inspector's next render.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* setInspectorJsonRenderer((container, value) => {
|
|
58
|
+
* const root = createRoot(container);
|
|
59
|
+
* root.render(<JsonView value={value} />);
|
|
60
|
+
* return () => root.unmount();
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function setInspectorJsonRenderer(renderer) {
|
|
65
|
+
inspectorJsonRenderer = renderer;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Hosts one value drawn by the host renderer. Inside a shadow root, the
|
|
69
|
+
* container lives in the shadow host's light DOM and shows here through a
|
|
70
|
+
* named slot, so the host page's own styles apply to what the renderer draws.
|
|
71
|
+
*/
|
|
72
|
+
var CpkJsonBlock = class extends (globalThis.HTMLElement ?? class {}) {
|
|
73
|
+
#value = void 0;
|
|
74
|
+
#hasValue = false;
|
|
75
|
+
#cleanup;
|
|
76
|
+
#container;
|
|
77
|
+
get value() {
|
|
78
|
+
return this.#value;
|
|
79
|
+
}
|
|
80
|
+
set value(next) {
|
|
81
|
+
if (this.#hasValue && Object.is(next, this.#value)) return;
|
|
82
|
+
this.#value = next;
|
|
83
|
+
this.#hasValue = true;
|
|
84
|
+
if (this.isConnected) this.#render();
|
|
85
|
+
}
|
|
86
|
+
connectedCallback() {
|
|
87
|
+
this.style.display = "block";
|
|
88
|
+
this.#render();
|
|
89
|
+
}
|
|
90
|
+
disconnectedCallback() {
|
|
91
|
+
this.#teardown();
|
|
92
|
+
}
|
|
93
|
+
#render() {
|
|
94
|
+
this.#teardown();
|
|
95
|
+
const renderer = inspectorJsonRenderer;
|
|
96
|
+
if (!renderer) return;
|
|
97
|
+
const root = this.getRootNode();
|
|
98
|
+
const container = this.ownerDocument.createElement("div");
|
|
99
|
+
if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE && "host" in root) {
|
|
100
|
+
inspectorJsonSlotCount += 1;
|
|
101
|
+
const name = `cpk-json-${inspectorJsonSlotCount}`;
|
|
102
|
+
const slot = this.ownerDocument.createElement("slot");
|
|
103
|
+
slot.name = name;
|
|
104
|
+
this.replaceChildren(slot);
|
|
105
|
+
container.slot = name;
|
|
106
|
+
root.host.append(container);
|
|
107
|
+
} else this.replaceChildren(container);
|
|
108
|
+
this.#container = container;
|
|
109
|
+
const cleanup = renderer(container, this.#value);
|
|
110
|
+
this.#cleanup = typeof cleanup === "function" ? cleanup : void 0;
|
|
111
|
+
}
|
|
112
|
+
#teardown() {
|
|
113
|
+
const cleanup = this.#cleanup;
|
|
114
|
+
this.#cleanup = void 0;
|
|
115
|
+
cleanup?.();
|
|
116
|
+
this.#container?.remove();
|
|
117
|
+
this.#container = void 0;
|
|
118
|
+
this.replaceChildren();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
function renderHighlightedJsonBlock(value, options = {}) {
|
|
122
|
+
if (inspectorJsonRenderer) return lit.html`<cpk-json-block
|
|
123
|
+
.value=${value}
|
|
124
|
+
style=${options.maxHeight ? `overflow:auto;max-height:${options.maxHeight}` : lit.nothing}
|
|
125
|
+
></cpk-json-block>`;
|
|
126
|
+
const parsed = coerceJsonValue(value);
|
|
127
|
+
return lit.html`<pre class="cpk-json-block" style=${(options.maxHeight ? `max-height:${options.maxHeight}` : void 0) || lit.nothing}>
|
|
128
|
+
${(0, lit_directives_unsafe_html_js.unsafeHTML)(highlightedJson(parsed))}</pre
|
|
129
|
+
>`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
exports.CpkJsonBlock = CpkJsonBlock;
|
|
134
|
+
exports.JSON_BLOCK_TAG = JSON_BLOCK_TAG;
|
|
135
|
+
exports.coerceJsonValue = coerceJsonValue;
|
|
136
|
+
exports.escapeHtml = escapeHtml;
|
|
137
|
+
exports.renderHighlightedJsonBlock = renderHighlightedJsonBlock;
|
|
138
|
+
exports.setInspectorJsonRenderer = setInspectorJsonRenderer;
|
|
139
|
+
//# sourceMappingURL=json-block.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-block.cjs","names":["#value","#hasValue","#render","#teardown","#container","#cleanup","nothing"],"sources":["../../src/lib/json-block.ts"],"sourcesContent":["import { html, nothing } from \"lit\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\n\n// ─── JSON syntax highlighter ─────────────────────────────────────────────────\n// Inline-styled so shadow DOM encapsulation preserves colors when the output\n// is injected via unsafeHTML. Only for structured data — never raw user HTML.\n\nexport function escapeHtml(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\n// Memoize highlight output by payload reference. Tab switches cause Lit to\n// re-render the active panel from scratch, and the JSON.stringify + regex\n// pass below is by far the most expensive thing in the events / state\n// panels (potentially MB of agent state). Caching by object reference\n// turns subsequent renders of an unchanged event list into near-zero JS work.\nconst highlightedJsonCache = new WeakMap<object, string>();\n\nfunction highlightedJson(obj: unknown): string {\n if (typeof obj === \"object\" && obj !== null) {\n const cached = highlightedJsonCache.get(obj);\n if (cached !== undefined) return cached;\n }\n const json = JSON.stringify(obj, null, 2);\n if (!json) return \"\";\n const parts: string[] = [];\n let lastIndex = 0;\n const re =\n /(\"(?:\\\\u[a-fA-F0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(?:\\s*:)?|\\b(?:true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)/g;\n let match: RegExpExecArray | null;\n while ((match = re.exec(json)) !== null) {\n parts.push(escapeHtml(json.slice(lastIndex, match.index)));\n const m = match[0];\n let token = \"num\";\n if (m.startsWith('\"')) {\n token = m.trimEnd().endsWith(\":\") ? \"key\" : \"str\";\n } else if (m === \"true\" || m === \"false\") {\n token = \"bool\";\n } else if (m === \"null\") {\n token = \"nil\";\n }\n parts.push(\n `<span class=\"cpk-json-token cpk-json-token--${token}\">${escapeHtml(m)}</span>`,\n );\n lastIndex = match.index + m.length;\n }\n parts.push(escapeHtml(json.slice(lastIndex)));\n const result = parts.join(\"\");\n if (typeof obj === \"object\" && obj !== null) {\n highlightedJsonCache.set(obj, result);\n }\n return result;\n}\n\nexport function coerceJsonValue(value: unknown): unknown {\n if (typeof value !== \"string\") {\n return value;\n }\n\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n return value;\n }\n\n const looksJson =\n (trimmed.startsWith(\"{\") && trimmed.endsWith(\"}\")) ||\n (trimmed.startsWith(\"[\") && trimmed.endsWith(\"]\"));\n if (!looksJson) {\n return value;\n }\n\n try {\n return JSON.parse(trimmed);\n } catch {\n return value;\n }\n}\n\n/**\n * Draws one JSON value of the inspector into a container that the host page\n * owns. `value` is the raw value: a JSON string or an already-parsed value.\n * Return a function to run when the inspector removes or replaces the value.\n */\nexport type InspectorJsonRenderer = (\n container: HTMLElement,\n value: unknown,\n) => (() => void) | void;\n\nlet inspectorJsonRenderer: InspectorJsonRenderer | null = null;\nlet inspectorJsonSlotCount = 0;\n\nexport const JSON_BLOCK_TAG = \"cpk-json-block\";\n\n/**\n * Replaces the inspector's built-in JSON blocks (tool arguments and results,\n * state, events, and context) with a host renderer. Pass `null` to restore the\n * built-in blocks. Set it before the inspector renders: a block that is\n * already on screen changes at the inspector's next render.\n *\n * @example\n * ```ts\n * setInspectorJsonRenderer((container, value) => {\n * const root = createRoot(container);\n * root.render(<JsonView value={value} />);\n * return () => root.unmount();\n * });\n * ```\n */\nexport function setInspectorJsonRenderer(\n renderer: InspectorJsonRenderer | null,\n): void {\n inspectorJsonRenderer = renderer;\n}\n\n/**\n * Hosts one value drawn by the host renderer. Inside a shadow root, the\n * container lives in the shadow host's light DOM and shows here through a\n * named slot, so the host page's own styles apply to what the renderer draws.\n */\n// The fallback keeps module import safe where no DOM exists (server render).\nexport class CpkJsonBlock extends ((globalThis.HTMLElement ??\n class {}) as typeof HTMLElement) {\n #value: unknown = undefined;\n #hasValue = false;\n #cleanup: (() => void) | undefined;\n #container: HTMLElement | undefined;\n\n get value(): unknown {\n return this.#value;\n }\n\n set value(next: unknown) {\n if (this.#hasValue && Object.is(next, this.#value)) return;\n this.#value = next;\n this.#hasValue = true;\n if (this.isConnected) this.#render();\n }\n\n connectedCallback(): void {\n this.style.display = \"block\";\n this.#render();\n }\n\n disconnectedCallback(): void {\n this.#teardown();\n }\n\n #render(): void {\n this.#teardown();\n const renderer = inspectorJsonRenderer;\n if (!renderer) return;\n const root = this.getRootNode();\n const container = this.ownerDocument.createElement(\"div\");\n // `instanceof ShadowRoot` fails across realms (the pop-out window).\n if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE && \"host\" in root) {\n inspectorJsonSlotCount += 1;\n const name = `cpk-json-${inspectorJsonSlotCount}`;\n const slot = this.ownerDocument.createElement(\"slot\");\n slot.name = name;\n this.replaceChildren(slot);\n container.slot = name;\n (root as ShadowRoot).host.append(container);\n } else {\n this.replaceChildren(container);\n }\n this.#container = container;\n const cleanup = renderer(container, this.#value);\n this.#cleanup = typeof cleanup === \"function\" ? cleanup : undefined;\n }\n\n #teardown(): void {\n const cleanup = this.#cleanup;\n this.#cleanup = undefined;\n cleanup?.();\n this.#container?.remove();\n this.#container = undefined;\n this.replaceChildren();\n }\n}\n\nexport function renderHighlightedJsonBlock(\n value: unknown,\n options: { maxHeight?: string } = {},\n) {\n if (inspectorJsonRenderer) {\n // The raw value keeps its identity across renders, so an unchanged\n // value does not draw again.\n return html`<cpk-json-block\n .value=${value}\n style=${\n options.maxHeight\n ? `overflow:auto;max-height:${options.maxHeight}`\n : nothing\n }\n ></cpk-json-block>`;\n }\n const parsed = coerceJsonValue(value);\n const style = options.maxHeight\n ? `max-height:${options.maxHeight}`\n : undefined;\n return html`<pre class=\"cpk-json-block\" style=${style || nothing}>\n${unsafeHTML(highlightedJson(parsed))}</pre\n >`;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/lib/json-block.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Draws one JSON value of the inspector into a container that the host page
|
|
4
|
+
* owns. `value` is the raw value: a JSON string or an already-parsed value.
|
|
5
|
+
* Return a function to run when the inspector removes or replaces the value.
|
|
6
|
+
*/
|
|
7
|
+
type InspectorJsonRenderer = (container: HTMLElement, value: unknown) => (() => void) | void;
|
|
8
|
+
/**
|
|
9
|
+
* Replaces the inspector's built-in JSON blocks (tool arguments and results,
|
|
10
|
+
* state, events, and context) with a host renderer. Pass `null` to restore the
|
|
11
|
+
* built-in blocks. Set it before the inspector renders: a block that is
|
|
12
|
+
* already on screen changes at the inspector's next render.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* setInspectorJsonRenderer((container, value) => {
|
|
17
|
+
* const root = createRoot(container);
|
|
18
|
+
* root.render(<JsonView value={value} />);
|
|
19
|
+
* return () => root.unmount();
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare function setInspectorJsonRenderer(renderer: InspectorJsonRenderer | null): void;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { InspectorJsonRenderer, setInspectorJsonRenderer };
|
|
26
|
+
//# sourceMappingURL=json-block.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-block.d.cts","names":[],"sources":["../../src/lib/json-block.ts"],"mappings":";;;;;;KAmFY,qBAAA,IACV,SAAA,EAAW,WAAA,EACX,KAAA;;AAuBF;;;;;;;;;;;;;;iBAAgB,wBAAA,CACd,QAAA,EAAU,qBAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import "lit";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/json-block.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Draws one JSON value of the inspector into a container that the host page
|
|
6
|
+
* owns. `value` is the raw value: a JSON string or an already-parsed value.
|
|
7
|
+
* Return a function to run when the inspector removes or replaces the value.
|
|
8
|
+
*/
|
|
9
|
+
type InspectorJsonRenderer = (container: HTMLElement, value: unknown) => (() => void) | void;
|
|
10
|
+
/**
|
|
11
|
+
* Replaces the inspector's built-in JSON blocks (tool arguments and results,
|
|
12
|
+
* state, events, and context) with a host renderer. Pass `null` to restore the
|
|
13
|
+
* built-in blocks. Set it before the inspector renders: a block that is
|
|
14
|
+
* already on screen changes at the inspector's next render.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* setInspectorJsonRenderer((container, value) => {
|
|
19
|
+
* const root = createRoot(container);
|
|
20
|
+
* root.render(<JsonView value={value} />);
|
|
21
|
+
* return () => root.unmount();
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function setInspectorJsonRenderer(renderer: InspectorJsonRenderer | null): void;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { InspectorJsonRenderer, setInspectorJsonRenderer };
|
|
28
|
+
//# sourceMappingURL=json-block.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-block.d.mts","names":[],"sources":["../../src/lib/json-block.ts"],"mappings":";;;;AAmFA;;;;KAAY,qBAAA,IACV,SAAA,EAAW,WAAA,EACX,KAAA;;;;AAuBF;;;;;;;;;;;;iBAAgB,wBAAA,CACd,QAAA,EAAU,qBAAA"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { html, nothing } from "lit";
|
|
2
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/json-block.ts
|
|
5
|
+
function escapeHtml(s) {
|
|
6
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
7
|
+
}
|
|
8
|
+
const highlightedJsonCache = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
function highlightedJson(obj) {
|
|
10
|
+
if (typeof obj === "object" && obj !== null) {
|
|
11
|
+
const cached = highlightedJsonCache.get(obj);
|
|
12
|
+
if (cached !== void 0) return cached;
|
|
13
|
+
}
|
|
14
|
+
const json = JSON.stringify(obj, null, 2);
|
|
15
|
+
if (!json) return "";
|
|
16
|
+
const parts = [];
|
|
17
|
+
let lastIndex = 0;
|
|
18
|
+
const re = /("(?:\\u[a-fA-F0-9]{4}|\\[^u]|[^\\"])*"(?:\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g;
|
|
19
|
+
let match;
|
|
20
|
+
while ((match = re.exec(json)) !== null) {
|
|
21
|
+
parts.push(escapeHtml(json.slice(lastIndex, match.index)));
|
|
22
|
+
const m = match[0];
|
|
23
|
+
let token = "num";
|
|
24
|
+
if (m.startsWith("\"")) token = m.trimEnd().endsWith(":") ? "key" : "str";
|
|
25
|
+
else if (m === "true" || m === "false") token = "bool";
|
|
26
|
+
else if (m === "null") token = "nil";
|
|
27
|
+
parts.push(`<span class="cpk-json-token cpk-json-token--${token}">${escapeHtml(m)}</span>`);
|
|
28
|
+
lastIndex = match.index + m.length;
|
|
29
|
+
}
|
|
30
|
+
parts.push(escapeHtml(json.slice(lastIndex)));
|
|
31
|
+
const result = parts.join("");
|
|
32
|
+
if (typeof obj === "object" && obj !== null) highlightedJsonCache.set(obj, result);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function coerceJsonValue(value) {
|
|
36
|
+
if (typeof value !== "string") return value;
|
|
37
|
+
const trimmed = value.trim();
|
|
38
|
+
if (trimmed.length === 0) return value;
|
|
39
|
+
if (!(trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]"))) return value;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(trimmed);
|
|
42
|
+
} catch {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
let inspectorJsonRenderer = null;
|
|
47
|
+
let inspectorJsonSlotCount = 0;
|
|
48
|
+
const JSON_BLOCK_TAG = "cpk-json-block";
|
|
49
|
+
/**
|
|
50
|
+
* Replaces the inspector's built-in JSON blocks (tool arguments and results,
|
|
51
|
+
* state, events, and context) with a host renderer. Pass `null` to restore the
|
|
52
|
+
* built-in blocks. Set it before the inspector renders: a block that is
|
|
53
|
+
* already on screen changes at the inspector's next render.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* setInspectorJsonRenderer((container, value) => {
|
|
58
|
+
* const root = createRoot(container);
|
|
59
|
+
* root.render(<JsonView value={value} />);
|
|
60
|
+
* return () => root.unmount();
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function setInspectorJsonRenderer(renderer) {
|
|
65
|
+
inspectorJsonRenderer = renderer;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Hosts one value drawn by the host renderer. Inside a shadow root, the
|
|
69
|
+
* container lives in the shadow host's light DOM and shows here through a
|
|
70
|
+
* named slot, so the host page's own styles apply to what the renderer draws.
|
|
71
|
+
*/
|
|
72
|
+
var CpkJsonBlock = class extends (globalThis.HTMLElement ?? class {}) {
|
|
73
|
+
#value = void 0;
|
|
74
|
+
#hasValue = false;
|
|
75
|
+
#cleanup;
|
|
76
|
+
#container;
|
|
77
|
+
get value() {
|
|
78
|
+
return this.#value;
|
|
79
|
+
}
|
|
80
|
+
set value(next) {
|
|
81
|
+
if (this.#hasValue && Object.is(next, this.#value)) return;
|
|
82
|
+
this.#value = next;
|
|
83
|
+
this.#hasValue = true;
|
|
84
|
+
if (this.isConnected) this.#render();
|
|
85
|
+
}
|
|
86
|
+
connectedCallback() {
|
|
87
|
+
this.style.display = "block";
|
|
88
|
+
this.#render();
|
|
89
|
+
}
|
|
90
|
+
disconnectedCallback() {
|
|
91
|
+
this.#teardown();
|
|
92
|
+
}
|
|
93
|
+
#render() {
|
|
94
|
+
this.#teardown();
|
|
95
|
+
const renderer = inspectorJsonRenderer;
|
|
96
|
+
if (!renderer) return;
|
|
97
|
+
const root = this.getRootNode();
|
|
98
|
+
const container = this.ownerDocument.createElement("div");
|
|
99
|
+
if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE && "host" in root) {
|
|
100
|
+
inspectorJsonSlotCount += 1;
|
|
101
|
+
const name = `cpk-json-${inspectorJsonSlotCount}`;
|
|
102
|
+
const slot = this.ownerDocument.createElement("slot");
|
|
103
|
+
slot.name = name;
|
|
104
|
+
this.replaceChildren(slot);
|
|
105
|
+
container.slot = name;
|
|
106
|
+
root.host.append(container);
|
|
107
|
+
} else this.replaceChildren(container);
|
|
108
|
+
this.#container = container;
|
|
109
|
+
const cleanup = renderer(container, this.#value);
|
|
110
|
+
this.#cleanup = typeof cleanup === "function" ? cleanup : void 0;
|
|
111
|
+
}
|
|
112
|
+
#teardown() {
|
|
113
|
+
const cleanup = this.#cleanup;
|
|
114
|
+
this.#cleanup = void 0;
|
|
115
|
+
cleanup?.();
|
|
116
|
+
this.#container?.remove();
|
|
117
|
+
this.#container = void 0;
|
|
118
|
+
this.replaceChildren();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
function renderHighlightedJsonBlock(value, options = {}) {
|
|
122
|
+
if (inspectorJsonRenderer) return html`<cpk-json-block
|
|
123
|
+
.value=${value}
|
|
124
|
+
style=${options.maxHeight ? `overflow:auto;max-height:${options.maxHeight}` : nothing}
|
|
125
|
+
></cpk-json-block>`;
|
|
126
|
+
const parsed = coerceJsonValue(value);
|
|
127
|
+
return html`<pre class="cpk-json-block" style=${(options.maxHeight ? `max-height:${options.maxHeight}` : void 0) || nothing}>
|
|
128
|
+
${unsafeHTML(highlightedJson(parsed))}</pre
|
|
129
|
+
>`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
export { CpkJsonBlock, JSON_BLOCK_TAG, coerceJsonValue, escapeHtml, renderHighlightedJsonBlock, setInspectorJsonRenderer };
|
|
134
|
+
//# sourceMappingURL=json-block.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-block.mjs","names":["#value","#hasValue","#render","#teardown","#container","#cleanup"],"sources":["../../src/lib/json-block.ts"],"sourcesContent":["import { html, nothing } from \"lit\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\n\n// ─── JSON syntax highlighter ─────────────────────────────────────────────────\n// Inline-styled so shadow DOM encapsulation preserves colors when the output\n// is injected via unsafeHTML. Only for structured data — never raw user HTML.\n\nexport function escapeHtml(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\n// Memoize highlight output by payload reference. Tab switches cause Lit to\n// re-render the active panel from scratch, and the JSON.stringify + regex\n// pass below is by far the most expensive thing in the events / state\n// panels (potentially MB of agent state). Caching by object reference\n// turns subsequent renders of an unchanged event list into near-zero JS work.\nconst highlightedJsonCache = new WeakMap<object, string>();\n\nfunction highlightedJson(obj: unknown): string {\n if (typeof obj === \"object\" && obj !== null) {\n const cached = highlightedJsonCache.get(obj);\n if (cached !== undefined) return cached;\n }\n const json = JSON.stringify(obj, null, 2);\n if (!json) return \"\";\n const parts: string[] = [];\n let lastIndex = 0;\n const re =\n /(\"(?:\\\\u[a-fA-F0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(?:\\s*:)?|\\b(?:true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)/g;\n let match: RegExpExecArray | null;\n while ((match = re.exec(json)) !== null) {\n parts.push(escapeHtml(json.slice(lastIndex, match.index)));\n const m = match[0];\n let token = \"num\";\n if (m.startsWith('\"')) {\n token = m.trimEnd().endsWith(\":\") ? \"key\" : \"str\";\n } else if (m === \"true\" || m === \"false\") {\n token = \"bool\";\n } else if (m === \"null\") {\n token = \"nil\";\n }\n parts.push(\n `<span class=\"cpk-json-token cpk-json-token--${token}\">${escapeHtml(m)}</span>`,\n );\n lastIndex = match.index + m.length;\n }\n parts.push(escapeHtml(json.slice(lastIndex)));\n const result = parts.join(\"\");\n if (typeof obj === \"object\" && obj !== null) {\n highlightedJsonCache.set(obj, result);\n }\n return result;\n}\n\nexport function coerceJsonValue(value: unknown): unknown {\n if (typeof value !== \"string\") {\n return value;\n }\n\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n return value;\n }\n\n const looksJson =\n (trimmed.startsWith(\"{\") && trimmed.endsWith(\"}\")) ||\n (trimmed.startsWith(\"[\") && trimmed.endsWith(\"]\"));\n if (!looksJson) {\n return value;\n }\n\n try {\n return JSON.parse(trimmed);\n } catch {\n return value;\n }\n}\n\n/**\n * Draws one JSON value of the inspector into a container that the host page\n * owns. `value` is the raw value: a JSON string or an already-parsed value.\n * Return a function to run when the inspector removes or replaces the value.\n */\nexport type InspectorJsonRenderer = (\n container: HTMLElement,\n value: unknown,\n) => (() => void) | void;\n\nlet inspectorJsonRenderer: InspectorJsonRenderer | null = null;\nlet inspectorJsonSlotCount = 0;\n\nexport const JSON_BLOCK_TAG = \"cpk-json-block\";\n\n/**\n * Replaces the inspector's built-in JSON blocks (tool arguments and results,\n * state, events, and context) with a host renderer. Pass `null` to restore the\n * built-in blocks. Set it before the inspector renders: a block that is\n * already on screen changes at the inspector's next render.\n *\n * @example\n * ```ts\n * setInspectorJsonRenderer((container, value) => {\n * const root = createRoot(container);\n * root.render(<JsonView value={value} />);\n * return () => root.unmount();\n * });\n * ```\n */\nexport function setInspectorJsonRenderer(\n renderer: InspectorJsonRenderer | null,\n): void {\n inspectorJsonRenderer = renderer;\n}\n\n/**\n * Hosts one value drawn by the host renderer. Inside a shadow root, the\n * container lives in the shadow host's light DOM and shows here through a\n * named slot, so the host page's own styles apply to what the renderer draws.\n */\n// The fallback keeps module import safe where no DOM exists (server render).\nexport class CpkJsonBlock extends ((globalThis.HTMLElement ??\n class {}) as typeof HTMLElement) {\n #value: unknown = undefined;\n #hasValue = false;\n #cleanup: (() => void) | undefined;\n #container: HTMLElement | undefined;\n\n get value(): unknown {\n return this.#value;\n }\n\n set value(next: unknown) {\n if (this.#hasValue && Object.is(next, this.#value)) return;\n this.#value = next;\n this.#hasValue = true;\n if (this.isConnected) this.#render();\n }\n\n connectedCallback(): void {\n this.style.display = \"block\";\n this.#render();\n }\n\n disconnectedCallback(): void {\n this.#teardown();\n }\n\n #render(): void {\n this.#teardown();\n const renderer = inspectorJsonRenderer;\n if (!renderer) return;\n const root = this.getRootNode();\n const container = this.ownerDocument.createElement(\"div\");\n // `instanceof ShadowRoot` fails across realms (the pop-out window).\n if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE && \"host\" in root) {\n inspectorJsonSlotCount += 1;\n const name = `cpk-json-${inspectorJsonSlotCount}`;\n const slot = this.ownerDocument.createElement(\"slot\");\n slot.name = name;\n this.replaceChildren(slot);\n container.slot = name;\n (root as ShadowRoot).host.append(container);\n } else {\n this.replaceChildren(container);\n }\n this.#container = container;\n const cleanup = renderer(container, this.#value);\n this.#cleanup = typeof cleanup === \"function\" ? cleanup : undefined;\n }\n\n #teardown(): void {\n const cleanup = this.#cleanup;\n this.#cleanup = undefined;\n cleanup?.();\n this.#container?.remove();\n this.#container = undefined;\n this.replaceChildren();\n }\n}\n\nexport function renderHighlightedJsonBlock(\n value: unknown,\n options: { maxHeight?: string } = {},\n) {\n if (inspectorJsonRenderer) {\n // The raw value keeps its identity across renders, so an unchanged\n // value does not draw again.\n return html`<cpk-json-block\n .value=${value}\n style=${\n options.maxHeight\n ? `overflow:auto;max-height:${options.maxHeight}`\n : nothing\n }\n ></cpk-json-block>`;\n }\n const parsed = coerceJsonValue(value);\n const style = options.maxHeight\n ? `max-height:${options.maxHeight}`\n : undefined;\n return html`<pre class=\"cpk-json-block\" style=${style || nothing}>\n${unsafeHTML(highlightedJson(parsed))}</pre\n >`;\n}\n"],"mappings":";;;;AAOA,SAAgB,WAAW,GAAmB;AAC5C,QAAO,EAAE,QAAQ,MAAM,QAAQ,CAAC,QAAQ,MAAM,OAAO,CAAC,QAAQ,MAAM,OAAO;;AAQ7E,MAAM,uCAAuB,IAAI,SAAyB;AAE1D,SAAS,gBAAgB,KAAsB;AAC7C,KAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;EAC3C,MAAM,SAAS,qBAAqB,IAAI,IAAI;AAC5C,MAAI,WAAW,OAAW,QAAO;;CAEnC,MAAM,OAAO,KAAK,UAAU,KAAK,MAAM,EAAE;AACzC,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,QAAkB,EAAE;CAC1B,IAAI,YAAY;CAChB,MAAM,KACJ;CACF,IAAI;AACJ,SAAQ,QAAQ,GAAG,KAAK,KAAK,MAAM,MAAM;AACvC,QAAM,KAAK,WAAW,KAAK,MAAM,WAAW,MAAM,MAAM,CAAC,CAAC;EAC1D,MAAM,IAAI,MAAM;EAChB,IAAI,QAAQ;AACZ,MAAI,EAAE,WAAW,KAAI,CACnB,SAAQ,EAAE,SAAS,CAAC,SAAS,IAAI,GAAG,QAAQ;WACnC,MAAM,UAAU,MAAM,QAC/B,SAAQ;WACC,MAAM,OACf,SAAQ;AAEV,QAAM,KACJ,+CAA+C,MAAM,IAAI,WAAW,EAAE,CAAC,SACxE;AACD,cAAY,MAAM,QAAQ,EAAE;;AAE9B,OAAM,KAAK,WAAW,KAAK,MAAM,UAAU,CAAC,CAAC;CAC7C,MAAM,SAAS,MAAM,KAAK,GAAG;AAC7B,KAAI,OAAO,QAAQ,YAAY,QAAQ,KACrC,sBAAqB,IAAI,KAAK,OAAO;AAEvC,QAAO;;AAGT,SAAgB,gBAAgB,OAAyB;AACvD,KAAI,OAAO,UAAU,SACnB,QAAO;CAGT,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,QAAQ,WAAW,EACrB,QAAO;AAMT,KAAI,EAFD,QAAQ,WAAW,IAAI,IAAI,QAAQ,SAAS,IAAI,IAChD,QAAQ,WAAW,IAAI,IAAI,QAAQ,SAAS,IAAI,EAEjD,QAAO;AAGT,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ;SACpB;AACN,SAAO;;;AAcX,IAAI,wBAAsD;AAC1D,IAAI,yBAAyB;AAE7B,MAAa,iBAAiB;;;;;;;;;;;;;;;;AAiB9B,SAAgB,yBACd,UACM;AACN,yBAAwB;;;;;;;AAS1B,IAAa,eAAb,eAAoC,WAAW,eAC7C,MAAM,IAA2B;CACjC,SAAkB;CAClB,YAAY;CACZ;CACA;CAEA,IAAI,QAAiB;AACnB,SAAO,MAAKA;;CAGd,IAAI,MAAM,MAAe;AACvB,MAAI,MAAKC,YAAa,OAAO,GAAG,MAAM,MAAKD,MAAO,CAAE;AACpD,QAAKA,QAAS;AACd,QAAKC,WAAY;AACjB,MAAI,KAAK,YAAa,OAAKC,QAAS;;CAGtC,oBAA0B;AACxB,OAAK,MAAM,UAAU;AACrB,QAAKA,QAAS;;CAGhB,uBAA6B;AAC3B,QAAKC,UAAW;;CAGlB,UAAgB;AACd,QAAKA,UAAW;EAChB,MAAM,WAAW;AACjB,MAAI,CAAC,SAAU;EACf,MAAM,OAAO,KAAK,aAAa;EAC/B,MAAM,YAAY,KAAK,cAAc,cAAc,MAAM;AAEzD,MAAI,KAAK,aAAa,KAAK,0BAA0B,UAAU,MAAM;AACnE,6BAA0B;GAC1B,MAAM,OAAO,YAAY;GACzB,MAAM,OAAO,KAAK,cAAc,cAAc,OAAO;AACrD,QAAK,OAAO;AACZ,QAAK,gBAAgB,KAAK;AAC1B,aAAU,OAAO;AACjB,GAAC,KAAoB,KAAK,OAAO,UAAU;QAE3C,MAAK,gBAAgB,UAAU;AAEjC,QAAKC,YAAa;EAClB,MAAM,UAAU,SAAS,WAAW,MAAKJ,MAAO;AAChD,QAAKK,UAAW,OAAO,YAAY,aAAa,UAAU;;CAG5D,YAAkB;EAChB,MAAM,UAAU,MAAKA;AACrB,QAAKA,UAAW;AAChB,aAAW;AACX,QAAKD,WAAY,QAAQ;AACzB,QAAKA,YAAa;AAClB,OAAK,iBAAiB;;;AAI1B,SAAgB,2BACd,OACA,UAAkC,EAAE,EACpC;AACA,KAAI,sBAGF,QAAO,IAAI;eACA,MAAM;cAEb,QAAQ,YACJ,4BAA4B,QAAQ,cACpC,QACL;;CAGL,MAAM,SAAS,gBAAgB,MAAM;AAIrC,QAAO,IAAI,sCAHG,QAAQ,YAClB,cAAc,QAAQ,cACtB,WACqD,QAAQ;EACjE,WAAW,gBAAgB,OAAO,CAAC,CAAC"}
|