@westopp/windo 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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/dist/chunk-5RM2VYAM.js +150 -0
  4. package/dist/chunk-5RM2VYAM.js.map +1 -0
  5. package/dist/cli.cjs +303 -0
  6. package/dist/cli.cjs.map +1 -0
  7. package/dist/cli.d.cts +1 -0
  8. package/dist/cli.d.ts +1 -0
  9. package/dist/cli.js +138 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/index.cjs +219 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.cts +374 -0
  14. package/dist/index.d.ts +374 -0
  15. package/dist/index.js +182 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/plugin.cjs +185 -0
  18. package/dist/plugin.cjs.map +1 -0
  19. package/dist/plugin.d.cts +11 -0
  20. package/dist/plugin.d.ts +11 -0
  21. package/dist/plugin.js +11 -0
  22. package/dist/plugin.js.map +1 -0
  23. package/package.json +95 -0
  24. package/src/cli/index.ts +160 -0
  25. package/src/client/App.tsx +310 -0
  26. package/src/client/Canvas.tsx +358 -0
  27. package/src/client/Inspector.tsx +586 -0
  28. package/src/client/Sidebar.tsx +108 -0
  29. package/src/client/bridge.ts +124 -0
  30. package/src/client/chrome.css +1966 -0
  31. package/src/client/icons.tsx +110 -0
  32. package/src/client/index.ts +15 -0
  33. package/src/client/internal-types.ts +147 -0
  34. package/src/client/persist.ts +38 -0
  35. package/src/define-config.ts +54 -0
  36. package/src/descriptor.test.ts +59 -0
  37. package/src/descriptor.ts +185 -0
  38. package/src/globals.d.ts +9 -0
  39. package/src/index.ts +54 -0
  40. package/src/plugin/index.ts +181 -0
  41. package/src/preview/ctx.ts +43 -0
  42. package/src/preview/index.ts +283 -0
  43. package/src/preview/preview.css +81 -0
  44. package/src/preview/registry.ts +159 -0
  45. package/src/preview/render.tsx +90 -0
  46. package/src/preview/virtual.d.ts +8 -0
  47. package/src/protocol.ts +59 -0
  48. package/src/types.ts +319 -0
