-
Notifications
You must be signed in to change notification settings - Fork 19
Add Inspector plugin for examining variable values #124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Copilot
wants to merge
28
commits into
main
Choose a base branch
from
copilot/add-variable-inspector-plugin
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
2ccea5c
Initial plan
Copilot 053a4a6
Add inspector plugin implementation and example
Copilot 8008fbc
Add validation support for inspector element
Copilot 9ca19c8
Add interactive array expand/collapse to inspector plugin
Copilot 855d484
Fix inspector element hydration by changing pre to div
Copilot f2659a9
Add raw option to inspector for copy/paste use case
Copilot 1a01afc
Make arrays start collapsed by default
Copilot 1cfa5e0
Remove all coloring and unnecessary styling from inspector
Copilot 1eaed6d
Refactor raw mode handling in inspector to simplify value display logic
danmarshall 1b84dbb
Add inspector case to markdown compiler
Copilot b8d3593
Add nested array demo and remove CSS styling
Copilot 8ec4793
Add Vega transforms example with filter, aggregate, pivot, and nest
Copilot 52260db
Fix validation error: change dataSources to dataLoaders
Copilot 93c374e
Add wildcard (*) feature to inspect all variables via signalDeps
Copilot 02ddee2
Change wildcard from * to optional variableId for all variables inspe…
Copilot 0fb5aa4
Refactor InspectorSpec to extend InspectorElementProps and remove unn…
danmarshall b9ce8d8
Remove unused label property from inspectorSpec in groupMarkdown func…
danmarshall 2c94b53
Fix type assertion for variableId in inspector element validation
danmarshall e155ede
Remove label properties from inspector outputs for consistency
danmarshall 3cb626e
Remove label, add duplicate validation, and add wildcard (*) for all-…
Copilot 1d2ee64
Remove incorrect duplicate validation from document.ts and fix demo
Copilot 5ec7ad8
Refactor inspector plugin: simplify initialSignals, extract repeated …
Copilot f8af03b
Extract renderValue helper to eliminate duplicate code for rendering …
Copilot 9744af0
Move renderValue outside renderArray and use it in displayValue to el…
Copilot 1c4a5b1
Remove displayValue wrapper and use renderValue directly everywhere
Copilot 6a5a55e
Fix * in signalDeps, simplify title, and fix nest transform
Copilot 2df6eed
Remove unused destroy method from inspectorPlugin
danmarshall 1b56550
Rename title
danmarshall File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| /** | ||
| * Copyright (c) Microsoft Corporation. | ||
| * Licensed under the MIT License. | ||
| */ | ||
|
|
||
| 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; | ||
| spec: InspectorSpec; | ||
| element: HTMLElement; | ||
| } | ||
|
|
||
| export interface InspectorSpec extends InspectorElementProps { | ||
| } | ||
|
|
||
| const pluginName: PluginNames = 'inspector'; | ||
| const className = pluginClassName(pluginName); | ||
|
|
||
| export const inspectorPlugin: Plugin<InspectorSpec> = { | ||
| ...flaggablePlugin<InspectorSpec>(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 = `<div class="inspector"> | ||
| <div class="inspector-value" id="${spec.variableId || 'all'}-value"></div> | ||
| </div>`; | ||
| 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; | ||
|
|
||
| // Special case: if variableId is undefined/omitted, inspect all variables from signalDeps | ||
| const isInspectAll = !spec.variableId; | ||
|
|
||
| const initialSignals = [{ | ||
| name: isInspectAll ? '*' : spec.variableId, | ||
| value: null, | ||
| priority: -1, | ||
| isData: false, | ||
| }]; | ||
|
|
||
| const renderValue = (container: HTMLElement, value: unknown, depth: number = 0) => { | ||
| // Clear previous content when rendering at root level | ||
| if (depth === 0) { | ||
| container.innerHTML = ''; | ||
| } | ||
|
|
||
| // If raw mode is enabled, always use JSON.stringify without interactivity | ||
| if (spec.raw) { | ||
| container.textContent = JSON.stringify(value, null, 2); | ||
| return; | ||
| } | ||
|
|
||
| // Interactive mode (default) | ||
| 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) => { | ||
| 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})`; | ||
|
|
||
| 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}]: `; | ||
| itemDiv.appendChild(indexLabel); | ||
|
|
||
| const valueSpan = document.createElement('span'); | ||
| renderValue(valueSpan, item, depth + 1); | ||
|
|
||
| itemDiv.appendChild(valueSpan); | ||
| content.appendChild(itemDiv); | ||
| }); | ||
|
|
||
| // Toggle functionality - start collapsed | ||
| let isExpanded = false; | ||
| content.style.display = 'none'; | ||
| 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); | ||
| }; | ||
|
|
||
| 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) { | ||
| renderValue(element, getAllVariables()); | ||
| } else if (batch[spec.variableId]) { | ||
| renderValue(element, batch[spec.variableId].value); | ||
| } | ||
| }, | ||
| beginListening() { | ||
| // Inspector is read-only, no event listeners needed | ||
| // For inspect-all mode, do initial display | ||
| if (isInspectAll) { | ||
| renderValue(element, getAllVariables()); | ||
| } | ||
| }, | ||
danmarshall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
| }); | ||
| return instances; | ||
| }, | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.