@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
@@ -0,0 +1,263 @@
1
+ // Emit the React render-registry: a map of component id → a render function
2
+ // `(props) => <Component {...props}>...slots...</Component>`. React maps the default
3
+ // slot to JSX children and named slots to props (e.g. a popover's `trigger`).
4
+ // A story spells a component's attributes as HTML does in every target; the
5
+ // fills here are spelled as React takes them (lib/react-spelling.ts), and the
6
+ // registry exports what the React pane spells a story's live props with.
7
+
8
+ import {
9
+ effectiveDefaultSlot,
10
+ namedSlots,
11
+ serialize,
12
+ type DefaultSlot,
13
+ } from './common.js';
14
+ import { storyRegistryId } from '../lib/scenario-story.js';
15
+ import { REACT_ATTRIBUTE_NAMES } from '@js-template-engine/extension-react';
16
+ import { reactFill, styleProperties } from '../lib/react-spelling.js';
17
+ import { singleJsxRoot } from '../lib/jsx-roots.js';
18
+ import { collectParts } from '../lib/slot-fill.js';
19
+ import { isVoidElement } from '../lib/void-elements.js';
20
+ import type {
21
+ JsonValue,
22
+ PartFill,
23
+ SlotFill,
24
+ ResolvedStory,
25
+ Target,
26
+ } from '../lib/types.js';
27
+
28
+ // A story file `style` string ('padding: 0 1ch;') → the React style object literal
29
+ // source. Story files author plain CSS text (the natural cross-target form - Vue and
30
+ // Svelte take it verbatim); only React needs the object mapping.
31
+ function styleObjectSource(css: string): string {
32
+ const entries = Object.entries(styleProperties(css)).map(
33
+ ([name, value]) =>
34
+ `${name.startsWith('--') ? JSON.stringify(name) : name}: ${JSON.stringify(value)}`
35
+ );
36
+ return `{ ${entries.join(', ')} }`;
37
+ }
38
+
39
+ // A JSX attribute list from `props` (named slots become slot-as-prop attributes,
40
+ // like reactEntry's top-level `namedProps`). For HTML tags, `class` maps to the
41
+ // JSX `className` spelling and a `style` string becomes the object mapping React
42
+ // requires.
43
+ function jsxAttributes(
44
+ props: Record<string, JsonValue> | undefined,
45
+ slots: Record<string, SlotFill> | undefined,
46
+ html: boolean
47
+ ): string {
48
+ const parts: string[] = [];
49
+ for (const [propName, value] of Object.entries(props ?? {})) {
50
+ const key = html && propName === 'class' ? 'className' : propName;
51
+ if (html && propName === 'style' && typeof value === 'string') {
52
+ parts.push(`style={${styleObjectSource(value)}}`);
53
+ continue;
54
+ }
55
+ parts.push(
56
+ typeof value === 'boolean' && value ? key : `${key}={${serialize(value)}}`
57
+ );
58
+ }
59
+ for (const [slot, fill] of Object.entries(slots ?? {}))
60
+ parts.push(`${slot}={${slotExpression(fill)}}`);
61
+ return parts.length ? ' ' + parts.join(' ') : '';
62
+ }
63
+
64
+ function elementJsx(fill: Extract<SlotFill, { kind: 'element' }>): string {
65
+ const inner = fill.children ? slotJsx(fill.children) : '';
66
+ return `<${fill.component}${jsxAttributes(fill.props, fill.slots, false)}>${inner}</${fill.component}>`;
67
+ }
68
+
69
+ function htmlJsx(fill: Extract<SlotFill, { kind: 'html' }>): string {
70
+ // A void element (img, input, br) takes no children and no closing tag.
71
+ if (isVoidElement(fill.tag))
72
+ return `<${fill.tag}${jsxAttributes(fill.props, undefined, true)} />`;
73
+ const inner = fill.children ? slotJsx(fill.children) : '';
74
+ return `<${fill.tag}${jsxAttributes(fill.props, undefined, true)}>${inner}</${fill.tag}>`;
75
+ }
76
+
77
+ // A call to a part: the function component the story's entry declares.
78
+ function partJsx(fill: PartFill): string {
79
+ return `<${fill.name}${jsxAttributes(fill.props, undefined, false)} />`;
80
+ }
81
+
82
+ function slotJsx(fill: SlotFill): string {
83
+ if (fill.kind === 'text') return `{${JSON.stringify(fill.value)}}`;
84
+ if (fill.kind === 'raw') return fill.react;
85
+ if (fill.kind === 'part') return partJsx(fill);
86
+ // A scoped fill is only valid at a top-level named slot (reactEntry); ignored nested.
87
+ if (fill.kind === 'scoped') return '';
88
+ if (fill.kind === 'fragment') return fill.children.map(slotJsx).join('');
89
+ if (fill.kind === 'html') return htmlJsx(fill);
90
+ return elementJsx(fill);
91
+ }
92
+
93
+ // A scoped fill's React arrow head. Vue and Svelte always destructure `parameters` off
94
+ // one scope object; React takes either shape, so the fill says which. The default
95
+ // is the single scope object a generated component's scoped slot supplies - a
96
+ // render prop there is always called with one object, so the same `parameters` list is
97
+ // correct in all three emitters. `positional` keeps the separate-parameters form
98
+ // for a hand-written driver prop whose React side really is positional.
99
+ function reactArrowHead(fill: Extract<SlotFill, { kind: 'scoped' }>): string {
100
+ return fill.reactScope === 'positional'
101
+ ? `(${fill.parameters.join(', ')})`
102
+ : `({ ${fill.parameters.join(', ')} })`;
103
+ }
104
+
105
+ // A slot fill in *prop* position (a named slot mapped to a React prop, e.g.
106
+ // `description={...}`). Unlike slotJsx (children position), the value must be a bare
107
+ // JSX *expression* - so text is a plain string literal (not `{"..."}`, which would
108
+ // double-brace) and a multi-child fragment is wrapped in `<>...</>` so it stays a
109
+ // single expression.
110
+ function slotExpression(fill: SlotFill): string {
111
+ if (fill.kind === 'text') return JSON.stringify(fill.value);
112
+ // A raw body of sibling roots, or of text, is one expression in a fragment.
113
+ if (fill.kind === 'raw') return singleJsxRoot(fill.react);
114
+ if (fill.kind === 'part') return partJsx(fill);
115
+ // A scoped fill becomes a render-prop arrow (handled in reactEntry, not here).
116
+ if (fill.kind === 'scoped')
117
+ return `${reactArrowHead(fill)} => (${singleJsxRoot(fill.react)})`;
118
+ if (fill.kind === 'fragment')
119
+ return `<>${fill.children.map(slotJsx).join('')}</>`;
120
+ if (fill.kind === 'html') return htmlJsx(fill);
121
+ return elementJsx(fill);
122
+ }
123
+
124
+ function defaultJsx(def: DefaultSlot): string {
125
+ if (!def) return '';
126
+ if (def.kind === 'propText') return `{props.${def.prop}}`;
127
+ // Scoped content for the default slot is the function child.
128
+ if (def.kind === 'scoped') return def.react ? `{${slotExpression(def)}}` : '';
129
+ return slotJsx(def);
130
+ }
131
+
132
+ function reactEntry(
133
+ model: ResolvedStory,
134
+ declares: (component: string, name: string) => boolean
135
+ ): string {
136
+ const given = effectiveDefaultSlot(model);
137
+ const def: DefaultSlot =
138
+ given?.kind === 'propText'
139
+ ? given
140
+ : reactFill(given, declares, REACT_ATTRIBUTE_NAMES);
141
+ const named = Object.fromEntries(
142
+ Object.entries(namedSlots(model)).map(([slot, fill]) => [
143
+ slot,
144
+ reactFill(fill, declares, REACT_ATTRIBUTE_NAMES),
145
+ ])
146
+ );
147
+ const namedProps = Object.entries(named)
148
+ .map(([slot, fill]) => {
149
+ // A scoped fill maps to a render-prop named per target (`fill.slot.react`), not
150
+ // the map key; an empty target body omits the prop.
151
+ if (fill.kind === 'scoped')
152
+ return fill.react
153
+ ? ` ${fill.slot.react}={${slotExpression(fill)}}`
154
+ : '';
155
+ return ` ${slot}={${slotExpression(fill)}}`;
156
+ })
157
+ .join('');
158
+ const children = defaultJsx(def);
159
+ // The content beside the component: its siblings, in a fragment with it.
160
+ const before = reactFill(
161
+ model.around?.before,
162
+ declares,
163
+ REACT_ATTRIBUTE_NAMES
164
+ );
165
+ const after = reactFill(model.around?.after, declares, REACT_ATTRIBUTE_NAMES);
166
+ const beside = before !== undefined || after !== undefined;
167
+ const open = `${beside ? `<>${before ? slotJsx(before) : ''}` : ''}<${model.name} {...props}${namedProps}>`;
168
+ const close = `</${model.name}>${beside ? `${after ? slotJsx(after) : ''}</>` : ''}`;
169
+ const body = children ? `\n ${children}\n ` : '';
170
+ const key = JSON.stringify(storyRegistryId(model));
171
+ const parts = collectParts([
172
+ before,
173
+ model.slots?.default,
174
+ ...Object.values(named),
175
+ after,
176
+ ]);
177
+ if (parts.size === 0)
178
+ return ` ${key}: (props) => (\n ${open}${body}${close}\n ),`;
179
+ // The story's parts are function components declared in its entry, so two
180
+ // stories can each declare a part of one name.
181
+ const declarations = [...parts.values()]
182
+ .map(
183
+ (part) =>
184
+ ` function ${part.name}({ ${part.parameters.join(', ')} }: Record<string, any>) {\n` +
185
+ ` return ${part.react ? `(${singleJsxRoot(part.react)})` : 'null'};\n` +
186
+ ` }\n`
187
+ )
188
+ .join('');
189
+ return (
190
+ ` ${key}: (() => {\n${declarations}` +
191
+ ` return (props: Record<string, any>) => (\n ${open}${children ? `\n ${children}\n ` : ''}${close}\n );\n` +
192
+ ` })(),`
193
+ );
194
+ }
195
+
196
+ // Group a component into its source's module bucket (insertion-ordered, so the core
197
+ // source's import line comes first and other sources' lines append after it).
198
+ function addImport(
199
+ byModule: Map<string, Set<string>>,
200
+ mod: string,
201
+ name: string
202
+ ): void {
203
+ let set = byModule.get(mod);
204
+ if (!set) byModule.set(mod, (set = new Set()));
205
+ set.add(name);
206
+ }
207
+
208
+ /**
209
+ * The React render-registry module: each story's id → its render function.
210
+ *
211
+ * @param models - The stories React renders.
212
+ * @param moduleFor - The module a component is imported from, per target.
213
+ * @param exportFor - The name a component is exported under.
214
+ * @param declaredAttributes - The names a component declares that React
215
+ * would otherwise respell (`class`, `style`, an attribute React renames),
216
+ * which it takes as they are; every other attribute a story gives a
217
+ * component is spelled as React takes it.
218
+ * @returns The registry module's source: the `registry`, and the
219
+ * `reactAttributeNames` and `declaredAttributes` (of each story's root, by
220
+ * id) the React pane spells a story's live props with.
221
+ */
222
+ export function emitReactRegistry(
223
+ models: ResolvedStory[],
224
+ moduleFor: (name: string, target: Target) => string,
225
+ exportFor: (name: string) => string = (name) => name,
226
+ declaredAttributes: (component: string) => readonly string[] = () => []
227
+ ): string {
228
+ const declares = (component: string, name: string): boolean =>
229
+ declaredAttributes(component).includes(name);
230
+ const rootDeclarations = Object.fromEntries(
231
+ models
232
+ .map((model) => [storyRegistryId(model), declaredAttributes(model.name)])
233
+ .filter(([, names]) => names.length > 0)
234
+ );
235
+ const byModule = new Map<string, Set<string>>();
236
+ for (const model of models) {
237
+ addImport(byModule, moduleFor(model.name, 'react'), model.name);
238
+ for (const slotComponent of model.slotComponents)
239
+ addImport(byModule, moduleFor(slotComponent, 'react'), slotComponent);
240
+ }
241
+ // A component whose module export differs from its preview name is aliased on import
242
+ // (`Toast as NoticeToast`) so another package's clean export can't clash with a core name.
243
+ const importSpecifier = (name: string): string =>
244
+ exportFor(name) === name ? name : `${exportFor(name)} as ${name}`;
245
+ const importLines = [...byModule.entries()]
246
+ .map(
247
+ ([mod, names]) =>
248
+ `import { ${[...names].map(importSpecifier).join(', ')} } from '${mod}';`
249
+ )
250
+ .join('\n');
251
+ return (
252
+ `// Generated by scaffold-ui-kit-preview - do not edit.\n` +
253
+ `${importLines}\n\n` +
254
+ `type RenderFn = (props: Record<string, any>) => any;\n\n` +
255
+ `export const registry: Record<string, RenderFn> = {\n` +
256
+ models.map((model) => reactEntry(model, declares)).join('\n') +
257
+ `\n};\n\n` +
258
+ `// How React spells the attributes it renames, by their HTML names.\n` +
259
+ `export const reactAttributeNames: Record<string, string> = ${JSON.stringify(REACT_ATTRIBUTE_NAMES)};\n\n` +
260
+ `// The names each story's root declares that React would otherwise respell.\n` +
261
+ `export const declaredAttributes: Record<string, string[]> = ${JSON.stringify(rootDeclarations)};\n`
262
+ );
263
+ }
@@ -0,0 +1,175 @@
1
+ // The scenario editor's regeneration target. The host's editable node tree is, in effect,
2
+ // a one-entry story: a root component with `props` and a slot plan. We build a
3
+ // synthetic `ResolvedStory` from the editor payload and run it through the *existing*
4
+ // emitters verbatim - no new per-framework codegen - writing a `scratch/` sibling
5
+ // of the generated registries. The preview's normal .preview-generated → Vite-HMR path
6
+ // then reloads the previews.
7
+ //
8
+ // Loaded by the Vite dev middleware via `ssrLoadModule` (so it runs in Node with
9
+ // fs + the TS emitters compiled on the fly). `writeScratchStubs` is also called at
10
+ // `preview:generate` time so the previews' static scratch imports always resolve.
11
+
12
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
13
+ import { resolve } from 'node:path';
14
+ import { emitReactRegistry } from './react.js';
15
+ import { emitVueRegistry } from './vue.js';
16
+ import { emitSvelteRegistry, emitSvelteWrapper } from './svelte.js';
17
+ import { collectSlotComponents } from '../lib/slot-fill.js';
18
+ import { moduleOfSource } from '../lib/module-of-source.js';
19
+ import { ALL_TARGETS } from '../lib/types.js';
20
+ import type {
21
+ JsonValue,
22
+ SlotPlan,
23
+ StoryAround,
24
+ ResolvedStory,
25
+ Target,
26
+ } from '../lib/types.js';
27
+
28
+ /** The module-resolution data the generator serializes beside the registries
29
+ * (`<generatedRoot>/modules.json`), so this dev-middleware module needs no kit
30
+ * imports: the default module for unregistered slot children, each manual
31
+ * component's source module, per-target overrides, and export aliases. */
32
+ export interface ScratchModules {
33
+ defaultModule: string;
34
+ moduleByName: Record<string, string>;
35
+ importOverrideByName: Record<string, Partial<Record<Target, string>>>;
36
+ exportByName: Record<string, string>;
37
+ /** The scripted targets the kit builds; a scratch registry for any other stays
38
+ * empty. All three when absent. */
39
+ targets?: Target[];
40
+ /** The names each component declares that React would otherwise respell
41
+ * (`class`, `style`, an attribute React renames), which it takes as they
42
+ * are. A component missing here declares none. */
43
+ declaredAttributes?: Record<string, string[]>;
44
+ }
45
+
46
+ // A component's per-target import module for the scratch registry, mirroring
47
+ // generate.ts: a per-component `importModule` override (a playground fixture
48
+ // outside its source's barrel) wins; otherwise the component's source module,
49
+ // defaulting to the kit's own source. Read from the generator's modules.json.
50
+ function loadResolvers(generatedRoot: string): {
51
+ moduleFor: (name: string, target: Target) => string;
52
+ exportFor: (name: string) => string;
53
+ targets: Target[];
54
+ declaredAttributes: (name: string) => string[];
55
+ } {
56
+ const data = JSON.parse(
57
+ readFileSync(resolve(generatedRoot, 'modules.json'), 'utf8')
58
+ ) as ScratchModules;
59
+ return {
60
+ targets: data.targets ?? ALL_TARGETS,
61
+ moduleFor: (name, target) =>
62
+ data.importOverrideByName[name]?.[target] ??
63
+ moduleOfSource(data.moduleByName[name] ?? data.defaultModule, target),
64
+ exportFor: (name) => data.exportByName[name] ?? name,
65
+ declaredAttributes: (name) => data.declaredAttributes?.[name] ?? [],
66
+ };
67
+ }
68
+
69
+ /** Reserved story id the editor renders under, in every target registry. */
70
+ export const SCRATCH_ID = 'scratch';
71
+
72
+ /** The host posts this shape on every structural edit. */
73
+ export interface ScratchPayload {
74
+ /** The root component name (e.g. `Button`), imported per the modules.json mapping. */
75
+ rootComponent: string;
76
+ /** The root component's own scalar props (also delivered live via the props channel). */
77
+ props: Record<string, JsonValue>;
78
+ /** The editable tree: the root's default-slot fill (and any named slots). */
79
+ slots?: SlotPlan;
80
+ /** The content beside the root, carried unchanged from the story. */
81
+ around?: StoryAround;
82
+ }
83
+
84
+ function buildScratchModel(payload: ScratchPayload): ResolvedStory {
85
+ const slotComponents = new Set<string>();
86
+ collectSlotComponents(payload.slots?.default, slotComponents);
87
+ for (const fill of Object.values(payload.slots?.named ?? {})) {
88
+ collectSlotComponents(fill, slotComponents);
89
+ }
90
+ collectSlotComponents(payload.around?.before, slotComponents);
91
+ collectSlotComponents(payload.around?.after, slotComponents);
92
+ slotComponents.delete(payload.rootComponent);
93
+ return {
94
+ name: payload.rootComponent,
95
+ title: SCRATCH_ID,
96
+ omit: false,
97
+ standalone: true,
98
+ unlisted: false,
99
+ propTypes: [],
100
+ defaultProps: payload.props ?? {},
101
+ slots: payload.slots,
102
+ ...(payload.around ? { around: payload.around } : {}),
103
+ scenarios: [],
104
+ slotComponents: [...slotComponents],
105
+ // The scratch story is rendered, never introspected for add-targets.
106
+ contentSlots: { default: false, named: [] },
107
+ // Unused by the scratch emitters (they emit the single model unconditionally), but
108
+ // the model shape requires it; the editor composes across all targets.
109
+ targets: ALL_TARGETS,
110
+ };
111
+ }
112
+
113
+ const EMPTY_REGISTRY = 'export const registry: Record<string, any> = {};\n';
114
+ // The React page reads the names each story's root declares beside its
115
+ // registry, so React's placeholder exports them too, none.
116
+ const EMPTY_REACT_REGISTRY =
117
+ EMPTY_REGISTRY +
118
+ 'export const declaredAttributes: Record<string, string[]> = {};\n';
119
+
120
+ function scratchDirectory(generatedRoot: string): string {
121
+ const directory = resolve(generatedRoot, 'scratch');
122
+ mkdirSync(directory, { recursive: true });
123
+ return directory;
124
+ }
125
+
126
+ /** Placeholder registries so the previews' static scratch imports resolve before
127
+ * the first edit. Written at `preview:generate` time. */
128
+ export function writeScratchStubs(generatedRoot: string): void {
129
+ const directory = scratchDirectory(generatedRoot);
130
+ writeFileSync(resolve(directory, 'react.tsx'), EMPTY_REACT_REGISTRY);
131
+ writeFileSync(resolve(directory, 'vue.ts'), EMPTY_REGISTRY);
132
+ writeFileSync(resolve(directory, 'registry.ts'), EMPTY_REGISTRY);
133
+ }
134
+
135
+ /** Regenerate the scratch registries from an editor payload (one synthetic story
136
+ * through the real emitters). Returns the absolute paths written, so the caller
137
+ * can invalidate them in Vite's module graph before the previews reload (a plain
138
+ * reload can otherwise serve Vite's stale transform if the file-change event
139
+ * hasn't been processed yet). */
140
+ export function writeScratch(
141
+ payload: ScratchPayload,
142
+ generatedRoot: string
143
+ ): string[] {
144
+ const model = buildScratchModel(payload);
145
+ const directory = scratchDirectory(generatedRoot);
146
+ const { moduleFor, exportFor, targets, declaredAttributes } =
147
+ loadResolvers(generatedRoot);
148
+ // A target the kit does not build keeps its empty registry: its
149
+ // components have no module to import.
150
+ const files: [string, string][] = [
151
+ [
152
+ resolve(directory, 'react.tsx'),
153
+ targets.includes('react')
154
+ ? emitReactRegistry([model], moduleFor, exportFor, declaredAttributes)
155
+ : EMPTY_REACT_REGISTRY,
156
+ ],
157
+ [
158
+ resolve(directory, 'vue.ts'),
159
+ targets.includes('vue')
160
+ ? emitVueRegistry([model], moduleFor, exportFor)
161
+ : EMPTY_REGISTRY,
162
+ ],
163
+ ];
164
+ if (targets.includes('svelte')) {
165
+ const wrapper = emitSvelteWrapper(model, moduleFor, exportFor);
166
+ files.push(
167
+ [resolve(directory, 'registry.ts'), emitSvelteRegistry([model])],
168
+ [resolve(directory, wrapper.fileName), wrapper.source]
169
+ );
170
+ } else {
171
+ files.push([resolve(directory, 'registry.ts'), EMPTY_REGISTRY]);
172
+ }
173
+ for (const [path, source] of files) writeFileSync(path, source);
174
+ return files.map(([path]) => path);
175
+ }