electrobun-preact-devtools 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/app-preload.d.mts +41 -0
- package/dist/app-preload.mjs +126 -0
- package/dist/bun.d.mts +70 -0
- package/dist/bun.mjs +298 -0
- package/dist/devtools-view.d.mts +36 -0
- package/dist/devtools-view.mjs +221 -0
- package/dist/index-ohWyACpe.d.mts +105 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/dist/logger-Bpp6JxH4.mjs +23 -0
- package/dist/protocol-rAxFKNGR.mjs +7 -0
- package/dist/rpc-Ct-cJYzf.d.mts +13 -0
- package/dist/style.css +1918 -0
- package/dist/upstream-CToW1NK7.d.mts +1 -0
- package/dist/upstream-DgCs5wGH.mjs +6603 -0
- package/docs/context.md +92 -0
- package/examples/compose-existing-rpc.ts +145 -0
- package/examples/minimal.ts +52 -0
- package/examples/tsconfig.json +16 -0
- package/package.json +75 -10
- package/readme.md +0 -1
package/docs/context.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
`electrobun-preact-devtools` lets an Electrobun app run the Preact devtools UI in a separate desktop window.
|
|
4
|
+
It keeps Bun in the middle, installs `window.__PREACT_DEVTOOLS__` in the inspected app window, and mounts the vendored Preact devtools UI in a standalone window.
|
|
5
|
+
|
|
6
|
+
# When to Use
|
|
7
|
+
|
|
8
|
+
- Your application is built with Electrobun and uses Preact.
|
|
9
|
+
- You want a standalone devtools window instead of a browser extension.
|
|
10
|
+
- You already use Electrobun RPC and need a composition-friendly integration.
|
|
11
|
+
- You want to keep using normal app-side imports like `preact/debug` or `preact/devtools`.
|
|
12
|
+
|
|
13
|
+
# When Not to Use
|
|
14
|
+
|
|
15
|
+
- Your app runs in a browser and can use the normal Preact devtools extension.
|
|
16
|
+
- Your windows are sandboxed and cannot use Electrobun RPC.
|
|
17
|
+
- You need one devtools window to retarget across many app windows.
|
|
18
|
+
- You need framework support beyond Preact.
|
|
19
|
+
|
|
20
|
+
# Core Abstractions
|
|
21
|
+
|
|
22
|
+
- `PreactDevtoolsHost`: the Bun-side router that tracks targets and opens devtools windows.
|
|
23
|
+
- App bridge: the preload-installed `window.__PREACT_DEVTOOLS__` object in the inspected app window.
|
|
24
|
+
- Devtools window: the standalone Electrobun window that renders the vendored Preact devtools UI.
|
|
25
|
+
- Target: one inspected app window identified by a `targetId`.
|
|
26
|
+
- Reserved message channel: `preactDevtoolsMessage`, the package-owned Electrobun RPC message name.
|
|
27
|
+
|
|
28
|
+
# Data Flow / Lifecycle
|
|
29
|
+
|
|
30
|
+
1. Bun creates a `PreactDevtoolsHost`.
|
|
31
|
+
2. The app window is created with host-owned or host-composed RPC and is registered with a `targetId`.
|
|
32
|
+
3. The app preload installs the bridge before the app imports `preact/debug` or `preact/devtools`.
|
|
33
|
+
4. When the user opens devtools, the host creates a standalone devtools window and wires its RPC.
|
|
34
|
+
5. The app bridge sends renderer events to Bun.
|
|
35
|
+
6. Bun forwards those events to the devtools window.
|
|
36
|
+
7. The devtools window updates its local store and sends UI commands back through Bun to the app bridge.
|
|
37
|
+
|
|
38
|
+
# Common Tasks -> Recommended APIs
|
|
39
|
+
|
|
40
|
+
- New integration with package-owned RPC:
|
|
41
|
+
Use `createPreactDevtoolsHost`, `host.createAppWindowRPC`, `composeAppWindowViewRPC`, `installPreactDevtoolsBridge`, `composeDevtoolsWindowViewRPC`, and `mountPreactDevtoolsWindow`.
|
|
42
|
+
- Existing app-window RPC:
|
|
43
|
+
Use `composeAppWindowRPC` in Bun and `composeAppWindowViewRPC` in the preload.
|
|
44
|
+
- Existing devtools-window RPC:
|
|
45
|
+
Use `composeDevtoolsWindowRPC` in Bun and `composeDevtoolsWindowViewRPC` in the devtools window.
|
|
46
|
+
- Logging package lifecycle issues:
|
|
47
|
+
Pass `logger` to `createPreactDevtoolsHost`, `installPreactDevtoolsBridge`, or `mountPreactDevtoolsWindow`.
|
|
48
|
+
|
|
49
|
+
# Recommended Patterns
|
|
50
|
+
|
|
51
|
+
- Create one `PreactDevtoolsHost` for the app process.
|
|
52
|
+
- Install the app bridge in preload before any Preact devtools import runs.
|
|
53
|
+
- Use the composition helpers when a window already has its own Electrobun RPC handlers.
|
|
54
|
+
- Treat `preactDevtoolsMessage` as reserved package transport, not application RPC.
|
|
55
|
+
- Keep one devtools window per target in v1.
|
|
56
|
+
|
|
57
|
+
# Patterns to Avoid
|
|
58
|
+
|
|
59
|
+
- Installing the bridge after the application has already imported `preact/debug`.
|
|
60
|
+
- Reusing `preactDevtoolsMessage` for unrelated application messages.
|
|
61
|
+
- Expecting direct browser-to-browser communication between the app window and the devtools window.
|
|
62
|
+
- Opening devtools for a target that has not been registered with the host.
|
|
63
|
+
|
|
64
|
+
# Invariants and Constraints
|
|
65
|
+
|
|
66
|
+
- Both the app window and the devtools window must have Electrobun RPC enabled.
|
|
67
|
+
- The package owns exactly one reserved message channel per window schema.
|
|
68
|
+
- Bun is the only supported relay between the inspected app window and the devtools window.
|
|
69
|
+
- The vendored `preact-devtools` snapshot defines the supported upstream bridge and UI behavior.
|
|
70
|
+
|
|
71
|
+
# Error Model
|
|
72
|
+
|
|
73
|
+
- `installPreactDevtoolsBridge` throws if the chosen global name is already occupied by a different value.
|
|
74
|
+
- `host.registerAppWindow(...)` throws if the same `targetId` is registered to a different window.
|
|
75
|
+
- `host.open(targetId)` throws if the target was never registered or the host has already been disposed.
|
|
76
|
+
- Composition helpers throw if consumer handlers try to claim the reserved `preactDevtoolsMessage` name.
|
|
77
|
+
- Missing RPC transports do not throw immediately in browser code; the package logs a warning through the optional logger.
|
|
78
|
+
|
|
79
|
+
# Terminology
|
|
80
|
+
|
|
81
|
+
- Target: one inspected application window.
|
|
82
|
+
- App bridge: the preload-side hook installation in the inspected window.
|
|
83
|
+
- Devtools window: the standalone UI window.
|
|
84
|
+
- Host: the Bun-side controller.
|
|
85
|
+
- Session: the pairing between one target and one devtools window.
|
|
86
|
+
|
|
87
|
+
# Non-Goals
|
|
88
|
+
|
|
89
|
+
- Emulating Chrome DevTools or browser-extension panels.
|
|
90
|
+
- Inspecting non-Preact applications.
|
|
91
|
+
- Supporting sandboxed windows in v1.
|
|
92
|
+
- Providing multi-target retargeting from one devtools window in v1.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { BrowserWindow, RPCSchema } from 'electrobun/bun'
|
|
2
|
+
import { Electroview } from 'electrobun/view'
|
|
3
|
+
import {
|
|
4
|
+
composeAppWindowRPC,
|
|
5
|
+
composeDevtoolsWindowRPC,
|
|
6
|
+
createPreactDevtoolsHost,
|
|
7
|
+
type PreactDevtoolsHost,
|
|
8
|
+
} from 'electrobun-preact-devtools/bun'
|
|
9
|
+
import {
|
|
10
|
+
composeAppWindowViewRPC,
|
|
11
|
+
installPreactDevtoolsBridge,
|
|
12
|
+
} from 'electrobun-preact-devtools/app-preload'
|
|
13
|
+
import {
|
|
14
|
+
composeDevtoolsWindowViewRPC,
|
|
15
|
+
mountPreactDevtoolsWindow,
|
|
16
|
+
} from 'electrobun-preact-devtools/devtools-view'
|
|
17
|
+
import type {
|
|
18
|
+
WithPreactDevtoolsAppWindowRPC,
|
|
19
|
+
WithPreactDevtoolsDevtoolsWindowRPC,
|
|
20
|
+
} from 'electrobun-preact-devtools/shared'
|
|
21
|
+
|
|
22
|
+
export type AppWindowRPC = WithPreactDevtoolsAppWindowRPC<{
|
|
23
|
+
bun: RPCSchema<{
|
|
24
|
+
requests: {
|
|
25
|
+
openFile: {
|
|
26
|
+
params: { path: string }
|
|
27
|
+
response: string
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
messages: {
|
|
31
|
+
logAnalytics: { event: string }
|
|
32
|
+
}
|
|
33
|
+
}>
|
|
34
|
+
webview: RPCSchema<{
|
|
35
|
+
requests: {
|
|
36
|
+
getTheme: {
|
|
37
|
+
params: {}
|
|
38
|
+
response: 'light' | 'dark'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
messages: {
|
|
42
|
+
themeChanged: { theme: 'light' | 'dark' }
|
|
43
|
+
}
|
|
44
|
+
}>
|
|
45
|
+
}>
|
|
46
|
+
|
|
47
|
+
export type DevtoolsWindowRPC = WithPreactDevtoolsDevtoolsWindowRPC<{
|
|
48
|
+
bun: RPCSchema<{
|
|
49
|
+
requests: {}
|
|
50
|
+
messages: {
|
|
51
|
+
trackWindowMetric: { metric: string; value: number }
|
|
52
|
+
}
|
|
53
|
+
}>
|
|
54
|
+
webview: RPCSchema<{
|
|
55
|
+
requests: {}
|
|
56
|
+
messages: {
|
|
57
|
+
setWindowChrome: { title: string }
|
|
58
|
+
}
|
|
59
|
+
}>
|
|
60
|
+
}>
|
|
61
|
+
|
|
62
|
+
declare const appWindow: BrowserWindow
|
|
63
|
+
declare const devtoolsWindow: BrowserWindow
|
|
64
|
+
declare const container: HTMLElement
|
|
65
|
+
|
|
66
|
+
export const host = createPreactDevtoolsHost({
|
|
67
|
+
openWindow() {
|
|
68
|
+
return devtoolsWindow
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
export function registerExistingAppWindow(currentHost: PreactDevtoolsHost) {
|
|
73
|
+
currentHost.registerAppWindow({
|
|
74
|
+
targetId: 'main',
|
|
75
|
+
window: appWindow,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function createAppWindowRPC(currentHost: PreactDevtoolsHost) {
|
|
80
|
+
return composeAppWindowRPC<AppWindowRPC>(currentHost, {
|
|
81
|
+
targetId: 'main',
|
|
82
|
+
handlers: {
|
|
83
|
+
requests: {
|
|
84
|
+
openFile: async ({ path }) => path,
|
|
85
|
+
},
|
|
86
|
+
messages: {
|
|
87
|
+
logAnalytics: ({ event }) => {
|
|
88
|
+
console.log('analytics', event)
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const appWindowViewRPC = composeAppWindowViewRPC<AppWindowRPC>({
|
|
96
|
+
handlers: {
|
|
97
|
+
requests: {
|
|
98
|
+
getTheme: () => 'dark',
|
|
99
|
+
},
|
|
100
|
+
messages: {
|
|
101
|
+
themeChanged: ({ theme }) => {
|
|
102
|
+
document.documentElement.dataset.theme = theme
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
export function installComposedBridge() {
|
|
109
|
+
const electroview = new Electroview({
|
|
110
|
+
rpc: appWindowViewRPC,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return installPreactDevtoolsBridge({ electroview })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function createStandaloneDevtoolsRPC(currentHost: PreactDevtoolsHost) {
|
|
117
|
+
return composeDevtoolsWindowRPC<DevtoolsWindowRPC>(currentHost, {
|
|
118
|
+
targetId: 'main',
|
|
119
|
+
handlers: {
|
|
120
|
+
messages: {
|
|
121
|
+
trackWindowMetric: ({ metric, value }) => {
|
|
122
|
+
console.log(metric, value)
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const devtoolsWindowViewRPC = composeDevtoolsWindowViewRPC<DevtoolsWindowRPC>({
|
|
130
|
+
handlers: {
|
|
131
|
+
messages: {
|
|
132
|
+
setWindowChrome: ({ title }) => {
|
|
133
|
+
document.title = title
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
export function mountComposedDevtools() {
|
|
140
|
+
const electroview = new Electroview({
|
|
141
|
+
rpc: devtoolsWindowViewRPC,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
return mountPreactDevtoolsWindow({ container, electroview })
|
|
145
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BrowserWindow } from 'electrobun/bun'
|
|
2
|
+
import { Electroview } from 'electrobun/view'
|
|
3
|
+
import { createPreactDevtoolsHost } from 'electrobun-preact-devtools/bun'
|
|
4
|
+
import {
|
|
5
|
+
composeAppWindowViewRPC,
|
|
6
|
+
installPreactDevtoolsBridge,
|
|
7
|
+
} from 'electrobun-preact-devtools/app-preload'
|
|
8
|
+
import {
|
|
9
|
+
composeDevtoolsWindowViewRPC,
|
|
10
|
+
mountPreactDevtoolsWindow,
|
|
11
|
+
} from 'electrobun-preact-devtools/devtools-view'
|
|
12
|
+
|
|
13
|
+
export const host = createPreactDevtoolsHost({
|
|
14
|
+
openWindow({ rpc }) {
|
|
15
|
+
return new BrowserWindow({
|
|
16
|
+
title: 'Preact Devtools',
|
|
17
|
+
url: 'views://preact-devtools/index.html',
|
|
18
|
+
rpc,
|
|
19
|
+
})
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export const appWindow = new BrowserWindow({
|
|
24
|
+
title: 'My App',
|
|
25
|
+
url: 'views://app/index.html',
|
|
26
|
+
preload: 'views://app/preload.js',
|
|
27
|
+
rpc: host.createAppWindowRPC({ targetId: 'main' }),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
host.registerAppWindow({
|
|
31
|
+
targetId: 'main',
|
|
32
|
+
window: appWindow,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export function installBridgeInAppPreload() {
|
|
36
|
+
const electroview = new Electroview({
|
|
37
|
+
rpc: composeAppWindowViewRPC({}),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return installPreactDevtoolsBridge({ electroview })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function mountStandaloneDevtools(container: HTMLElement) {
|
|
44
|
+
const electroview = new Electroview({
|
|
45
|
+
rpc: composeDevtoolsWindowViewRPC({}),
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return mountPreactDevtoolsWindow({
|
|
49
|
+
container,
|
|
50
|
+
electroview,
|
|
51
|
+
})
|
|
52
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"include": ["./"],
|
|
4
|
+
"exclude": ["../src/vendor/preact-devtools"],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"baseUrl": "..",
|
|
7
|
+
"ignoreDeprecations": "6.0",
|
|
8
|
+
"paths": {
|
|
9
|
+
"electrobun-preact-devtools": ["./dist/index.d.mts"],
|
|
10
|
+
"electrobun-preact-devtools/shared": ["./dist/index.d.mts"],
|
|
11
|
+
"electrobun-preact-devtools/bun": ["./dist/bun.d.mts"],
|
|
12
|
+
"electrobun-preact-devtools/app-preload": ["./dist/app-preload.d.mts"],
|
|
13
|
+
"electrobun-preact-devtools/devtools-view": ["./dist/devtools-view.d.mts"]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,78 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electrobun-preact-devtools",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
-
},
|
|
9
|
-
"keywords": [],
|
|
10
|
-
"author": "",
|
|
3
|
+
"version": "0.1.0",
|
|
11
4
|
"license": "MIT",
|
|
12
|
-
"
|
|
13
|
-
|
|
5
|
+
"author": "Alec Larson",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/aleclarson/electrobun-preact-devtools.git"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"docs",
|
|
13
|
+
"examples"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"default": "./dist/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./shared": {
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"default": "./dist/index.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./bun": {
|
|
26
|
+
"types": "./dist/bun.d.mts",
|
|
27
|
+
"default": "./dist/bun.mjs"
|
|
28
|
+
},
|
|
29
|
+
"./app-preload": {
|
|
30
|
+
"types": "./dist/app-preload.d.mts",
|
|
31
|
+
"default": "./dist/app-preload.mjs"
|
|
32
|
+
},
|
|
33
|
+
"./devtools-view": {
|
|
34
|
+
"types": "./dist/devtools-view.d.mts",
|
|
35
|
+
"default": "./dist/devtools-view.mjs"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@preact/signals": "2.9.0",
|
|
40
|
+
"@preact/signals-core": "^1.14.1",
|
|
41
|
+
"errorstacks": "2.4.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@tsdown/css": "^0.21.7",
|
|
45
|
+
"@types/node": "^25.6.0",
|
|
46
|
+
"@types/three": "^0.183.1",
|
|
47
|
+
"degit": "^2.8.4",
|
|
48
|
+
"electrobun": "1.16.0",
|
|
49
|
+
"oxfmt": "^0.44.0",
|
|
50
|
+
"oxlint": "^1.59.0",
|
|
51
|
+
"preact": "10.29.1",
|
|
52
|
+
"tsdown": "^0.21.7",
|
|
53
|
+
"tsnapi": "^0.1.1",
|
|
54
|
+
"typescript": "^6.0.2",
|
|
55
|
+
"vitest": "^4.1.4"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"electrobun": "*",
|
|
59
|
+
"preact": "*"
|
|
60
|
+
},
|
|
61
|
+
"peerDependenciesMeta": {
|
|
62
|
+
"electrobun": {
|
|
63
|
+
"optional": false
|
|
64
|
+
},
|
|
65
|
+
"preact": {
|
|
66
|
+
"optional": false
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"dev": "tsdown --sourcemap --watch",
|
|
71
|
+
"build": "tsdown",
|
|
72
|
+
"format": "oxfmt .",
|
|
73
|
+
"lint": "oxlint src",
|
|
74
|
+
"typecheck": "tsc --noEmit && tsc -p test --noEmit",
|
|
75
|
+
"check:examples": "tsc -p examples --noEmit",
|
|
76
|
+
"test": "vitest"
|
|
77
|
+
}
|
|
78
|
+
}
|
package/readme.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Coming soon...
|