From 2ccea5c35178b5bca8f912c2a71fb006e11bd825 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:31:55 +0000 Subject: [PATCH 01/28] Initial plan From 053a4a66593a0e0c036eb787ca2d4bf5fdfe4d26 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:38:25 +0000 Subject: [PATCH 02/28] Add inspector plugin implementation and example Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- docs/schema/idoc_v1.d.ts | 13 +- packages/markdown/src/plugins/index.ts | 2 + packages/markdown/src/plugins/inspector.ts | 93 ++++++++ packages/markdown/src/plugins/interfaces.ts | 2 + .../json/features/12.inspector.idoc.json | 212 ++++++++++++++++++ 5 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 packages/markdown/src/plugins/inspector.ts create mode 100644 packages/web-deploy/json/features/12.inspector.idoc.json diff --git a/docs/schema/idoc_v1.d.ts b/docs/schema/idoc_v1.d.ts index 7fb3df38..0f81c403 100644 --- a/docs/schema/idoc_v1.d.ts +++ b/docs/schema/idoc_v1.d.ts @@ -207,6 +207,15 @@ interface ImageElementProps { height?: number; width?: number; } +/** + * Inspector + * use for examining and displaying the current value of a variable + */ +interface InspectorElement extends InspectorElementProps { + type: 'inspector'; +} +interface InspectorElementProps extends VariableControl { +} /** * Treebark * use for rendering cards and structured HTML from templates @@ -256,7 +265,7 @@ interface TabulatorElementProps extends OptionalVariableControl { /** * Union type for all possible interactive elements */ -type InteractiveElement = ChartElement | CheckboxElement | DropdownElement | ImageElement | MermaidElement | NumberElement | PresetsElement | SliderElement | TabulatorElement | TextboxElement | TreebarkElement; +type InteractiveElement = ChartElement | CheckboxElement | DropdownElement | ImageElement | InspectorElement | MermaidElement | NumberElement | PresetsElement | SliderElement | TabulatorElement | TextboxElement | TreebarkElement; interface ElementGroup { groupId: string; elements: PageElement[]; @@ -316,4 +325,4 @@ interface GoogleFontsSpec { type InteractiveDocumentWithSchema = InteractiveDocument & { $schema?: string; }; -export type { Calculation, ChartElement, CheckboxElement, CheckboxProps, DataFrameCalculation, DataLoader, DataLoaderBySpec, DataSource, DataSourceBase, DataSourceBaseFormat, DataSourceByDynamicURL, DataSourceByFile, DataSourceInline, DropdownElement, DropdownElementProps, DynamicDropdownOptions, ElementBase, ElementGroup, GoogleFontsSpec, ImageElement, ImageElementProps, InteractiveDocument, InteractiveDocumentWithSchema, InteractiveElement, MarkdownElement, MermaidElement, MermaidElementProps, MermaidTemplate, NumberElement, NumberElementProps, OptionalVariableControl, PageElement, PageStyle, Preset, PresetsElement, PresetsElementProps, ReturnType, ScalarCalculation, SliderElement, SliderElementProps, TabulatorElement, TabulatorElementProps, TemplatedUrl, TextboxElement, TextboxElementProps, TreebarkElement, TreebarkElementProps, Variable, VariableControl, VariableID, VariableType, VariableValue, VariableValueArray, VariableValuePrimitive, Vega_or_VegaLite_spec }; \ No newline at end of file +export type { Calculation, ChartElement, CheckboxElement, CheckboxProps, DataFrameCalculation, DataLoader, DataLoaderBySpec, DataSource, DataSourceBase, DataSourceBaseFormat, DataSourceByDynamicURL, DataSourceByFile, DataSourceInline, DropdownElement, DropdownElementProps, DynamicDropdownOptions, ElementBase, ElementGroup, GoogleFontsSpec, ImageElement, ImageElementProps, InspectorElement, InspectorElementProps, InteractiveDocument, InteractiveDocumentWithSchema, InteractiveElement, MarkdownElement, MermaidElement, MermaidElementProps, MermaidTemplate, NumberElement, NumberElementProps, OptionalVariableControl, PageElement, PageStyle, Preset, PresetsElement, PresetsElementProps, ReturnType, ScalarCalculation, SliderElement, SliderElementProps, TabulatorElement, TabulatorElementProps, TemplatedUrl, TextboxElement, TextboxElementProps, TreebarkElement, TreebarkElementProps, Variable, VariableControl, VariableID, VariableType, VariableValue, VariableValueArray, VariableValuePrimitive, Vega_or_VegaLite_spec }; \ No newline at end of file diff --git a/packages/markdown/src/plugins/index.ts b/packages/markdown/src/plugins/index.ts index 9071173e..df2bf23b 100644 --- a/packages/markdown/src/plugins/index.ts +++ b/packages/markdown/src/plugins/index.ts @@ -13,6 +13,7 @@ import { dsvPlugin } from './dsv.js'; import { googleFontsPlugin } from './google-fonts.js'; import { dropdownPlugin } from './dropdown.js'; import { imagePlugin } from './image.js'; +import { inspectorPlugin } from './inspector.js'; import { mermaidPlugin } from './mermaid.js'; import { numberPlugin } from './number.js'; import { placeholdersPlugin } from './placeholders.js'; @@ -34,6 +35,7 @@ export function registerNativePlugins() { registerMarkdownPlugin(googleFontsPlugin); registerMarkdownPlugin(dropdownPlugin); registerMarkdownPlugin(imagePlugin); + registerMarkdownPlugin(inspectorPlugin); registerMarkdownPlugin(mermaidPlugin); registerMarkdownPlugin(numberPlugin); registerMarkdownPlugin(placeholdersPlugin); diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts new file mode 100644 index 00000000..23d592ce --- /dev/null +++ b/packages/markdown/src/plugins/inspector.ts @@ -0,0 +1,93 @@ +/** +* Copyright (c) Microsoft Corporation. +* Licensed under the MIT License. +*/ + +import { VariableControl } from '@microsoft/chartifact-schema'; +import { Batch, IInstance, Plugin } from '../factory.js'; +import { pluginClassName } from './util.js'; +import { flaggablePlugin } from './config.js'; +import { PluginNames } from './interfaces.js'; + +interface InspectorInstance { + id: string; + spec: InspectorSpec; + element: HTMLElement; +} + +export interface InspectorSpec extends VariableControl { +} + +const pluginName: PluginNames = 'inspector'; +const className = pluginClassName(pluginName); + +export const inspectorPlugin: Plugin = { + ...flaggablePlugin(pluginName, className), + hydrateComponent: async (renderer, errorHandler, specs) => { + const { signalBus } = renderer; + const inspectorInstances: InspectorInstance[] = []; + for (let index = 0; index < specs.length; index++) { + const specReview = specs[index]; + if (!specReview.approvedSpec) { + continue; + } + const container = renderer.element.querySelector(`#${specReview.containerId}`); + + const spec: InspectorSpec = specReview.approvedSpec; + + const html = `
+
${spec.label || spec.variableId}
+

+                
`; + container.innerHTML = html; + const element = container.querySelector('.inspector-value') as HTMLElement; + + const inspectorInstance: InspectorInstance = { id: `${pluginName}-${index}`, spec, element }; + inspectorInstances.push(inspectorInstance); + } + + const instances = inspectorInstances.map((inspectorInstance): IInstance => { + const { element, spec } = inspectorInstance; + const initialSignals = [{ + name: spec.variableId, + value: null, + priority: -1, + isData: false, + }]; + + const updateDisplay = (value: unknown) => { + if (value === null || value === undefined) { + element.textContent = String(value); + } else if (typeof value === 'string') { + element.textContent = `"${value}"`; + } else if (typeof value === 'object') { + element.textContent = JSON.stringify(value, null, 2); + } else { + element.textContent = String(value); + } + }; + + return { + ...inspectorInstance, + initialSignals, + receiveBatch: async (batch) => { + if (batch[spec.variableId]) { + const value = batch[spec.variableId].value; + updateDisplay(value); + } + }, + beginListening() { + // Inspector is read-only, no event listeners needed + }, + getCurrentSignalValue: () => { + // Inspector doesn't modify the signal, return undefined + return undefined; + }, + destroy: () => { + // No cleanup needed + }, + }; + }); + return instances; + }, +}; diff --git a/packages/markdown/src/plugins/interfaces.ts b/packages/markdown/src/plugins/interfaces.ts index ab81a30e..f481857b 100644 --- a/packages/markdown/src/plugins/interfaces.ts +++ b/packages/markdown/src/plugins/interfaces.ts @@ -7,6 +7,7 @@ export { CsvSpec } from './csv.js'; export { DropdownSpec } from './dropdown.js'; export { DsvSpec } from './dsv.js'; export { ImageSpec } from './image.js'; +export { InspectorSpec } from './inspector.js'; export { MermaidSpec } from './mermaid.js'; export { NumberSpec } from './number.js'; export { PresetsSpec } from './presets.js'; @@ -25,6 +26,7 @@ export type PluginNames = 'dsv' | 'image' | 'google-fonts' | + 'inspector' | 'mermaid' | 'number' | 'placeholders' | diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json new file mode 100644 index 00000000..d1ae2c9f --- /dev/null +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -0,0 +1,212 @@ +{ + "$schema": "../../../../docs/schema/idoc_v1.json", + "title": "Variable Inspector Demo", + "variables": [ + { + "variableId": "userName", + "type": "string", + "initialValue": "Alice" + }, + { + "variableId": "temperature", + "type": "number", + "initialValue": 20 + }, + { + "variableId": "isEnabled", + "type": "boolean", + "initialValue": true + }, + { + "variableId": "selectedItems", + "type": "string", + "isArray": true, + "initialValue": ["apple", "banana", "cherry"] + }, + { + "variableId": "config", + "type": "object", + "initialValue": { + "theme": "dark", + "fontSize": 14, + "features": { + "autosave": true, + "notifications": false + } + } + }, + { + "variableId": "doubledTemp", + "type": "number", + "initialValue": 40, + "calculation": { + "vegaExpression": "temperature * 2" + } + } + ], + "groups": [ + { + "groupId": "main", + "elements": [ + "# Variable Inspector", + "", + "The inspector plugin allows you to examine the current values of variables in real-time. This is useful for debugging and understanding how variables change as you interact with the document.", + "", + "---", + "", + "## String Variable", + "Use the textbox below to change the value:", + { + "type": "textbox", + "variableId": "userName", + "label": "User Name" + }, + "", + "**Inspector output:**", + { + "type": "inspector", + "variableId": "userName", + "label": "userName" + }, + "", + "---", + "", + "## Number Variable", + "Adjust the slider to see the value change:", + { + "type": "slider", + "variableId": "temperature", + "label": "Temperature (°C)", + "min": -10, + "max": 50, + "step": 1 + }, + "", + "**Inspector output:**", + { + "type": "inspector", + "variableId": "temperature", + "label": "temperature" + }, + "", + "---", + "", + "## Boolean Variable", + "Toggle the checkbox:", + { + "type": "checkbox", + "variableId": "isEnabled", + "label": "Enable Feature" + }, + "", + "**Inspector output:**", + { + "type": "inspector", + "variableId": "isEnabled", + "label": "isEnabled" + }, + "", + "---", + "", + "## Array Variable", + "Select multiple items:", + { + "type": "dropdown", + "variableId": "selectedItems", + "label": "Selected Items", + "options": ["apple", "banana", "cherry", "date", "elderberry"], + "multiple": true, + "size": 5 + }, + "", + "**Inspector output:**", + { + "type": "inspector", + "variableId": "selectedItems", + "label": "selectedItems" + }, + "", + "---", + "", + "## Object Variable", + "This is a complex object with nested properties:", + "", + "**Inspector output:**", + { + "type": "inspector", + "variableId": "config", + "label": "config" + }, + "", + "---", + "", + "## Calculated Variable", + "This variable is calculated from the temperature (temperature × 2):", + "", + "**Inspector output:**", + { + "type": "inspector", + "variableId": "doubledTemp", + "label": "doubledTemp (calculated)" + }, + "", + "---", + "", + "## Use Cases", + "", + "The inspector is particularly useful for:", + "- **Debugging**: See exactly what values variables have at any moment", + "- **Development**: Verify that calculations and data transformations work correctly", + "- **Documentation**: Show users the current state of the system", + "- **Learning**: Understand how variables change when interacting with controls" + ] + } + ], + "style": { + "css": [ + "body {", + " max-width: 800px;", + " margin: 0 auto;", + " padding: 2em;", + " font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;", + "}", + "", + ".inspector {", + " background-color: #f5f5f5;", + " border: 1px solid #ddd;", + " border-radius: 4px;", + " padding: 12px;", + " margin: 10px 0;", + "}", + "", + ".inspector-label {", + " font-weight: 600;", + " color: #333;", + " margin-bottom: 8px;", + " font-size: 0.9em;", + "}", + "", + ".inspector-value {", + " background-color: #fff;", + " border: 1px solid #ccc;", + " border-radius: 3px;", + " padding: 8px;", + " margin: 0;", + " font-family: 'Courier New', Courier, monospace;", + " font-size: 0.9em;", + " color: #0066cc;", + " overflow-x: auto;", + "}", + "", + ".vega-bindings {", + " margin: 15px 0;", + "}", + "", + "hr {", + " margin: 2em 0;", + " border: none;", + " border-top: 1px solid #e0e0e0;", + "}" + ] + } +} From 8008fbc296a95e603c54a39b36763c1593777e6f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:47:37 +0000 Subject: [PATCH 03/28] Add validation support for inspector element Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- docs/schema/idoc_v1.json | 25 +++++++++++++++++++++++ packages/compiler/src/validate/element.ts | 6 +++++- packages/schema-doc/src/interactive.ts | 11 ++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/schema/idoc_v1.json b/docs/schema/idoc_v1.json index c93a9361..e236c0bf 100644 --- a/docs/schema/idoc_v1.json +++ b/docs/schema/idoc_v1.json @@ -3143,6 +3143,28 @@ ], "type": "object" }, + "InspectorElement": { + "additionalProperties": false, + "description": "Inspector use for examining and displaying the current value of a variable", + "properties": { + "label": { + "description": "optional label if the variableId is not descriptive enough", + "type": "string" + }, + "type": { + "const": "inspector", + "type": "string" + }, + "variableId": { + "$ref": "#/definitions/VariableID" + } + }, + "required": [ + "type", + "variableId" + ], + "type": "object" + }, "InteractiveDocumentWithSchema": { "additionalProperties": false, "description": "JSON Schema version with $schema property for validation", @@ -3216,6 +3238,9 @@ { "$ref": "#/definitions/ImageElement" }, + { + "$ref": "#/definitions/InspectorElement" + }, { "$ref": "#/definitions/MermaidElement" }, diff --git a/packages/compiler/src/validate/element.ts b/packages/compiler/src/validate/element.ts index a8b3307b..71cdf9da 100644 --- a/packages/compiler/src/validate/element.ts +++ b/packages/compiler/src/validate/element.ts @@ -1,4 +1,4 @@ -import { PageElement, Variable, DataLoader, CheckboxElement, DropdownElement, SliderElement, TextboxElement, ChartElement, ImageElement, MermaidElement, TreebarkElement, Vega_or_VegaLite_spec } from "@microsoft/chartifact-schema"; +import { PageElement, Variable, DataLoader, CheckboxElement, DropdownElement, SliderElement, TextboxElement, ChartElement, ImageElement, InspectorElement, MermaidElement, TreebarkElement, Vega_or_VegaLite_spec } from "@microsoft/chartifact-schema"; import { getChartType } from "../util.js"; import { validateVegaLite, validateVegaChart } from "./chart.js"; import { validateVariableID, validateRequiredString, validateOptionalString, validateOptionalPositiveNumber, validateOptionalBoolean, validateOptionalObject, validateInputElementWithVariableId, validateMarkdownString } from "./common.js"; @@ -94,6 +94,10 @@ export async function validateElement(element: PageElement, groupIndex: number, break; } + case 'inspector': { + errors.push(...validateInputElementWithVariableId(element)); + break; + } case 'mermaid': { const mermaidElement = element as MermaidElement; diff --git a/packages/schema-doc/src/interactive.ts b/packages/schema-doc/src/interactive.ts index 70753307..e2617cd9 100644 --- a/packages/schema-doc/src/interactive.ts +++ b/packages/schema-doc/src/interactive.ts @@ -137,6 +137,16 @@ export interface ImageElementProps { width?: number; } +/** + * Inspector + * use for examining and displaying the current value of a variable + */ +export interface InspectorElement extends InspectorElementProps { + type: 'inspector'; +} +export interface InspectorElementProps extends VariableControl { +} + /** * Treebark * use for rendering cards and structured HTML from templates @@ -243,6 +253,7 @@ export type InteractiveElement = | CheckboxElement | DropdownElement | ImageElement + | InspectorElement | MermaidElement | NumberElement | PresetsElement From 9ca19c8431849ff53ef7cb587b3fa13a287196c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:01:23 +0000 Subject: [PATCH 04/28] Add interactive array expand/collapse to inspector plugin Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 88 +++++++++++++++++++ .../json/features/12.inspector.idoc.json | 27 ++++++ 2 files changed, 115 insertions(+) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 23d592ce..47186d92 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -56,17 +56,105 @@ export const inspectorPlugin: Plugin = { }]; const updateDisplay = (value: unknown) => { + element.innerHTML = ''; // Clear previous content + if (value === null || value === undefined) { element.textContent = String(value); } else if (typeof value === 'string') { element.textContent = `"${value}"`; + } else if (Array.isArray(value)) { + // Create interactive collapsible array display + renderArray(element, value); } else if (typeof value === 'object') { + // For objects, use JSON.stringify with indentation element.textContent = JSON.stringify(value, null, 2); } else { element.textContent = String(value); } }; + const renderArray = (container: HTMLElement, arr: unknown[], depth: number = 0) => { + const indent = ' '.repeat(depth); + + // Create collapsible array structure + const arrayWrapper = document.createElement('div'); + arrayWrapper.className = 'inspector-array'; + + // Array header with toggle + const header = document.createElement('div'); + header.className = 'inspector-array-header'; + header.style.cursor = 'pointer'; + header.style.userSelect = 'none'; + + const toggleIcon = document.createElement('span'); + toggleIcon.className = 'inspector-toggle'; + toggleIcon.textContent = '▼ '; + toggleIcon.style.display = 'inline-block'; + toggleIcon.style.width = '1em'; + + const arrayLabel = document.createElement('span'); + arrayLabel.textContent = `Array(${arr.length})`; + arrayLabel.style.color = '#0066cc'; + + header.appendChild(toggleIcon); + header.appendChild(arrayLabel); + + // Array content + const content = document.createElement('div'); + content.className = 'inspector-array-content'; + content.style.paddingLeft = '1.5em'; + + arr.forEach((item, index) => { + const itemDiv = document.createElement('div'); + itemDiv.className = 'inspector-array-item'; + + const indexLabel = document.createElement('span'); + indexLabel.textContent = `[${index}]: `; + indexLabel.style.color = '#666'; + itemDiv.appendChild(indexLabel); + + const valueSpan = document.createElement('span'); + + if (item === null || item === undefined) { + valueSpan.textContent = String(item); + } else if (typeof item === 'string') { + valueSpan.textContent = `"${item}"`; + valueSpan.style.color = '#008000'; + } else if (typeof item === 'number') { + valueSpan.textContent = String(item); + valueSpan.style.color = '#0066cc'; + } else if (typeof item === 'boolean') { + valueSpan.textContent = String(item); + valueSpan.style.color = '#d73a49'; + } else if (Array.isArray(item)) { + // Nested array + renderArray(valueSpan, item, depth + 1); + } else if (typeof item === 'object') { + valueSpan.textContent = JSON.stringify(item, null, 2); + valueSpan.style.whiteSpace = 'pre'; + } else { + valueSpan.textContent = String(item); + } + + itemDiv.appendChild(valueSpan); + content.appendChild(itemDiv); + }); + + // Toggle functionality + let isExpanded = true; + const toggle = () => { + isExpanded = !isExpanded; + content.style.display = isExpanded ? 'block' : 'none'; + toggleIcon.textContent = isExpanded ? '▼ ' : '▶ '; + }; + + header.addEventListener('click', toggle); + + arrayWrapper.appendChild(header); + arrayWrapper.appendChild(content); + container.appendChild(arrayWrapper); + }; + return { ...inspectorInstance, initialSignals, diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index d1ae2c9f..fb4ace63 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -198,6 +198,33 @@ " overflow-x: auto;", "}", "", + ".inspector-array {", + " margin: 0;", + "}", + "", + ".inspector-array-header {", + " padding: 2px 0;", + " font-weight: 600;", + "}", + "", + ".inspector-array-header:hover {", + " background-color: rgba(0, 102, 204, 0.1);", + "}", + "", + ".inspector-toggle {", + " color: #666;", + " font-size: 0.8em;", + "}", + "", + ".inspector-array-content {", + " margin: 2px 0;", + "}", + "", + ".inspector-array-item {", + " padding: 1px 0;", + " line-height: 1.4;", + "}", + "", ".vega-bindings {", " margin: 15px 0;", "}", From 855d4844a00c2c96d7ff8dda21aa86c52e85a75e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:21:16 +0000 Subject: [PATCH 05/28] Fix inspector element hydration by changing pre to div Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 2 +- packages/web-deploy/json/features/12.inspector.idoc.json | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 47186d92..03e9388e 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -37,7 +37,7 @@ export const inspectorPlugin: Plugin = { const html = `
${spec.label || spec.variableId}
-

+                    
`; container.innerHTML = html; const element = container.querySelector('.inspector-value') as HTMLElement; diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index fb4ace63..46f7945e 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -196,6 +196,8 @@ " font-size: 0.9em;", " color: #0066cc;", " overflow-x: auto;", + " white-space: pre-wrap;", + " word-wrap: break-word;", "}", "", ".inspector-array {", From f2659a911817ab5385c16bdef7d1703710c6425e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 02:43:14 +0000 Subject: [PATCH 06/28] Add raw option to inspector for copy/paste use case Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- docs/schema/idoc_v1.d.ts | 2 ++ packages/markdown/src/plugins/inspector.ts | 17 +++++++++++++++++ packages/schema-doc/src/interactive.ts | 2 ++ .../json/features/12.inspector.idoc.json | 10 +++++++++- 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/schema/idoc_v1.d.ts b/docs/schema/idoc_v1.d.ts index 0f81c403..0964a77b 100644 --- a/docs/schema/idoc_v1.d.ts +++ b/docs/schema/idoc_v1.d.ts @@ -215,6 +215,8 @@ interface InspectorElement extends InspectorElementProps { type: 'inspector'; } interface InspectorElementProps extends VariableControl { + /** When true, displays raw JSON output without interactive elements (for copy/paste). Default is false. */ + raw?: boolean; } /** * Treebark diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 03e9388e..51662868 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -16,6 +16,7 @@ interface InspectorInstance { } export interface InspectorSpec extends VariableControl { + raw?: boolean; } const pluginName: PluginNames = 'inspector'; @@ -58,6 +59,22 @@ export const inspectorPlugin: Plugin = { const updateDisplay = (value: unknown) => { element.innerHTML = ''; // Clear previous content + // If raw mode is enabled, always use JSON.stringify without interactivity + if (spec.raw) { + if (value === null || value === undefined) { + element.textContent = String(value); + } else if (typeof value === 'string') { + element.textContent = `"${value}"`; + } else if (typeof value === 'object') { + // For arrays and objects, use JSON.stringify with indentation + element.textContent = JSON.stringify(value, null, 2); + } else { + element.textContent = String(value); + } + return; + } + + // Interactive mode (default) if (value === null || value === undefined) { element.textContent = String(value); } else if (typeof value === 'string') { diff --git a/packages/schema-doc/src/interactive.ts b/packages/schema-doc/src/interactive.ts index e2617cd9..483439b0 100644 --- a/packages/schema-doc/src/interactive.ts +++ b/packages/schema-doc/src/interactive.ts @@ -145,6 +145,8 @@ export interface InspectorElement extends InspectorElementProps { type: 'inspector'; } export interface InspectorElementProps extends VariableControl { + /** When true, displays raw JSON output without interactive elements (for copy/paste). Default is false. */ + raw?: boolean; } /** diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 46f7945e..3779e335 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -119,13 +119,21 @@ "size": 5 }, "", - "**Inspector output:**", + "**Inspector output (interactive):**", { "type": "inspector", "variableId": "selectedItems", "label": "selectedItems" }, "", + "**Inspector output (raw mode for copy/paste):**", + { + "type": "inspector", + "variableId": "selectedItems", + "label": "selectedItems (raw)", + "raw": true + }, + "", "---", "", "## Object Variable", From 1a01afc43bb6c558e8df05a67bac0853b5c641e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 03:03:47 +0000 Subject: [PATCH 07/28] Make arrays start collapsed by default Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 51662868..c73c05e4 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -105,7 +105,7 @@ export const inspectorPlugin: Plugin = { const toggleIcon = document.createElement('span'); toggleIcon.className = 'inspector-toggle'; - toggleIcon.textContent = '▼ '; + toggleIcon.textContent = '▶ '; toggleIcon.style.display = 'inline-block'; toggleIcon.style.width = '1em'; @@ -157,8 +157,9 @@ export const inspectorPlugin: Plugin = { content.appendChild(itemDiv); }); - // Toggle functionality - let isExpanded = true; + // Toggle functionality - start collapsed + let isExpanded = false; + content.style.display = 'none'; const toggle = () => { isExpanded = !isExpanded; content.style.display = isExpanded ? 'block' : 'none'; From 1cfa5e0e26629761a386752d7fc8224ae0fd38c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 03:31:22 +0000 Subject: [PATCH 08/28] Remove all coloring and unnecessary styling from inspector Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 5 ----- .../json/features/12.inspector.idoc.json | 19 ------------------- 2 files changed, 24 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index c73c05e4..c9bf8734 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -111,7 +111,6 @@ export const inspectorPlugin: Plugin = { const arrayLabel = document.createElement('span'); arrayLabel.textContent = `Array(${arr.length})`; - arrayLabel.style.color = '#0066cc'; header.appendChild(toggleIcon); header.appendChild(arrayLabel); @@ -127,7 +126,6 @@ export const inspectorPlugin: Plugin = { const indexLabel = document.createElement('span'); indexLabel.textContent = `[${index}]: `; - indexLabel.style.color = '#666'; itemDiv.appendChild(indexLabel); const valueSpan = document.createElement('span'); @@ -136,13 +134,10 @@ export const inspectorPlugin: Plugin = { valueSpan.textContent = String(item); } else if (typeof item === 'string') { valueSpan.textContent = `"${item}"`; - valueSpan.style.color = '#008000'; } else if (typeof item === 'number') { valueSpan.textContent = String(item); - valueSpan.style.color = '#0066cc'; } else if (typeof item === 'boolean') { valueSpan.textContent = String(item); - valueSpan.style.color = '#d73a49'; } else if (Array.isArray(item)) { // Nested array renderArray(valueSpan, item, depth + 1); diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 3779e335..8ffadd29 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -180,29 +180,21 @@ "}", "", ".inspector {", - " background-color: #f5f5f5;", " border: 1px solid #ddd;", - " border-radius: 4px;", " padding: 12px;", " margin: 10px 0;", "}", "", ".inspector-label {", " font-weight: 600;", - " color: #333;", " margin-bottom: 8px;", - " font-size: 0.9em;", "}", "", ".inspector-value {", - " background-color: #fff;", " border: 1px solid #ccc;", - " border-radius: 3px;", " padding: 8px;", " margin: 0;", " font-family: 'Courier New', Courier, monospace;", - " font-size: 0.9em;", - " color: #0066cc;", " overflow-x: auto;", " white-space: pre-wrap;", " word-wrap: break-word;", @@ -214,16 +206,6 @@ "", ".inspector-array-header {", " padding: 2px 0;", - " font-weight: 600;", - "}", - "", - ".inspector-array-header:hover {", - " background-color: rgba(0, 102, 204, 0.1);", - "}", - "", - ".inspector-toggle {", - " color: #666;", - " font-size: 0.8em;", "}", "", ".inspector-array-content {", @@ -232,7 +214,6 @@ "", ".inspector-array-item {", " padding: 1px 0;", - " line-height: 1.4;", "}", "", ".vega-bindings {", From 1eaed6dfce03048900fd5a6a2d1f90ac60092e20 Mon Sep 17 00:00:00 2001 From: Dan Marshall Date: Mon, 20 Oct 2025 22:02:48 -0700 Subject: [PATCH 09/28] Refactor raw mode handling in inspector to simplify value display logic --- packages/markdown/src/plugins/inspector.ts | 31 ++++------------------ 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index c9bf8734..5337e5f7 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -61,32 +61,19 @@ export const inspectorPlugin: Plugin = { // If raw mode is enabled, always use JSON.stringify without interactivity if (spec.raw) { - if (value === null || value === undefined) { - element.textContent = String(value); - } else if (typeof value === 'string') { - element.textContent = `"${value}"`; - } else if (typeof value === 'object') { - // For arrays and objects, use JSON.stringify with indentation - element.textContent = JSON.stringify(value, null, 2); - } else { - element.textContent = String(value); - } + element.textContent = JSON.stringify(value, null, 2); return; } // Interactive mode (default) - if (value === null || value === undefined) { - element.textContent = String(value); - } else if (typeof value === 'string') { - element.textContent = `"${value}"`; - } else if (Array.isArray(value)) { + if (Array.isArray(value)) { // Create interactive collapsible array display renderArray(element, value); } else if (typeof value === 'object') { // For objects, use JSON.stringify with indentation element.textContent = JSON.stringify(value, null, 2); } else { - element.textContent = String(value); + element.textContent = JSON.stringify(value); } }; @@ -130,22 +117,14 @@ export const inspectorPlugin: Plugin = { const valueSpan = document.createElement('span'); - if (item === null || item === undefined) { - valueSpan.textContent = String(item); - } else if (typeof item === 'string') { - valueSpan.textContent = `"${item}"`; - } else if (typeof item === 'number') { - valueSpan.textContent = String(item); - } else if (typeof item === 'boolean') { - valueSpan.textContent = String(item); - } else if (Array.isArray(item)) { + if (Array.isArray(item)) { // Nested array renderArray(valueSpan, item, depth + 1); } else if (typeof item === 'object') { valueSpan.textContent = JSON.stringify(item, null, 2); valueSpan.style.whiteSpace = 'pre'; } else { - valueSpan.textContent = String(item); + valueSpan.textContent = JSON.stringify(item); } itemDiv.appendChild(valueSpan); From 1b84dbb610b9fc663483720fdca909fe7d41cb7e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 05:24:07 +0000 Subject: [PATCH 10/28] Add inspector case to markdown compiler Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/compiler/src/md.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/compiler/src/md.ts b/packages/compiler/src/md.ts index 1c49d9fd..ab5faf9a 100644 --- a/packages/compiler/src/md.ts +++ b/packages/compiler/src/md.ts @@ -202,7 +202,7 @@ function dataLoaderMarkdown(dataSources: DataSource[], variables: Variable[], ta return { vegaScope, inlineDataMd }; } -type pluginSpecs = Plugins.CheckboxSpec | Plugins.DropdownSpec | Plugins.ImageSpec | Plugins.MermaidSpec | Plugins.NumberSpec | Plugins.PresetsSpec | Plugins.SliderSpec | Plugins.TabulatorSpec | Plugins.TextboxSpec | Plugins.TreebarkSpec; +type pluginSpecs = Plugins.CheckboxSpec | Plugins.DropdownSpec | Plugins.ImageSpec | Plugins.InspectorSpec | Plugins.MermaidSpec | Plugins.NumberSpec | Plugins.PresetsSpec | Plugins.SliderSpec | Plugins.TabulatorSpec | Plugins.TextboxSpec | Plugins.TreebarkSpec; function groupMarkdown(group: ElementGroup, variables: Variable[], vegaScope: VegaScope, resources: { charts?: { [chartKey: string]: VegaSpec | VegaLiteSpec } }, pluginFormat: Record) { const mdElements: string[] = []; @@ -281,6 +281,18 @@ function groupMarkdown(group: ElementGroup, variables: Variable[], vegaScope: Ve addSpec('image', imageSpec); break; } + case 'inspector': { + const { variableId, label, raw } = element; + const inspectorSpec: Plugins.InspectorSpec = { + variableId, + label, + }; + if (raw) { + inspectorSpec.raw = raw; + } + addSpec('inspector', inspectorSpec, false); + break; + } case 'mermaid': { const { diagramText, template, variableId } = element; if (diagramText) { From b8d359392c57f436332f71c24a4311ba84868923 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 05:42:52 +0000 Subject: [PATCH 11/28] Add nested array demo and remove CSS styling Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- .../json/features/12.inspector.idoc.json | 80 +++++-------------- 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 8ffadd29..7e5a01b4 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -23,6 +23,15 @@ "isArray": true, "initialValue": ["apple", "banana", "cherry"] }, + { + "variableId": "nestedArray", + "type": "object", + "initialValue": [ + ["row1-col1", "row1-col2"], + ["row2-col1", "row2-col2"], + ["row3-col1", "row3-col2"] + ] + }, { "variableId": "config", "type": "object", @@ -136,6 +145,18 @@ "", "---", "", + "## Nested Array Variable", + "This is a 2D array (array of arrays):", + "", + "**Inspector output:**", + { + "type": "inspector", + "variableId": "nestedArray", + "label": "nestedArray" + }, + "", + "---", + "", "## Object Variable", "This is a complex object with nested properties:", "", @@ -169,62 +190,5 @@ "- **Learning**: Understand how variables change when interacting with controls" ] } - ], - "style": { - "css": [ - "body {", - " max-width: 800px;", - " margin: 0 auto;", - " padding: 2em;", - " font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;", - "}", - "", - ".inspector {", - " border: 1px solid #ddd;", - " padding: 12px;", - " margin: 10px 0;", - "}", - "", - ".inspector-label {", - " font-weight: 600;", - " margin-bottom: 8px;", - "}", - "", - ".inspector-value {", - " border: 1px solid #ccc;", - " padding: 8px;", - " margin: 0;", - " font-family: 'Courier New', Courier, monospace;", - " overflow-x: auto;", - " white-space: pre-wrap;", - " word-wrap: break-word;", - "}", - "", - ".inspector-array {", - " margin: 0;", - "}", - "", - ".inspector-array-header {", - " padding: 2px 0;", - "}", - "", - ".inspector-array-content {", - " margin: 2px 0;", - "}", - "", - ".inspector-array-item {", - " padding: 1px 0;", - "}", - "", - ".vega-bindings {", - " margin: 15px 0;", - "}", - "", - "hr {", - " margin: 2em 0;", - " border: none;", - " border-top: 1px solid #e0e0e0;", - "}" - ] - } + ] } From 8ec4793e1f762d919db480a8e46138d73a3d5373 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 13:52:18 +0000 Subject: [PATCH 12/28] Add Vega transforms example with filter, aggregate, pivot, and nest Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- .../json/features/12.inspector.idoc.json | 221 ++++++++++-------- 1 file changed, 118 insertions(+), 103 deletions(-) diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 7e5a01b4..226cfb64 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -1,55 +1,100 @@ { "$schema": "../../../../docs/schema/idoc_v1.json", - "title": "Variable Inspector Demo", + "title": "Variable Inspector Demo with Data Transforms", + "dataSources": [ + { + "dataSourceName": "salesData", + "format": "json", + "data": [ + {"category": "Electronics", "product": "Laptop", "region": "North", "sales": 1200, "quantity": 3}, + {"category": "Electronics", "product": "Phone", "region": "North", "sales": 800, "quantity": 5}, + {"category": "Electronics", "product": "Laptop", "region": "South", "sales": 1500, "quantity": 4}, + {"category": "Furniture", "product": "Desk", "region": "North", "sales": 600, "quantity": 2}, + {"category": "Furniture", "product": "Chair", "region": "South", "sales": 300, "quantity": 6}, + {"category": "Furniture", "product": "Desk", "region": "South", "sales": 700, "quantity": 3} + ] + } + ], "variables": [ { - "variableId": "userName", + "variableId": "selectedRegion", "type": "string", - "initialValue": "Alice" + "initialValue": "North" }, { - "variableId": "temperature", + "variableId": "minSales", "type": "number", - "initialValue": 20 + "initialValue": 500 }, { - "variableId": "isEnabled", - "type": "boolean", - "initialValue": true + "variableId": "salesData", + "type": "object", + "isArray": true, + "initialValue": [] }, { - "variableId": "selectedItems", - "type": "string", + "variableId": "filteredData", + "type": "object", "isArray": true, - "initialValue": ["apple", "banana", "cherry"] + "initialValue": [], + "calculation": { + "dataSourceNames": ["salesData"], + "dataFrameTransformations": [ + { + "type": "filter", + "expr": "datum.region == selectedRegion && datum.sales >= minSales" + } + ] + } }, { - "variableId": "nestedArray", + "variableId": "aggregatedByCategory", "type": "object", - "initialValue": [ - ["row1-col1", "row1-col2"], - ["row2-col1", "row2-col2"], - ["row3-col1", "row3-col2"] - ] + "isArray": true, + "initialValue": [], + "calculation": { + "dataSourceNames": ["filteredData"], + "dataFrameTransformations": [ + { + "type": "aggregate", + "groupby": ["category"], + "ops": ["sum", "sum", "count"], + "fields": ["sales", "quantity", null], + "as": ["totalSales", "totalQuantity", "productCount"] + } + ] + } }, { - "variableId": "config", + "variableId": "pivotedData", "type": "object", - "initialValue": { - "theme": "dark", - "fontSize": 14, - "features": { - "autosave": true, - "notifications": false - } + "isArray": true, + "initialValue": [], + "calculation": { + "dataSourceNames": ["salesData"], + "dataFrameTransformations": [ + { + "type": "pivot", + "groupby": ["product"], + "field": "region", + "value": "sales", + "op": "sum" + } + ] } }, { - "variableId": "doubledTemp", - "type": "number", - "initialValue": 40, + "variableId": "nestedByCategory", + "type": "object", + "initialValue": [], "calculation": { - "vegaExpression": "temperature * 2" + "dataSourceNames": ["salesData"], + "dataFrameTransformations": [ + { + "type": "nest", + "keys": ["category"] + } + ] } } ], @@ -57,126 +102,96 @@ { "groupId": "main", "elements": [ - "# Variable Inspector", + "# Variable Inspector with Data Transforms", "", - "The inspector plugin allows you to examine the current values of variables in real-time. This is useful for debugging and understanding how variables change as you interact with the document.", + "The inspector plugin is particularly useful for examining transformed data. This example demonstrates Vega data transforms like **filter**, **aggregate**, **pivot**, and **nest**.", "", "---", "", - "## String Variable", - "Use the textbox below to change the value:", - { - "type": "textbox", - "variableId": "userName", - "label": "User Name" - }, - "", - "**Inspector output:**", - { - "type": "inspector", - "variableId": "userName", - "label": "userName" - }, + "## Filter Controls", "", - "---", + "Select a region and minimum sales threshold to filter the data:", "", - "## Number Variable", - "Adjust the slider to see the value change:", { - "type": "slider", - "variableId": "temperature", - "label": "Temperature (°C)", - "min": -10, - "max": 50, - "step": 1 + "type": "dropdown", + "variableId": "selectedRegion", + "label": "Region", + "options": ["North", "South"] }, "", - "**Inspector output:**", { - "type": "inspector", - "variableId": "temperature", - "label": "temperature" + "type": "slider", + "variableId": "minSales", + "label": "Minimum Sales", + "min": 0, + "max": 2000, + "step": 100 }, "", "---", "", - "## Boolean Variable", - "Toggle the checkbox:", - { - "type": "checkbox", - "variableId": "isEnabled", - "label": "Enable Feature" - }, + "## Filtered Data", + "", + "Filtered by selected region and minimum sales using Vega **filter** transform:", "", "**Inspector output:**", { "type": "inspector", - "variableId": "isEnabled", - "label": "isEnabled" + "variableId": "filteredData", + "label": "filteredData" }, "", "---", "", - "## Array Variable", - "Select multiple items:", - { - "type": "dropdown", - "variableId": "selectedItems", - "label": "Selected Items", - "options": ["apple", "banana", "cherry", "date", "elderberry"], - "multiple": true, - "size": 5 - }, + "## Aggregated by Category", "", - "**Inspector output (interactive):**", - { - "type": "inspector", - "variableId": "selectedItems", - "label": "selectedItems" - }, + "Data grouped by category with totals using Vega **aggregate** transform:", "", - "**Inspector output (raw mode for copy/paste):**", + "**Inspector output:**", { "type": "inspector", - "variableId": "selectedItems", - "label": "selectedItems (raw)", - "raw": true + "variableId": "aggregatedByCategory", + "label": "aggregatedByCategory" }, "", "---", "", - "## Nested Array Variable", - "This is a 2D array (array of arrays):", + "## Pivoted Data", + "", + "Sales pivoted by product and region using Vega **pivot** transform:", "", "**Inspector output:**", { "type": "inspector", - "variableId": "nestedArray", - "label": "nestedArray" + "variableId": "pivotedData", + "label": "pivotedData" }, "", "---", "", - "## Object Variable", - "This is a complex object with nested properties:", + "## Nested by Category", + "", + "Data nested by category using Vega **nest** transform:", "", "**Inspector output:**", { "type": "inspector", - "variableId": "config", - "label": "config" + "variableId": "nestedByCategory", + "label": "nestedByCategory" }, "", "---", "", - "## Calculated Variable", - "This variable is calculated from the temperature (temperature × 2):", + "## Raw Source Data", + "", + "Original sales data (raw mode for copy/paste):", "", "**Inspector output:**", { "type": "inspector", - "variableId": "doubledTemp", - "label": "doubledTemp (calculated)" + "variableId": "salesData", + "label": "salesData (raw)", + "raw": true }, "", "---", @@ -184,10 +199,10 @@ "## Use Cases", "", "The inspector is particularly useful for:", - "- **Debugging**: See exactly what values variables have at any moment", - "- **Development**: Verify that calculations and data transformations work correctly", - "- **Documentation**: Show users the current state of the system", - "- **Learning**: Understand how variables change when interacting with controls" + "- **Debugging transforms**: See exactly how filter, aggregate, pivot, and nest operations transform your data", + "- **Verifying calculations**: Ensure data transformations produce expected results", + "- **Understanding data flow**: Trace how data changes through the pipeline", + "- **Development**: Build and test complex data transformations interactively" ] } ] From 52260db67169a38fb731e7c7c005e28277d31530 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 18:45:14 +0000 Subject: [PATCH 13/28] Fix validation error: change dataSources to dataLoaders Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/web-deploy/json/features/12.inspector.idoc.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 226cfb64..9b11cf72 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -1,11 +1,12 @@ { "$schema": "../../../../docs/schema/idoc_v1.json", "title": "Variable Inspector Demo with Data Transforms", - "dataSources": [ + "dataLoaders": [ { "dataSourceName": "salesData", + "type": "inline", "format": "json", - "data": [ + "content": [ {"category": "Electronics", "product": "Laptop", "region": "North", "sales": 1200, "quantity": 3}, {"category": "Electronics", "product": "Phone", "region": "North", "sales": 800, "quantity": 5}, {"category": "Electronics", "product": "Laptop", "region": "South", "sales": 1500, "quantity": 4}, From 93c374e1434140f744be57fd3e26350580cd11ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 19:14:59 +0000 Subject: [PATCH 14/28] Add wildcard (*) feature to inspect all variables via signalDeps Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 23 +++++++++++++++++-- .../json/features/12.inspector.idoc.json | 13 +++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 5337e5f7..87131750 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -49,7 +49,11 @@ export const inspectorPlugin: Plugin = { const instances = inspectorInstances.map((inspectorInstance): IInstance => { const { element, spec } = inspectorInstance; - const initialSignals = [{ + + // Special case: if variableId is "*", inspect all variables from signalDeps + const isInspectAll = spec.variableId === '*'; + + const initialSignals = isInspectAll ? [] : [{ name: spec.variableId, value: null, priority: -1, @@ -151,13 +155,28 @@ export const inspectorPlugin: Plugin = { ...inspectorInstance, initialSignals, receiveBatch: async (batch) => { - if (batch[spec.variableId]) { + if (isInspectAll) { + // Extract all variable values from signalDeps + const allVars: { [key: string]: unknown } = {}; + for (const signalName in signalBus.signalDeps) { + allVars[signalName] = signalBus.signalDeps[signalName].value; + } + updateDisplay(allVars); + } else if (batch[spec.variableId]) { const value = batch[spec.variableId].value; updateDisplay(value); } }, beginListening() { // Inspector is read-only, no event listeners needed + // For inspect-all mode, do initial display + if (isInspectAll) { + const allVars: { [key: string]: unknown } = {}; + for (const signalName in signalBus.signalDeps) { + allVars[signalName] = signalBus.signalDeps[signalName].value; + } + updateDisplay(allVars); + } }, getCurrentSignalValue: () => { // Inspector doesn't modify the signal, return undefined diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 9b11cf72..5db6fe1c 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -197,6 +197,19 @@ "", "---", "", + "## All Variables (Debug Inspector)", + "", + "Using `variableId: \"*\"` to inspect all variables in the document:", + "", + "**Inspector output:**", + { + "type": "inspector", + "variableId": "*", + "label": "All Variables (signalDeps)" + }, + "", + "---", + "", "## Use Cases", "", "The inspector is particularly useful for:", From 02ddee233662773780f14b470b452529eaeac1f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:09:41 +0000 Subject: [PATCH 15/28] Change wildcard from * to optional variableId for all variables inspection Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/compiler/src/md.ts | 11 +++++++---- packages/compiler/src/validate/element.ts | 5 ++++- packages/markdown/src/plugins/inspector.ts | 13 +++++++------ packages/schema-doc/src/interactive.ts | 4 +++- .../web-deploy/json/features/12.inspector.idoc.json | 1 - 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/compiler/src/md.ts b/packages/compiler/src/md.ts index ab5faf9a..c7c2d1a4 100644 --- a/packages/compiler/src/md.ts +++ b/packages/compiler/src/md.ts @@ -283,10 +283,13 @@ function groupMarkdown(group: ElementGroup, variables: Variable[], vegaScope: Ve } case 'inspector': { const { variableId, label, raw } = element; - const inspectorSpec: Plugins.InspectorSpec = { - variableId, - label, - }; + const inspectorSpec: Plugins.InspectorSpec = {} as any; + if (variableId) { + inspectorSpec.variableId = variableId; + } + if (label) { + inspectorSpec.label = label; + } if (raw) { inspectorSpec.raw = raw; } diff --git a/packages/compiler/src/validate/element.ts b/packages/compiler/src/validate/element.ts index 71cdf9da..3bdb15f9 100644 --- a/packages/compiler/src/validate/element.ts +++ b/packages/compiler/src/validate/element.ts @@ -95,7 +95,10 @@ export async function validateElement(element: PageElement, groupIndex: number, break; } case 'inspector': { - errors.push(...validateInputElementWithVariableId(element)); + // Inspector has optional variableId (if omitted, inspects all variables) + if (element.variableId) { + errors.push(...validateInputElementWithVariableId(element)); + } break; } case 'mermaid': { diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 87131750..54a88cce 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. */ -import { VariableControl } from '@microsoft/chartifact-schema'; import { Batch, IInstance, Plugin } from '../factory.js'; import { pluginClassName } from './util.js'; import { flaggablePlugin } from './config.js'; @@ -15,7 +14,9 @@ interface InspectorInstance { element: HTMLElement; } -export interface InspectorSpec extends VariableControl { +export interface InspectorSpec { + variableId?: string; + label?: string; raw?: boolean; } @@ -37,8 +38,8 @@ export const inspectorPlugin: Plugin = { const spec: InspectorSpec = specReview.approvedSpec; const html = `
-
${spec.label || spec.variableId}
-
+
${spec.label || spec.variableId || 'All Variables'}
+
`; container.innerHTML = html; const element = container.querySelector('.inspector-value') as HTMLElement; @@ -50,8 +51,8 @@ export const inspectorPlugin: Plugin = { const instances = inspectorInstances.map((inspectorInstance): IInstance => { const { element, spec } = inspectorInstance; - // Special case: if variableId is "*", inspect all variables from signalDeps - const isInspectAll = spec.variableId === '*'; + // Special case: if variableId is undefined/omitted, inspect all variables from signalDeps + const isInspectAll = !spec.variableId; const initialSignals = isInspectAll ? [] : [{ name: spec.variableId, diff --git a/packages/schema-doc/src/interactive.ts b/packages/schema-doc/src/interactive.ts index 483439b0..e3c9797c 100644 --- a/packages/schema-doc/src/interactive.ts +++ b/packages/schema-doc/src/interactive.ts @@ -144,9 +144,11 @@ export interface ImageElementProps { export interface InspectorElement extends InspectorElementProps { type: 'inspector'; } -export interface InspectorElementProps extends VariableControl { +export interface InspectorElementProps extends OptionalVariableControl { /** When true, displays raw JSON output without interactive elements (for copy/paste). Default is false. */ raw?: boolean; + /** optional label if the variableId is not descriptive enough */ + label?: string; } /** diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 5db6fe1c..eaddb677 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -204,7 +204,6 @@ "**Inspector output:**", { "type": "inspector", - "variableId": "*", "label": "All Variables (signalDeps)" }, "", From 0fb5aa4d4dbcab09dc4b6edad53d18242b57d47d Mon Sep 17 00:00:00 2001 From: Dan Marshall Date: Tue, 21 Oct 2025 13:23:15 -0700 Subject: [PATCH 16/28] Refactor InspectorSpec to extend InspectorElementProps and remove unnecessary properties --- packages/markdown/src/plugins/inspector.ts | 10 ++++------ packages/schema-doc/src/interactive.ts | 2 -- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 54a88cce..37734d3d 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. */ -import { Batch, IInstance, Plugin } from '../factory.js'; +import { IInstance, Plugin } from '../factory.js'; import { pluginClassName } from './util.js'; import { flaggablePlugin } from './config.js'; import { PluginNames } from './interfaces.js'; +import { InspectorElementProps } from '@microsoft/chartifact-schema'; interface InspectorInstance { id: string; @@ -14,10 +15,7 @@ interface InspectorInstance { element: HTMLElement; } -export interface InspectorSpec { - variableId?: string; - label?: string; - raw?: boolean; +export interface InspectorSpec extends InspectorElementProps { } const pluginName: PluginNames = 'inspector'; @@ -38,7 +36,7 @@ export const inspectorPlugin: Plugin = { const spec: InspectorSpec = specReview.approvedSpec; const html = `
-
${spec.label || spec.variableId || 'All Variables'}
+
${spec.variableId || 'All Variables'}
`; container.innerHTML = html; diff --git a/packages/schema-doc/src/interactive.ts b/packages/schema-doc/src/interactive.ts index e3c9797c..43e09c79 100644 --- a/packages/schema-doc/src/interactive.ts +++ b/packages/schema-doc/src/interactive.ts @@ -147,8 +147,6 @@ export interface InspectorElement extends InspectorElementProps { export interface InspectorElementProps extends OptionalVariableControl { /** When true, displays raw JSON output without interactive elements (for copy/paste). Default is false. */ raw?: boolean; - /** optional label if the variableId is not descriptive enough */ - label?: string; } /** From b9ce8d82634702388371615b8946ef64f3e92733 Mon Sep 17 00:00:00 2001 From: Dan Marshall Date: Tue, 21 Oct 2025 13:53:07 -0700 Subject: [PATCH 17/28] Remove unused label property from inspectorSpec in groupMarkdown function --- packages/compiler/src/md.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/compiler/src/md.ts b/packages/compiler/src/md.ts index c7c2d1a4..5af310e8 100644 --- a/packages/compiler/src/md.ts +++ b/packages/compiler/src/md.ts @@ -282,14 +282,11 @@ function groupMarkdown(group: ElementGroup, variables: Variable[], vegaScope: Ve break; } case 'inspector': { - const { variableId, label, raw } = element; + const { variableId, raw } = element; const inspectorSpec: Plugins.InspectorSpec = {} as any; if (variableId) { inspectorSpec.variableId = variableId; } - if (label) { - inspectorSpec.label = label; - } if (raw) { inspectorSpec.raw = raw; } From 2c94b5379b5c49b4712ac2439ef7f810cc4dc78e Mon Sep 17 00:00:00 2001 From: Dan Marshall Date: Tue, 21 Oct 2025 13:53:31 -0700 Subject: [PATCH 18/28] Fix type assertion for variableId in inspector element validation --- packages/compiler/src/validate/element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler/src/validate/element.ts b/packages/compiler/src/validate/element.ts index 3bdb15f9..31291c45 100644 --- a/packages/compiler/src/validate/element.ts +++ b/packages/compiler/src/validate/element.ts @@ -97,7 +97,7 @@ export async function validateElement(element: PageElement, groupIndex: number, case 'inspector': { // Inspector has optional variableId (if omitted, inspects all variables) if (element.variableId) { - errors.push(...validateInputElementWithVariableId(element)); + errors.push(...validateInputElementWithVariableId(element as { type: string; variableId: string })); } break; } From e155ede775292c6222881b216df10306a91f3b32 Mon Sep 17 00:00:00 2001 From: Dan Marshall Date: Tue, 21 Oct 2025 13:53:56 -0700 Subject: [PATCH 19/28] Remove label properties from inspector outputs for consistency --- .../json/features/12.inspector.idoc.json | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index eaddb677..6ed013ba 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -138,8 +138,7 @@ "**Inspector output:**", { "type": "inspector", - "variableId": "filteredData", - "label": "filteredData" + "variableId": "filteredData" }, "", "---", @@ -151,8 +150,7 @@ "**Inspector output:**", { "type": "inspector", - "variableId": "aggregatedByCategory", - "label": "aggregatedByCategory" + "variableId": "aggregatedByCategory" }, "", "---", @@ -164,8 +162,7 @@ "**Inspector output:**", { "type": "inspector", - "variableId": "pivotedData", - "label": "pivotedData" + "variableId": "pivotedData" }, "", "---", @@ -177,8 +174,7 @@ "**Inspector output:**", { "type": "inspector", - "variableId": "nestedByCategory", - "label": "nestedByCategory" + "variableId": "nestedByCategory" }, "", "---", @@ -191,7 +187,6 @@ { "type": "inspector", "variableId": "salesData", - "label": "salesData (raw)", "raw": true }, "", @@ -203,8 +198,7 @@ "", "**Inspector output:**", { - "type": "inspector", - "label": "All Variables (signalDeps)" + "type": "inspector" }, "", "---", From 3cb626ea3b6352ad022412ab6e303294919a694c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 21:06:29 +0000 Subject: [PATCH 20/28] Remove label, add duplicate validation, and add wildcard (*) for all-variables auto-update Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- docs/schema/idoc_v1.d.ts | 4 +++- packages/compiler/src/validate/document.ts | 18 ++++++++++++++++++ packages/markdown/src/plugins/inspector.ts | 8 ++++++-- packages/markdown/src/signalbus.ts | 2 +- packages/schema-doc/src/interactive.ts | 5 ++++- .../json/features/12.inspector.idoc.json | 6 ------ 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/schema/idoc_v1.d.ts b/docs/schema/idoc_v1.d.ts index 0964a77b..6ace9bce 100644 --- a/docs/schema/idoc_v1.d.ts +++ b/docs/schema/idoc_v1.d.ts @@ -214,7 +214,9 @@ interface ImageElementProps { interface InspectorElement extends InspectorElementProps { type: 'inspector'; } -interface InspectorElementProps extends VariableControl { +interface InspectorElementProps { + /** Optional variable ID. If omitted, inspects all variables from signalBus.signalDeps */ + variableId?: VariableID; /** When true, displays raw JSON output without interactive elements (for copy/paste). Default is false. */ raw?: boolean; } diff --git a/packages/compiler/src/validate/document.ts b/packages/compiler/src/validate/document.ts index 071d49c5..f1744728 100644 --- a/packages/compiler/src/validate/document.ts +++ b/packages/compiler/src/validate/document.ts @@ -14,6 +14,24 @@ export async function validateDocument(page: InteractiveDocument) { const variables = page.variables || []; const tabulatorElements = page.groups.flatMap(group => group.elements.filter(e => typeof e !== 'string' && e.type === 'tabulator')); + // Check for duplicate dataLoader names + const dataLoaderNames = new Set(); + for (const dataLoader of dataLoaders) { + if (dataLoaderNames.has(dataLoader.dataSourceName)) { + errors.push(`Duplicate dataLoader name: '${dataLoader.dataSourceName}' - each dataLoader must have a unique dataSourceName`); + } + dataLoaderNames.add(dataLoader.dataSourceName); + } + + // Check for duplicate variable IDs + const variableIds = new Set(); + for (const variable of variables) { + if (variableIds.has(variable.variableId)) { + errors.push(`Duplicate variable ID: '${variable.variableId}' - each variable must have a unique variableId`); + } + variableIds.add(variable.variableId); + } + for (const dataLoader of dataLoaders) { const otherDataLoaders = dataLoaders.filter(dl => dl !== dataLoader); errors.push(...await validateDataLoader(dataLoader, variables, tabulatorElements, otherDataLoaders)); diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 37734d3d..31715fd4 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -36,7 +36,6 @@ export const inspectorPlugin: Plugin = { const spec: InspectorSpec = specReview.approvedSpec; const html = `
-
${spec.variableId || 'All Variables'}
`; container.innerHTML = html; @@ -52,7 +51,12 @@ export const inspectorPlugin: Plugin = { // Special case: if variableId is undefined/omitted, inspect all variables from signalDeps const isInspectAll = !spec.variableId; - const initialSignals = isInspectAll ? [] : [{ + const initialSignals = isInspectAll ? [{ + name: '*', + value: null, + priority: -1, + isData: false, + }] : [{ name: spec.variableId, value: null, priority: -1, diff --git a/packages/markdown/src/signalbus.ts b/packages/markdown/src/signalbus.ts index ec7faf9a..9c93a5ef 100644 --- a/packages/markdown/src/signalbus.ts +++ b/packages/markdown/src/signalbus.ts @@ -71,7 +71,7 @@ export class SignalBus { let hasBatch = false; for (const signalName in batch) { if ( - peer.initialSignals.some(s => s.name === signalName) + peer.initialSignals.some(s => s.name === signalName || s.name === '*') && ( (batch[signalName].value !== this.signalDeps[signalName].value) || diff --git a/packages/schema-doc/src/interactive.ts b/packages/schema-doc/src/interactive.ts index 43e09c79..3b9de261 100644 --- a/packages/schema-doc/src/interactive.ts +++ b/packages/schema-doc/src/interactive.ts @@ -144,7 +144,10 @@ export interface ImageElementProps { export interface InspectorElement extends InspectorElementProps { type: 'inspector'; } -export interface InspectorElementProps extends OptionalVariableControl { +export interface InspectorElementProps { + /** Optional variable ID. If omitted, inspects all variables from signalBus.signalDeps */ + variableId?: VariableID; + /** When true, displays raw JSON output without interactive elements (for copy/paste). Default is false. */ raw?: boolean; } diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 6ed013ba..9d5132c5 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -27,12 +27,6 @@ "type": "number", "initialValue": 500 }, - { - "variableId": "salesData", - "type": "object", - "isArray": true, - "initialValue": [] - }, { "variableId": "filteredData", "type": "object", From 1d2ee64d749fb8c16dca16685d3c7a47b4f775ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 21:59:07 +0000 Subject: [PATCH 21/28] Remove incorrect duplicate validation from document.ts and fix demo Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/compiler/src/validate/document.ts | 18 ------------------ .../json/features/12.inspector.idoc.json | 15 +-------------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/packages/compiler/src/validate/document.ts b/packages/compiler/src/validate/document.ts index f1744728..071d49c5 100644 --- a/packages/compiler/src/validate/document.ts +++ b/packages/compiler/src/validate/document.ts @@ -14,24 +14,6 @@ export async function validateDocument(page: InteractiveDocument) { const variables = page.variables || []; const tabulatorElements = page.groups.flatMap(group => group.elements.filter(e => typeof e !== 'string' && e.type === 'tabulator')); - // Check for duplicate dataLoader names - const dataLoaderNames = new Set(); - for (const dataLoader of dataLoaders) { - if (dataLoaderNames.has(dataLoader.dataSourceName)) { - errors.push(`Duplicate dataLoader name: '${dataLoader.dataSourceName}' - each dataLoader must have a unique dataSourceName`); - } - dataLoaderNames.add(dataLoader.dataSourceName); - } - - // Check for duplicate variable IDs - const variableIds = new Set(); - for (const variable of variables) { - if (variableIds.has(variable.variableId)) { - errors.push(`Duplicate variable ID: '${variable.variableId}' - each variable must have a unique variableId`); - } - variableIds.add(variable.variableId); - } - for (const dataLoader of dataLoaders) { const otherDataLoaders = dataLoaders.filter(dl => dl !== dataLoader); errors.push(...await validateDataLoader(dataLoader, variables, tabulatorElements, otherDataLoaders)); diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index 9d5132c5..c06f5048 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -173,22 +173,9 @@ "", "---", "", - "## Raw Source Data", - "", - "Original sales data (raw mode for copy/paste):", - "", - "**Inspector output:**", - { - "type": "inspector", - "variableId": "salesData", - "raw": true - }, - "", - "---", - "", "## All Variables (Debug Inspector)", "", - "Using `variableId: \"*\"` to inspect all variables in the document:", + "Omitting `variableId` to inspect all variables in the document:", "", "**Inspector output:**", { From 5ec7ad81b111e6e84a2c7c568372a2714e2f3284 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 22:02:45 +0000 Subject: [PATCH 22/28] Refactor inspector plugin: simplify initialSignals, extract repeated logic, remove unused code Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 39 ++++++++-------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 31715fd4..dd5951b2 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -51,19 +51,14 @@ export const inspectorPlugin: Plugin = { // Special case: if variableId is undefined/omitted, inspect all variables from signalDeps const isInspectAll = !spec.variableId; - const initialSignals = isInspectAll ? [{ - name: '*', - value: null, - priority: -1, - isData: false, - }] : [{ - name: spec.variableId, + const initialSignals = [{ + name: isInspectAll ? '*' : spec.variableId, value: null, priority: -1, isData: false, }]; - const updateDisplay = (value: unknown) => { + const displayValue = (value: unknown) => { element.innerHTML = ''; // Clear previous content // If raw mode is enabled, always use JSON.stringify without interactivity @@ -154,37 +149,31 @@ export const inspectorPlugin: Plugin = { container.appendChild(arrayWrapper); }; + const getAllVariables = () => { + const allVars: { [key: string]: unknown } = {}; + for (const signalName in signalBus.signalDeps) { + allVars[signalName] = signalBus.signalDeps[signalName].value; + } + return allVars; + }; + return { ...inspectorInstance, initialSignals, receiveBatch: async (batch) => { if (isInspectAll) { - // Extract all variable values from signalDeps - const allVars: { [key: string]: unknown } = {}; - for (const signalName in signalBus.signalDeps) { - allVars[signalName] = signalBus.signalDeps[signalName].value; - } - updateDisplay(allVars); + displayValue(getAllVariables()); } else if (batch[spec.variableId]) { - const value = batch[spec.variableId].value; - updateDisplay(value); + displayValue(batch[spec.variableId].value); } }, beginListening() { // Inspector is read-only, no event listeners needed // For inspect-all mode, do initial display if (isInspectAll) { - const allVars: { [key: string]: unknown } = {}; - for (const signalName in signalBus.signalDeps) { - allVars[signalName] = signalBus.signalDeps[signalName].value; - } - updateDisplay(allVars); + displayValue(getAllVariables()); } }, - getCurrentSignalValue: () => { - // Inspector doesn't modify the signal, return undefined - return undefined; - }, destroy: () => { // No cleanup needed }, From f8af03b1e9e794e832ed86eeaa29a5bbcfa3a2b0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 22:09:32 +0000 Subject: [PATCH 23/28] Extract renderValue helper to eliminate duplicate code for rendering array items Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index dd5951b2..e2a38f93 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -109,16 +109,7 @@ export const inspectorPlugin: Plugin = { content.className = 'inspector-array-content'; content.style.paddingLeft = '1.5em'; - arr.forEach((item, index) => { - const itemDiv = document.createElement('div'); - itemDiv.className = 'inspector-array-item'; - - const indexLabel = document.createElement('span'); - indexLabel.textContent = `[${index}]: `; - itemDiv.appendChild(indexLabel); - - const valueSpan = document.createElement('span'); - + const renderValue = (valueSpan: HTMLSpanElement, item: unknown, depth: number) => { if (Array.isArray(item)) { // Nested array renderArray(valueSpan, item, depth + 1); @@ -128,6 +119,18 @@ export const inspectorPlugin: Plugin = { } else { valueSpan.textContent = JSON.stringify(item); } + }; + + arr.forEach((item, index) => { + const itemDiv = document.createElement('div'); + itemDiv.className = 'inspector-array-item'; + + const indexLabel = document.createElement('span'); + indexLabel.textContent = `[${index}]: `; + itemDiv.appendChild(indexLabel); + + const valueSpan = document.createElement('span'); + renderValue(valueSpan, item, depth); itemDiv.appendChild(valueSpan); content.appendChild(itemDiv); From 9744af08cec3701c72dc856447ac738640d603aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 22:16:41 +0000 Subject: [PATCH 24/28] Move renderValue outside renderArray and use it in displayValue to eliminate all duplication Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 36 +++++++++------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index e2a38f93..60fe36da 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -58,6 +58,18 @@ export const inspectorPlugin: Plugin = { isData: false, }]; + const renderValue = (container: HTMLElement, value: unknown, depth: number = 0) => { + if (Array.isArray(value)) { + // Nested array + renderArray(container, value, depth); + } else if (typeof value === 'object') { + container.textContent = JSON.stringify(value, null, 2); + container.style.whiteSpace = 'pre'; + } else { + container.textContent = JSON.stringify(value); + } + }; + const displayValue = (value: unknown) => { element.innerHTML = ''; // Clear previous content @@ -68,15 +80,7 @@ export const inspectorPlugin: Plugin = { } // Interactive mode (default) - if (Array.isArray(value)) { - // Create interactive collapsible array display - renderArray(element, value); - } else if (typeof value === 'object') { - // For objects, use JSON.stringify with indentation - element.textContent = JSON.stringify(value, null, 2); - } else { - element.textContent = JSON.stringify(value); - } + renderValue(element, value); }; const renderArray = (container: HTMLElement, arr: unknown[], depth: number = 0) => { @@ -108,18 +112,6 @@ export const inspectorPlugin: Plugin = { const content = document.createElement('div'); content.className = 'inspector-array-content'; content.style.paddingLeft = '1.5em'; - - const renderValue = (valueSpan: HTMLSpanElement, item: unknown, depth: number) => { - if (Array.isArray(item)) { - // Nested array - renderArray(valueSpan, item, depth + 1); - } else if (typeof item === 'object') { - valueSpan.textContent = JSON.stringify(item, null, 2); - valueSpan.style.whiteSpace = 'pre'; - } else { - valueSpan.textContent = JSON.stringify(item); - } - }; arr.forEach((item, index) => { const itemDiv = document.createElement('div'); @@ -130,7 +122,7 @@ export const inspectorPlugin: Plugin = { itemDiv.appendChild(indexLabel); const valueSpan = document.createElement('span'); - renderValue(valueSpan, item, depth); + renderValue(valueSpan, item, depth + 1); itemDiv.appendChild(valueSpan); content.appendChild(itemDiv); From 1c4a5b19fb026b567ed906cae1f9e8257b7c5a5d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 22:17:53 +0000 Subject: [PATCH 25/28] Remove displayValue wrapper and use renderValue directly everywhere Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/markdown/src/plugins/inspector.ts | 32 ++++++++++------------ 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index 60fe36da..e89f0067 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -59,28 +59,26 @@ export const inspectorPlugin: Plugin = { }]; const renderValue = (container: HTMLElement, value: unknown, depth: number = 0) => { - if (Array.isArray(value)) { - // Nested array - renderArray(container, value, depth); - } else if (typeof value === 'object') { - container.textContent = JSON.stringify(value, null, 2); - container.style.whiteSpace = 'pre'; - } else { - container.textContent = JSON.stringify(value); + // Clear previous content when rendering at root level + if (depth === 0) { + container.innerHTML = ''; } - }; - - const displayValue = (value: unknown) => { - element.innerHTML = ''; // Clear previous content // If raw mode is enabled, always use JSON.stringify without interactivity if (spec.raw) { - element.textContent = JSON.stringify(value, null, 2); + container.textContent = JSON.stringify(value, null, 2); return; } // Interactive mode (default) - renderValue(element, value); + if (Array.isArray(value)) { + renderArray(container, value, depth); + } else if (typeof value === 'object') { + container.textContent = JSON.stringify(value, null, 2); + container.style.whiteSpace = 'pre'; + } else { + container.textContent = JSON.stringify(value); + } }; const renderArray = (container: HTMLElement, arr: unknown[], depth: number = 0) => { @@ -157,16 +155,16 @@ export const inspectorPlugin: Plugin = { initialSignals, receiveBatch: async (batch) => { if (isInspectAll) { - displayValue(getAllVariables()); + renderValue(element, getAllVariables()); } else if (batch[spec.variableId]) { - displayValue(batch[spec.variableId].value); + renderValue(element, batch[spec.variableId].value); } }, beginListening() { // Inspector is read-only, no event listeners needed // For inspect-all mode, do initial display if (isInspectAll) { - displayValue(getAllVariables()); + renderValue(element, getAllVariables()); } }, destroy: () => { From 6a5a55e1330a1e3c01381e385e0cee6e3dd9cd99 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 23:34:39 +0000 Subject: [PATCH 26/28] Fix * in signalDeps, simplify title, and fix nest transform Co-authored-by: danmarshall <11507384+danmarshall@users.noreply.github.com> --- packages/web-deploy/json/features/12.inspector.idoc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index c06f5048..c2adef07 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -1,6 +1,6 @@ { "$schema": "../../../../docs/schema/idoc_v1.json", - "title": "Variable Inspector Demo with Data Transforms", + "title": "Inspector", "dataLoaders": [ { "dataSourceName": "salesData", From 2df6eed18d0e5019759da141197f572b43178d5d Mon Sep 17 00:00:00 2001 From: Dan Marshall Date: Wed, 22 Oct 2025 19:45:07 -0700 Subject: [PATCH 27/28] Remove unused destroy method from inspectorPlugin --- packages/markdown/src/plugins/inspector.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/markdown/src/plugins/inspector.ts b/packages/markdown/src/plugins/inspector.ts index e89f0067..520f9071 100644 --- a/packages/markdown/src/plugins/inspector.ts +++ b/packages/markdown/src/plugins/inspector.ts @@ -167,9 +167,6 @@ export const inspectorPlugin: Plugin = { renderValue(element, getAllVariables()); } }, - destroy: () => { - // No cleanup needed - }, }; }); return instances; From 1b5655056d69c2db89b27e00993f270fe881b3fb Mon Sep 17 00:00:00 2001 From: Dan Marshall Date: Wed, 22 Oct 2025 19:45:17 -0700 Subject: [PATCH 28/28] Rename title --- packages/web-deploy/json/features/12.inspector.idoc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-deploy/json/features/12.inspector.idoc.json b/packages/web-deploy/json/features/12.inspector.idoc.json index c2adef07..abefd9fc 100644 --- a/packages/web-deploy/json/features/12.inspector.idoc.json +++ b/packages/web-deploy/json/features/12.inspector.idoc.json @@ -97,7 +97,7 @@ { "groupId": "main", "elements": [ - "# Variable Inspector with Data Transforms", + "# Variable Inspector", "", "The inspector plugin is particularly useful for examining transformed data. This example demonstrates Vega data transforms like **filter**, **aggregate**, **pivot**, and **nest**.", "",