Skip to content

Commit

Permalink
Simplify createDevtools interface
Browse files Browse the repository at this point in the history
  • Loading branch information
thetarnav committed Dec 31, 2024
1 parent cd277a6 commit eef06c4
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 146 deletions.
7 changes: 7 additions & 0 deletions .changeset/dull-papayas-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@solid-devtools/frontend": minor
"@solid-devtools/overlay": patch
"@solid-devtools/extension": patch
---

Simplify createDevtools interface
39 changes: 21 additions & 18 deletions extension/src/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,24 @@ function App() {
}
const [versions, setVersions] = s.createSignal<Versions>(empty_versions)

const devtools = frontend.createDevtools()
const devtools = frontend.createDevtools({
headerSubtitle() {
let {extension, client, client_expected} = versions()
return `#${extension}_${client}/${client_expected}`
},
errorOverlayFooter() {
return <>
<ul>
<li>Solid: {versions().solid}</li>
<li>Extension: {versions().extension}</li>
<li>Client: {versions().client}</li>
<li>Expected client: {versions().client_expected}</li>
</ul>
</>
},
useShortcuts: true,
catchWindowErrors: true,
})

const port = chrome.runtime.connect({name: ConnectionName.Panel})
port_on_message(port, e => {
Expand All @@ -38,15 +55,15 @@ function App() {
break
default:
/* Client -> Devtools */
devtools.bridge.input.emit(
devtools.input.emit(
// @ts-expect-error
e
)
}
})

/* Devtools -> Client */
devtools.bridge.output.listen(e => port_post_message_obj(port, e))
devtools.output.listen(e => port_post_message_obj(port, e))

return (
<div
Expand All @@ -57,21 +74,7 @@ function App() {
inset: '0',
}}
>
<devtools.Devtools
headerSubtitle={`#${versions().extension}_${versions().client}/${
versions().client_expected
}`}
errorOverlayFooter={
<ul>
<li>Solid: {versions().solid}</li>
<li>Extension: {versions().extension}</li>
<li>Client: {versions().client}</li>
<li>Expected client: {versions().client_expected}</li>
</ul>
}
useShortcuts
catchWindowErrors
/>
<devtools.Devtools />
<frontend.MountIcons />
</div>
)
Expand Down
131 changes: 65 additions & 66 deletions packages/frontend/src/controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,58 @@ import createInspector from './inspector.tsx'
import {type Structure} from './structure.tsx'
import * as ui from './ui/index.ts'

