@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/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
- export type WindoDefaultProps<Props, State = unknown> = Props | ((ctx: WindoRenderContext<State>) => Props)
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?: WindoPlacement
173
- /** Initial component-local state. Its shape is the `State` generic; `ctx.state`/`ctx.setState` derive from it. */
174
- state?: 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?: WindoVariant<Props>[]
184
- /** Authored documentation table (not derived from the schema). */
185
- props?: WindoPropDoc[]
186
- /** Optional authored code snippet for the Code tab. */
187
- code?: (values: Props) => string
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":[]}