@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,182 @@
1
+ // One preview window: a quiet charcell Frame titled by what it shows, filling its
2
+ // container, around the element the controller mounts the iframe in - a
3
+ // target's story, the story's html, or a custom view. The pane only provides that element;
4
+ // the iframe and what drives it are the controller's. A target's pane
5
+ // takes the height its story reports as its content's, so a row that starts
6
+ // at its content's height fits the story; it still fills the row it is in.
7
+ // While the controller lays the story panes out at a width (`paneWidth`), a
8
+ // target's or the html pane is that wide and as tall as its story, in a
9
+ // charcell ScrollArea: centered in a wider pane, and scrolled across, its
10
+ // scrollbar drawn in cells on a row of its own, in a narrower one, or down
11
+ // in a shorter one. A view's pane always fills its own.
12
+ // The Frame's top row holds, at its end, `reload`, which loads the pane again,
13
+ // and, on a target's pane, `open`, which opens its story as it is now in a
14
+ // window of its own. Where they do not fit beside the title they give way a
15
+ // whole cell at a time, `open` first, and go once too little of a label is
16
+ // left (fit-labels.ts), so a label is never cut through a cell. While the iframe loads, hidden until it shows its content, the
17
+ // pane says `loading` once a moment has passed (index.css). While the html
18
+ // pane has no html for the story, such as the static build's for changed
19
+ // props, the pane says why over it, as the code pane does (index.css). While
20
+ // the story's html leaves out what it cannot evaluate at rest, such as a
21
+ // part shown on a condition its script holds, the html pane's legend says how
22
+ // many warnings, the count opening them in a popover
23
+ // (HtmlWarnings), as the code pane's row does.
24
+ import { useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type RefObject } from 'react';
25
+ import { Button, Frame, ScrollArea } from 'charcell/react/unlinked';
26
+ import { PREVIEW_SCRIPTED_TARGETS, type PreviewScriptedTarget, type PreviewTarget } from '../../../../../src/shell';
27
+ import { usePreviewController, usePreviewState } from '../../controller-context';
28
+ import { Hint } from '../Hint';
29
+ import { HtmlWarnings } from '../HtmlWarnings';
30
+ import { fitLabels } from '../../fit-labels';
31
+ import { htmlWarningsLabel } from '../../html-warnings';
32
+ import './index.css';
33
+
34
+ /** The cells of the top row beside its title's text and its actions: the
35
+ * ring's corner and a cell of its stroke at each side, the cell at each side
36
+ * of the title, and at least one of the stroke between the two. */
37
+ const TOP_ROW_CELLS = 7;
38
+
39
+ /** How far under a whole cell a width may fall and still be that cell's. */
40
+ const CELL_TOLERANCE = 0.01;
41
+
42
+ /** The pane's whole width in cells, as it changes; `undefined` until measured. */
43
+ function useColumns(frame: RefObject<HTMLDivElement | null>, cell: RefObject<HTMLSpanElement | null>) {
44
+ const [columns, setColumns] = useState<number | undefined>(undefined);
45
+ useLayoutEffect(() => {
46
+ const element = frame.current;
47
+ if (!element) return;
48
+ const measure = () => {
49
+ const width = cell.current?.getBoundingClientRect().width;
50
+ if (width) setColumns(Math.floor(element.getBoundingClientRect().width / width + CELL_TOLERANCE));
51
+ };
52
+ measure();
53
+ const observer = new ResizeObserver(measure);
54
+ observer.observe(element);
55
+ return () => observer.disconnect();
56
+ }, [frame, cell]);
57
+ return columns;
58
+ }
59
+
60
+ export function PreviewPane(props: { title: string } & ({ target: PreviewTarget } | { viewId: string })) {
61
+ const controller = usePreviewController();
62
+ const element = useRef<HTMLDivElement>(null);
63
+ const frame = useRef<HTMLDivElement>(null);
64
+ const cell = useRef<HTMLSpanElement>(null);
65
+ const columns = useColumns(frame, cell);
66
+ const target = 'target' in props ? props.target : undefined;
67
+ const viewId = 'viewId' in props ? props.viewId : undefined;
68
+ const height = usePreviewState((state) => (target === undefined ? undefined : state.frameSizes[target]?.height));
69
+ const width = usePreviewState((state) => (target === undefined ? undefined : state.paneWidth));
70
+ const fixed = width !== undefined;
71
+ const unavailable = usePreviewState((state) =>
72
+ target === 'html' && state.code?.html.status === 'unavailable' ? state.code.html.reason : undefined,
73
+ );
74
+ const warnings = usePreviewState((state) =>
75
+ target === 'html' && state.code?.html.status === 'ready' ? state.code.html.warnings : undefined,
76
+ );
77
+ // A fixed pane mounts in a scroll area, a fitted one in the Frame itself:
78
+ // the element changes between the two, so the iframe mounts again.
79
+ useEffect(
80
+ () =>
81
+ target !== undefined
82
+ ? controller.frames.mount(element.current!, target)
83
+ : controller.frames.mountView(element.current!, viewId!),
84
+ [controller, target, viewId, fixed],
85
+ );
86
+ const style = {
87
+ ...(height === undefined ? {} : { '--preview-pane-content': `${Math.ceil(height)}px` }),
88
+ ...(width === undefined ? {} : { '--preview-pane-width': `${width}px` }),
89
+ } as CSSProperties;
90
+ const actions = [
91
+ {
92
+ label: 'reload',
93
+ hint: `load the ${props.title} pane again`,
94
+ press: () => controller.frames.reload(target ?? 'view'),
95
+ },
96
+ ...(target !== undefined && isScriptedTarget(target)
97
+ ? [
98
+ {
99
+ label: 'open',
100
+ hint: `open the ${props.title} story in a window of its own`,
101
+ press: () => window.open(controller.frames.standaloneUrl(target), '_blank'),
102
+ },
103
+ ]
104
+ : []),
105
+ ];
106
+ const warningsLabel = warnings && warnings.length > 0 ? htmlWarningsLabel(warnings) : undefined;
107
+ const titleCells = [...props.title].length + (warningsLabel === undefined ? 0 : warningsLabel.length + 1);
108
+ const fitted = fitLabels(
109
+ actions.map((action) => action.label),
110
+ columns === undefined ? undefined : columns - titleCells - TOP_ROW_CELLS,
111
+ );
112
+ return (
113
+ <div ref={frame} className="PreviewPane__frame">
114
+ <span ref={cell} className="PreviewPane__cell" aria-hidden="true" />
115
+ <Frame
116
+ title={props.title}
117
+ quiet
118
+ flush
119
+ fill
120
+ legend={
121
+ warnings && warnings.length > 0 ? (
122
+ <HtmlWarnings
123
+ warnings={warnings}
124
+ className="PreviewPane__warnings"
125
+ attributes={{ 'data-preview-html-warnings': '' }}
126
+ />
127
+ ) : undefined
128
+ }
129
+ legendEnd={
130
+ fitted.length > 0 ? (
131
+ <span className="PreviewPane__actions">
132
+ {fitted.map((text, index) => {
133
+ const action = actions[index]!;
134
+ return (
135
+ <Hint key={action.label} text={action.hint}>
136
+ {(hint) => (
137
+ <Button
138
+ {...hint}
139
+ variant="dense"
140
+ label={text}
141
+ aria-label={action.label}
142
+ onClick={action.press}
143
+ />
144
+ )}
145
+ </Hint>
146
+ );
147
+ })}
148
+ </span>
149
+ ) : undefined
150
+ }
151
+ >
152
+ {fixed ? (
153
+ <ScrollArea label={`${props.title} story`} orientation="both" overscroll="none" fill>
154
+ <div ref={element} key="fixed" className="PreviewPane PreviewPane--fixed" style={style}>
155
+ {note(unavailable)}
156
+ </div>
157
+ </ScrollArea>
158
+ ) : (
159
+ <div ref={element} key="fitted" className="PreviewPane" style={style}>
160
+ {note(unavailable)}
161
+ </div>
162
+ )}
163
+ </Frame>
164
+ </div>
165
+ );
166
+ }
167
+
168
+ /** The note over the html pane saying why it has no html, beside the
169
+ * iframe the controller mounts in the same element. */
170
+ function note(reason: string | undefined) {
171
+ return reason === undefined ? null : (
172
+ <p className="PreviewPane__note" role="status" data-preview-html-note="">
173
+ {reason}
174
+ </p>
175
+ );
176
+ }
177
+
178
+ /** Whether a target is a scripted one, whose pane can open on its own (the
179
+ * html pane shows the markup its host gives it). */
180
+ function isScriptedTarget(target: PreviewTarget): target is PreviewScriptedTarget {
181
+ return (PREVIEW_SCRIPTED_TARGETS as readonly string[]).includes(target);
182
+ }
@@ -0,0 +1,133 @@
1
+ // The shell's top bar: the tool sigil, the kit and its
2
+ // version, and the options placed in the top bar (the kit's own settings, such
3
+ // as its theme) at the start; at the end, two groups of toggles at one level,
4
+ // the views (the navigation, then the stage's rows: scenario, output, code,
5
+ // events) and the targets (its columns), then the kit's `top-bar-end` slot. One build of one kit is served,
6
+ // so the kit and its version are plain labels. A target the kit does not
7
+ // build gets no target toggle; html gets one, after the targets', where the
8
+ // kit builds it. Each toggle is on while the stage shows its view or target;
9
+ // in a narrow window each group shows and toggles one at a time
10
+ // (stage-toggles.ts), the navigation apart from that, being no row of the
11
+ // stage.
12
+ import { Field as CharcellField, Toggle } from 'charcell/react/unlinked';
13
+ import { useId } from 'react';
14
+ import type { PreviewTarget } from '../../../../../src/shell';
15
+ import { Bar } from '../Bar';
16
+ import { Hint } from '../Hint';
17
+ import { usePreviewController, usePreviewState } from '../../controller-context';
18
+ import { OptionControls } from '../OptionControls';
19
+ import {
20
+ CODE_VIEW_OPTION,
21
+ EVENTS_VIEW_OPTION,
22
+ NAVIGATION_OPTION,
23
+ OUTPUT_VIEW_OPTION,
24
+ SCENARIO_VIEW_OPTION,
25
+ } from '../../shell-options';
26
+ import { SlotOutlet } from '../SlotOutlet';
27
+ import { toggleTarget, toggleView, useStage } from '../../stage-toggles';
28
+
29
+ /** The stage's views, top to bottom: each a shell option, so the address keeps it. */
30
+ const VIEWS = [
31
+ { option: SCENARIO_VIEW_OPTION, label: 'scenario', title: 'the props and content editor' },
32
+ { option: OUTPUT_VIEW_OPTION, label: 'output', title: 'the story rendered in each target' },
33
+ { option: CODE_VIEW_OPTION, label: 'code', title: 'the story’s source in each target' },
34
+ { option: EVENTS_VIEW_OPTION, label: 'events', title: 'the events the story’s panes fire' },
35
+ ] as const;
36
+
37
+ export function PromptBar() {
38
+ const controller = usePreviewController();
39
+ const { kitName, kitVersion, targets: kitTargets } = controller.catalog;
40
+ const targets = usePreviewState((state) => state.targets);
41
+ const viewsLabel = useId();
42
+ const targetsLabel = useId();
43
+ const navigationShown = usePreviewState((state) => state.options[NAVIGATION_OPTION] !== false);
44
+ // Narrow, the stage shows one view and one target at a time.
45
+ const { narrow, views, panes } = useStage();
46
+ return (
47
+ <Bar
48
+ joined
49
+ lead="preview$"
50
+ end={
51
+ <>
52
+ <CharcellField label="views" labelId={viewsLabel} role="group" aria-labelledby={viewsLabel}>
53
+ <Hint text="the components and views" around>
54
+ {(hint) => (
55
+ <Toggle
56
+ variant="dense"
57
+ {...hint}
58
+ label="navigation"
59
+ pressed={navigationShown}
60
+ onPressedChange={() => controller.actions.setOption(NAVIGATION_OPTION, !navigationShown)}
61
+ />
62
+ )}
63
+ </Hint>
64
+ {VIEWS.map((view) => {
65
+ const shown = views.has(view.option);
66
+ return (
67
+ <Hint key={view.option} text={view.title} around>
68
+ {(hint) => (
69
+ <Toggle
70
+ variant="dense"
71
+ {...hint}
72
+ label={view.label}
73
+ pressed={shown}
74
+ onPressedChange={() => toggleView(controller, view.option, narrow)}
75
+ />
76
+ )}
77
+ </Hint>
78
+ );
79
+ })}
80
+ </CharcellField>
81
+ <CharcellField label="targets" labelId={targetsLabel} role="group" aria-labelledby={targetsLabel}>
82
+ {kitTargets.map((target) => {
83
+ // The selected component may not render into this target (a React-only
84
+ // fixture of another package's components) - then the toggle is disabled and forced off,
85
+ // its hint saying why. A hint either way keeps the toggle where it is as the selection
86
+ // changes, so a focused toggle keeps the focus.
87
+ const supported = targets.supported.includes(target);
88
+ const on = supported && panes.includes(target);
89
+ return (
90
+ <Hint
91
+ key={target}
92
+ text={supported ? targetHint(target) : 'not supported by this component'}
93
+ around
94
+ focusable={!supported}
95
+ label={target}
96
+ >
97
+ {(hint) => (
98
+ <Toggle
99
+ variant="dense"
100
+ {...hint}
101
+ label={target}
102
+ pressed={on}
103
+ disabled={!supported}
104
+ onPressedChange={() => toggleTarget(controller, target, narrow)}
105
+ />
106
+ )}
107
+ </Hint>
108
+ );
109
+ })}
110
+ </CharcellField>
111
+ <SlotOutlet name="top-bar-end" />
112
+ </>
113
+ }
114
+ >
115
+ {/* Which kit, and which version of it, is being previewed. A kit with
116
+ no package version shows none. */}
117
+ <CharcellField label="kit" data-testid="kit-name">
118
+ <span>{kitName}</span>
119
+ </CharcellField>
120
+ {kitVersion && (
121
+ <CharcellField label="version" data-testid="kit-version">
122
+ <span>{kitVersion}</span>
123
+ </CharcellField>
124
+ )}
125
+ <OptionControls placement="top-bar" />
126
+ </Bar>
127
+ );
128
+ }
129
+
130
+ /** What a target's toggle shows or hides. */
131
+ function targetHint(target: PreviewTarget): string {
132
+ return target === 'html' ? 'the story’s markup, with no script' : `the story rendered in ${target}`;
133
+ }
@@ -0,0 +1,36 @@
1
+ @layer charcell.components {
2
+ /* The scenario row fills its panel of the split (StageSplit), which
3
+ holds its height in whole rows: the view's heading, then a charcell
4
+ Frame taking the rest, whose body the editor's ScrollArea fills. A
5
+ column, so the Frame takes the rest of the row where the split sets its
6
+ height and its content's where the split measures the content to start
7
+ the row at. */
8
+ .ScenarioRow {
9
+ flex: 1;
10
+ display: flex;
11
+ flex-direction: column;
12
+ min-width: 0;
13
+ min-height: 0;
14
+ }
15
+ /* The picker and its info button, side by side with no cell between. */
16
+ .ScenarioRow__pick {
17
+ display: flex;
18
+ align-items: center;
19
+ }
20
+ .ScenarioRow__frame {
21
+ flex: 1 1 auto;
22
+ display: flex;
23
+ flex-direction: column;
24
+ min-height: 0;
25
+ }
26
+ /* The Frame is flush, so the scrollbar runs beside its ring: the note and
27
+ the tree each keep a cell from it. */
28
+ .ScenarioRow__note {
29
+ margin: 0;
30
+ padding: 0 1ch;
31
+ color: var(--charcell-mid);
32
+ }
33
+ .ScenarioRow__tree {
34
+ padding: 0 1ch;
35
+ }
36
+ }
@@ -0,0 +1,71 @@
1
+ // The scenario row, the stage's top row while the scenario view shows: the
2
+ // view's heading, with `pick`, the scenario picker, at the heading row's end
3
+ // and an info button for the picked scenario's description; then the props
4
+ // and content editor (EditorPanel) of the picked scenario in a quiet charcell
5
+ // Frame, as the target panes are, scrolling in a charcell ScrollArea, a whole
6
+ // row at a time. The picker shows only when there is a real choice beyond
7
+ // the default (an injected Empty, where the preview can regenerate, and/or
8
+ // named scenarios); its baseline option is "(default)", or
9
+ // "<label> (default)" when a story file names it. The story's context sits in
10
+ // the stage bar (StageBar). Where the preview cannot regenerate there is no
11
+ // editor, and the row says so. The Frame is flush, so the editor's scrollbar
12
+ // runs beside its ring; the note and the tree keep their own cell from it.
13
+ // Styled in its index.css.
14
+ import { Frame, ScrollArea } from 'charcell/react/unlinked';
15
+ import { scenarioChoices } from '../../../../../src/shell';
16
+ import { usePreviewController, usePreviewState } from '../../controller-context';
17
+ import { Description } from '../Description';
18
+ import { EditorPanel } from '../EditorPanel';
19
+ import { Select } from '../Select';
20
+ import { HeadingRow } from '../HeadingRow';
21
+ import { ViewControls } from '../ViewControls';
22
+ import './index.css';
23
+
24
+ export function ScenarioRow() {
25
+ const controller = usePreviewController();
26
+ const selection = usePreviewState((state) => state.selection);
27
+ const scenario = usePreviewState((state) => state.scenario);
28
+ const editing = usePreviewState((state) => state.editor.open && state.editor.tree !== undefined);
29
+ const selected = controller.catalog.components.find((component) => component.id === selection.id);
30
+ const choices = scenarioChoices(selected, controller.capabilities);
31
+ const picked = selected?.scenarios.find((candidate) => candidate.id === scenario);
32
+ return (
33
+ <div className="ScenarioRow" data-preview-scenario>
34
+ <HeadingRow
35
+ heading="scenario"
36
+ end={
37
+ <ViewControls view="scenario">
38
+ {selected && choices.length > 0 && (
39
+ <span className="ScenarioRow__pick">
40
+ <Select
41
+ label="pick"
42
+ testId="select-scenario"
43
+ value={scenario}
44
+ onChange={controller.actions.pickScenario}
45
+ options={choices}
46
+ />
47
+ <Description text={picked?.description} label={`about ${picked?.label ?? 'the scenario'}`} />
48
+ </span>
49
+ )}
50
+ </ViewControls>
51
+ }
52
+ />
53
+ <div className="ScenarioRow__frame">
54
+ <Frame quiet flush fill>
55
+ {selected && editing ? (
56
+ <ScrollArea label="scenario" overscroll="none" fill>
57
+ <div className="ScenarioRow__tree">
58
+ {/* Reseed the panel's own view state when the story or scenario changes. */}
59
+ <EditorPanel key={`${selected.id}:${scenario}`} />
60
+ </div>
61
+ </ScrollArea>
62
+ ) : (
63
+ !controller.capabilities.regeneration && (
64
+ <p className="ScenarioRow__note">Editing a scenario needs the preview's development server.</p>
65
+ )
66
+ )}
67
+ </Frame>
68
+ </div>
69
+ </div>
70
+ );
71
+ }
@@ -0,0 +1,48 @@
1
+ // The preview's labeled dropdown - charcell's Select (its own monochrome
2
+ // listbox; a native <select>'s popup is the operating system's own popup).
3
+ // This wrapper keeps the preview-wide call-site API (string value in/out,
4
+ // `label`, `testId`). charcell's Select is bare, so a label puts a charcell
5
+ // Field around it, pointed at the trigger by id; the test id is stamped on
6
+ // the trigger. charcell's layer tokens stack the listbox above a popover panel,
7
+ // so a select opened inside the add-content dialog needs no variant of its own.
8
+ import { useId } from 'react';
9
+ import { Field, Select as CharcellSelect } from 'charcell/react/unlinked';
10
+
11
+ export interface SelectOption {
12
+ label: string;
13
+ value: string;
14
+ }
15
+
16
+ export function Select(props: {
17
+ /** Omit for a bare trigger (no inline label), e.g. the editor's node pickers. */
18
+ label?: string;
19
+ /** A bare trigger's id, for a label of the caller's own to point at. */
20
+ id?: string;
21
+ value: string;
22
+ options: SelectOption[];
23
+ /** Omit for a display-only select. */
24
+ onChange?: (value: string) => void;
25
+ /** Stamped on the trigger for tests to open the widget. */
26
+ testId?: string;
27
+ }) {
28
+ const { label, value, options, onChange, testId } = props;
29
+ const ownId = useId();
30
+ const id = label === undefined ? props.id : ownId;
31
+ const select = (
32
+ <CharcellSelect
33
+ variant="dense"
34
+ id={id}
35
+ items={options.map((option) => ({ value: option.value, label: option.label }))}
36
+ value={value}
37
+ onValueChange={onChange === undefined ? undefined : (next) => onChange(String(next))}
38
+ triggerProps={testId === undefined ? undefined : { 'data-testid': testId }}
39
+ />
40
+ );
41
+ return label === undefined ? (
42
+ select
43
+ ) : (
44
+ <Field label={label} htmlFor={id}>
45
+ {select}
46
+ </Field>
47
+ );
48
+ }
@@ -0,0 +1,7 @@
1
+ @layer charcell.components {
2
+ /* A slot's outlet has no box of its own: what the kit mounts in it
3
+ sits in the bar's zone beside the option controls. */
4
+ .SlotOutlet {
5
+ display: contents;
6
+ }
7
+ }
@@ -0,0 +1,25 @@
1
+ // A named slot of the shell: where the kit's fill for it mounts. The outlet
2
+ // renders one element and hands it to the fill with the controller; the fill
3
+ // renders whatever it likes inside, in its own framework, and is unmounted
4
+ // with the outlet. A slot with no fill renders nothing.
5
+ import { useEffect, useRef } from 'react';
6
+ import type { CharcellSlotName } from '../../configuration';
7
+ import { usePreviewController, useShellConfiguration } from '../../controller-context';
8
+ import './index.css';
9
+
10
+ export function SlotOutlet({ name }: { name: CharcellSlotName }) {
11
+ const controller = usePreviewController();
12
+ const fill = useShellConfiguration().slots?.[name];
13
+ const element = useRef<HTMLDivElement>(null);
14
+ useEffect(() => {
15
+ if (!fill || !element.current) return;
16
+ const unmount = fill(element.current, controller);
17
+ return () => {
18
+ unmount?.();
19
+ };
20
+ }, [fill, controller]);
21
+ if (!fill) return null;
22
+ // `display: contents` (index.css): the fill's children sit in the
23
+ // bar's zone as the option controls do, with no box of the outlet's own.
24
+ return <div ref={element} className="SlotOutlet" data-preview-slot={name} />;
25
+ }
@@ -0,0 +1,7 @@
1
+ @layer charcell.components {
2
+ /* A picker and its info button, side by side with no cell between. */
3
+ .StageBar__pick {
4
+ display: flex;
5
+ align-items: center;
6
+ }
7
+ }
@@ -0,0 +1,109 @@
1
+ // The stage bar, over a component story or a custom view: what is on the
2
+ // stage and how it is set up. At its start `component` (`view` on a custom view), a combobox of everything the
3
+ // navigation lists (the selected entry too, when it is unlisted), narrowed
4
+ // as the reader types, as wide as its longest entry and titled with the
5
+ // selected one's label, with an info button for the component's
6
+ // description; at its end, the options a kit places in the stage bar and its
7
+ // `stage-bar-end` slot, both empty by default. The scenario picker is the
8
+ // scenario row's (ScenarioRow), and the context options the output view's
9
+ // (ViewControls). The bar's rule row is left empty, so no line crosses the
10
+ // navigation's. Styled in its index.css.
11
+ import { Combobox, Field } from 'charcell/react/unlinked';
12
+ import { useId } from 'react';
13
+ import type { PreviewCatalog, PreviewNavigationNode } from '../../../../../src/shell';
14
+ import { usePreviewController, usePreviewState } from '../../controller-context';
15
+ import { Bar } from '../Bar';
16
+ import { Description } from '../Description';
17
+ import { OptionControls } from '../OptionControls';
18
+ import type { SelectOption } from '../Select';
19
+ import { SlotOutlet } from '../SlotOutlet';
20
+ import './index.css';
21
+
22
+ export function StageBar() {
23
+ const controller = usePreviewController();
24
+ const selection = usePreviewState((state) => state.selection);
25
+ const { catalog } = controller;
26
+ const selected =
27
+ selection.kind === 'view' ? undefined : catalog.components.find((component) => component.id === selection.id);
28
+ const entries = stageEntries(catalog, selection);
29
+ const current = entries.find((entry) => entry.value === entryValue(selection))?.label;
30
+ const findId = useId();
31
+ return (
32
+ <Bar
33
+ unruled
34
+ flush
35
+ end={
36
+ <>
37
+ <OptionControls placement="stage-bar" />
38
+ <SlotOutlet name="stage-bar-end" />
39
+ </>
40
+ }
41
+ >
42
+ <span className="StageBar__pick">
43
+ <Field label={selection.kind === 'view' ? 'view' : 'component'} htmlFor={findId}>
44
+ <Combobox
45
+ variant="dense"
46
+ id={findId}
47
+ items={entries}
48
+ value={entryValue(selection)}
49
+ onValueChange={(value) => controller.actions.select(entryOf(value))}
50
+ inputProps={{ 'data-testid': 'select-component', title: current }}
51
+ style={{ width: pickerWidth(entries) }}
52
+ />
53
+ </Field>
54
+ <Description text={selected?.description} label={`about ${selected?.shortTitle ?? 'the component'}`} />
55
+ </span>
56
+ </Bar>
57
+ );
58
+ }
59
+
60
+ type Entry = { kind: 'component' | 'view'; id: string };
61
+
62
+ /** The cells a picker's field takes beside its text: a cell of padding at
63
+ * each end, the cell between the text and its `▾`, and the `▾`. */
64
+ const CELLS_BESIDE_TEXT = 4;
65
+
66
+ /** The width the picker's field takes: its text as wide as the longest
67
+ * entry, and never narrower than charcell's own 20 cells, so no name is
68
+ * cut short and the field keeps its width as the reader moves between
69
+ * entries. */
70
+ function pickerWidth(entries: SelectOption[]): string {
71
+ const longest = Math.max(20, ...entries.map((entry) => [...entry.label].length));
72
+ return `${longest + CELLS_BESIDE_TEXT}ch`;
73
+ }
74
+
75
+ /** A select value for a navigation entry: a view's id and a component's may coincide. */
76
+ function entryValue(entry: Entry): string {
77
+ return `${entry.kind}:${entry.id}`;
78
+ }
79
+
80
+ function entryOf(value: string): Entry {
81
+ const separator = value.indexOf(':');
82
+ return { kind: value.slice(0, separator) as Entry['kind'], id: value.slice(separator + 1) };
83
+ }
84
+
85
+ /** Every entry the navigation lists, in its order, components by their short
86
+ * title; the selection too, where the navigation leaves it out. */
87
+ function stageEntries(catalog: PreviewCatalog, selection: Entry): SelectOption[] {
88
+ const entries: SelectOption[] = [];
89
+ const shortTitle = (id: string) => catalog.components.find((component) => component.id === id)?.shortTitle ?? id;
90
+ const walk = (nodes: PreviewNavigationNode[]) => {
91
+ for (const node of nodes) {
92
+ if (node.kind === 'group') walk(node.children);
93
+ else
94
+ entries.push({
95
+ value: entryValue(node),
96
+ label: node.kind === 'component' ? shortTitle(node.id) : node.label,
97
+ });
98
+ }
99
+ };
100
+ walk(catalog.navigation);
101
+ if (!entries.some((entry) => entry.value === entryValue(selection))) {
102
+ const view = catalog.views.find((candidate) => candidate.id === selection.id);
103
+ entries.push({
104
+ value: entryValue(selection),
105
+ label: selection.kind === 'view' ? (view?.label ?? selection.id) : shortTitle(selection.id),
106
+ });
107
+ }
108
+ return entries;
109
+ }
@@ -0,0 +1,11 @@
1
+ @layer charcell.components {
2
+ /* The split of the stage's rows fills the body, and leaves
3
+ it while no view shows (`hidden`). */
4
+ .StageSplit:not([hidden]) {
5
+ flex: 1;
6
+ display: flex;
7
+ flex-direction: column;
8
+ min-width: 0;
9
+ min-height: 0;
10
+ }
11
+ }