@oddkin/workbench 0.0.0-stage → 1.0.0-beta.1

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 (243) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -2
  3. package/dist/app/card-previews.d.ts +14 -0
  4. package/dist/app/card-previews.d.ts.map +1 -0
  5. package/dist/app/card-previews.js +140 -0
  6. package/dist/app/card-previews.js.map +1 -0
  7. package/dist/app/copy.d.ts +48 -0
  8. package/dist/app/copy.d.ts.map +1 -0
  9. package/dist/app/copy.js +52 -0
  10. package/dist/app/copy.js.map +1 -0
  11. package/dist/app/define.d.ts +3 -0
  12. package/dist/app/define.d.ts.map +1 -0
  13. package/dist/app/define.js +10 -0
  14. package/dist/app/define.js.map +1 -0
  15. package/dist/app/details.d.ts +22 -0
  16. package/dist/app/details.d.ts.map +1 -0
  17. package/dist/app/details.js +17 -0
  18. package/dist/app/details.js.map +1 -0
  19. package/dist/app/index.d.ts +12 -0
  20. package/dist/app/index.d.ts.map +1 -0
  21. package/dist/app/index.js +9 -0
  22. package/dist/app/index.js.map +1 -0
  23. package/dist/app/routes.d.ts +26 -0
  24. package/dist/app/routes.d.ts.map +1 -0
  25. package/dist/app/routes.js +96 -0
  26. package/dist/app/routes.js.map +1 -0
  27. package/dist/app/search.d.ts +17 -0
  28. package/dist/app/search.d.ts.map +1 -0
  29. package/dist/app/search.js +52 -0
  30. package/dist/app/search.js.map +1 -0
  31. package/dist/app/shell.d.ts +29 -0
  32. package/dist/app/shell.d.ts.map +1 -0
  33. package/dist/app/shell.js +478 -0
  34. package/dist/app/shell.js.map +1 -0
  35. package/dist/app/styles.d.ts +2 -0
  36. package/dist/app/styles.d.ts.map +1 -0
  37. package/dist/app/styles.js +356 -0
  38. package/dist/app/styles.js.map +1 -0
  39. package/dist/app/theme-studio.d.ts +12 -0
  40. package/dist/app/theme-studio.d.ts.map +1 -0
  41. package/dist/app/theme-studio.js +14 -0
  42. package/dist/app/theme-studio.js.map +1 -0
  43. package/dist/app/views.d.ts +38 -0
  44. package/dist/app/views.d.ts.map +1 -0
  45. package/dist/app/views.js +252 -0
  46. package/dist/app/views.js.map +1 -0
  47. package/dist/config/index.d.ts +170 -0
  48. package/dist/config/index.d.ts.map +1 -0
  49. package/dist/config/index.js +148 -0
  50. package/dist/config/index.js.map +1 -0
  51. package/dist/conformance/button.d.ts +2 -0
  52. package/dist/conformance/button.d.ts.map +1 -0
  53. package/dist/conformance/button.js +233 -0
  54. package/dist/conformance/button.js.map +1 -0
  55. package/dist/conformance/index.d.ts +107 -0
  56. package/dist/conformance/index.d.ts.map +1 -0
  57. package/dist/conformance/index.js +60 -0
  58. package/dist/conformance/index.js.map +1 -0
  59. package/dist/data/build.d.ts +36 -0
  60. package/dist/data/build.d.ts.map +1 -0
  61. package/dist/data/build.js +260 -0
  62. package/dist/data/build.js.map +1 -0
  63. package/dist/data/import-map.d.ts +59 -0
  64. package/dist/data/import-map.d.ts.map +1 -0
  65. package/dist/data/import-map.js +195 -0
  66. package/dist/data/import-map.js.map +1 -0
  67. package/dist/data/index.d.ts +14 -0
  68. package/dist/data/index.d.ts.map +1 -0
  69. package/dist/data/index.js +10 -0
  70. package/dist/data/index.js.map +1 -0
  71. package/dist/data/theme-studio.d.ts +21 -0
  72. package/dist/data/theme-studio.d.ts.map +1 -0
  73. package/dist/data/theme-studio.js +112 -0
  74. package/dist/data/theme-studio.js.map +1 -0
  75. package/dist/data/types.d.ts +187 -0
  76. package/dist/data/types.d.ts.map +1 -0
  77. package/dist/data/types.js +4 -0
  78. package/dist/data/types.js.map +1 -0
  79. package/dist/examples/index.d.ts +101 -0
  80. package/dist/examples/index.d.ts.map +1 -0
  81. package/dist/examples/index.js +106 -0
  82. package/dist/examples/index.js.map +1 -0
  83. package/dist/frame/index.d.ts +9 -0
  84. package/dist/frame/index.d.ts.map +1 -0
  85. package/dist/frame/index.js +482 -0
  86. package/dist/frame/index.js.map +1 -0
  87. package/dist/internal/diagnostics.d.ts +17 -0
  88. package/dist/internal/diagnostics.d.ts.map +1 -0
  89. package/dist/internal/diagnostics.js +19 -0
  90. package/dist/internal/diagnostics.js.map +1 -0
  91. package/dist/node/bin.d.ts +3 -0
  92. package/dist/node/bin.d.ts.map +1 -0
  93. package/dist/node/bin.js +12 -0
  94. package/dist/node/bin.js.map +1 -0
  95. package/dist/node/cli.d.ts +17 -0
  96. package/dist/node/cli.d.ts.map +1 -0
  97. package/dist/node/cli.js +198 -0
  98. package/dist/node/cli.js.map +1 -0
  99. package/dist/node/config.d.ts +28 -0
  100. package/dist/node/config.d.ts.map +1 -0
  101. package/dist/node/config.js +80 -0
  102. package/dist/node/config.js.map +1 -0
  103. package/dist/node/example-modules.d.ts +8 -0
  104. package/dist/node/example-modules.d.ts.map +1 -0
  105. package/dist/node/example-modules.js +28 -0
  106. package/dist/node/example-modules.js.map +1 -0
  107. package/dist/node/generated.d.ts +19 -0
  108. package/dist/node/generated.d.ts.map +1 -0
  109. package/dist/node/generated.js +109 -0
  110. package/dist/node/generated.js.map +1 -0
  111. package/dist/node/html.d.ts +35 -0
  112. package/dist/node/html.d.ts.map +1 -0
  113. package/dist/node/html.js +51 -0
  114. package/dist/node/html.js.map +1 -0
  115. package/dist/node/index.d.ts +24 -0
  116. package/dist/node/index.d.ts.map +1 -0
  117. package/dist/node/index.js +15 -0
  118. package/dist/node/index.js.map +1 -0
  119. package/dist/node/inputs.d.ts +26 -0
  120. package/dist/node/inputs.d.ts.map +1 -0
  121. package/dist/node/inputs.js +282 -0
  122. package/dist/node/inputs.js.map +1 -0
  123. package/dist/node/packages.d.ts +35 -0
  124. package/dist/node/packages.d.ts.map +1 -0
  125. package/dist/node/packages.js +87 -0
  126. package/dist/node/packages.js.map +1 -0
  127. package/dist/node/preload.d.ts +10 -0
  128. package/dist/node/preload.d.ts.map +1 -0
  129. package/dist/node/preload.js +61 -0
  130. package/dist/node/preload.js.map +1 -0
  131. package/dist/node/server.d.ts +42 -0
  132. package/dist/node/server.d.ts.map +1 -0
  133. package/dist/node/server.js +410 -0
  134. package/dist/node/server.js.map +1 -0
  135. package/dist/node/static.d.ts +43 -0
  136. package/dist/node/static.d.ts.map +1 -0
  137. package/dist/node/static.js +157 -0
  138. package/dist/node/static.js.map +1 -0
  139. package/dist/node/workbench.d.ts +43 -0
  140. package/dist/node/workbench.d.ts.map +1 -0
  141. package/dist/node/workbench.js +204 -0
  142. package/dist/node/workbench.js.map +1 -0
  143. package/dist/pages/build.d.ts +22 -0
  144. package/dist/pages/build.d.ts.map +1 -0
  145. package/dist/pages/build.js +363 -0
  146. package/dist/pages/build.js.map +1 -0
  147. package/dist/pages/defaults.d.ts +16 -0
  148. package/dist/pages/defaults.d.ts.map +1 -0
  149. package/dist/pages/defaults.js +88 -0
  150. package/dist/pages/defaults.js.map +1 -0
  151. package/dist/pages/index.d.ts +14 -0
  152. package/dist/pages/index.d.ts.map +1 -0
  153. package/dist/pages/index.js +10 -0
  154. package/dist/pages/index.js.map +1 -0
  155. package/dist/pages/navigation.d.ts +48 -0
  156. package/dist/pages/navigation.d.ts.map +1 -0
  157. package/dist/pages/navigation.js +83 -0
  158. package/dist/pages/navigation.js.map +1 -0
  159. package/dist/pages/types.d.ts +148 -0
  160. package/dist/pages/types.d.ts.map +1 -0
  161. package/dist/pages/types.js +3 -0
  162. package/dist/pages/types.js.map +1 -0
  163. package/dist/panels/catalog-define.d.ts +3 -0
  164. package/dist/panels/catalog-define.d.ts.map +1 -0
  165. package/dist/panels/catalog-define.js +7 -0
  166. package/dist/panels/catalog-define.js.map +1 -0
  167. package/dist/panels/catalog-layout-styles.d.ts +2 -0
  168. package/dist/panels/catalog-layout-styles.d.ts.map +1 -0
  169. package/dist/panels/catalog-layout-styles.js +54 -0
  170. package/dist/panels/catalog-layout-styles.js.map +1 -0
  171. package/dist/panels/catalog-styles.d.ts +2 -0
  172. package/dist/panels/catalog-styles.d.ts.map +1 -0
  173. package/dist/panels/catalog-styles.js +106 -0
  174. package/dist/panels/catalog-styles.js.map +1 -0
  175. package/dist/panels/catalog.d.ts +37 -0
  176. package/dist/panels/catalog.d.ts.map +1 -0
  177. package/dist/panels/catalog.js +74 -0
  178. package/dist/panels/catalog.js.map +1 -0
  179. package/dist/panels/component-define.d.ts +3 -0
  180. package/dist/panels/component-define.d.ts.map +1 -0
  181. package/dist/panels/component-define.js +6 -0
  182. package/dist/panels/component-define.js.map +1 -0
  183. package/dist/panels/component-detail.d.ts +49 -0
  184. package/dist/panels/component-detail.d.ts.map +1 -0
  185. package/dist/panels/component-detail.js +1352 -0
  186. package/dist/panels/component-detail.js.map +1 -0
  187. package/dist/panels/component.d.ts +17 -0
  188. package/dist/panels/component.d.ts.map +1 -0
  189. package/dist/panels/component.js +10 -0
  190. package/dist/panels/component.js.map +1 -0
  191. package/dist/panels/example-styles.d.ts +2 -0
  192. package/dist/panels/example-styles.d.ts.map +1 -0
  193. package/dist/panels/example-styles.js +343 -0
  194. package/dist/panels/example-styles.js.map +1 -0
  195. package/dist/panels/foundation-define.d.ts +3 -0
  196. package/dist/panels/foundation-define.d.ts.map +1 -0
  197. package/dist/panels/foundation-define.js +6 -0
  198. package/dist/panels/foundation-define.js.map +1 -0
  199. package/dist/panels/foundation-detail.d.ts +23 -0
  200. package/dist/panels/foundation-detail.d.ts.map +1 -0
  201. package/dist/panels/foundation-detail.js +216 -0
  202. package/dist/panels/foundation-detail.js.map +1 -0
  203. package/dist/panels/foundation.d.ts +15 -0
  204. package/dist/panels/foundation.d.ts.map +1 -0
  205. package/dist/panels/foundation.js +9 -0
  206. package/dist/panels/foundation.js.map +1 -0
  207. package/dist/panels/model.d.ts +184 -0
  208. package/dist/panels/model.d.ts.map +1 -0
  209. package/dist/panels/model.js +488 -0
  210. package/dist/panels/model.js.map +1 -0
  211. package/dist/panels/styles.d.ts +3 -0
  212. package/dist/panels/styles.d.ts.map +1 -0
  213. package/dist/panels/styles.js +265 -0
  214. package/dist/panels/styles.js.map +1 -0
  215. package/dist/panels/theme-model.d.ts +69 -0
  216. package/dist/panels/theme-model.d.ts.map +1 -0
  217. package/dist/panels/theme-model.js +166 -0
  218. package/dist/panels/theme-model.js.map +1 -0
  219. package/dist/panels/theme-studio.d.ts +32 -0
  220. package/dist/panels/theme-studio.d.ts.map +1 -0
  221. package/dist/panels/theme-studio.js +375 -0
  222. package/dist/panels/theme-studio.js.map +1 -0
  223. package/dist/preview/frame.d.ts +44 -0
  224. package/dist/preview/frame.d.ts.map +1 -0
  225. package/dist/preview/frame.js +260 -0
  226. package/dist/preview/frame.js.map +1 -0
  227. package/dist/preview/index.d.ts +26 -0
  228. package/dist/preview/index.d.ts.map +1 -0
  229. package/dist/preview/index.js +58 -0
  230. package/dist/preview/index.js.map +1 -0
  231. package/dist/preview/theme-generation.d.ts +33 -0
  232. package/dist/preview/theme-generation.d.ts.map +1 -0
  233. package/dist/preview/theme-generation.js +48 -0
  234. package/dist/preview/theme-generation.js.map +1 -0
  235. package/dist/protocol/index.d.ts +222 -0
  236. package/dist/protocol/index.d.ts.map +1 -0
  237. package/dist/protocol/index.js +272 -0
  238. package/dist/protocol/index.js.map +1 -0
  239. package/dist/testing/index.d.ts +47 -0
  240. package/dist/testing/index.d.ts.map +1 -0
  241. package/dist/testing/index.js +145 -0
  242. package/dist/testing/index.js.map +1 -0
  243. package/package.json +118 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Brian Brady
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,67 @@
1
- # Temporary Holding Version
1
+ # @oddkin/workbench
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Metadata-driven documentation tooling for any custom-element library. **Status:** the DOM-free contract entries, the data files, the shell, panels, isolated previews, Theme Studio, and the Node tooling (`oddkin analyze`, `oddkin workbench dev`, `oddkin workbench build`) exist (design: [workbench.md](../../docs/architecture/workbench.md) §2.1 and §5.1).
4
+
5
+ **Stability:** Workbench is versioned `1.0.0` with the other Oddkin packages, but its own contracts are **not frozen by 1.0.0** ([ADR 0047](../../docs/architecture/decisions/0047-m8-checkpoint-candidate-v1-contracts.md)): the configuration (`workbench-config.v1`), the page registry (`page-registry.v1`), authored content (`docs-content.v1`), example links (`share-payload.v1`), Theme Studio compositions (`theme-studio-composition.v1`), the preview frame protocol, and the conformance suites and results (`conformance.v1`, `conformance-result.v1`). Until they are frozen they can change in a minor release ([release-process.md](../../docs/release-process.md) §1). The format `defineExamples()` writes, `examples.v1`, is a candidate v1 contract.
6
+
7
+ | Entry | Contents |
8
+ | --- | --- |
9
+ | `@oddkin/workbench/examples` | `defineExamples(tag, examples)`, typed from `HTMLElementTagNameMap` (unknown property keys and wrong value types are compile errors); `toExamplesJSON` (the `examples.v1` serializable layer, with fixtures reduced to `hasFixture`); `checkExamples(set, contract)` for plain-JavaScript authors; `exampleId` |
10
+ | `@oddkin/workbench/config` | `defineWorkbenchConfig`, `resolveWorkbenchConfig` (defaults and diagnostics matching `workbench-config.v1`), `defineDocs` (`docs-content.v1`) |
11
+ | `@oddkin/workbench/pages` | `buildPageRegistry({ config, cem, metadata?, docs? })` producing `page-registry.v1`, plus the projections every navigation surface uses: `primaryNavigation`, `sidebar`, `cards`, `searchEntries`, `onThisPage`, `href`, `resolveLegacyRoute`, `staticRedirects` |
12
+ | `@oddkin/workbench/data` | `buildWorkbenchData({ config, libraries, docs?, tokens?, diagnostics? })`: one or more packages' CEM and enrichment, authored content, and a token inspection → a small index (registry, branding, libraries, the components that can preview, diagnostics) and one data file per visible page (`pageFile(id)`), serialized byte-stably with `serializeData`; `createImportMap(packages, { base, versioned?, conditions? })` builds import maps from package `exports`, and `resolveExport` resolves one subpath to its file |
13
+ | `@oddkin/workbench/node` | Node only. `buildWorkbench({ config?, events? })` loads `oddkin.config.*` (TypeScript through Node's type stripping), the documented packages and their dependencies, their manifests, enrichment, and referenced token inspection, and authored `oddkin.docs.*`, and returns the data files, both import maps, and the shell page; `buildStatic` (`workbench build`), `shellPaths`, `startDevServer`, `writeGenerated` (the marked `.oddkin/workbench/`), `loadWorkbenchConfig`, `loadInputs`, `packageGraph`, and `run` (the command) |
14
+ | `@oddkin/workbench/testing` | `mountExample(set, name, container?)` runs an authored example and its fixture in a browser test; `runConformance` runs a conformance suite with the project's own `expect` |
15
+ | `@oddkin/workbench/panels/{catalog,component,foundation}` | The embeddable panels' classes; `…/define` registers `<oddkin-catalog-panel>`, `<oddkin-component-panel>`, or `<oddkin-foundation-panel>` (see [Embedding the panels](#embedding-the-panels)) |
16
+ | `@oddkin/workbench/frame` | The preview frame runtime, imported by the frame document |
17
+
18
+ The examples, config, pages, and data entries import without a DOM and have no runtime dependencies (`@oddkin/core` is used for types only). Shipped component code never imports them; colocated `*.examples.ts` files may, because they are development-only sources excluded from the package build (ADR 0021).
19
+
20
+ ## The `oddkin` command
21
+
22
+ ```sh
23
+ oddkin analyze <tsconfig.json> [options] # @oddkin/analyzer's own command, same options and exit codes
24
+ oddkin workbench dev [--config <file or directory>] [--port <number>] [--host <name>] [--no-watch]
25
+ oddkin workbench build [--config <file or directory>] [--out <directory>] [--base <path>] [--routing history|hash]
26
+ ```
27
+
28
+ `workbench dev` serves the Workbench at `http://localhost:4175/` (the configured base path): the shell for page paths (history mode; in hash mode only at the entry, like a static host without rewrites), the data under `_oddkin/`, and package files under `_oddkin/m/<name>@<version>/`. It writes the same data into `.oddkin/workbench/`, a directory it marks and owns, watches the configuration, content, manifests, metadata, and the packages' files, and pushes an `update` event over Server-Sent Events (`_oddkin/events`) after each rebuild. An invalid edit keeps the last valid data and reports diagnostics. A watched directory that is deleted or replaced, as reinstalling a package does, is watched again once it is back, and the data is rebuilt from it. Exit codes: 0 success, 1 errors, 2 usage.
29
+
30
+ `workbench build` writes the same layout as files for a static host, by default into `.oddkin/workbench-build/` (marked and owned like the dev directory; an unmarked directory is refused): the shell, `_oddkin/` data and frame document, and each package's published browser files (no type declarations, sources, or source maps). `--base` and `--routing` replace the configuration's `hosting` values. In history mode every page path and legacy path is a copy of the shell, so deep links and refreshes load on a host without rewrites, and `404.html` is the shell for hosts that serve it; legacy paths redirect in the app, keeping their query. Hash mode writes the shell once. Nothing is written when the configuration or metadata has errors; an import-map target that a package does not publish is a `node.build.unpublished` warning. Output is deterministic, and a rebuild rewrites only changed files and removes stale ones. Theme Studio generates in the browser, so the result needs no Node or analyzer backend.
31
+
32
+ ## Embedding the panels
33
+
34
+ A site that renders its own pages can show the catalog, component, and foundation panels without the Workbench shell; the Oddkin docs site does (`apps/docs`). The contract is not frozen by 1.0.0 (see Stability above); `e2e/workbench-embed.spec.ts` checks it with a page of its own in three browsers.
35
+
36
+ 1. **Build the files in Node.** `buildWorkbench({ config, hosting: { basePath } })` from `@oddkin/workbench/node` reads the configuration (`oddkin.config.*`, and authored content in `oddkin.docs.*` beside it), the documented packages, their metadata, and their authored examples. `embedFiles(build)` returns every file the panels and previews load, as paths below the base path, each a string or `{ source }` (a file to copy):
37
+ - `_oddkin/index.json`: the index (registry, branding, libraries, the components whose element registers and so can preview, diagnostics), the panels' `index`;
38
+ - `_oddkin/pages/…json`: one file per visible page, named by `index.pages[id]`, the panels' `page`, with each example's values (read in Node from the `*.examples.ts` sources; a browser cannot build them; for an installed package, which ships no sources, the values its `oddkin.metadata.json` records);
39
+ - `_oddkin/examples/<tag>.js`: for a component whose examples have fixtures, its `*.examples.ts` source with the types stripped, which a frame imports to run the fixture of the example it mounts (the page file names it as `exampleModule`). Fixtures that write markup with `innerHTML` need a frame without enforced Trusted Types, or a policy the site chooses. When the module is not served or a fixture throws, the frame keeps the element and the panel says that the example's setup did not run;
40
+ - `_oddkin/frame.html`, the preview frame document, and the import maps as JSON: `frame.importmap.json` (the documented packages and the frame runtime) and `shell.importmap.json` (also Workbench's own entries, the panels included);
41
+ - `_oddkin/theme-studio.worker.js`, Theme Studio's generator;
42
+ - `_oddkin/m/<name>@<version>/…`: each package's published browser files.
43
+
44
+ Serve them at `<basePath>_oddkin/…`, the URLs `oddkin workbench dev` uses. A `node.build.unpublished` warning names an import-map target that a package does not publish.
45
+ 2. **The frame document.** Previews run in same-origin frames, one realm each. `_oddkin/frame.html` loads the theme stylesheet, maps the packages with an inline import map, and starts the runtime with an inline module script (`import "@oddkin/workbench/frame"`); its body is `<main data-oddkin-preview>`. A site whose policy allows no inline script or style writes its own frame document with the same parts: an element with `data-oddkin-preview`, the theme's stylesheet, an import map that resolves the documented packages and `@oddkin/workbench/frame`, and a module script file that imports `@oddkin/workbench/frame` (`apps/docs/frame.html`). The runtime imports each component's registration entry by its bare specifier through that import map: the `registration` of its `oddkin.metadata.json` entry, or, for a library with a standard Custom Elements Manifest only, the module whose exports hold the tag's `custom-element-definition`, reached through the package's `exports` (the page file names it as `registration`). The host adds `frame` and `generation` query parameters to the frame's URL.
46
+ 3. **The host page.** Import the registrations (`@oddkin/workbench/panels/catalog/define`, `…/component/define`, `…/foundation/define`) with a bundler or through an import map (`shell.importmap.json` maps them), load a theme stylesheet (the panels read Oddkin tokens), and set the panels' properties from the files above.
47
+ 4. **Policy.** Previews need `frame-src 'self'`, and the data fetches `connect-src 'self'`. The panels render with Oddkin's template engine, which works under `require-trusted-types-for 'script'` with `trusted-types oddkin-template`, and need no inline script or style; only the generated frame document has them.
48
+ 5. **Panels.**
49
+
50
+ | Element | Properties | Events and methods |
51
+ | --- | --- | --- |
52
+ | `<oddkin-catalog-panel>` | `index`; `href(route)`, which maps a Workbench route such as `/components/odd-button` to the site's URL for it; `groups`, the categories to list (unset: all); `preview(page)`, a live preview node for a card, the same node for the same page (cards load no component code without it); `query`; `category` | `oddkin-catalog-filter` (`detail: { query, category }`, bubbles, composed) when the reader filters, for the site to mirror in its URL |
53
+ | `<oddkin-component-panel>` | `index`; `page`, the component's page file; `href`; `frameUrl`, the frame document's URL (empty: no preview); `layout`, `'workbench'` (default) or `'reference'`, the documentation page: links to the page's sections, the interactive example without Workbench's preset, preview settings, example link, and event log, and absent items left out instead of labelled; `examplePreview(page, exampleId)`, a live element for an example card, which only a host that loads the library can supply; `sections`, the section ids to show for a host's tabs (`overview`, `interactive-example`, `api`, `examples`, `tokens`, `behavior-and-accessibility`, `guidance`, `source`, `known-limitations`; `null`, the default, shows every section): the others stay rendered but hidden, so the example's preview and controls survive a tab switch, and the panel leaves out its own section links | `previewState` and `previews` (read-only); `replacePreview()` loads fresh frames after the component's code changed. Copy example link (Workbench layout) writes `href(route)` with a `preview` query parameter, which a panel reads when it loads |
54
+ | `<oddkin-foundation-panel>` | `index`, `page`, `href` | — |
55
+
56
+ 6. **Ordering.** A frame handles requests one at a time, in the order they were sent, and `createIsolatedPreview` sends each one after the previous one has settled (its timeout counts from then). An edit made while a preview still loads its module graph applies to the element that mount creates.
57
+
58
+ ## Registry rules
59
+
60
+ - **Inputs:** standard CEM is enough for the Components baseline, previews included when a module the package exports holds the element's `custom-element-definition`. `oddkin.metadata.json` enrichment adds foundations, categories, examples (with their values when the analyzer could read them from the source), products, and `docs` facts. Authored content (`defineDocs`) overrides prose and presentation only; generated facts win.
61
+ - **Stable ids:** `foundations/<slug>`, `components/<tag>`, `theme-studio`, `products`. Routes and the base path can change without changing ids.
62
+ - **Honest absence:** a foundation page appears only when enrichment declares it and at least one section has data. Foundations stays as a destination with `availability: 'unavailable'` when no foundation data exists. Components without enrichment are `partial`. Authored `visible: true` cannot show a page or section whose data is missing.
63
+ - **Theme Studio** is a primary destination and never a Foundations card or sidebar entry. **Products and modes** is absent unless enabled, and hidden with `pages.products.empty` when no products resolve.
64
+ - **Dates** come only from authored content or enrichment facts (authored or source history); unknown dates are omitted and build time is never used.
65
+ - **Legacy routes:** `/playground` → `/components` (or `/components/<tag>` from `?component=`, keeping other parameters); `/foundations/theme-studio` → Theme Studio; `/foundations/products` → Products, or a `products.disabled` notice.
66
+
67
+ Tests: `test/pages.test.ts` (real analyzer output for `examples/core-counter` plus an independent fixture library, validated against `page-registry.v1` and `oddkin-metadata.v1`), `test/data.test.ts` (the published outputs of `@oddkin/components` and `@oddkin/ai`, the default theme's token inspection, the Acme fixture, and import maps from real package `exports`), `test/node-*.test.ts` (throwaway projects with an Acme library, the real components through `tests/fixtures/workbench-oddkin`, the dev server over HTTP and Server-Sent Events, and the built command in a real Node process), `test/config.test.ts`, `test/examples.test.ts`, and the type-level `test/examples.types.ts`.
@@ -0,0 +1,14 @@
1
+ import type { ComponentPageData } from '../data/types.js';
2
+ import type { PageEntry } from '../pages/types.js';
3
+ export interface CardPreviews {
4
+ /** The card's preview host: the same node for the same page. */
5
+ preview(entry: PageEntry): Node | undefined;
6
+ dispose(): void;
7
+ }
8
+ export declare function cardPreviews(options: {
9
+ readonly document: Document;
10
+ readonly frameUrl: string;
11
+ /** The component page's data, or `undefined` when it has none. */
12
+ readonly page: (id: string, signal: AbortSignal) => Promise<ComponentPageData | undefined>;
13
+ }): CardPreviews;
14
+ //# sourceMappingURL=card-previews.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-previews.d.ts","sourceRoot":"","sources":["../../src/app/card-previews.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAInD,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAAC;IAC5C,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;CAC5F,GAAG,YAAY,CAsHf"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Live previews for the shell's catalog cards. The shell registers no library element in its own
3
+ * realm, so each card shows its component's first example in a preview frame, created when the
4
+ * card nears the view and loaded two at a time (a card scrolled past before its turn waits until
5
+ * it comes back); the card's link stays its only stop. Frames are bounded: past `KEPT`, the frames
6
+ * of the cards longest out of view are released and load again when their cards come back, as a
7
+ * frame does whose card a filter took out and back (moving a frame reloads its document).
8
+ */
9
+ import { createScope } from '@oddkin/core/scope';
10
+ import { initialState, presets, registrationOf } from '../panels/model.js';
11
+ import { createIsolatedPreview } from '../preview/frame.js';
12
+ const LOADING = 2;
13
+ /** Frames kept in all; the frames of cards near the view are never released. */
14
+ const KEPT = 12;
15
+ export function cardPreviews(options) {
16
+ const { document: doc, frameUrl } = options;
17
+ const scope = createScope({ name: 'workbench-card-previews' });
18
+ const hosts = new Map();
19
+ const loads = new Map();
20
+ /** Hosts whose load has started: loading, showing a frame, or with nothing to show. */
21
+ const started = new Set();
22
+ /** Frames by host, the card that came near the view longest ago first. */
23
+ const frames = new Map();
24
+ const near = new Set();
25
+ /** Hosts near the view that have not started, in the order they came near. */
26
+ const queue = [];
27
+ let loading = 0;
28
+ const release = (host) => {
29
+ frames.get(host)?.dispose();
30
+ frames.delete(host);
31
+ started.delete(host);
32
+ };
33
+ const trim = () => {
34
+ for (const host of [...frames.keys()]) {
35
+ if (frames.size <= KEPT)
36
+ return;
37
+ if (!near.has(host))
38
+ release(host);
39
+ }
40
+ };
41
+ const next = () => {
42
+ while (loading < LOADING && queue.length && !scope.disposed) {
43
+ const host = queue.shift();
44
+ started.add(host);
45
+ loading++;
46
+ void loads.get(host)()
47
+ .catch(() => { })
48
+ .finally(() => {
49
+ loading--;
50
+ next();
51
+ });
52
+ }
53
+ };
54
+ const observer = new IntersectionObserver((records) => {
55
+ for (const { target, isIntersecting } of records) {
56
+ const at = queue.indexOf(target);
57
+ if (isIntersecting) {
58
+ near.add(target);
59
+ const frame = frames.get(target);
60
+ if (frame) {
61
+ // Most recently near the view: the last to be released.
62
+ frames.delete(target);
63
+ frames.set(target, frame);
64
+ }
65
+ else if (!started.has(target) && at < 0)
66
+ queue.push(target);
67
+ }
68
+ else {
69
+ near.delete(target);
70
+ if (at >= 0)
71
+ queue.splice(at, 1);
72
+ }
73
+ }
74
+ trim();
75
+ next();
76
+ }, { rootMargin: '200px' });
77
+ scope.add(() => {
78
+ observer.disconnect();
79
+ for (const frame of frames.values())
80
+ frame.dispose();
81
+ frames.clear();
82
+ }, 'card previews');
83
+ const load = (host, entry) => async () => {
84
+ const page = await options.page(entry.id, scope.signal);
85
+ const registration = page && registrationOf(page);
86
+ // Released while its page data loaded: it starts again when its card comes back.
87
+ if (!page || registration === undefined || scope.disposed || !started.has(host))
88
+ return;
89
+ const state = initialState(page);
90
+ const fixture = presets(page).some((p) => p.name === state.preset && p.fixture);
91
+ const preview = createIsolatedPreview({
92
+ document: doc,
93
+ container: host,
94
+ frameUrl,
95
+ title: `${entry.title} preview`,
96
+ });
97
+ frames.set(host, preview);
98
+ trim();
99
+ preview.iframe.style.blockSize = '100%';
100
+ preview.iframe.tabIndex = -1;
101
+ await preview.request('mount', {
102
+ tag: page.tag,
103
+ registration,
104
+ example: `example:${page.tag}/${state.preset}`,
105
+ properties: state.properties,
106
+ attributes: state.attributes,
107
+ slots: state.slots,
108
+ styles: state.styles ?? {},
109
+ ...(fixture && page.exampleModule ? { fixture: page.exampleModule } : {}),
110
+ });
111
+ // Moving a frame (a filter took its card out and back) loads a new, empty document.
112
+ const mounted = preview.iframe.contentDocument;
113
+ preview.iframe.addEventListener('load', () => {
114
+ if (frames.get(host) !== preview || preview.iframe.contentDocument === mounted)
115
+ return;
116
+ release(host);
117
+ if (near.has(host))
118
+ queue.push(host);
119
+ next();
120
+ });
121
+ };
122
+ return {
123
+ preview(entry) {
124
+ if (entry.kind !== 'component' || scope.disposed)
125
+ return undefined;
126
+ let host = hosts.get(entry.id);
127
+ if (!host) {
128
+ host = doc.createElement('div');
129
+ host.className = 'wb-card-frame';
130
+ host.inert = true;
131
+ hosts.set(entry.id, host);
132
+ loads.set(host, load(host, entry));
133
+ observer.observe(host);
134
+ }
135
+ return host;
136
+ },
137
+ dispose: () => scope.dispose(),
138
+ };
139
+ }
140
+ //# sourceMappingURL=card-previews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-previews.js","sourceRoot":"","sources":["../../src/app/card-previews.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Built-in shell copy (English). Generic and conditional: it never states facts about the
3
+ * library, which come from generated data. Moves to the localizable `./content` catalog (item 818).
4
+ */
5
+ export declare const COPY: {
6
+ readonly skip: 'Skip to main content';
7
+ readonly loading: 'Loading the Workbench…';
8
+ readonly indexFailed: 'The Workbench index could not be loaded';
9
+ readonly indexIncompatible: 'This index was written by a different Workbench version';
10
+ readonly primaryNavigation: 'Primary';
11
+ readonly search: 'Search';
12
+ readonly searchPlaceholder: 'Search pages';
13
+ readonly componentSearch: 'Search components';
14
+ readonly componentSearchPlaceholder: 'Find a component…';
15
+ readonly componentSearchHint: 'Search by name or purpose';
16
+ readonly componentCount: (count: number) => string;
17
+ readonly categoryCount: (count: number) => string;
18
+ readonly categoryFilter: 'Category';
19
+ readonly allCategories: 'All';
20
+ readonly viewPage: (component: boolean) => "Read more" | "View reference";
21
+ readonly componentResults: (shown: number, total: number) => string;
22
+ readonly noComponentResults: 'No components match these filters.';
23
+ readonly searchResults: (shown: number, total: number) => string;
24
+ readonly appearance: 'Appearance';
25
+ readonly appearances: {
26
+ readonly system: 'System';
27
+ readonly light: 'Light';
28
+ readonly dark: 'Dark';
29
+ };
30
+ readonly overviewEyebrow: 'Design system';
31
+ readonly summaries: Readonly<Record<string, string>>;
32
+ readonly allPages: (title: string) => string;
33
+ readonly showSection: (title: string) => string;
34
+ readonly hideSection: (title: string) => string;
35
+ readonly onThisPage: 'On this page';
36
+ readonly updated: 'Updated';
37
+ readonly sectionPending: 'This Workbench build does not display this section yet.';
38
+ readonly toolPending: 'This Workbench build does not include Theme Studio yet.';
39
+ readonly noFoundations: 'No foundation data supplied';
40
+ readonly noFoundationsHelp: 'Foundation pages appear when the library publishes Oddkin metadata with foundations, generated from a theme configuration.';
41
+ readonly noComponents: 'No components were found in the library’s Custom Elements Manifest.';
42
+ readonly notFound: 'Page not found';
43
+ readonly notFoundText: (path: string) => string;
44
+ readonly pageFailed: 'This page could not be loaded';
45
+ readonly productsDisabled: 'Products and modes is not enabled in this Workbench.';
46
+ readonly stale: (errors: number) => string;
47
+ };
48
+ //# sourceMappingURL=copy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../../src/app/copy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,IAAI;aACf,IAAI,EAAE,sBAAsB;aAC5B,OAAO,EAAE,wBAAwB;aACjC,WAAW,EAAE,yCAAyC;aACtD,iBAAiB,EAAE,yDAAyD;aAC5E,iBAAiB,EAAE,SAAS;aAC5B,MAAM,EAAE,QAAQ;aAChB,iBAAiB,EAAE,cAAc;aACjC,eAAe,EAAE,mBAAmB;aACpC,0BAA0B,EAAE,mBAAmB;aAC/C,mBAAmB,EAAE,2BAA2B;aAChD,cAAc,UAAU,MAAM;aAC9B,aAAa,UAAU,MAAM;aAC7B,cAAc,EAAE,UAAU;aAC1B,aAAa,EAAE,KAAK;aACpB,QAAQ,cAAc,OAAO;aAC7B,gBAAgB,UAAU,MAAM,SAAS,MAAM;aAE/C,kBAAkB,EAAE,oCAAoC;aACxD,aAAa,UAAU,MAAM,SAAS,MAAM;aAM5C,UAAU,EAAE,YAAY;aACxB,WAAW;iBAAI,MAAM,EAAE,QAAQ;iBAAE,KAAK,EAAE,OAAO;iBAAE,IAAI,EAAE,MAAM;;aAC7D,eAAe,EAAE,eAAe;aAChC,SAAS,EAIJ,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;aACrC,QAAQ,UAAU,MAAM;aACxB,WAAW,UAAU,MAAM;aAC3B,WAAW,UAAU,MAAM;aAC3B,UAAU,EAAE,cAAc;aAC1B,OAAO,EAAE,SAAS;aAClB,cAAc,EAAE,yDAAyD;aACzE,WAAW,EAAE,yDAAyD;aACtE,aAAa,EAAE,6BAA6B;aAC5C,iBAAiB,EACf,4HAA4H;aAC9H,YAAY,EAAE,qEAAqE;aACnF,QAAQ,EAAE,gBAAgB;aAC1B,YAAY,SAAS,MAAM;aAC3B,UAAU,EAAE,+BAA+B;aAC3C,gBAAgB,EAAE,sDAAsD;aACxE,KAAK,WAAW,MAAM;CAEd,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Built-in shell copy (English). Generic and conditional: it never states facts about the
3
+ * library, which come from generated data. Moves to the localizable `./content` catalog (item 818).
4
+ */
5
+ export const COPY = {
6
+ skip: 'Skip to main content',
7
+ loading: 'Loading the Workbench…',
8
+ indexFailed: 'The Workbench index could not be loaded',
9
+ indexIncompatible: 'This index was written by a different Workbench version',
10
+ primaryNavigation: 'Primary',
11
+ search: 'Search',
12
+ searchPlaceholder: 'Search pages',
13
+ componentSearch: 'Search components',
14
+ componentSearchPlaceholder: 'Find a component…',
15
+ componentSearchHint: 'Search by name or purpose',
16
+ componentCount: (count) => `${count} ${count === 1 ? 'component' : 'components'}`,
17
+ categoryCount: (count) => `${count} ${count === 1 ? 'category' : 'categories'}`,
18
+ categoryFilter: 'Category',
19
+ allCategories: 'All',
20
+ viewPage: (component) => (component ? 'View reference' : 'Read more'),
21
+ componentResults: (shown, total) => `Showing ${shown} of ${total} ${total === 1 ? 'component' : 'components'}`,
22
+ noComponentResults: 'No components match these filters.',
23
+ searchResults: (shown, total) => total === 0
24
+ ? 'No results'
25
+ : shown < total
26
+ ? `Showing ${shown} of ${total} results`
27
+ : `${total} ${total === 1 ? 'result' : 'results'}`,
28
+ appearance: 'Appearance',
29
+ appearances: { system: 'System', light: 'Light', dark: 'Dark' },
30
+ overviewEyebrow: 'Design system',
31
+ summaries: {
32
+ foundations: 'The shared values and rules that components are built on.',
33
+ components: 'Every documented component, grouped by category.',
34
+ 'theme-studio': 'Edit the theme configuration and preview it on real components.',
35
+ },
36
+ allPages: (title) => `All ${title.toLowerCase()}`,
37
+ showSection: (title) => `Show ${title.toLowerCase()}`,
38
+ hideSection: (title) => `Hide ${title.toLowerCase()}`,
39
+ onThisPage: 'On this page',
40
+ updated: 'Updated',
41
+ sectionPending: 'This Workbench build does not display this section yet.',
42
+ toolPending: 'This Workbench build does not include Theme Studio yet.',
43
+ noFoundations: 'No foundation data supplied',
44
+ noFoundationsHelp: 'Foundation pages appear when the library publishes Oddkin metadata with foundations, generated from a theme configuration.',
45
+ noComponents: 'No components were found in the library’s Custom Elements Manifest.',
46
+ notFound: 'Page not found',
47
+ notFoundText: (path) => `There is no page at ${path}.`,
48
+ pageFailed: 'This page could not be loaded',
49
+ productsDisabled: 'Products and modes is not enabled in this Workbench.',
50
+ stale: (errors) => `The latest changes could not be applied (${errors} ${errors === 1 ? 'error' : 'errors'}); the last valid version is shown.`,
51
+ };
52
+ //# sourceMappingURL=copy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.js","sourceRoot":"","sources":["../../src/app/copy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,IAAI,EAAE,sBAAsB;IAC5B,OAAO,EAAE,wBAAwB;IACjC,WAAW,EAAE,yCAAyC;IACtD,iBAAiB,EAAE,yDAAyD;IAC5E,iBAAiB,EAAE,SAAS;IAC5B,MAAM,EAAE,QAAQ;IAChB,iBAAiB,EAAE,cAAc;IACjC,eAAe,EAAE,mBAAmB;IACpC,0BAA0B,EAAE,mBAAmB;IAC/C,mBAAmB,EAAE,2BAA2B;IAChD,cAAc,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,EAAE;IACzF,aAAa,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,EAAE;IACvF,cAAc,EAAE,UAAU;IAC1B,aAAa,EAAE,KAAK;IACpB,QAAQ,EAAE,CAAC,SAAkB,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,WAAW,CAAC;IAC9E,gBAAgB,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,EAAE,CACjD,WAAW,KAAK,OAAO,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,EAAE;IAC5E,kBAAkB,EAAE,oCAAoC;IACxD,aAAa,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,EAAE,CAC9C,KAAK,KAAK,CAAC;QACT,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,KAAK,GAAG,KAAK;YACb,CAAC,CAAC,WAAW,KAAK,OAAO,KAAK,UAAU;YACxC,CAAC,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE;IACxD,UAAU,EAAE,YAAY;IACxB,WAAW,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE;IAC/D,eAAe,EAAE,eAAe;IAChC,SAAS,EAAE;QACT,WAAW,EAAE,2DAA2D;QACxE,UAAU,EAAE,kDAAkD;QAC9D,cAAc,EAAE,iEAAiE;KAC9C;IACrC,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,WAAW,EAAE,EAAE;IACzD,WAAW,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,QAAQ,KAAK,CAAC,WAAW,EAAE,EAAE;IAC7D,WAAW,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,QAAQ,KAAK,CAAC,WAAW,EAAE,EAAE;IAC7D,UAAU,EAAE,cAAc;IAC1B,OAAO,EAAE,SAAS;IAClB,cAAc,EAAE,yDAAyD;IACzE,WAAW,EAAE,yDAAyD;IACtE,aAAa,EAAE,6BAA6B;IAC5C,iBAAiB,EACf,4HAA4H;IAC9H,YAAY,EAAE,qEAAqE;IACnF,QAAQ,EAAE,gBAAgB;IAC1B,YAAY,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,uBAAuB,IAAI,GAAG;IAC9D,UAAU,EAAE,+BAA+B;IAC3C,gBAAgB,EAAE,sDAAsD;IACxE,KAAK,EAAE,CAAC,MAAc,EAAE,EAAE,CACxB,4CAA4C,MAAM,IAAI,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,qCAAqC;CACtH,CAAC"}
@@ -0,0 +1,3 @@
1
+ /// <reference path="shell.d.ts" preserve="true" />
2
+ import '@oddkin/components/define/card';
3
+ //# sourceMappingURL=define.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/app/define.ts"],"names":[],"mappings":";AAEA,OAAO,gCAAgC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /// <reference path="./shell.ts" preserve="true" />
2
+ // Registers <oddkin-workbench>, its detail-content elements, and the components the shell renders.
3
+ import '@oddkin/components/define/card';
4
+ import { define } from '@oddkin/core/element';
5
+ import { WorkbenchComponentDetail, WorkbenchFoundationDetail } from './details.js';
6
+ import { OddkinWorkbench } from './shell.js';
7
+ define('oddkin-workbench', OddkinWorkbench);
8
+ define('oddkin-workbench-component', WorkbenchComponentDetail);
9
+ define('oddkin-workbench-foundation', WorkbenchFoundationDetail);
10
+ //# sourceMappingURL=define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/app/define.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,mGAAmG;AACnG,OAAO,gCAAgC,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,CAAC,kBAAkB,EAAE,eAAe,CAAC,CAAC;AAC5C,MAAM,CAAC,4BAA4B,EAAE,wBAAwB,CAAC,CAAC;AAC/D,MAAM,CAAC,6BAA6B,EAAE,yBAAyB,CAAC,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The shell's detail content: the public panels' renderers in the light DOM, so the shell's
3
+ * "On this page" links and URL fragments reach the section headings.
4
+ */
5
+ import { ComponentDetailElement } from '../panels/component-detail.js';
6
+ import { FoundationDetailElement } from '../panels/foundation-detail.js';
7
+ import type { StyleInput } from '@oddkin/core/styles';
8
+ export declare class WorkbenchComponentDetail extends ComponentDetailElement {
9
+ static shadowRootOptions: null;
10
+ static styles: StyleInput;
11
+ }
12
+ export declare class WorkbenchFoundationDetail extends FoundationDetailElement {
13
+ static shadowRootOptions: null;
14
+ static styles: StyleInput;
15
+ }
16
+ declare global {
17
+ interface HTMLElementTagNameMap {
18
+ 'oddkin-workbench-component': WorkbenchComponentDetail;
19
+ 'oddkin-workbench-foundation': WorkbenchFoundationDetail;
20
+ }
21
+ }
22
+ //# sourceMappingURL=details.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"details.d.ts","sourceRoot":"","sources":["../../src/app/details.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItD,qBAAa,wBAAyB,SAAQ,sBAAsB;IAClE,OAAgB,iBAAiB,OAAQ;IACzC,OAAgB,MAAM,EAAE,UAAU,CAAkC;CACrE;AAED,qBAAa,yBAA0B,SAAQ,uBAAuB;IACpE,OAAgB,iBAAiB,OAAQ;IACzC,OAAgB,MAAM,EAAE,UAAU,CAAiB;CACpD;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,4BAA4B,EAAE,wBAAwB,CAAC;QACvD,6BAA6B,EAAE,yBAAyB,CAAC;KAC1D;CACF"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The shell's detail content: the public panels' renderers in the light DOM, so the shell's
3
+ * "On this page" links and URL fragments reach the section headings.
4
+ */
5
+ import { ComponentDetailElement } from '../panels/component-detail.js';
6
+ import { FoundationDetailElement } from '../panels/foundation-detail.js';
7
+ import { contentStyles } from '../panels/styles.js';
8
+ import { exampleStyles } from '../panels/example-styles.js';
9
+ export class WorkbenchComponentDetail extends ComponentDetailElement {
10
+ static shadowRootOptions = null;
11
+ static styles = [contentStyles, exampleStyles];
12
+ }
13
+ export class WorkbenchFoundationDetail extends FoundationDetailElement {
14
+ static shadowRootOptions = null;
15
+ static styles = contentStyles;
16
+ }
17
+ //# sourceMappingURL=details.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"details.js","sourceRoot":"","sources":["../../src/app/details.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,OAAO,wBAAyB,SAAQ,sBAAsB;IAClE,MAAM,CAAU,iBAAiB,GAAG,IAAI,CAAC;IACzC,MAAM,CAAU,MAAM,GAAe,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;CACrE;AAED,MAAM,OAAO,yBAA0B,SAAQ,uBAAuB;IACpE,MAAM,CAAU,iBAAiB,GAAG,IAAI,CAAC;IACzC,MAAM,CAAU,MAAM,GAAe,aAAa,CAAC;CACpD"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `@oddkin/workbench/app` — the Workbench shell (workbench.md §2.1): `<oddkin-workbench>` and the
3
+ * DOM-free pieces it is built from (registry routes, search). Registration is the separate,
4
+ * side-effecting `@oddkin/workbench/app/define`.
5
+ */
6
+ export { OddkinWorkbench } from './shell.js';
7
+ export type { WorkbenchSource } from './shell.js';
8
+ export { shellRoutes } from './routes.js';
9
+ export type { PageLoader, ShellView } from './routes.js';
10
+ export { search, searchItems } from './search.js';
11
+ export type { SearchItem } from './search.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/app/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `@oddkin/workbench/app` — the Workbench shell (workbench.md §2.1): `<oddkin-workbench>` and the
3
+ * DOM-free pieces it is built from (registry routes, search). Registration is the separate,
4
+ * side-effecting `@oddkin/workbench/app/define`.
5
+ */
6
+ export { OddkinWorkbench } from './shell.js';
7
+ export { shellRoutes } from './routes.js';
8
+ export { search, searchItems } from './search.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/app/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The shell's routes, generated from the page registry (workbench-pages.md §2): every visible
3
+ * page, legacy routes as redirects, and the Products notice. DOM-free, so it can be tested
4
+ * with `matchRoutes` in Node.
5
+ */
6
+ import { type AnyRoute, type RouteContext } from '@oddkin/app/router';
7
+ import type { PageEntry, PageRegistry } from '../pages/types.js';
8
+ /** What a route level shows: a registry page with the data its loader returned, or a notice. */
9
+ export type ShellView = {
10
+ readonly kind: 'page';
11
+ readonly page: PageEntry;
12
+ readonly data: unknown;
13
+ } | {
14
+ readonly kind: 'notice';
15
+ readonly code: 'products.disabled';
16
+ };
17
+ /** Loads a page's data (and anything its view needs) for one navigation. */
18
+ export type PageLoader = (page: PageEntry, context: RouteContext<unknown, unknown>) => unknown | PromiseLike<unknown>;
19
+ /**
20
+ * Routes for every visible page (nested by path segment), each legacy route of a visible page
21
+ * as a redirect resolved like `resolveLegacyRoute`, `/foundations/products` as a notice when
22
+ * Products and modes is not shown, and, without a home page, `/` redirecting to the first
23
+ * primary destination that has content.
24
+ */
25
+ export declare function shellRoutes(registry: PageRegistry, load: PageLoader): AnyRoute[];
26
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/app/routes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAS,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE7E,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjE,gGAAgG;AAChG,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC3E;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAEpE,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAAG,CACvB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,KACpC,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AA6DpC;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,QAAQ,EAAE,CAgChF"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The shell's routes, generated from the page registry (workbench-pages.md §2): every visible
3
+ * page, legacy routes as redirects, and the Products notice. DOM-free, so it can be tested
4
+ * with `matchRoutes` in Node.
5
+ */
6
+ import { route } from '@oddkin/app/router';
7
+ import { resolveLegacyRoute } from '../pages/navigation.js';
8
+ function level(registry, node, load) {
9
+ const { page, redirect, notice } = node;
10
+ if (page) {
11
+ return {
12
+ name: page.id,
13
+ title: page.title,
14
+ load: async (context) => ({
15
+ kind: 'page',
16
+ page,
17
+ data: await load(page, context),
18
+ }),
19
+ };
20
+ }
21
+ if (redirect) {
22
+ const from = redirect;
23
+ return {
24
+ redirect: ({ location }) => {
25
+ const result = resolveLegacyRoute(registry, from, location.search);
26
+ return result?.kind === 'redirect' ? result.to : '/';
27
+ },
28
+ };
29
+ }
30
+ if (notice) {
31
+ return {
32
+ title: 'Products and modes',
33
+ load: () => ({ kind: 'notice', code: notice }),
34
+ };
35
+ }
36
+ return undefined;
37
+ }
38
+ function toRoutes(registry, node, load) {
39
+ const routes = [];
40
+ for (const [segment, child] of node.children) {
41
+ const own = level(registry, child, load);
42
+ const children = toRoutes(registry, child, load);
43
+ if (children.length)
44
+ routes.push(route({
45
+ path: segment,
46
+ children: own ? [route({ ...own, path: '' }), ...children] : children,
47
+ }));
48
+ else if (own)
49
+ routes.push(route({ ...own, path: segment }));
50
+ }
51
+ return routes;
52
+ }
53
+ /**
54
+ * Routes for every visible page (nested by path segment), each legacy route of a visible page
55
+ * as a redirect resolved like `resolveLegacyRoute`, `/foundations/products` as a notice when
56
+ * Products and modes is not shown, and, without a home page, `/` redirecting to the first
57
+ * primary destination that has content.
58
+ */
59
+ export function shellRoutes(registry, load) {
60
+ const root = { children: new Map() };
61
+ const at = (path) => {
62
+ let node = root;
63
+ for (const segment of path.split('/').filter(Boolean)) {
64
+ let child = node.children.get(segment);
65
+ if (!child)
66
+ node.children.set(segment, (child = { children: new Map() }));
67
+ node = child;
68
+ }
69
+ return node;
70
+ };
71
+ for (const page of registry.pages)
72
+ if (page.visible)
73
+ at(page.route).page = page;
74
+ for (const page of registry.pages) {
75
+ if (!page.visible)
76
+ continue;
77
+ for (const legacy of page.legacyRoutes ?? []) {
78
+ const node = at(legacy);
79
+ if (!node.page)
80
+ node.redirect = legacy;
81
+ }
82
+ }
83
+ if (!registry.pages.some((p) => p.id === 'products' && p.visible)) {
84
+ const node = at('/foundations/products');
85
+ if (!node.page)
86
+ node.notice = 'products.disabled';
87
+ }
88
+ const own = level(registry, root, load);
89
+ // Without a home page, the base URL goes to the first primary destination with content: a
90
+ // library without foundation data opens on its components, not on an explanation.
91
+ const primary = registry.primaryNav.flatMap((id) => registry.pages.filter((p) => p.id === id && p.visible));
92
+ const first = primary.find((p) => p.availability !== 'unavailable') ?? primary[0];
93
+ const entry = own ?? (first && first.route !== '/' ? { redirect: first.route } : undefined);
94
+ return [...(entry ? [route({ ...entry, path: '' })] : []), ...toRoutes(registry, root, load)];
95
+ }
96
+ //# sourceMappingURL=routes.js.map