@scaffold-ui-kit/preview 0.0.0-stage → 2.3.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 (350) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1868 -2
  3. package/application/host/bootstrap.ts +43 -0
  4. package/application/host/main.ts +31 -0
  5. package/application/host/shell-host.ts +47 -0
  6. package/application/index.html +12 -0
  7. package/application/lib/base-url.ts +15 -0
  8. package/application/lib/no-code-block.ts +7 -0
  9. package/application/lib/no-setup.ts +3 -0
  10. package/application/lib/no-views.ts +6 -0
  11. package/application/preview/code/code.html +12 -0
  12. package/application/preview/code/main.ts +89 -0
  13. package/application/preview/globals.ts +254 -0
  14. package/application/preview/html/main.ts +93 -0
  15. package/application/preview/html/preview.html +12 -0
  16. package/application/preview/react/main.tsx +146 -0
  17. package/application/preview/react/preview.html +12 -0
  18. package/application/preview/svelte/Preview.svelte +104 -0
  19. package/application/preview/svelte/main.ts +13 -0
  20. package/application/preview/svelte/preview.html +12 -0
  21. package/application/preview/svelte/story-props.ts +172 -0
  22. package/application/preview/view/main.ts +124 -0
  23. package/application/preview/view/view.html +12 -0
  24. package/application/preview/vue/main.ts +145 -0
  25. package/application/preview/vue/preview.html +12 -0
  26. package/application/shells/charcell/components/Application/index.css +37 -0
  27. package/application/shells/charcell/components/Application/index.tsx +65 -0
  28. package/application/shells/charcell/components/Bar/index.css +68 -0
  29. package/application/shells/charcell/components/Bar/index.tsx +60 -0
  30. package/application/shells/charcell/components/CodePane/index.css +60 -0
  31. package/application/shells/charcell/components/CodePane/index.tsx +113 -0
  32. package/application/shells/charcell/components/ComponentStage/index.css +26 -0
  33. package/application/shells/charcell/components/ComponentStage/index.tsx +110 -0
  34. package/application/shells/charcell/components/Control/index.css +20 -0
  35. package/application/shells/charcell/components/Control/index.tsx +118 -0
  36. package/application/shells/charcell/components/CopyLink/index.tsx +35 -0
  37. package/application/shells/charcell/components/Description/index.css +6 -0
  38. package/application/shells/charcell/components/Description/index.tsx +24 -0
  39. package/application/shells/charcell/components/EditorAddDialog/index.css +9 -0
  40. package/application/shells/charcell/components/EditorAddDialog/index.tsx +158 -0
  41. package/application/shells/charcell/components/EditorPanel/index.css +48 -0
  42. package/application/shells/charcell/components/EditorPanel/index.tsx +496 -0
  43. package/application/shells/charcell/components/EventsRow/index.css +42 -0
  44. package/application/shells/charcell/components/EventsRow/index.tsx +68 -0
  45. package/application/shells/charcell/components/HeadingRow/index.css +11 -0
  46. package/application/shells/charcell/components/HeadingRow/index.tsx +16 -0
  47. package/application/shells/charcell/components/Hint/index.css +16 -0
  48. package/application/shells/charcell/components/Hint/index.tsx +81 -0
  49. package/application/shells/charcell/components/HtmlWarnings/index.tsx +45 -0
  50. package/application/shells/charcell/components/KeyHints/index.css +27 -0
  51. package/application/shells/charcell/components/KeyHints/index.tsx +106 -0
  52. package/application/shells/charcell/components/KitCodeBlock/index.css +55 -0
  53. package/application/shells/charcell/components/KitCodeBlock/index.tsx +18 -0
  54. package/application/shells/charcell/components/Markdown/index.tsx +16 -0
  55. package/application/shells/charcell/components/Navigation/index.css +13 -0
  56. package/application/shells/charcell/components/Navigation/index.tsx +98 -0
  57. package/application/shells/charcell/components/NavigationSplit/index.css +16 -0
  58. package/application/shells/charcell/components/NavigationSplit/index.tsx +82 -0
  59. package/application/shells/charcell/components/OptionControls/index.tsx +93 -0
  60. package/application/shells/charcell/components/PaneStatus/index.css +7 -0
  61. package/application/shells/charcell/components/PaneStatus/index.tsx +47 -0
  62. package/application/shells/charcell/components/PaneWidth/index.tsx +45 -0
  63. package/application/shells/charcell/components/PreviewGrid/index.css +39 -0
  64. package/application/shells/charcell/components/PreviewGrid/index.tsx +344 -0
  65. package/application/shells/charcell/components/PreviewPane/index.css +127 -0
  66. package/application/shells/charcell/components/PreviewPane/index.tsx +182 -0
  67. package/application/shells/charcell/components/PromptBar/index.tsx +133 -0
  68. package/application/shells/charcell/components/ScenarioRow/index.css +36 -0
  69. package/application/shells/charcell/components/ScenarioRow/index.tsx +71 -0
  70. package/application/shells/charcell/components/Select/index.tsx +48 -0
  71. package/application/shells/charcell/components/SlotOutlet/index.css +7 -0
  72. package/application/shells/charcell/components/SlotOutlet/index.tsx +25 -0
  73. package/application/shells/charcell/components/StageBar/index.css +7 -0
  74. package/application/shells/charcell/components/StageBar/index.tsx +109 -0
  75. package/application/shells/charcell/components/StageSplit/index.css +11 -0
  76. package/application/shells/charcell/components/StageSplit/index.tsx +99 -0
  77. package/application/shells/charcell/components/StatusBar/index.tsx +30 -0
  78. package/application/shells/charcell/components/ViewControls/index.css +15 -0
  79. package/application/shells/charcell/components/ViewControls/index.tsx +58 -0
  80. package/application/shells/charcell/components/ViewHeading/index.css +13 -0
  81. package/application/shells/charcell/components/ViewHeading/index.tsx +8 -0
  82. package/application/shells/charcell/components/ViewStage/index.css +9 -0
  83. package/application/shells/charcell/components/ViewStage/index.tsx +25 -0
  84. package/application/shells/charcell/components/WrapToggle/index.tsx +27 -0
  85. package/application/shells/charcell/configuration.ts +45 -0
  86. package/application/shells/charcell/controller-context.ts +27 -0
  87. package/application/shells/charcell/fit-labels.ts +42 -0
  88. package/application/shells/charcell/html-warnings.ts +18 -0
  89. package/application/shells/charcell/index.tsx +82 -0
  90. package/application/shells/charcell/page-loading.html +57 -0
  91. package/application/shells/charcell/placement.ts +24 -0
  92. package/application/shells/charcell/shell-options.ts +23 -0
  93. package/application/shells/charcell/shell.css +37 -0
  94. package/application/shells/charcell/shell.tsx +25 -0
  95. package/application/shells/charcell/split-reset.ts +47 -0
  96. package/application/shells/charcell/stacked-rows.ts +117 -0
  97. package/application/shells/charcell/stage-toggles.ts +93 -0
  98. package/application/types/manifest.d.ts +6 -0
  99. package/application/types/preview-configuration.d.ts +10 -0
  100. package/application/types/preview-shell.d.ts +7 -0
  101. package/application/types/react-registry.d.ts +15 -0
  102. package/application/types/vue-registry.d.ts +6 -0
  103. package/dist/build.d.ts +24 -0
  104. package/dist/build.js +34 -0
  105. package/dist/check.d.ts +48 -0
  106. package/dist/check.js +781 -0
  107. package/dist/cli.d.ts +2 -0
  108. package/dist/cli.js +88 -0
  109. package/dist/context.d.ts +179 -0
  110. package/dist/context.js +783 -0
  111. package/dist/controller/catalog.d.ts +339 -0
  112. package/dist/controller/catalog.js +367 -0
  113. package/dist/controller/create-preview-controller.d.ts +532 -0
  114. package/dist/controller/create-preview-controller.js +1707 -0
  115. package/dist/controller/editor-tree.d.ts +161 -0
  116. package/dist/controller/editor-tree.js +373 -0
  117. package/dist/controller/html-source.d.ts +62 -0
  118. package/dist/controller/html-source.js +61 -0
  119. package/dist/controller/index.d.ts +9 -0
  120. package/dist/controller/index.js +12 -0
  121. package/dist/controller/persistence.d.ts +191 -0
  122. package/dist/controller/persistence.js +315 -0
  123. package/dist/controller/regeneration.d.ts +30 -0
  124. package/dist/controller/regeneration.js +27 -0
  125. package/dist/controller/source.d.ts +80 -0
  126. package/dist/controller/source.js +236 -0
  127. package/dist/controller/store.d.ts +27 -0
  128. package/dist/controller/store.js +40 -0
  129. package/dist/declarations/application/shells/charcell/components/Application/index.d.ts +2 -0
  130. package/dist/declarations/application/shells/charcell/components/Bar/index.d.ts +11 -0
  131. package/dist/declarations/application/shells/charcell/components/CodePane/index.d.ts +5 -0
  132. package/dist/declarations/application/shells/charcell/components/ComponentStage/index.d.ts +2 -0
  133. package/dist/declarations/application/shells/charcell/components/Control/index.d.ts +9 -0
  134. package/dist/declarations/application/shells/charcell/components/CopyLink/index.d.ts +1 -0
  135. package/dist/declarations/application/shells/charcell/components/Description/index.d.ts +5 -0
  136. package/dist/declarations/application/shells/charcell/components/EditorAddDialog/index.d.ts +6 -0
  137. package/dist/declarations/application/shells/charcell/components/EditorPanel/index.d.ts +2 -0
  138. package/dist/declarations/application/shells/charcell/components/EventsRow/index.d.ts +4 -0
  139. package/dist/declarations/application/shells/charcell/components/HeadingRow/index.d.ts +6 -0
  140. package/dist/declarations/application/shells/charcell/components/Hint/index.d.ts +11 -0
  141. package/dist/declarations/application/shells/charcell/components/HtmlWarnings/index.d.ts +5 -0
  142. package/dist/declarations/application/shells/charcell/components/KeyHints/index.d.ts +2 -0
  143. package/dist/declarations/application/shells/charcell/components/KitCodeBlock/index.d.ts +5 -0
  144. package/dist/declarations/application/shells/charcell/components/Markdown/index.d.ts +3 -0
  145. package/dist/declarations/application/shells/charcell/components/Navigation/index.d.ts +8 -0
  146. package/dist/declarations/application/shells/charcell/components/NavigationSplit/index.d.ts +7 -0
  147. package/dist/declarations/application/shells/charcell/components/OptionControls/index.d.ts +7 -0
  148. package/dist/declarations/application/shells/charcell/components/PaneStatus/index.d.ts +2 -0
  149. package/dist/declarations/application/shells/charcell/components/PaneWidth/index.d.ts +4 -0
  150. package/dist/declarations/application/shells/charcell/components/PreviewGrid/index.d.ts +7 -0
  151. package/dist/declarations/application/shells/charcell/components/PreviewPane/index.d.ts +9 -0
  152. package/dist/declarations/application/shells/charcell/components/PromptBar/index.d.ts +1 -0
  153. package/dist/declarations/application/shells/charcell/components/ScenarioRow/index.d.ts +2 -0
  154. package/dist/declarations/application/shells/charcell/components/Select/index.d.ts +16 -0
  155. package/dist/declarations/application/shells/charcell/components/SlotOutlet/index.d.ts +5 -0
  156. package/dist/declarations/application/shells/charcell/components/StageBar/index.d.ts +2 -0
  157. package/dist/declarations/application/shells/charcell/components/StageSplit/index.d.ts +17 -0
  158. package/dist/declarations/application/shells/charcell/components/StatusBar/index.d.ts +1 -0
  159. package/dist/declarations/application/shells/charcell/components/ViewControls/index.d.ts +9 -0
  160. package/dist/declarations/application/shells/charcell/components/ViewHeading/index.d.ts +4 -0
  161. package/dist/declarations/application/shells/charcell/components/ViewStage/index.d.ts +4 -0
  162. package/dist/declarations/application/shells/charcell/components/WrapToggle/index.d.ts +1 -0
  163. package/dist/declarations/application/shells/charcell/configuration.d.ts +31 -0
  164. package/dist/declarations/application/shells/charcell/controller-context.d.ts +9 -0
  165. package/dist/declarations/application/shells/charcell/fit-labels.d.ts +18 -0
  166. package/dist/declarations/application/shells/charcell/html-warnings.d.ts +5 -0
  167. package/dist/declarations/application/shells/charcell/index.d.ts +39 -0
  168. package/dist/declarations/application/shells/charcell/placement.d.ts +7 -0
  169. package/dist/declarations/application/shells/charcell/shell-options.d.ts +15 -0
  170. package/dist/declarations/application/shells/charcell/shell.d.ts +9 -0
  171. package/dist/declarations/application/shells/charcell/split-reset.d.ts +30 -0
  172. package/dist/declarations/application/shells/charcell/stacked-rows.d.ts +58 -0
  173. package/dist/declarations/application/shells/charcell/stage-toggles.d.ts +28 -0
  174. package/dist/declarations/src/controller/catalog.d.ts +339 -0
  175. package/dist/declarations/src/controller/create-preview-controller.d.ts +532 -0
  176. package/dist/declarations/src/controller/editor-tree.d.ts +161 -0
  177. package/dist/declarations/src/controller/html-source.d.ts +62 -0
  178. package/dist/declarations/src/controller/index.d.ts +9 -0
  179. package/dist/declarations/src/controller/persistence.d.ts +191 -0
  180. package/dist/declarations/src/controller/regeneration.d.ts +30 -0
  181. package/dist/declarations/src/controller/source.d.ts +80 -0
  182. package/dist/declarations/src/controller/store.d.ts +27 -0
  183. package/dist/declarations/src/emit/common.d.ts +103 -0
  184. package/dist/declarations/src/emit/manifest.d.ts +138 -0
  185. package/dist/declarations/src/emit/source.d.ts +87 -0
  186. package/dist/declarations/src/lib/jsx-roots.d.ts +22 -0
  187. package/dist/declarations/src/lib/module-of-source.d.ts +18 -0
  188. package/dist/declarations/src/lib/naming.d.ts +29 -0
  189. package/dist/declarations/src/lib/react-spelling.d.ts +68 -0
  190. package/dist/declarations/src/lib/scenario-story.d.ts +41 -0
  191. package/dist/declarations/src/lib/slot-fill.d.ts +15 -0
  192. package/dist/declarations/src/lib/types.d.ts +741 -0
  193. package/dist/declarations/src/options.d.ts +224 -0
  194. package/dist/declarations/src/protocol.d.ts +541 -0
  195. package/dist/declarations/src/react-view.d.ts +22 -0
  196. package/dist/declarations/src/react.d.ts +17 -0
  197. package/dist/declarations/src/shell.d.ts +99 -0
  198. package/dist/declarations/src/views.d.ts +152 -0
  199. package/dist/development-server.d.ts +121 -0
  200. package/dist/development-server.js +331 -0
  201. package/dist/emit/common.d.ts +103 -0
  202. package/dist/emit/common.js +170 -0
  203. package/dist/emit/html-source.d.ts +46 -0
  204. package/dist/emit/html-source.js +322 -0
  205. package/dist/emit/manifest.d.ts +138 -0
  206. package/dist/emit/manifest.js +74 -0
  207. package/dist/emit/react.d.ts +16 -0
  208. package/dist/emit/react.js +210 -0
  209. package/dist/emit/scratch.d.ts +40 -0
  210. package/dist/emit/scratch.js +116 -0
  211. package/dist/emit/source.d.ts +87 -0
  212. package/dist/emit/source.js +789 -0
  213. package/dist/emit/svelte.d.ts +9 -0
  214. package/dist/emit/svelte.js +177 -0
  215. package/dist/emit/vue.d.ts +2 -0
  216. package/dist/emit/vue.js +152 -0
  217. package/dist/enumerate.d.ts +2 -0
  218. package/dist/enumerate.js +162 -0
  219. package/dist/generate.d.ts +43 -0
  220. package/dist/generate.js +302 -0
  221. package/dist/html-stylesheet.d.ts +43 -0
  222. package/dist/html-stylesheet.js +145 -0
  223. package/dist/index.d.ts +18 -0
  224. package/dist/index.js +21 -0
  225. package/dist/landing.d.ts +16 -0
  226. package/dist/landing.js +45 -0
  227. package/dist/lib/exported-types.d.ts +42 -0
  228. package/dist/lib/exported-types.js +86 -0
  229. package/dist/lib/html-attributes.d.ts +2 -0
  230. package/dist/lib/html-attributes.js +111 -0
  231. package/dist/lib/html-warnings.d.ts +19 -0
  232. package/dist/lib/html-warnings.js +79 -0
  233. package/dist/lib/jsx-roots.d.ts +22 -0
  234. package/dist/lib/jsx-roots.js +243 -0
  235. package/dist/lib/module-of-source.d.ts +18 -0
  236. package/dist/lib/module-of-source.js +24 -0
  237. package/dist/lib/naming.d.ts +29 -0
  238. package/dist/lib/naming.js +56 -0
  239. package/dist/lib/parse-type.d.ts +32 -0
  240. package/dist/lib/parse-type.js +103 -0
  241. package/dist/lib/react-spelling.d.ts +68 -0
  242. package/dist/lib/react-spelling.js +197 -0
  243. package/dist/lib/scenario-story.d.ts +41 -0
  244. package/dist/lib/scenario-story.js +84 -0
  245. package/dist/lib/slot-fill.d.ts +15 -0
  246. package/dist/lib/slot-fill.js +92 -0
  247. package/dist/lib/template-files.d.ts +14 -0
  248. package/dist/lib/template-files.js +25 -0
  249. package/dist/lib/types.d.ts +741 -0
  250. package/dist/lib/types.js +13 -0
  251. package/dist/lib/void-elements.d.ts +7 -0
  252. package/dist/lib/void-elements.js +13 -0
  253. package/dist/load.d.ts +37 -0
  254. package/dist/load.js +82 -0
  255. package/dist/model.d.ts +44 -0
  256. package/dist/model.js +464 -0
  257. package/dist/options.d.ts +224 -0
  258. package/dist/options.js +223 -0
  259. package/dist/prop-types.d.ts +26 -0
  260. package/dist/prop-types.js +102 -0
  261. package/dist/props-from.d.ts +84 -0
  262. package/dist/props-from.js +510 -0
  263. package/dist/protocol.d.ts +541 -0
  264. package/dist/protocol.js +819 -0
  265. package/dist/react-view.d.ts +22 -0
  266. package/dist/react-view.js +36 -0
  267. package/dist/react.d.ts +17 -0
  268. package/dist/react.js +23 -0
  269. package/dist/sass-importers.d.ts +26 -0
  270. package/dist/sass-importers.js +32 -0
  271. package/dist/shell-acceptance.d.ts +21 -0
  272. package/dist/shell-acceptance.js +37 -0
  273. package/dist/shell-page.d.ts +36 -0
  274. package/dist/shell-page.js +90 -0
  275. package/dist/shell-peers.d.ts +38 -0
  276. package/dist/shell-peers.js +61 -0
  277. package/dist/shell.d.ts +99 -0
  278. package/dist/shell.js +52 -0
  279. package/dist/shells/charcell/page.d.ts +8 -0
  280. package/dist/shells/charcell/page.js +69 -0
  281. package/dist/story-source.d.ts +113 -0
  282. package/dist/story-source.js +271 -0
  283. package/dist/story.d.ts +5 -0
  284. package/dist/story.js +28 -0
  285. package/dist/testing.d.ts +218 -0
  286. package/dist/testing.js +284 -0
  287. package/dist/views.d.ts +152 -0
  288. package/dist/views.js +7 -0
  289. package/dist/vite-configuration.d.ts +114 -0
  290. package/dist/vite-configuration.js +578 -0
  291. package/package.json +123 -3
  292. package/src/build.ts +51 -0
  293. package/src/check.ts +1062 -0
  294. package/src/cli.ts +123 -0
  295. package/src/context.ts +1168 -0
  296. package/src/controller/catalog.ts +719 -0
  297. package/src/controller/create-preview-controller.ts +2683 -0
  298. package/src/controller/editor-tree.ts +530 -0
  299. package/src/controller/html-source.ts +114 -0
  300. package/src/controller/index.ts +105 -0
  301. package/src/controller/persistence.ts +403 -0
  302. package/src/controller/regeneration.ts +55 -0
  303. package/src/controller/source.ts +333 -0
  304. package/src/controller/store.ts +55 -0
  305. package/src/development-server.ts +442 -0
  306. package/src/emit/common.ts +201 -0
  307. package/src/emit/html-source.ts +402 -0
  308. package/src/emit/manifest.ts +241 -0
  309. package/src/emit/react.ts +263 -0
  310. package/src/emit/scratch.ts +175 -0
  311. package/src/emit/source.ts +1094 -0
  312. package/src/emit/svelte.ts +230 -0
  313. package/src/emit/vue.ts +194 -0
  314. package/src/enumerate.ts +220 -0
  315. package/src/generate.ts +443 -0
  316. package/src/html-stylesheet.ts +173 -0
  317. package/src/index.ts +60 -0
  318. package/src/landing.ts +58 -0
  319. package/src/lib/exported-types.ts +114 -0
  320. package/src/lib/html-attributes.ts +113 -0
  321. package/src/lib/html-warnings.ts +81 -0
  322. package/src/lib/jsx-roots.ts +238 -0
  323. package/src/lib/module-of-source.ts +26 -0
  324. package/src/lib/naming.ts +65 -0
  325. package/src/lib/parse-type.ts +143 -0
  326. package/src/lib/react-spelling.ts +235 -0
  327. package/src/lib/scenario-story.ts +97 -0
  328. package/src/lib/slot-fill.ts +102 -0
  329. package/src/lib/template-files.ts +31 -0
  330. package/src/lib/types.ts +778 -0
  331. package/src/lib/void-elements.ts +15 -0
  332. package/src/load.ts +144 -0
  333. package/src/model.ts +568 -0
  334. package/src/options.ts +435 -0
  335. package/src/prop-types.ts +133 -0
  336. package/src/props-from.ts +678 -0
  337. package/src/protocol.ts +1135 -0
  338. package/src/react-view.ts +45 -0
  339. package/src/react.ts +34 -0
  340. package/src/sass-importers.ts +45 -0
  341. package/src/shell-acceptance.ts +45 -0
  342. package/src/shell-page.ts +133 -0
  343. package/src/shell-peers.ts +70 -0
  344. package/src/shell.ts +194 -0
  345. package/src/shells/charcell/page.ts +82 -0
  346. package/src/story-source.ts +391 -0
  347. package/src/story.ts +42 -0
  348. package/src/testing.ts +497 -0
  349. package/src/views.ts +183 -0
  350. package/src/vite-configuration.ts +675 -0