export type Input_Message = {

export type InputMessage = {
[K in keyof Debugger.OutputChannels]: {
name: K,
details: Debugger.OutputChannels[K],
}
}[keyof Debugger.OutputChannels]
export type Input_Listener = (e: Input_Message) => void
export type InputListener = (e: InputMessage) => void

export type InputEventBus = {
emit: (e: InputMessage) => void,
listen: (fn: InputListener) => void,
}

export type Output_Message = {
export type OutputMessage = {
[K in keyof Debugger.InputChannels]: {
name: K,
details: Debugger.InputChannels[K],
}
}[keyof Debugger.InputChannels]
export type Output_Listener = (e: Output_Message) => void
export type OutputListener = (e: OutputMessage) => void

export type OutputEventBus = {
emit: (e: OutputMessage) => void,
listen: (fn: OutputListener) => void,
}


/**
* devtools options provided to {@link Devtools} component
* with their default values
*/
export type DevtoolsOptionsWithDefaults = {
errorOverlayFooter: () => s.JSX.Element
headerSubtitle: () => s.JSX.Element
useShortcuts: boolean
catchWindowErrors: boolean
}

function createDebuggerBridge() {

let output_listeners: Output_Listener[] = []
const output = {
listen(listener: Output_Listener) {
export type DevtoolsOptions = Partial<DevtoolsOptionsWithDefaults>

export function createDevtools(props: DevtoolsOptions) {

let output_listeners: OutputListener[] = []
const output: OutputEventBus = {
listen(listener) {
output_listeners.push(listener)
s.onCleanup(() => {
mutate_remove(output_listeners, listener)
})
},
emit(e: Output_Message) {
emit(e) {
s.batch(() => {
for (let fn of output_listeners) {
fn(e)
Expand All @@ -45,15 +70,15 @@ function createDebuggerBridge() {
},
}

let input_listeners: Input_Listener[] = []
const input = {
listen(listener: Input_Listener) {
let input_listeners: InputListener[] = []
const input: InputEventBus = {
listen(listener) {
input_listeners.push(listener)
s.onCleanup(() => {
mutate_remove(input_listeners, listener)
})
},
emit(e: Input_Message) {
emit(e) {
s.batch(() => {
for (let fn of input_listeners) {
fn(e)
Expand All @@ -62,60 +87,29 @@ function createDebuggerBridge() {
},
}

return {input, output}
}

export type DebuggerBridge = ReturnType<typeof createDebuggerBridge>

export type DevtoolsProps = {
errorOverlayFooter?: s.JSX.Element
headerSubtitle?: s.JSX.Element
useShortcuts?: boolean
catchWindowErrors?: boolean
}

/**
* devtools options provided to {@link Devtools} component
* with their default values
*/
export type DevtoolsOptions = {
useShortcuts: boolean
}

const DevtoolsOptionsCtx = s.createContext<DevtoolsOptions>(
'DevtoolsOptionsCtx' as any as DevtoolsOptions,
)

export const useDevtoolsOptions = () => s.useContext(DevtoolsOptionsCtx)

export function devtoolsPropsToOptions(props: DevtoolsProps): DevtoolsOptions {
return {
useShortcuts: props.useShortcuts ?? false,
let options: DevtoolsOptionsWithDefaults = {
errorOverlayFooter: props.errorOverlayFooter ?? (() => null),
headerSubtitle: props.headerSubtitle ?? (() => null),
useShortcuts: props.useShortcuts ?? false,
catchWindowErrors: props.catchWindowErrors ?? false,
}
}

export function createDevtools() {
const bridge = createDebuggerBridge()
const controller = createController(output, input, options)

return {
bridge,
Devtools(props: DevtoolsProps) {
const options = devtoolsPropsToOptions(props)

const controller = createController(bridge, options)

output,
input,
Devtools() {
return (
<div class={ui.devtools_root_class + ' h-inherit'}>
<ui.Styles />
<ui.ErrorOverlay
footer={props.errorOverlayFooter}
catchWindowErrors={props.catchWindowErrors}
footer={options.errorOverlayFooter()}
catchWindowErrors={options.catchWindowErrors}
>
<DevtoolsOptionsCtx.Provider value={options}>
<ControllerCtx.Provider value={controller}>
<App headerSubtitle={props.headerSubtitle} />
</ControllerCtx.Provider>
</DevtoolsOptionsCtx.Provider>
<ControllerCtx.Provider value={controller}>
<App headerSubtitle={options.headerSubtitle()} />
</ControllerCtx.Provider>
</ui.ErrorOverlay>
</div>
)
Expand Down Expand Up @@ -163,7 +157,11 @@ function createViewCache() {
return {set: setCacheGetter, get: getCache}
}

function createController(bridge: DebuggerBridge, options: DevtoolsOptions) {
function createController(
output: OutputEventBus,
input: InputEventBus,
options: DevtoolsOptions,
) {
//
// LOCATOR
//
Expand All @@ -173,7 +171,7 @@ function createController(bridge: DebuggerBridge, options: DevtoolsOptions) {

// send devtools locator state
s.createEffect(defer(devtoolsLocatorEnabled, enabled => {
bridge.output.emit({
output.emit({
name: 'ToggleModule',
details: {module: DebuggerModule.Locator, enabled}
})
Expand All @@ -197,7 +195,7 @@ function createController(bridge: DebuggerBridge, options: DevtoolsOptions) {

// highlight hovered element
s.createEffect(defer(extHoveredNode, node => {
bridge.output.emit({
output.emit({
name: 'HighlightElementChange',
details: node,
})
Expand Down Expand Up @@ -231,7 +229,7 @@ function createController(bridge: DebuggerBridge, options: DevtoolsOptions) {
}

s.createEffect(defer(openedView, view => {
bridge.output.emit({name: 'ViewChange', details: view})
output.emit({name: 'ViewChange', details: view})
}))

//
Expand All @@ -242,12 +240,12 @@ function createController(bridge: DebuggerBridge, options: DevtoolsOptions) {
//
// INSPECTOR
//
const inspector = createInspector({bridge})
const inspector = createInspector(output, input)

//
// Client events
//
bridge.input.listen(e => {
input.listen(e => {
switch (e.name) {
case 'NodeUpdates':
for (let id of e.details) {
Expand Down Expand Up @@ -301,7 +299,8 @@ function createController(bridge: DebuggerBridge, options: DevtoolsOptions) {
},
inspector,
options,
bridge,
output,
input,
viewCache,
listenToNodeUpdate(id: NodeID, fn: VoidFunction) {
return nodeUpdates.listen(updatedId => updatedId === id && fn())
Expand Down
12 changes: 6 additions & 6 deletions packages/frontend/src/dgraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import {useController} from './controller.tsx'
import * as ui from './ui/index.ts'

export function createDependencyGraph() {
const {bridge, inspector} = useController()
const ctx = useController()

const [graph, setGraph] = s.createSignal<debug.DGraphUpdate>(null)

bridge.input.listen(e => {
ctx.input.listen(e => {
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
switch (e.name) {
case 'DgraphUpdate':
Expand All @@ -20,12 +20,12 @@ export function createDependencyGraph() {
}
})

bridge.output.emit({
ctx.output.emit({
name: 'ToggleModule',
details: {module: debug.DebuggerModule.Dgraph, enabled: true},
})
s.onCleanup(() => {
bridge.output.emit({
ctx.output.emit({
name: 'ToggleModule',
details: {module: debug.DebuggerModule.Dgraph, enabled: false},
})
Expand All @@ -37,9 +37,9 @@ export function createDependencyGraph() {
if (!node) return console.warn('inspectNode: node not found', id)

if (node.type === debug.NodeType.Signal) {
inspector.setInspectedNode(node.graph ?? null, id)
ctx.inspector.setInspectedNode(node.graph ?? null, id)
} else {
inspector.setInspectedOwner(id)
ctx.inspector.setInspectedOwner(id)
}
}

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 {DebuggerBridge, DevtoolsProps} from './controller.tsx'
export type {OutputEventBus, InputEventBus, DevtoolsOptions as DevtoolsProps} from './controller.tsx'
export {Icon, MountIcons} from './ui/index.ts'
export type {IconComponent} from './ui/index.ts'
Loading

0 comments on commit eef06c4

Please sign in to comment.