Skip to content

Commit

Permalink
feat: Inspecting values in the console (#328)
Browse files Browse the repository at this point in the history
* wip: Add inspect action for values

* fix: Fix value rows highlighting styles

* feat: inspecting props

* refactor: update getValueActionInspect to accept Inspector.ValueItem and streamline action handling

* chore: Cleanup

* feat: Support value inspecting for overlay

* Create lucky-rats-care.md
  • Loading branch information
thetarnav authored Jan 1, 2025
1 parent 0bee80d commit ba5b62a
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 133 deletions.
7 changes: 7 additions & 0 deletions .changeset/lucky-rats-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@solid-devtools/extension": minor
"@solid-devtools/debugger": minor
"@solid-devtools/frontend": minor
---

New feature: inspecting values in the console. (Closes #166)
35 changes: 32 additions & 3 deletions extension/src/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

import * as s from 'solid-js'
import * as web from 'solid-js/web'
import {log} from '@solid-devtools/shared/utils'
import {error, log} from '@solid-devtools/shared/utils'
import * as frontend from '@solid-devtools/frontend'
import * as debug from '@solid-devtools/debugger/types'

import {
ConnectionName, Place_Name, port_on_message, port_post_message_obj,
Expand Down Expand Up @@ -62,8 +63,36 @@ function App() {
}
})

/* Devtools -> Client */
devtools.output.listen(e => port_post_message_obj(port, e))
devtools.output.listen(e => {
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
switch (e.name) {
case 'ConsoleInspectValue': {
/*
`chrome.devtools.inspectedWindow.eval` runs in a devtools console
so the value can be additionally inspected with `inspect()`
*/
let get_value = `window[${JSON.stringify(debug.GLOBAL_GET_VALUE)}]`
let value_id = JSON.stringify(e.details)

chrome.devtools.inspectedWindow.eval(
/*js*/`typeof ${get_value} === 'function' && (() => {
let v = ${get_value}(${value_id})
inspect(v)
console.log(v)
})()`,
(_, err?: chrome.devtools.inspectedWindow.EvaluationExceptionInfo) => {
if (err && (err.isError || err.isException)) {
error(err.description)
}
})
break
}
default:
/* Devtools -> Client */
port_post_message_obj(port, e)
break
}
})

return (
<div
Expand Down
22 changes: 18 additions & 4 deletions packages/debugger/src/inspector/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {onOwnerDispose} from '../main/utils.ts'
import {type ObservedPropsMap, ValueNodeMap, clearOwnerObservers, collectOwnerDetails} from './inspector.ts'
import {encodeValue} from './serialize.ts'
import {type StoreNodeProperty, type StoreUpdateData, observeStoreNode, setOnStoreNodeUpdate} from './store.ts'
import {type InspectorUpdate, type InspectorUpdateMap, PropGetterState} from './types.ts'
import {GLOBAL_GET_VALUE, type InspectorUpdate, type InspectorUpdateMap, PropGetterState} from './types.ts'

export * from './types.ts'

Expand All @@ -24,13 +24,22 @@ export function createInspector(props: {
resetInspectedNode: VoidFunction
emit: OutputEmit
}) {

let lastDetails: Mapped.OwnerDetails | undefined
let inspectedOwner: Solid.Owner | null
let valueMap = new ValueNodeMap()
const propsMap: ObservedPropsMap = new WeakMap()
/** compare props object with the previous one to see whats changed */
let checkProxyProps: (() => InspectorUpdateMap['propKeys'] | null) | null

/*
For the extension for inspecting values through `inspect()`
*/
function getValue(id: ValueItemID): unknown {
return valueMap.get(id)?.getValue?.()
}
window[GLOBAL_GET_VALUE] = getValue

// Batch and dedupe inspector updates
// these will include updates to signals, stores, props, and node value
const {pushPropState, pushValueUpdate, pushInspectToggle, triggerPropsCheck, clearUpdates} =
Expand Down Expand Up @@ -156,11 +165,12 @@ export function createInspector(props: {
})

props.emit('InspectedNodeDetails', result.details)
valueMap = result.valueMap
lastDetails = result.details

valueMap = result.valueMap
lastDetails = result.details
checkProxyProps = result.checkProxyProps || null
} else {
lastDetails = undefined
lastDetails = undefined
checkProxyProps = null
}

Expand All @@ -186,5 +196,9 @@ export function createInspector(props: {
node.setSelected(selected)
pushInspectToggle(id, selected)
},
consoleLogValue(value_id: ValueItemID): void {
// eslint-disable-next-line no-console
console.log(getValue(value_id))
}
}
}
11 changes: 11 additions & 0 deletions packages/debugger/src/inspector/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,14 @@ export type InspectorUpdateMap = {
export type InspectorUpdate = {
[T in keyof InspectorUpdateMap]: [type: T, data: InspectorUpdateMap[T]]
}[keyof InspectorUpdateMap]

/*
For the extension for inspecting values through `inspect()`
*/
export const GLOBAL_GET_VALUE = '$SdtGetValue'

declare global {
interface Window {
[GLOBAL_GET_VALUE]?: (id: ValueItemID) => unknown
}
}
6 changes: 5 additions & 1 deletion packages/debugger/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {createStructure, type StructureUpdates} from '../structure/index.ts'
import {DebuggerModule, DEFAULT_MAIN_VIEW, DevtoolsMainView, TreeWalkerMode} from './constants.ts'
import {getObjectById, getSdtId, ObjectType} from './id.ts'
import setup from './setup.ts'
import {type Mapped, type NodeID} from './types.ts'
import {type Mapped, type NodeID, type ValueItemID} from './types.ts'

export type InspectedState = {
readonly ownerId: NodeID | null
Expand All @@ -37,6 +37,7 @@ export type InputChannels = {
ResetState: void
InspectNode: {ownerId: NodeID | null; signalId: NodeID | null} | null
InspectValue: ToggleInspectedValueData
ConsoleInspectValue: ValueItemID
HighlightElementChange: HighlightElementPayload
OpenLocation: void
TreeViewModeChange: TreeWalkerMode
Expand Down Expand Up @@ -280,6 +281,9 @@ function createDebugger() {
case 'InspectValue':
inspector.toggleValueNode(e.details)
break
case 'ConsoleInspectValue':
inspector.consoleLogValue(e.details)
break
case 'OpenLocation':
openInspectedNodeLocation()
break
Expand Down
10 changes: 5 additions & 5 deletions packages/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const App: s.Component<{headerSubtitle?: s.JSX.Element}> = props => {
>
<header class="p-2 flex items-center gap-x-2 bg-panel-bg b-b b-solid b-panel-border text-text">
<div class="flex items-center gap-x-2">
<ui.Icon.SolidWhite class="w-4 h-4 text-disabled" />
<ui.icon.SolidWhite class="w-4 h-4 text-disabled" />
<div>
<h3>Solid Developer Tools</h3>
{props.headerSubtitle && (
Expand Down Expand Up @@ -80,7 +80,7 @@ const Options: s.Component = () => {
}}>
<summary
class={`${ui.toggle_button} rounded-md ml-auto w-7 h-7`}>
<ui.Icon.Options
<ui.icon.Options
class="w-4.5 h-4.5"
/>
</summary>
Expand All @@ -97,7 +97,7 @@ const Options: s.Component = () => {
class='
flex items-center gap-1 p-1 rounded-md outline-none
text-text transition-colors hover:bg-orange-500/10 focus:bg-orange-500/10'>
<ui.Icon.Bug class='w-3 h-3 mb-px text-orange-500 dark:text-orange-400' />
<ui.icon.Bug class='w-3 h-3 mb-px text-orange-500 dark:text-orange-400' />
Report a bug
</a>
<a
Expand All @@ -108,7 +108,7 @@ const Options: s.Component = () => {
class='
flex items-center gap-1 p-1 rounded-md outline-none
text-text transition-colors hover:bg-pink-500/10 focus:bg-pink-500/10'>
<ui.Icon.Heart class='w-3 h-3 mb-px text-pink-500 dark:text-pink-400' />
<ui.icon.Heart class='w-3 h-3 mb-px text-pink-500 dark:text-pink-400' />
Support the project
</a>
</div>
Expand Down Expand Up @@ -141,4 +141,4 @@ const Options: s.Component = () => {
// View: {view.get().toUpperCase()}
// </button>
// )
// }
// }
4 changes: 2 additions & 2 deletions packages/frontend/src/SidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ export function createSidePanel() {
class={action_button}
onClick={openComponentLocation}
>
<ui.Icon.Code class={action_icon} />
<ui.icon.Code class={action_icon} />
</button>
)}
<button
title="Close inspector"
class={action_button}
onClick={() => setInspectedOwner(null)}
>
<ui.Icon.Close class={action_icon} />
<ui.icon.Close class={action_icon} />
</button>
</div>
<ui.ToggleTabs
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export {createDevtools} from './controller.tsx'
export type {OutputEventBus, InputEventBus, DevtoolsOptions as DevtoolsProps} from './controller.tsx'
export {Icon, MountIcons} from './ui/index.ts'
export {icon as Icon, MountIcons} from './ui/index.ts'
export type {IconComponent} from './ui/index.ts'
Loading

0 comments on commit ba5b62a

Please sign in to comment.