@westopp/windo 0.1.1 → 0.1.3
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/dist/{chunk-5RM2VYAM.js → chunk-IBJ6MG77.js} +2 -1
- package/dist/chunk-IBJ6MG77.js.map +1 -0
- package/dist/cli.cjs +2 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -10
- package/dist/index.d.ts +53 -10
- package/dist/index.js.map +1 -1
- package/dist/plugin.cjs +1 -0
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +1 -1
- package/src/cli/index.ts +1 -1
- package/src/client/App.tsx +64 -1
- package/src/client/Inspector.tsx +37 -0
- package/src/client/bridge.ts +24 -0
- package/src/client/chrome.css +72 -47
- package/src/client/internal-types.ts +14 -0
- package/src/index.ts +2 -0
- package/src/plugin/index.ts +1 -0
- package/src/preview/ctx.ts +8 -1
- package/src/preview/index.ts +153 -10
- package/src/preview/render.tsx +5 -3
- package/src/protocol.ts +7 -0
- package/src/types.ts +35 -9
- package/dist/chunk-5RM2VYAM.js.map +0 -1
package/src/types.ts
CHANGED
|
@@ -131,6 +131,20 @@ export interface WindoRenderContext<State = unknown> {
|
|
|
131
131
|
setState: (patch: Partial<State>) => void
|
|
132
132
|
/** Resolved values of opted-in contexts, keyed by context name. */
|
|
133
133
|
contexts: Record<string, unknown>
|
|
134
|
+
/**
|
|
135
|
+
* Shared, cross-component state seeded from the config's `ctxState`. Unlike
|
|
136
|
+
* `state` (per-windo, reset on selection), this persists across selection and
|
|
137
|
+
* is global: any component can read it and write it via `setCtxState`. Use it
|
|
138
|
+
* to drive providers that wrap every component — e.g. a theme provider toggled
|
|
139
|
+
* from any component on the canvas.
|
|
140
|
+
*/
|
|
141
|
+
ctxState: Record<string, unknown>
|
|
142
|
+
/** Merge a patch into the shared `ctxState` and re-render every consumer. */
|
|
143
|
+
setCtxState: (patch: Record<string, unknown>) => void
|
|
144
|
+
/** Set the canvas colour scheme from a component or action. */
|
|
145
|
+
setColorScheme: (scheme: 'light' | 'dark') => void
|
|
146
|
+
/** Flip the canvas colour scheme between light and dark. */
|
|
147
|
+
toggleTheme: () => void
|
|
134
148
|
}
|
|
135
149
|
|
|
136
150
|
/* ------------------------------------------------------------------ *
|
|
@@ -151,7 +165,13 @@ export interface WindoPropDoc {
|
|
|
151
165
|
desc?: string
|
|
152
166
|
}
|
|
153
167
|
|
|
154
|
-
|
|
168
|
+
/** A value that may be authored statically or as a context-aware `ctx => value` function resolved at render-time. */
|
|
169
|
+
export type Ctxual<T, State = unknown> = T | ((ctx: WindoRenderContext<State>) => T)
|
|
170
|
+
|
|
171
|
+
/** The ctx surface available while resolving a windo's initial state — the render-time `state`/`setState` pair is excluded because it does not exist yet. */
|
|
172
|
+
export type WindoInitContext<State = unknown> = Omit<WindoRenderContext<State>, 'state' | 'setState'>
|
|
173
|
+
|
|
174
|
+
export type WindoDefaultProps<Props, State = unknown> = Ctxual<Props, State>
|
|
155
175
|
|
|
156
176
|
/**
|
|
157
177
|
* The object returned by a `windo(...)` factory.
|
|
@@ -169,9 +189,10 @@ export interface WindoDefinition<Props = unknown, State = unknown, GroupSlug ext
|
|
|
169
189
|
status?: WindoStatus
|
|
170
190
|
description?: string
|
|
171
191
|
deprecation?: string
|
|
172
|
-
placement
|
|
173
|
-
|
|
174
|
-
state
|
|
192
|
+
/** Where the component anchors in the canvas frame. Either a static placement or a function resolved with the live `ctx`. */
|
|
193
|
+
placement?: Ctxual<WindoPlacement, State>
|
|
194
|
+
/** Initial component-local state. Its shape is the `State` generic; `ctx.state`/`ctx.setState` derive from it. Either a static value or a function resolved with the init `ctx` (no `state`/`setState`) when the component is selected. */
|
|
195
|
+
state?: State | ((ctx: WindoInitContext<State>) => State)
|
|
175
196
|
/** Out-of-band actions that drive state: toolbar buttons (`click`) and stage pointer triggers (`enter`/`exit`/`hover`). */
|
|
176
197
|
actions?: WindoAction<State>[]
|
|
177
198
|
/** zod schema: validator + parser for the JSON-editable prop subset. `z.output ⊆ Props`. */
|
|
@@ -180,11 +201,12 @@ export interface WindoDefinition<Props = unknown, State = unknown, GroupSlug ext
|
|
|
180
201
|
defaultProps: WindoDefaultProps<Props, State>
|
|
181
202
|
/** Names of provider contexts this component opts into. */
|
|
182
203
|
uses?: string[]
|
|
183
|
-
variants
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
code
|
|
204
|
+
/** Gallery variants. Either a static array or a function resolved with the live `ctx` when the component is selected. */
|
|
205
|
+
variants?: Ctxual<WindoVariant<Props>[], State>
|
|
206
|
+
/** Authored documentation table (not derived from the schema). Either a static array or a function resolved with the live `ctx` when the component is selected. */
|
|
207
|
+
props?: Ctxual<WindoPropDoc[], State>
|
|
208
|
+
/** Optional authored code snippet for the Code tab, resolved with the JSON-editable values and the live `ctx`. */
|
|
209
|
+
code?: (values: Props, ctx: WindoRenderContext<State>) => string
|
|
188
210
|
/** A local provider wrapping just this windo (in addition to `uses`). */
|
|
189
211
|
providers?: ComponentType<{ children: ReactNode; ctx: WindoRenderContext<State> }>
|
|
190
212
|
component: (props: Props, ctx: WindoRenderContext<State>) => ReactNode
|
|
@@ -220,10 +242,14 @@ export interface WindoConfig<Groups extends readonly WindoGroup[] = readonly Win
|
|
|
220
242
|
contexts?: Contexts
|
|
221
243
|
/** The set of tags components may be assigned. A component's `tags` must be drawn from this list; the sidebar filters by them. */
|
|
222
244
|
tags?: Tags
|
|
245
|
+
/** Initial shared state exposed on `ctx.ctxState`. Global across every component and persisted across selection — write it from any component via `ctx.setCtxState`. */
|
|
246
|
+
ctxState?: Record<string, unknown>
|
|
223
247
|
/** Glob(s) for discovery, relative to project root. Default `**\/*.windo.tsx`. */
|
|
224
248
|
include?: string | string[]
|
|
225
249
|
/** Title shown in the workbench chrome. */
|
|
226
250
|
title?: string
|
|
251
|
+
/** Icon URL for the workbench logo and the browser favicon. When set, it replaces the built-in `wn` mark in both places. Any `<img>`/favicon source — a path, URL, or data URI. */
|
|
252
|
+
faviconUrl?: string
|
|
227
253
|
}
|
|
228
254
|
|
|
229
255
|
/* ------------------------------------------------------------------ *
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/plugin/index.ts"],"sourcesContent":["// Vite plugin (node side). Pairs `@vitejs/plugin-react` with the windo plugin:\n// serves the chrome + iframe HTML documents ourselves (appType custom) and\n// exposes a `virtual:windo-registry` module that re-exports the user config and\n// glob-imports every `*.windo.tsx` file.\n\nimport { existsSync } from 'node:fs'\nimport { isAbsolute, resolve as resolvePath } from 'node:path'\nimport reactPlugin from '@vitejs/plugin-react'\nimport type { Plugin, PluginOption, UserConfig, ViteDevServer } from 'vite'\n\nexport interface WindoPluginOptions {\n include?: string | string[]\n config?: string\n root?: string\n}\n\nconst VIRTUAL_ID = 'virtual:windo-registry'\nconst RESOLVED_VIRTUAL_ID = `\\0${VIRTUAL_ID}`\n\nconst CONFIG_CANDIDATES = ['windo.config.ts', 'windo.config.tsx', 'windo.config.mts', 'windo.config.js', 'windo.config.mjs'] as const\n\nfunction findConfig(root: string, configOpt?: string): string | null {\n if (configOpt) return isAbsolute(configOpt) ? configOpt : resolvePath(root, configOpt)\n for (const candidate of CONFIG_CANDIDATES) {\n const full = resolvePath(root, candidate)\n if (existsSync(full)) return full\n }\n return null\n}\n\nfunction toGlob(include?: string | string[]): string {\n const first = Array.isArray(include) ? include[0] : include\n const pattern = first ?? '**/*.windo.tsx'\n return pattern.startsWith('/') ? pattern : `/${pattern}`\n}\n\nconst CHROME_HTML = `<!doctype html>\n<html data-theme=\"light\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>windo</title>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\">\n import('@westopp/windo/client')\n </script>\n </body>\n</html>\n`\n\nconst IFRAME_HTML = `<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n </head>\n <body style=\"background: transparent\">\n <div id=\"root\"></div>\n <script type=\"module\">\n import('@westopp/windo/preview')\n </script>\n </body>\n</html>\n`\n\n// Absolute ids for the two HTML entry documents, relative to the project root.\n// They are virtual — the files need not exist on disk; the plugin's `load`\n// returns their contents. Keeping them rooted at `<root>/index.html` and\n// `<root>/__windo/iframe.html` makes `vite build` emit them at the matching\n// paths in the output directory (Vite derives an HTML asset's output path from\n// the input id's path relative to root).\nfunction htmlIds(root: string) {\n return {\n index: resolvePath(root, 'index.html'),\n iframe: resolvePath(root, '__windo/iframe.html'),\n }\n}\n\nexport function windo(options: WindoPluginOptions = {}): PluginOption[] {\n let resolvedRoot = process.cwd()\n let ids = htmlIds(resolvedRoot)\n\n const registryModule = (): string => {\n const root = options.root ?? resolvedRoot ?? process.cwd()\n const configPath = findConfig(root, options.config)\n const glob = toGlob(options.include)\n const importPath = configPath ? JSON.stringify(configPath) : 'null'\n const globLiteral = JSON.stringify(glob)\n return [\n `import * as __cfg from ${importPath}`,\n 'const config = __cfg.config ?? (__cfg.default && __cfg.default.config) ?? __cfg.default',\n 'export { config }',\n `export const modules = import.meta.glob(${globLiteral})`,\n '',\n ].join('\\n')\n }\n\n const plugin: Plugin = {\n name: 'windo',\n // `pre` so our resolveId/load claim the virtual registry and the two HTML\n // entries before Vite's filesystem fallback tries (and fails) to read them.\n enforce: 'pre',\n\n config(userConfig: UserConfig, { command, isPreview }) {\n const root = resolvePath(options.root ?? userConfig.root ?? '.')\n ids = htmlIds(root)\n const base: UserConfig = {\n // Dev serves the chrome + iframe HTML from `configureServer`, so it runs\n // headless (`custom`). Build and preview want the normal SPA behaviour so\n // `/` resolves to the emitted index.html.\n appType: command === 'serve' && !isPreview ? 'custom' : 'spa',\n define: {\n __WINDO_IFRAME_SRC__: JSON.stringify(command === 'build' ? './__windo/iframe.html' : './__windo/iframe'),\n },\n resolve: {\n dedupe: ['react', 'react-dom'],\n },\n optimizeDeps: {\n // Pre-bundle every React entrypoint in one pass so React's identity is\n // fixed up front. If `react-dom/client` (the preview renderer) is\n // discovered late, Vite re-optimizes and can split React across two\n // instances — fine until a component calls a hook, then \"invalid hook\n // call\". Keeping them all here prevents that re-optimize.\n include: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime', 'react/jsx-dev-runtime'],\n },\n }\n\n // Wire the chrome + iframe as build inputs unless the user supplied their\n // own. Both are emitted as static HTML the canvas can be hosted from.\n if (command === 'build' && !userConfig.build?.rollupOptions?.input) {\n base.build = { rollupOptions: { input: { index: ids.index, iframe: ids.iframe } } }\n }\n return base\n },\n\n configResolved(resolved) {\n resolvedRoot = resolved.root\n ids = htmlIds(resolvedRoot)\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ID) return RESOLVED_VIRTUAL_ID\n if (id === ids.index || id === ids.iframe) return id\n return null\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) return registryModule()\n if (id === ids.index) return CHROME_HTML\n if (id === ids.iframe) return IFRAME_HTML\n return null\n },\n\n configureServer(server: ViteDevServer) {\n server.middlewares.use(async (req, res, next) => {\n if (req.method !== 'GET') return next()\n const url = (req.url ?? '').split('?')[0]\n let html: string | null = null\n if (url === '/' || url === '/index.html') html = CHROME_HTML\n else if (url.startsWith('/__windo/iframe')) html = IFRAME_HTML\n if (html === null) return next()\n try {\n const transformed = await server.transformIndexHtml(url, html)\n res.statusCode = 200\n res.setHeader('Content-Type', 'text/html')\n res.end(transformed)\n } catch (err) {\n next(err)\n }\n })\n },\n }\n\n return [reactPlugin(), plugin]\n}\n\nexport const windoPlugin = windo\n\nexport default windo\n"],"mappings":";AAKA,SAAS,kBAAkB;AAC3B,SAAS,YAAY,WAAW,mBAAmB;AACnD,OAAO,iBAAiB;AASxB,IAAM,aAAa;AACnB,IAAM,sBAAsB,KAAK,UAAU;AAE3C,IAAM,oBAAoB,CAAC,mBAAmB,oBAAoB,oBAAoB,mBAAmB,kBAAkB;AAE3H,SAAS,WAAW,MAAc,WAAmC;AACnE,MAAI,UAAW,QAAO,WAAW,SAAS,IAAI,YAAY,YAAY,MAAM,SAAS;AACrF,aAAW,aAAa,mBAAmB;AACzC,UAAM,OAAO,YAAY,MAAM,SAAS;AACxC,QAAI,WAAW,IAAI,EAAG,QAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAEA,SAAS,OAAO,SAAqC;AACnD,QAAM,QAAQ,MAAM,QAAQ,OAAO,IAAI,QAAQ,CAAC,IAAI;AACpD,QAAM,UAAU,SAAS;AACzB,SAAO,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI,OAAO;AACxD;AAEA,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBpB,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBpB,SAAS,QAAQ,MAAc;AAC7B,SAAO;AAAA,IACL,OAAO,YAAY,MAAM,YAAY;AAAA,IACrC,QAAQ,YAAY,MAAM,qBAAqB;AAAA,EACjD;AACF;AAEO,SAAS,MAAM,UAA8B,CAAC,GAAmB;AACtE,MAAI,eAAe,QAAQ,IAAI;AAC/B,MAAI,MAAM,QAAQ,YAAY;AAE9B,QAAM,iBAAiB,MAAc;AACnC,UAAM,OAAO,QAAQ,QAAQ,gBAAgB,QAAQ,IAAI;AACzD,UAAM,aAAa,WAAW,MAAM,QAAQ,MAAM;AAClD,UAAM,OAAO,OAAO,QAAQ,OAAO;AACnC,UAAM,aAAa,aAAa,KAAK,UAAU,UAAU,IAAI;AAC7D,UAAM,cAAc,KAAK,UAAU,IAAI;AACvC,WAAO;AAAA,MACL,0BAA0B,UAAU;AAAA,MACpC;AAAA,MACA;AAAA,MACA,2CAA2C,WAAW;AAAA,MACtD;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AAEA,QAAM,SAAiB;AAAA,IACrB,MAAM;AAAA;AAAA;AAAA,IAGN,SAAS;AAAA,IAET,OAAO,YAAwB,EAAE,SAAS,UAAU,GAAG;AACrD,YAAM,OAAO,YAAY,QAAQ,QAAQ,WAAW,QAAQ,GAAG;AAC/D,YAAM,QAAQ,IAAI;AAClB,YAAM,OAAmB;AAAA;AAAA;AAAA;AAAA,QAIvB,SAAS,YAAY,WAAW,CAAC,YAAY,WAAW;AAAA,QACxD,QAAQ;AAAA,UACN,sBAAsB,KAAK,UAAU,YAAY,UAAU,0BAA0B,kBAAkB;AAAA,QACzG;AAAA,QACA,SAAS;AAAA,UACP,QAAQ,CAAC,SAAS,WAAW;AAAA,QAC/B;AAAA,QACA,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMZ,SAAS,CAAC,SAAS,aAAa,oBAAoB,qBAAqB,uBAAuB;AAAA,QAClG;AAAA,MACF;AAIA,UAAI,YAAY,WAAW,CAAC,WAAW,OAAO,eAAe,OAAO;AAClE,aAAK,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,IAAI,OAAO,EAAE,EAAE;AAAA,MACpF;AACA,aAAO;AAAA,IACT;AAAA,IAEA,eAAe,UAAU;AACvB,qBAAe,SAAS;AACxB,YAAM,QAAQ,YAAY;AAAA,IAC5B;AAAA,IAEA,UAAU,IAAI;AACZ,UAAI,OAAO,WAAY,QAAO;AAC9B,UAAI,OAAO,IAAI,SAAS,OAAO,IAAI,OAAQ,QAAO;AAClD,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,IAAI;AACP,UAAI,OAAO,oBAAqB,QAAO,eAAe;AACtD,UAAI,OAAO,IAAI,MAAO,QAAO;AAC7B,UAAI,OAAO,IAAI,OAAQ,QAAO;AAC9B,aAAO;AAAA,IACT;AAAA,IAEA,gBAAgB,QAAuB;AACrC,aAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;AAC/C,YAAI,IAAI,WAAW,MAAO,QAAO,KAAK;AACtC,cAAM,OAAO,IAAI,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AACxC,YAAI,OAAsB;AAC1B,YAAI,QAAQ,OAAO,QAAQ,cAAe,QAAO;AAAA,iBACxC,IAAI,WAAW,iBAAiB,EAAG,QAAO;AACnD,YAAI,SAAS,KAAM,QAAO,KAAK;AAC/B,YAAI;AACF,gBAAM,cAAc,MAAM,OAAO,mBAAmB,KAAK,IAAI;AAC7D,cAAI,aAAa;AACjB,cAAI,UAAU,gBAAgB,WAAW;AACzC,cAAI,IAAI,WAAW;AAAA,QACrB,SAAS,KAAK;AACZ,eAAK,GAAG;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,CAAC,YAAY,GAAG,MAAM;AAC/B;AAEO,IAAM,cAAc;AAE3B,IAAO,iBAAQ;","names":[]}
|