@@ -0,0 +1,124 @@
1
+ // Host-side bridge. Owns the postMessage conversation with the preview iframe:
2
+ // subscribes to preview messages, surfaces the latest state, and exposes the
3
+ // outbound host messages as stable callbacks.
4
+
5
+ import type { RefObject } from 'react'
6
+ import { useCallback, useEffect, useRef, useState } from 'react'
7
+ import type { WindoHostMessage } from '../protocol'
8
+ import { isPreviewMessage, isWindoMessage, WINDO_MSG } from '../protocol'
9
+ import type { WindoContextMeta, WindoEnvState, WindoFieldError, WindoGroup, WindoManifestEntry } from '../types'
10
+ import type { BridgeApi, WindoDescribe, WindoLogRow } from './internal-types'
11
+
12
+ const MAX_LOGS = 200
13
+
14
+ export function useWindoBridge(iframeRef: RefObject<HTMLIFrameElement | null>): BridgeApi {
15
+ const [ready, setReady] = useState(false)
16
+ const [readyNonce, setReadyNonce] = useState(0)
17
+ const [title, setTitle] = useState('')
18
+ const [groups, setGroups] = useState<WindoGroup[]>([])
19
+ const [contexts, setContexts] = useState<WindoContextMeta[]>([])
20
+ const [manifest, setManifest] = useState<WindoManifestEntry[]>([])
21
+ const [describe, setDescribe] = useState<WindoDescribe | null>(null)
22
+ const [parseErrors, setParseErrors] = useState<WindoFieldError[]>([])
23
+ const [renderError, setRenderError] = useState<{ message: string; stack?: string } | null>(null)
24
+ const [logs, setLogs] = useState<WindoLogRow[]>([])
25
+ const [stateValues, setStateValues] = useState<Record<string, Record<string, unknown>>>({})
26
+ const [actionDisabled, setActionDisabled] = useState<Record<string, Record<string, boolean>>>({})
27
+ const seqRef = useRef(0)
28
+
29
+ const post = useCallback(
30
+ (msg: WindoHostMessage) => {
31
+ iframeRef.current?.contentWindow?.postMessage(msg, '*')
32
+ },
33
+ [iframeRef]
34
+ )
35
+
36
+ const handler = useRef((e: MessageEvent) => {
37
+ // Only the main preview iframe drives this bridge. Variant-gallery cells run
38
+ // their own preview iframes and post the same message shapes; ignore them so
39
+ // their logs / describe / errors don't leak into the main panels.
40
+ if (e.source !== iframeRef.current?.contentWindow) return
41
+ if (!isWindoMessage(e.data)) return
42
+ if (!isPreviewMessage(e.data)) return
43
+ const msg = e.data
44
+ switch (msg.type) {
45
+ case 'ready':
46
+ setReady(true)
47
+ setReadyNonce(n => n + 1)
48
+ post({ source: WINDO_MSG, dir: 'host', type: 'request-manifest' })
49
+ break
50
+ case 'manifest':
51
+ setTitle(msg.title)
52
+ setManifest(msg.entries)
53
+ setGroups(msg.groups)
54
+ setContexts(msg.contexts)
55
+ break
56
+ case 'describe':
57
+ setDescribe({
58
+ id: msg.id,
59
+ descriptor: msg.descriptor,
60
+ props: msg.props,
61
+ variants: msg.variants,
62
+ defaults: msg.defaults,
63
+ code: msg.code,
64
+ })
65
+ setRenderError(null)
66
+ break
67
+ case 'parse-ok':
68
+ setParseErrors([])
69
+ setRenderError(null)
70
+ break
71
+ case 'parse-error':
72
+ setParseErrors(msg.errors)
73
+ break
74
+ case 'log': {
75
+ const row: WindoLogRow = { ...msg.entry, seq: seqRef.current++ }
76
+ setLogs(prev => [...prev, row].slice(-MAX_LOGS))
77
+ break
78
+ }
79
+ case 'state':
80
+ setStateValues(prev => ({ ...prev, [msg.id]: msg.state }))
81
+ setActionDisabled(prev => ({ ...prev, [msg.id]: Object.fromEntries(msg.actions.map(a => [a.id, a.disabled])) }))
82
+ break
83
+ case 'render-error':
84
+ setRenderError({ message: msg.message, stack: msg.stack })
85
+ break
86
+ }
87
+ })
88
+
89
+ useEffect(() => {
90
+ const onMessage = (e: MessageEvent) => handler.current(e)
91
+ window.addEventListener('message', onMessage)
92
+ return () => window.removeEventListener('message', onMessage)
93
+ }, [])
94
+
95
+ const select = useCallback((id: string) => post({ source: WINDO_MSG, dir: 'host', type: 'select', id }), [post])
96
+
97
+ const setProps = useCallback((id: string, json: string) => post({ source: WINDO_MSG, dir: 'host', type: 'set-props', id, json }), [post])
98
+
99
+ const setEnv = useCallback((env: WindoEnvState) => post({ source: WINDO_MSG, dir: 'host', type: 'set-env', env }), [post])
100
+
101
+ const invokeAction = useCallback((id: string, actionId: string) => post({ source: WINDO_MSG, dir: 'host', type: 'invoke-action', id, actionId }), [post])
102
+
103
+ const clearLogs = useCallback(() => setLogs([]), [])
104
+
105
+ return {
106
+ ready,
107
+ readyNonce,
108
+ title,
109
+ groups,
110
+ contexts,
111
+ manifest,
112
+ describe,
113
+ parseErrors,
114
+ renderError,
115
+ logs,
116
+ clearLogs,
117
+ stateValues,
118
+ actionDisabled,
119
+ select,
120
+ setProps,
121
+ setEnv,
122
+ invokeAction,
123
+ }
124
+ }