package/src/options.ts ADDED
@@ -0,0 +1,435 @@
1
+ // The composable preview-options contract: the preview block's `options`,
2
+ // `canvas`, `mount` and `viewCanvas`, and the derived helpers the host +
3
+ // iframes share. Browser-safe - no node imports; both the Vite app and the
4
+ // kit's configuration type against this file.
5
+
6
+ import type {
7
+ PreviewPaneWidth,
8
+ PreviewThemeConfiguration,
9
+ } from './lib/types.js';
10
+
11
+ /** An option's runtime value: a string (a choice) or a boolean. */
12
+ export type OptionValue = string | boolean;
13
+
14
+ /** One value a choice option offers. */
15
+ export interface OptionChoice {
16
+ value: string;
17
+ label: string;
18
+ }
19
+
20
+ /** What kind of value an option holds: one of its `values`, or a boolean. */
21
+ export type OptionKind = 'choice' | 'boolean';
22
+
23
+ /** The group of the kit's own settings, such as its theme: what sets up the
24
+ * kit as a whole rather than the setting a component is shown in. */
25
+ export const KIT_OPTION_GROUP = 'kit';
26
+
27
+ /** The group of the options that set what a component is shown in, such as a
28
+ * color mode, a surface or the padding around it. An option without a group
29
+ * belongs to it. */
30
+ export const CONTEXT_OPTION_GROUP = 'context';
31
+
32
+ /** The prefix of a shell option's id in the preview's state, address and
33
+ * storage: a shell's `mode` is `shell.mode`, so it cannot collide with a kit
34
+ * option of the same name. */
35
+ export const SHELL_OPTION_PREFIX = 'shell.';
36
+
37
+ /** Which element an apply targets. `'root'` is the preview `<html>` (the
38
+ * default), `'body'` is `<body>`, and `{ ref }` is a named node in the
39
+ * kit's `mount` wrapper (resolved against the ref map). */
40
+ export type ApplyTarget = 'root' | 'body' | { ref: string };
41
+
42
+ /** How an option affects the preview canvas. The union is open by design - a
43
+ * new strategy slots in without touching the registry. `root-attribute` /
44
+ * `class` / `css-var` may carry a `target` to hit a `mount` wrapper
45
+ * node instead of the document root. */
46
+ export type OptionApply =
47
+ /** Set `<attribute>="<value>"` on the target element (default the preview
48
+ * `<html>`); `false` or an empty value removes it. With `omitAtDefault`,
49
+ * the option's default value removes it too, for a kit whose default
50
+ * renders with no attribute, such as a default color mode. */
51
+ | {
52
+ type: 'root-attribute';
53
+ attribute: string;
54
+ target?: ApplyTarget;
55
+ omitAtDefault?: boolean;
56
+ }
57
+ /** Swap the active theme stylesheet by value (`themes/<id>/theme.css`
58
+ * under the URL the preview is served from). */
59
+ | { type: 'stylesheet' }
60
+ /** Toggle `className` on the target by the value's truth; without
61
+ * `className`, set the value as the target's `data-<namePrefix>-preview-class`
62
+ * attribute for the kit's CSS to key off, `false` or an empty value removing
63
+ * it. `on` names `'root'` or `'body'`; `target` names either as well, or
64
+ * a wrapper node, and wins where both are given. */
65
+ | {
66
+ type: 'class';
67
+ on?: 'root' | 'body';
68
+ className?: string;
69
+ target?: ApplyTarget;
70
+ }
71
+ /** Set a CSS custom property to the value on the target element; `false` or
72
+ * an empty value removes it. */
73
+ | { type: 'css-var'; var: string; target?: ApplyTarget };
74
+
75
+ /**
76
+ * One preview option: what it means, never where or how it is shown. Its kind
77
+ * follows from its fields - an option with `values` is a choice among them
78
+ * with a string `default`, and one without is a boolean with a boolean
79
+ * `default` - and the preview's interface picks the widget for that kind.
80
+ */
81
+ export interface PreviewOption {
82
+ /** Stable key - the message payload key and the manifest per-story override key. */
83
+ id: string;
84
+ /** The option's name, as its control is labeled. */
85
+ label: string;
86
+ /** A longer description, such as a control's tooltip; markdown allowed. */
87
+ title?: string;
88
+ /** The values of a choice option. Omitted, the option is a boolean. */
89
+ values?: OptionChoice[];
90
+ /** The starting value: one of `values` for a choice, a boolean otherwise. */
91
+ default: OptionValue;
92
+ /** A free-form hint for where the option belongs: `'context'` (the
93
+ * default) for the setting a component is shown in, such as its color
94
+ * mode or the padding around it; `'kit'` for the kit's own settings, such
95
+ * as its theme; or a name of the kit's choosing for a set that belongs
96
+ * together, such as canvas settings. An interface places a group it does
97
+ * not know in a default place. */
98
+ group?: string;
99
+ /** `global` = one value for the whole session; `story` = a value per
100
+ * selected story, seeded from its story file, that lasts until another story
101
+ * is selected. */
102
+ scope: 'global' | 'story';
103
+ /** How the value hits the canvas. */
104
+ apply: OptionApply;
105
+ }
106
+
107
+ /**
108
+ * An option a shell declares for itself, such as its own color mode or
109
+ * density: a {@link PreviewOption} without `apply` or `scope`. Its value
110
+ * never reaches the kit's iframes; the shell reads it from the preview's
111
+ * state under the id {@link shellOptionId} gives it, and it is global.
112
+ */
113
+ export type PreviewShellOption = Omit<PreviewOption, 'apply' | 'scope'>;
114
+
115
+ /**
116
+ * The id a shell option has in the preview's state, address and storage.
117
+ *
118
+ * @param id - The option's id as the shell declares it.
119
+ * @returns The id prefixed with `shell.`.
120
+ * @example
121
+ * ```ts
122
+ * shellOptionId('mode'); // 'shell.mode'
123
+ * ```
124
+ */
125
+ export function shellOptionId(id: string): string {
126
+ return `${SHELL_OPTION_PREFIX}${id}`;
127
+ }
128
+
129
+ /** The kit-authored canvas styling: what the preview page paints with. The
130
+ * preview's canvas stylesheet is a generic template over these values - the
131
+ * kit's token vocabulary lives HERE, never in the preview. Any wrapper
132
+ * geometry (surface fills, insets, padding) a kit wants lives in `extraCss`,
133
+ * keyed off the nodes it declares in `mount`. Values are raw CSS. */
134
+ export interface PreviewCanvasConfiguration {
135
+ /** Prefix for every preview-owned DOM name - injected element ids, the
136
+ * class-apply data attribute (`data-<prefix>-preview-class`). */
137
+ namePrefix: string;
138
+ /** Canvas page background, painted on `<body>`. Optional: a kit whose
139
+ * stylesheet already states its page canvas (the kit's own `html`/`body`
140
+ * rules, loaded with the theme) omits it, and the preview paints nothing -
141
+ * the preview document *is* the kit's page, and what the page is stays
142
+ * the kit's (and the theme's) call rather than a value restated here. */
143
+ pageBackground?: string;
144
+ /** Extra kit-authored CSS appended to the canvas stylesheet - where a kit
145
+ * styles its `mount` wrapper nodes and any option-driven geometry. */
146
+ extraCss?: string;
147
+ }
148
+
149
+ /** The canvas of a custom view's iframe, which shows a kit-authored view
150
+ * rather than a mounted component: no `mount` wrapper, so none of the
151
+ * component frame's geometry applies. A kit sets it as `viewCanvas`;
152
+ * without it a view gets `canvas.pageBackground` and nothing else. The
153
+ * name prefix stays `canvas.namePrefix`. Options still apply to a view,
154
+ * except one targeting a `mount` node by `ref`, which a view lacks. */
155
+ export interface PreviewViewCanvasConfiguration {
156
+ /** The view page's background, painted on `<body>`. */
157
+ pageBackground?: string;
158
+ /** Extra kit-authored CSS for the view page. */
159
+ extraCss?: string;
160
+ }
161
+
162
+ /** A node in a kit's `mount` wrapper tree - plain, framework-agnostic
163
+ * DOM rendered around the `#app` mount, identical across every preview iframe.
164
+ * The preview knows nothing of what these nodes mean; the kit styles them via
165
+ * `canvas.extraCss` and targets options at them by `ref`. */
166
+ export interface PreviewMountNode {
167
+ /** Element tag. Defaults to `'div'`. */
168
+ tag?: string;
169
+ /** Static attributes rendered on the element (e.g. a surface-cascade attribute). */
170
+ attributes?: Record<string, string>;
171
+ /** Static class names. */
172
+ classes?: string[];
173
+ /** Inline `style` text. */
174
+ style?: string;
175
+ /** Name for the ref map - the handle `apply.target = { ref }` resolves to,
176
+ * and how kit CSS and option targeting address this node. */
177
+ ref?: string;
178
+ /** Child nodes, rendered in order. */
179
+ children?: PreviewMountNode[];
180
+ /** Marks the node the `#app` mount is reparented into. Exactly one node in
181
+ * the tree sets this. */
182
+ mount?: boolean;
183
+ }
184
+
185
+ /**
186
+ * The kind of an option: a choice when it has `values`, a boolean otherwise.
187
+ *
188
+ * @param option - The option.
189
+ * @returns `'choice'` or `'boolean'`.
190
+ * @example
191
+ * ```ts
192
+ * optionKind({ values: [{ value: 'light', label: 'light' }] }); // 'choice'
193
+ * optionKind({}); // 'boolean'
194
+ * ```
195
+ */
196
+ export function optionKind(option: Pick<PreviewOption, 'values'>): OptionKind {
197
+ return option.values !== undefined ? 'choice' : 'boolean';
198
+ }
199
+
200
+ /**
201
+ * The group an option belongs to: its `group`, or `'context'` without one.
202
+ *
203
+ * @param option - The option.
204
+ * @returns The group name.
205
+ * @example
206
+ * ```ts
207
+ * optionGroup({ group: 'kit' }); // 'kit'
208
+ * optionGroup({}); // 'context'
209
+ * ```
210
+ */
211
+ export function optionGroup(option: Pick<PreviewOption, 'group'>): string {
212
+ return option.group ?? CONTEXT_OPTION_GROUP;
213
+ }
214
+
215
+ /**
216
+ * Whether `value` is a value `option` can hold: one of its values for a
217
+ * choice, a boolean for a boolean.
218
+ *
219
+ * @param option - The option.
220
+ * @param value - The candidate value.
221
+ * @returns Whether the option accepts it.
222
+ */
223
+ export function acceptsOptionValue(
224
+ option: Pick<PreviewOption, 'values'>,
225
+ value: unknown
226
+ ): value is OptionValue {
227
+ if (optionKind(option) === 'boolean') return typeof value === 'boolean';
228
+ return (
229
+ typeof value === 'string' &&
230
+ (option.values ?? []).some((choice) => choice.value === value)
231
+ );
232
+ }
233
+
234
+ /** Every key of `T`, each `true`: a record a key list is read from, so a
235
+ * field added to `T` fails to type-check until the record lists it. */
236
+ type KeySet<T> = { [Key in keyof Required<T>]: true };
237
+
238
+ /** The keys an option takes. */
239
+ const OPTION_KEYS = Object.keys({
240
+ id: true,
241
+ label: true,
242
+ title: true,
243
+ values: true,
244
+ default: true,
245
+ group: true,
246
+ scope: true,
247
+ apply: true,
248
+ } satisfies KeySet<PreviewOption>);
249
+
250
+ /** The keys a choice option's value takes. */
251
+ const CHOICE_KEYS = Object.keys({
252
+ value: true,
253
+ label: true,
254
+ } satisfies KeySet<OptionChoice>);
255
+
256
+ /** The keys each apply strategy takes, by its type: one entry per member of
257
+ * `OptionApply`, listing every key of that member. A strategy of a type
258
+ * the preview does not know is not checked. */
259
+ const APPLY_KEY_SETS = {
260
+ 'root-attribute': {
261
+ type: true,
262
+ attribute: true,
263
+ target: true,
264
+ omitAtDefault: true,
265
+ },
266
+ stylesheet: { type: true },
267
+ class: { type: true, on: true, className: true, target: true },
268
+ 'css-var': { type: true, var: true, target: true },
269
+ } satisfies {
270
+ [Type in OptionApply['type']]: KeySet<Extract<OptionApply, { type: Type }>>;
271
+ };
272
+ const APPLY_KEYS: Record<string, readonly string[]> = Object.fromEntries(
273
+ Object.entries(APPLY_KEY_SETS).map(([type, keys]) => [
274
+ type,
275
+ Object.keys(keys),
276
+ ])
277
+ );
278
+
279
+ /** The keys of `value` that are not among `allowed`, `skipped` aside. A
280
+ * key set to `undefined` is absent. */
281
+ function unknownKeys(
282
+ value: object,
283
+ allowed: readonly string[],
284
+ skipped: readonly string[] = []
285
+ ): string[] {
286
+ return Object.entries(value)
287
+ .filter(
288
+ ([key, entry]) =>
289
+ entry !== undefined && !allowed.includes(key) && !skipped.includes(key)
290
+ )
291
+ .map(([key]) => key);
292
+ }
293
+
294
+ /**
295
+ * The problems with a kit's options, each naming the option: a missing or
296
+ * repeated id, a scope other than `global` or `story`, an option whose
297
+ * kind is ambiguous - a choice needs `values` and a string `default` among
298
+ * them, a boolean no `values` and a boolean `default` - and a key the
299
+ * option, one of its values or its `apply` does not take. Empty when every
300
+ * option is sound.
301
+ *
302
+ * @param options - The preview block's `options`.
303
+ * @returns One message per problem.
304
+ */
305
+ export function optionProblems(options: unknown): string[] {
306
+ if (!Array.isArray(options)) return ['preview.options must be an array'];
307
+ const problems: string[] = [];
308
+ const seen = new Set<string>();
309
+ options.forEach((candidate: unknown, index) => {
310
+ const option = candidate as Partial<PreviewOption> &
311
+ Record<string, unknown>;
312
+ if (!option || typeof option !== 'object') {
313
+ problems.push(`preview.options[${index}] must be an object`);
314
+ return;
315
+ }
316
+ const name =
317
+ typeof option.id === 'string' && option.id
318
+ ? `preview.options[${index}] ('${option.id}')`
319
+ : `preview.options[${index}]`;
320
+ if (typeof option.id !== 'string' || !option.id) {
321
+ problems.push(`${name}: id must be a non-empty string`);
322
+ } else if (seen.has(option.id)) {
323
+ problems.push(`${name}: another option has the id '${option.id}'`);
324
+ } else {
325
+ seen.add(option.id);
326
+ }
327
+ if (typeof option.label !== 'string')
328
+ problems.push(`${name}: label must be a string`);
329
+ if (option.scope !== 'global' && option.scope !== 'story')
330
+ problems.push(`${name}: scope must be 'global' or 'story'`);
331
+ if (
332
+ option.group !== undefined &&
333
+ (typeof option.group !== 'string' || !option.group)
334
+ )
335
+ problems.push(`${name}: group must be a non-empty string`);
336
+ if ('slot' in option)
337
+ problems.push(
338
+ `${name}: slot is not an option field - an option says where it belongs with group`
339
+ );
340
+ if ('control' in option)
341
+ problems.push(
342
+ `${name}: control is not an option field - an option with values is a choice, one with a boolean default a boolean`
343
+ );
344
+ // `slot` and `control` are named above with where their meaning went.
345
+ for (const key of unknownKeys(option, OPTION_KEYS, ['slot', 'control']))
346
+ problems.push(
347
+ `${name}: unknown key '${key}'; an option takes ${OPTION_KEYS.join(', ')}`
348
+ );
349
+ if (option.values !== undefined) {
350
+ const values = option.values as unknown;
351
+ if (
352
+ !Array.isArray(values) ||
353
+ values.length === 0 ||
354
+ values.some(
355
+ (choice: Partial<OptionChoice>) =>
356
+ !choice ||
357
+ typeof choice.value !== 'string' ||
358
+ typeof choice.label !== 'string'
359
+ )
360
+ ) {
361
+ problems.push(
362
+ `${name}: values must be a non-empty list of { value, label } strings`
363
+ );
364
+ } else if (typeof option.default !== 'string') {
365
+ problems.push(
366
+ `${name}: a choice option (one with values) needs a string default`
367
+ );
368
+ } else if (
369
+ !values.some((choice: OptionChoice) => choice.value === option.default)
370
+ ) {
371
+ problems.push(
372
+ `${name}: default '${option.default}' is not one of its values`
373
+ );
374
+ }
375
+ } else if (typeof option.default !== 'boolean') {
376
+ problems.push(
377
+ `${name}: an option without values is a boolean and needs a boolean default; a choice lists its values`
378
+ );
379
+ }
380
+ if (Array.isArray(option.values))
381
+ option.values.forEach((choice: unknown, choiceIndex) => {
382
+ if (!choice || typeof choice !== 'object') return;
383
+ for (const key of unknownKeys(choice, CHOICE_KEYS))
384
+ problems.push(
385
+ `${name}: unknown key 'values[${choiceIndex}].${key}'; a value takes ${CHOICE_KEYS.join(', ')}`
386
+ );
387
+ });
388
+ if (!option.apply || typeof option.apply !== 'object') {
389
+ problems.push(
390
+ `${name}: apply must describe how the value hits the canvas`
391
+ );
392
+ } else {
393
+ const type = (option.apply as { type?: unknown }).type;
394
+ if (typeof type === 'string' && Object.hasOwn(APPLY_KEYS, type)) {
395
+ const allowed = APPLY_KEYS[type];
396
+ for (const key of unknownKeys(option.apply, allowed))
397
+ problems.push(
398
+ `${name}: unknown key 'apply.${key}'; a '${type}' apply takes ${allowed.join(', ')}`
399
+ );
400
+ }
401
+ }
402
+ });
403
+ return problems;
404
+ }
405
+
406
+ /** The option registry keyed by id - O(1) lookup when applying a payload. */
407
+ export function optionsById(
408
+ options: PreviewOption[]
409
+ ): Record<string, PreviewOption> {
410
+ return Object.fromEntries(options.map((option) => [option.id, option]));
411
+ }
412
+
413
+ /** Default values for the options in a scope (or all scopes). */
414
+ export function optionDefaults(
415
+ options: Array<Pick<PreviewOption, 'id' | 'default' | 'scope'>>,
416
+ scope?: 'global' | 'story'
417
+ ): Record<string, OptionValue> {
418
+ const out: Record<string, OptionValue> = {};
419
+ for (const option of options) {
420
+ if (!scope || option.scope === scope) out[option.id] = option.default;
421
+ }
422
+ return out;
423
+ }
424
+
425
+ /**
426
+ * The served URL of a theme's stylesheet (the generator copies it there).
427
+ *
428
+ * @param themeId - The theme's id.
429
+ * @param baseUrl - The URL the preview is served under, ending in `/`; the
430
+ * root of the origin, `/`, by default.
431
+ * @returns The stylesheet's URL, such as `/themes/light/theme.css`.
432
+ */
433
+ export function themeHref(themeId: string, baseUrl = '/'): string {
434
+ return `${baseUrl}themes/${themeId}/theme.css`;
435
+ }
@@ -0,0 +1,133 @@
1
+ // Map one component prop → a control spec for the host's controls panel. Pure
2
+ // function of the prop (name, parsed type, default) plus the kit's control
3
+ // hints (its token-config hook - see PreviewControlHints).
4
+
5
+ import { parseType } from './lib/parse-type.js';
6
+ import type {
7
+ PreviewControlHints,
8
+ PropTypeSpecification,
9
+ JsonValue,
10
+ } from './lib/types.js';
11
+
12
+ function isColorProp(name: string): boolean {
13
+ return name === 'color' || /color$/i.test(name);
14
+ }
15
+
16
+ /** Whether the kit gives color tokens for a color select to offer. */
17
+ function hasColorTokens(hints: PreviewControlHints): boolean {
18
+ return (hints.colorTokens ?? []).some((token) => token != null);
19
+ }
20
+
21
+ /** A union of string literals, template literal types (`` `accent-${string}` ``),
22
+ * `string`, or named types - the shapes a color-token type takes, which the
23
+ * color select can offer from the kit's token list whatever they expand to. */
24
+ const NON_STRING_KEYWORDS = new Set([
25
+ 'boolean',
26
+ 'number',
27
+ 'bigint',
28
+ 'object',
29
+ 'symbol',
30
+ 'unknown',
31
+ 'any',
32
+ 'never',
33
+ 'null',
34
+ 'void',
35
+ ]);
36
+
37
+ function isStringLikeType(type: string): boolean {
38
+ return type
39
+ .split('|')
40
+ .map((part) => part.trim())
41
+ .every(
42
+ (part) =>
43
+ part === 'string' ||
44
+ part === 'undefined' ||
45
+ /^'[^']*'$/.test(part) ||
46
+ /^"[^"]*"$/.test(part) ||
47
+ /^`[^`]*`$/.test(part) ||
48
+ (!NON_STRING_KEYWORDS.has(part) &&
49
+ /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(part))
50
+ );
51
+ }
52
+
53
+ /**
54
+ * The control a prop gets in the controls panel, from its declared type. A
55
+ * prop whose name ends in `color`, in any case (`color`, `borderColor`), and
56
+ * whose type is string-like gets the
57
+ * color select, offering the kit's color tokens, when the kit gives any;
58
+ * otherwise it is treated by its declared type like any other prop.
59
+ *
60
+ * @param componentName - The component the prop belongs to.
61
+ * @param propName - The prop's name.
62
+ * @param def - The prop's declared type, resolved type and default.
63
+ * @param hints - The kit's control hints; their `colorTokens` turn on the
64
+ * color select.
65
+ * @returns The prop's control specification.
66
+ */
67
+ export function propTypeForProp(
68
+ componentName: string,
69
+ propName: string,
70
+ def: { type: string; resolvedType?: string; default?: JsonValue },
71
+ hints: PreviewControlHints = {}
72
+ ): PropTypeSpecification {
73
+ const parsed = parseType(def.resolvedType ?? def.type);
74
+ const hasDefault = def.default !== undefined;
75
+
76
+ if (
77
+ hasColorTokens(hints) &&
78
+ isColorProp(propName) &&
79
+ (parsed.kind === 'string' ||
80
+ (parsed.kind === 'enum' &&
81
+ parsed.options.every((option) => typeof option === 'string')) ||
82
+ isStringLikeType(def.type))
83
+ ) {
84
+ return { name: propName, control: 'color-select', include: true };
85
+ }
86
+
87
+ switch (parsed.kind) {
88
+ case 'boolean':
89
+ return { name: propName, control: 'boolean', include: true };
90
+
91
+ case 'number': {
92
+ if (propName === 'size') {
93
+ return {
94
+ name: propName,
95
+ control: 'number',
96
+ number: { min: 0.25, max: 4, step: 0.05 },
97
+ include: true,
98
+ };
99
+ }
100
+ return { name: propName, control: 'number', include: true };
101
+ }
102
+
103
+ case 'enum': {
104
+ const options: (string | boolean | undefined)[] = [...parsed.options];
105
+ if (!hasDefault) options.unshift(undefined);
106
+ return { name: propName, control: 'select', options, include: true };
107
+ }
108
+
109
+ case 'string':
110
+ case 'stringOrNumber':
111
+ return { name: propName, control: 'text', include: true };
112
+
113
+ case 'complex':
114
+ // Record<...>, X[], injected interfaces - driver-internal or too complex
115
+ // for a control. Omit; a story file can supply a default prop if required.
116
+ return { name: propName, control: 'text', include: false };
117
+ }
118
+ }
119
+
120
+ /** Suggested default prop value from the template default (and the kit's
121
+ * component-size hint, when it supplies one). */
122
+ export function defaultPropForProp(
123
+ componentName: string,
124
+ propName: string,
125
+ def: { type: string; default?: JsonValue },
126
+ hints: PreviewControlHints
127
+ ): JsonValue | undefined {
128
+ if (def.default !== undefined) return def.default;
129
+ if (propName === 'size' && parseType(def.type).kind === 'number') {
130
+ return hints.componentSizeRem?.(componentName);
131
+ }
132
+ return undefined;
133
+ }