@panphora/bevel 0.1.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 (539) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +226 -0
  3. package/bevel-fonts.css +16 -0
  4. package/bevel.css +3075 -0
  5. package/bevel.js +2126 -0
  6. package/dist/react/components/activity-feed.d.ts +89 -0
  7. package/dist/react/components/activity-feed.js +54 -0
  8. package/dist/react/components/alert-dialog.d.ts +14 -0
  9. package/dist/react/components/alert-dialog.js +19 -0
  10. package/dist/react/components/app-shell.d.ts +98 -0
  11. package/dist/react/components/app-shell.js +86 -0
  12. package/dist/react/components/app-toolbar.d.ts +19 -0
  13. package/dist/react/components/app-toolbar.js +22 -0
  14. package/dist/react/components/avatar-group.d.ts +67 -0
  15. package/dist/react/components/avatar-group.js +49 -0
  16. package/dist/react/components/badge-and-pill.d.ts +34 -0
  17. package/dist/react/components/badge-and-pill.js +12 -0
  18. package/dist/react/components/breadcrumbs.d.ts +56 -0
  19. package/dist/react/components/breadcrumbs.js +44 -0
  20. package/dist/react/components/bulk-bar.d.ts +22 -0
  21. package/dist/react/components/bulk-bar.js +59 -0
  22. package/dist/react/components/button.d.ts +15 -0
  23. package/dist/react/components/button.js +20 -0
  24. package/dist/react/components/callout-and-hint.d.ts +17 -0
  25. package/dist/react/components/callout-and-hint.js +8 -0
  26. package/dist/react/components/checkbox.d.ts +5 -0
  27. package/dist/react/components/checkbox.js +13 -0
  28. package/dist/react/components/chip.d.ts +149 -0
  29. package/dist/react/components/chip.js +29 -0
  30. package/dist/react/components/code-block.d.ts +50 -0
  31. package/dist/react/components/code-block.js +77 -0
  32. package/dist/react/components/code-editor.d.ts +122 -0
  33. package/dist/react/components/code-editor.js +695 -0
  34. package/dist/react/components/column-manager.d.ts +28 -0
  35. package/dist/react/components/column-manager.js +85 -0
  36. package/dist/react/components/combobox.d.ts +32 -0
  37. package/dist/react/components/combobox.js +37 -0
  38. package/dist/react/components/data-grid.d.ts +108 -0
  39. package/dist/react/components/data-grid.js +159 -0
  40. package/dist/react/components/data-table.d.ts +2 -0
  41. package/dist/react/components/data-table.js +6 -0
  42. package/dist/react/components/date.d.ts +23 -0
  43. package/dist/react/components/date.js +11 -0
  44. package/dist/react/components/details.d.ts +13 -0
  45. package/dist/react/components/details.js +20 -0
  46. package/dist/react/components/dialog.d.ts +74 -0
  47. package/dist/react/components/dialog.js +31 -0
  48. package/dist/react/components/diff-view.d.ts +73 -0
  49. package/dist/react/components/diff-view.js +68 -0
  50. package/dist/react/components/documents.d.ts +70 -0
  51. package/dist/react/components/documents.js +10 -0
  52. package/dist/react/components/drawer.d.ts +45 -0
  53. package/dist/react/components/drawer.js +35 -0
  54. package/dist/react/components/editable-grid-cells.d.ts +32 -0
  55. package/dist/react/components/editable-grid-cells.js +116 -0
  56. package/dist/react/components/eyebrow-kicker-lede-pull-quote.d.ts +39 -0
  57. package/dist/react/components/eyebrow-kicker-lede-pull-quote.js +12 -0
  58. package/dist/react/components/field.d.ts +32 -0
  59. package/dist/react/components/field.js +27 -0
  60. package/dist/react/components/file-tree.d.ts +75 -0
  61. package/dist/react/components/file-tree.js +247 -0
  62. package/dist/react/components/file.d.ts +43 -0
  63. package/dist/react/components/file.js +73 -0
  64. package/dist/react/components/files-table.d.ts +174 -0
  65. package/dist/react/components/files-table.js +237 -0
  66. package/dist/react/components/filter-builder.d.ts +37 -0
  67. package/dist/react/components/filter-builder.js +109 -0
  68. package/dist/react/components/form-error-summary.d.ts +16 -0
  69. package/dist/react/components/form-error-summary.js +49 -0
  70. package/dist/react/components/icon-button.d.ts +8 -0
  71. package/dist/react/components/icon-button.js +14 -0
  72. package/dist/react/components/icon.d.ts +7 -0
  73. package/dist/react/components/icon.js +15 -0
  74. package/dist/react/components/image.d.ts +72 -0
  75. package/dist/react/components/image.js +24 -0
  76. package/dist/react/components/inline-edit.d.ts +70 -0
  77. package/dist/react/components/inline-edit.js +149 -0
  78. package/dist/react/components/input.d.ts +29 -0
  79. package/dist/react/components/input.js +20 -0
  80. package/dist/react/components/kanban-board.d.ts +140 -0
  81. package/dist/react/components/kanban-board.js +374 -0
  82. package/dist/react/components/layout.d.ts +51 -0
  83. package/dist/react/components/layout.js +28 -0
  84. package/dist/react/components/list.d.ts +28 -0
  85. package/dist/react/components/list.js +6 -0
  86. package/dist/react/components/masthead-and-verdict.d.ts +60 -0
  87. package/dist/react/components/masthead-and-verdict.js +8 -0
  88. package/dist/react/components/menu.d.ts +26 -0
  89. package/dist/react/components/menu.js +31 -0
  90. package/dist/react/components/multi-select-picker.d.ts +49 -0
  91. package/dist/react/components/multi-select-picker.js +20 -0
  92. package/dist/react/components/note.d.ts +33 -0
  93. package/dist/react/components/note.js +8 -0
  94. package/dist/react/components/number-field.d.ts +16 -0
  95. package/dist/react/components/number-field.js +17 -0
  96. package/dist/react/components/pagination.d.ts +45 -0
  97. package/dist/react/components/pagination.js +87 -0
  98. package/dist/react/components/persistent-action-banner.d.ts +71 -0
  99. package/dist/react/components/persistent-action-banner.js +39 -0
  100. package/dist/react/components/popconfirm.d.ts +42 -0
  101. package/dist/react/components/popconfirm.js +59 -0
  102. package/dist/react/components/popover.d.ts +16 -0
  103. package/dist/react/components/popover.js +21 -0
  104. package/dist/react/components/progress-and-gauge.d.ts +44 -0
  105. package/dist/react/components/progress-and-gauge.js +19 -0
  106. package/dist/react/components/radio.d.ts +7 -0
  107. package/dist/react/components/radio.js +13 -0
  108. package/dist/react/components/rich-text-toolbar.d.ts +26 -0
  109. package/dist/react/components/rich-text-toolbar.js +64 -0
  110. package/dist/react/components/rows-and-card.d.ts +88 -0
  111. package/dist/react/components/rows-and-card.js +10 -0
  112. package/dist/react/components/save-status-indicator.d.ts +11 -0
  113. package/dist/react/components/save-status-indicator.js +33 -0
  114. package/dist/react/components/segmented-control.d.ts +21 -0
  115. package/dist/react/components/segmented-control.js +15 -0
  116. package/dist/react/components/select.d.ts +29 -0
  117. package/dist/react/components/select.js +29 -0
  118. package/dist/react/components/selectable-cards.d.ts +30 -0
  119. package/dist/react/components/selectable-cards.js +29 -0
  120. package/dist/react/components/settings-rows.d.ts +51 -0
  121. package/dist/react/components/settings-rows.js +8 -0
  122. package/dist/react/components/sidebar-nav.d.ts +77 -0
  123. package/dist/react/components/sidebar-nav.js +41 -0
  124. package/dist/react/components/skeleton-and-empty.d.ts +51 -0
  125. package/dist/react/components/skeleton-and-empty.js +14 -0
  126. package/dist/react/components/slider.d.ts +19 -0
  127. package/dist/react/components/slider.js +17 -0
  128. package/dist/react/components/small-charts.d.ts +60 -0
  129. package/dist/react/components/small-charts.js +76 -0
  130. package/dist/react/components/sortable.d.ts +30 -0
  131. package/dist/react/components/sortable.js +85 -0
  132. package/dist/react/components/split-button.d.ts +38 -0
  133. package/dist/react/components/split-button.js +43 -0
  134. package/dist/react/components/split-panes.d.ts +35 -0
  135. package/dist/react/components/split-panes.js +102 -0
  136. package/dist/react/components/stat-tiles.d.ts +54 -0
  137. package/dist/react/components/stat-tiles.js +8 -0
  138. package/dist/react/components/steps.d.ts +11 -0
  139. package/dist/react/components/steps.js +13 -0
  140. package/dist/react/components/switch.d.ts +6 -0
  141. package/dist/react/components/switch.js +13 -0
  142. package/dist/react/components/table.d.ts +61 -0
  143. package/dist/react/components/table.js +6 -0
  144. package/dist/react/components/tabs.d.ts +7 -0
  145. package/dist/react/components/tabs.js +13 -0
  146. package/dist/react/components/tags.d.ts +33 -0
  147. package/dist/react/components/tags.js +83 -0
  148. package/dist/react/components/textarea.d.ts +10 -0
  149. package/dist/react/components/textarea.js +6 -0
  150. package/dist/react/components/toast.d.ts +35 -0
  151. package/dist/react/components/toast.js +90 -0
  152. package/dist/react/components/tooltip-and-key.d.ts +20 -0
  153. package/dist/react/components/tooltip-and-key.js +20 -0
  154. package/dist/react/components/tree-view.d.ts +34 -0
  155. package/dist/react/components/tree-view.js +118 -0
  156. package/dist/react/components/upload-dropzone.d.ts +92 -0
  157. package/dist/react/components/upload-dropzone.js +116 -0
  158. package/dist/react/components/wizard-shell.d.ts +78 -0
  159. package/dist/react/components/wizard-shell.js +176 -0
  160. package/dist/react/fonts.css.d.ts +1 -0
  161. package/dist/react/index.d.ts +76 -0
  162. package/dist/react/index.js +77 -0
  163. package/dist/react/integrations/quickcrop.d.ts +58 -0
  164. package/dist/react/integrations/quickcrop.js +137 -0
  165. package/dist/react/integrations/richclay.d.ts +43 -0
  166. package/dist/react/integrations/richclay.js +129 -0
  167. package/dist/react/internal/classes.d.ts +3 -0
  168. package/dist/react/internal/classes.js +10 -0
  169. package/dist/react/internal/close-mark.d.ts +1 -0
  170. package/dist/react/internal/close-mark.js +10 -0
  171. package/dist/react/internal/controllable-state.d.ts +9 -0
  172. package/dist/react/internal/controllable-state.js +22 -0
  173. package/dist/react/internal/drop-effect.d.ts +28 -0
  174. package/dist/react/internal/drop-effect.js +8 -0
  175. package/dist/react/internal/form-reset.d.ts +2 -0
  176. package/dist/react/internal/form-reset.js +22 -0
  177. package/dist/react/internal/format.d.ts +1 -0
  178. package/dist/react/internal/format.js +14 -0
  179. package/dist/react/internal/link.d.ts +2 -0
  180. package/dist/react/internal/link.js +8 -0
  181. package/dist/react/internal/part.d.ts +20 -0
  182. package/dist/react/internal/part.js +47 -0
  183. package/dist/react/internal/pointer-session.d.ts +8 -0
  184. package/dist/react/internal/pointer-session.js +60 -0
  185. package/dist/react/internal/refs.d.ts +2 -0
  186. package/dist/react/internal/refs.js +22 -0
  187. package/dist/react/internal/rich-text.d.ts +28 -0
  188. package/dist/react/internal/rich-text.js +4 -0
  189. package/dist/react/internal/table-context.d.ts +39 -0
  190. package/dist/react/internal/table-context.js +12 -0
  191. package/dist/react/internal/use-object-url.d.ts +1 -0
  192. package/dist/react/internal/use-object-url.js +15 -0
  193. package/dist/react/layout.css +274 -0
  194. package/dist/react/layout.css.d.ts +1 -0
  195. package/dist/react/models/filter.d.ts +31 -0
  196. package/dist/react/models/filter.js +79 -0
  197. package/dist/react/models/kanban.d.ts +51 -0
  198. package/dist/react/models/kanban.js +35 -0
  199. package/dist/react/models/reorder.d.ts +9 -0
  200. package/dist/react/models/reorder.js +23 -0
  201. package/dist/react/models/table.d.ts +85 -0
  202. package/dist/react/models/table.js +144 -0
  203. package/dist/react/models/tree.d.ts +16 -0
  204. package/dist/react/models/tree.js +26 -0
  205. package/dist/react/provider.d.ts +18 -0
  206. package/dist/react/provider.js +31 -0
  207. package/dist/react/recipes/context-hypercms.d.ts +17 -0
  208. package/dist/react/recipes/context-hypercms.js +71 -0
  209. package/dist/react/recipes/context-quickcrop.d.ts +8 -0
  210. package/dist/react/recipes/context-quickcrop.js +49 -0
  211. package/dist/react/theme.css +8004 -0
  212. package/dist/react/theme.css.d.ts +1 -0
  213. package/examples/README.md +34 -0
  214. package/examples/components/activity-feed.html +28 -0
  215. package/examples/components/app-shell.html +12 -0
  216. package/examples/components/app-toolbar.html +38 -0
  217. package/examples/components/avatar-group.html +27 -0
  218. package/examples/components/breadcrumbs.html +26 -0
  219. package/examples/components/bulk-bar.html +22 -0
  220. package/examples/components/code-block.html +12 -0
  221. package/examples/components/code-editor.html +25 -0
  222. package/examples/components/column-manager.html +9 -0
  223. package/examples/components/combobox.html +13 -0
  224. package/examples/components/context-hypercms.css +41 -0
  225. package/examples/components/context-hypercms.html +40 -0
  226. package/examples/components/context-hypercms.js +62 -0
  227. package/examples/components/context-quickcrop.html +137 -0
  228. package/examples/components/diff-view.html +9 -0
  229. package/examples/components/drawer.html +11 -0
  230. package/examples/components/editable-grid-cells.html +9 -0
  231. package/examples/components/file-browser.html +53 -0
  232. package/examples/components/file-tree.html +34 -0
  233. package/examples/components/files-table.html +80 -0
  234. package/examples/components/filter-builder.html +11 -0
  235. package/examples/components/form-error-summary.html +11 -0
  236. package/examples/components/index.html +51 -0
  237. package/examples/components/inline-edit.html +42 -0
  238. package/examples/components/kanban-board.html +42 -0
  239. package/examples/components/manifest.json +3279 -0
  240. package/examples/components/modal.html +11 -0
  241. package/examples/components/multi-select-picker.html +13 -0
  242. package/examples/components/pagination.html +32 -0
  243. package/examples/components/persistent-action-banner.html +23 -0
  244. package/examples/components/popconfirm.html +13 -0
  245. package/examples/components/popover-menu.html +13 -0
  246. package/examples/components/reference/baseline/DECISIONS.md +85 -0
  247. package/examples/components/reference/baseline/README.md +58 -0
  248. package/examples/components/reference/baseline/bevel-fonts.css +16 -0
  249. package/examples/components/reference/baseline/bevel.css +1339 -0
  250. package/examples/components/reference/baseline/bevel.js +496 -0
  251. package/examples/components/reference/baseline/fonts/LICENSE-ibm-plex-mono.txt +93 -0
  252. package/examples/components/reference/baseline/fonts/LICENSE-ibm-plex-sans.txt +93 -0
  253. package/examples/components/reference/baseline/fonts/LICENSE-newsreader.txt +93 -0
  254. package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  255. package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  256. package/examples/components/reference/baseline/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  257. package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-400-italic.woff2 +0 -0
  258. package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  259. package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  260. package/examples/components/reference/baseline/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  261. package/examples/components/reference/baseline/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  262. package/examples/components/reference/baseline/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  263. package/examples/components/reference/fable/IMPROVEMENTS.md +106 -0
  264. package/examples/components/reference/fable/PARENT-REPAIRS.md +17 -0
  265. package/examples/components/reference/fable/__pycache__/build_repairs.cpython-311.pyc +0 -0
  266. package/examples/components/reference/fable/__pycache__/kanban.cpython-311.pyc +0 -0
  267. package/examples/components/reference/fable/as-authored/IMPROVEMENTS-completed.md +106 -0
  268. package/examples/components/reference/fable/as-authored/base-astra.css +17 -0
  269. package/examples/components/reference/fable/as-authored/base-parent.css +416 -0
  270. package/examples/components/reference/fable/as-authored/build.py +858 -0
  271. package/examples/components/reference/fable/as-authored/fable.css +823 -0
  272. package/examples/components/reference/fable/as-authored/kanban-completed.css +235 -0
  273. package/examples/components/reference/fable/as-authored/kanban-completed.html +53 -0
  274. package/examples/components/reference/fable/as-authored/kanban-completed.py +212 -0
  275. package/examples/components/reference/fable/as-authored/kanban.py +207 -0
  276. package/examples/components/reference/fable/base-astra.css +17 -0
  277. package/examples/components/reference/fable/base-parent.css +416 -0
  278. package/examples/components/reference/fable/build.py +858 -0
  279. package/examples/components/reference/fable/build_repairs.py +25 -0
  280. package/examples/components/reference/fable/components/activity-feed.html +3 -0
  281. package/examples/components/reference/fable/components/app-toolbar.html +3 -0
  282. package/examples/components/reference/fable/components/avatar-group.html +4 -0
  283. package/examples/components/reference/fable/components/breadcrumbs.html +3 -0
  284. package/examples/components/reference/fable/components/code-block.html +9 -0
  285. package/examples/components/reference/fable/components/column-manager.html +3 -0
  286. package/examples/components/reference/fable/components/combobox.html +6 -0
  287. package/examples/components/reference/fable/components/context-hypercms.html +3 -0
  288. package/examples/components/reference/fable/components/context-quickcrop.html +4 -0
  289. package/examples/components/reference/fable/components/diff-view.html +13 -0
  290. package/examples/components/reference/fable/components/drawer.html +3 -0
  291. package/examples/components/reference/fable/components/editable-grid-cells.html +3 -0
  292. package/examples/components/reference/fable/components/filter-builder.html +3 -0
  293. package/examples/components/reference/fable/components/form-error-summary.html +4 -0
  294. package/examples/components/reference/fable/components/inline-edit.html +3 -0
  295. package/examples/components/reference/fable/components/kanban-board.html +53 -0
  296. package/examples/components/reference/fable/components/modal.html +3 -0
  297. package/examples/components/reference/fable/components/multi-select-picker.html +5 -0
  298. package/examples/components/reference/fable/components/pagination.html +3 -0
  299. package/examples/components/reference/fable/components/persistent-action-banner.html +4 -0
  300. package/examples/components/reference/fable/components/popconfirm.html +5 -0
  301. package/examples/components/reference/fable/components/popover-menu.html +3 -0
  302. package/examples/components/reference/fable/components/rich-text-toolbar.html +3 -0
  303. package/examples/components/reference/fable/components/save-status-indicator.html +2 -0
  304. package/examples/components/reference/fable/components/selectable-cards.html +3 -0
  305. package/examples/components/reference/fable/components/settings-rows.html +3 -0
  306. package/examples/components/reference/fable/components/sidebar-nav.html +4 -0
  307. package/examples/components/reference/fable/components/small-charts.html +5 -0
  308. package/examples/components/reference/fable/components/split-button.html +4 -0
  309. package/examples/components/reference/fable/components/split-panes.html +8 -0
  310. package/examples/components/reference/fable/components/stat-tiles.html +3 -0
  311. package/examples/components/reference/fable/components/toast-stack.html +5 -0
  312. package/examples/components/reference/fable/components/tree-view.html +4 -0
  313. package/examples/components/reference/fable/components/upload-dropzone.html +6 -0
  314. package/examples/components/reference/fable/components/wizard-shell.html +3 -0
  315. package/examples/components/reference/fable/coverage.json +414 -0
  316. package/examples/components/reference/fable/fable.css +823 -0
  317. package/examples/components/reference/fable/index.html +5 -0
  318. package/examples/components/reference/fable/kanban.css +237 -0
  319. package/examples/components/reference/fable/kanban.py +212 -0
  320. package/examples/components/reference/fable/parent-repairs.css +9 -0
  321. package/examples/components/reference/fixtures/data.json +102 -0
  322. package/examples/components/reference/fixtures/host.js +47 -0
  323. package/examples/components/reference/fixtures/static-host.js +39 -0
  324. package/examples/components/reference/fixtures/workspace.jpg +0 -0
  325. package/examples/components/reference/parent/CHANGES.md +179 -0
  326. package/examples/components/reference/parent/__pycache__/kanban.cpython-311.pyc +0 -0
  327. package/examples/components/reference/parent/base-astra.css +17 -0
  328. package/examples/components/reference/parent/base-parent.css +416 -0
  329. package/examples/components/reference/parent/build.py +148 -0
  330. package/examples/components/reference/parent/components/activity-feed.html +2 -0
  331. package/examples/components/reference/parent/components/app-toolbar.html +2 -0
  332. package/examples/components/reference/parent/components/avatar-group.html +4 -0
  333. package/examples/components/reference/parent/components/breadcrumbs.html +2 -0
  334. package/examples/components/reference/parent/components/code-block.html +8 -0
  335. package/examples/components/reference/parent/components/column-manager.html +2 -0
  336. package/examples/components/reference/parent/components/combobox.html +6 -0
  337. package/examples/components/reference/parent/components/context-hypercms.html +2 -0
  338. package/examples/components/reference/parent/components/context-quickcrop.html +4 -0
  339. package/examples/components/reference/parent/components/diff-view.html +4 -0
  340. package/examples/components/reference/parent/components/drawer.html +2 -0
  341. package/examples/components/reference/parent/components/editable-grid-cells.html +2 -0
  342. package/examples/components/reference/parent/components/filter-builder.html +2 -0
  343. package/examples/components/reference/parent/components/form-error-summary.html +2 -0
  344. package/examples/components/reference/parent/components/inline-edit.html +2 -0
  345. package/examples/components/reference/parent/components/kanban-board.html +84 -0
  346. package/examples/components/reference/parent/components/modal.html +2 -0
  347. package/examples/components/reference/parent/components/multi-select-picker.html +5 -0
  348. package/examples/components/reference/parent/components/pagination.html +2 -0
  349. package/examples/components/reference/parent/components/persistent-action-banner.html +4 -0
  350. package/examples/components/reference/parent/components/popconfirm.html +5 -0
  351. package/examples/components/reference/parent/components/popover-menu.html +2 -0
  352. package/examples/components/reference/parent/components/rich-text-toolbar.html +2 -0
  353. package/examples/components/reference/parent/components/save-status-indicator.html +2 -0
  354. package/examples/components/reference/parent/components/selectable-cards.html +2 -0
  355. package/examples/components/reference/parent/components/settings-rows.html +2 -0
  356. package/examples/components/reference/parent/components/sidebar-nav.html +4 -0
  357. package/examples/components/reference/parent/components/small-charts.html +2 -0
  358. package/examples/components/reference/parent/components/split-button.html +5 -0
  359. package/examples/components/reference/parent/components/split-panes.html +8 -0
  360. package/examples/components/reference/parent/components/stat-tiles.html +2 -0
  361. package/examples/components/reference/parent/components/toast-stack.html +5 -0
  362. package/examples/components/reference/parent/components/tree-view.html +4 -0
  363. package/examples/components/reference/parent/components/upload-dropzone.html +6 -0
  364. package/examples/components/reference/parent/components/wizard-shell.html +2 -0
  365. package/examples/components/reference/parent/coverage.json +414 -0
  366. package/examples/components/reference/parent/index.html +1 -0
  367. package/examples/components/reference/parent/kanban.css +114 -0
  368. package/examples/components/reference/parent/kanban.py +55 -0
  369. package/examples/components/reference/parent/refinement.css +53 -0
  370. package/examples/components/reference/selected/build.py +70 -0
  371. package/examples/components/reference/selected/components/activity-feed.html +3 -0
  372. package/examples/components/reference/selected/components/app-toolbar.html +3 -0
  373. package/examples/components/reference/selected/components/avatar-group.html +4 -0
  374. package/examples/components/reference/selected/components/breadcrumbs.html +3 -0
  375. package/examples/components/reference/selected/components/code-block.html +9 -0
  376. package/examples/components/reference/selected/components/column-manager.html +3 -0
  377. package/examples/components/reference/selected/components/combobox.html +6 -0
  378. package/examples/components/reference/selected/components/context-hypercms.html +3 -0
  379. package/examples/components/reference/selected/components/context-quickcrop.html +4 -0
  380. package/examples/components/reference/selected/components/diff-view.html +13 -0
  381. package/examples/components/reference/selected/components/drawer.html +3 -0
  382. package/examples/components/reference/selected/components/editable-grid-cells.html +3 -0
  383. package/examples/components/reference/selected/components/filter-builder.html +3 -0
  384. package/examples/components/reference/selected/components/form-error-summary.html +4 -0
  385. package/examples/components/reference/selected/components/inline-edit.html +3 -0
  386. package/examples/components/reference/selected/components/kanban-board.html +53 -0
  387. package/examples/components/reference/selected/components/modal.html +2 -0
  388. package/examples/components/reference/selected/components/multi-select-picker.html +5 -0
  389. package/examples/components/reference/selected/components/pagination.html +3 -0
  390. package/examples/components/reference/selected/components/persistent-action-banner.html +4 -0
  391. package/examples/components/reference/selected/components/popconfirm.html +5 -0
  392. package/examples/components/reference/selected/components/popover-menu.html +3 -0
  393. package/examples/components/reference/selected/components/rich-text-toolbar.html +3 -0
  394. package/examples/components/reference/selected/components/save-status-indicator.html +2 -0
  395. package/examples/components/reference/selected/components/selectable-cards.html +3 -0
  396. package/examples/components/reference/selected/components/settings-rows.html +3 -0
  397. package/examples/components/reference/selected/components/sidebar-nav.html +4 -0
  398. package/examples/components/reference/selected/components/small-charts.html +5 -0
  399. package/examples/components/reference/selected/components/split-button.html +4 -0
  400. package/examples/components/reference/selected/components/split-panes.html +8 -0
  401. package/examples/components/reference/selected/components/stat-tiles.html +3 -0
  402. package/examples/components/reference/selected/components/toast-stack.html +5 -0
  403. package/examples/components/reference/selected/components/tree-view.html +4 -0
  404. package/examples/components/reference/selected/components/upload-dropzone.html +6 -0
  405. package/examples/components/reference/selected/components/wizard-shell.html +3 -0
  406. package/examples/components/reference/selected/corrections.css +11 -0
  407. package/examples/components/reference/selected/coverage.json +413 -0
  408. package/examples/components/reference/selected/gallery-build.mjs +49 -0
  409. package/examples/components/reference/selected/gallery-spec.json +12 -0
  410. package/examples/components/reference/selected/index.html +1 -0
  411. package/examples/components/reference/selected/kanban-comparison.json +6 -0
  412. package/examples/components/reference/selected/manifest.json +476 -0
  413. package/examples/components/reference/sha256.json +142 -0
  414. package/examples/components/reference/sources/diagonal-close.svg +1 -0
  415. package/examples/components/reference/sources/hyperclay/public-assets/css/hyperclay-app-source.css +614 -0
  416. package/examples/components/reference/sources/hyperclay/public-assets/js/app.js +94 -0
  417. package/examples/components/reference/sources/hyperclayjs/src/ui/theModal.js +897 -0
  418. package/examples/components/reference/sources/hypercms/scripts/build-theme.js +203 -0
  419. package/examples/components/reference/sources/hypercms/src/anchor.js +24 -0
  420. package/examples/components/reference/sources/hypercms/src/apply-loop.js +167 -0
  421. package/examples/components/reference/sources/hypercms/src/diagnostics.js +44 -0
  422. package/examples/components/reference/sources/hypercms/src/enhance.js +173 -0
  423. package/examples/components/reference/sources/hypercms/src/events.js +955 -0
  424. package/examples/components/reference/sources/hypercms/src/form-builder.js +518 -0
  425. package/examples/components/reference/sources/hypercms/src/form-rules.js +208 -0
  426. package/examples/components/reference/sources/hypercms/src/hooks.js +100 -0
  427. package/examples/components/reference/sources/hypercms/src/hypercms-bundle.js +10 -0
  428. package/examples/components/reference/sources/hypercms/src/hypercms.js +347 -0
  429. package/examples/components/reference/sources/hypercms/src/morph.js +10 -0
  430. package/examples/components/reference/sources/hypercms/src/path.js +61 -0
  431. package/examples/components/reference/sources/hypercms/src/place.js +131 -0
  432. package/examples/components/reference/sources/hypercms/src/platform.js +131 -0
  433. package/examples/components/reference/sources/hypercms/src/refresh.js +87 -0
  434. package/examples/components/reference/sources/hypercms/src/richclay-bridge.js +140 -0
  435. package/examples/components/reference/sources/hypercms/src/row-identity.js +111 -0
  436. package/examples/components/reference/sources/hypercms/src/scaffold.js +10 -0
  437. package/examples/components/reference/sources/hypercms/src/session.js +303 -0
  438. package/examples/components/reference/sources/hypercms/src/shell.js +248 -0
  439. package/examples/components/reference/sources/hypercms/src/styles.css +298 -0
  440. package/examples/components/reference/sources/hypercms/src/targets.js +144 -0
  441. package/examples/components/reference/sources/hypercms/src/templates.js +656 -0
  442. package/examples/components/reference/sources/hypercms/src/theme/pixel-quiet.overrides.css +761 -0
  443. package/examples/components/reference/sources/hypercms/src/theme.generated.css +1772 -0
  444. package/examples/components/reference/sources/hypercms/src/toggle.js +666 -0
  445. package/examples/components/reference/sources/hypercms/src/unresolved.js +211 -0
  446. package/examples/components/reference/sources/hypercms/src/vendor/mirk.vendor.js +212 -0
  447. package/examples/components/reference/sources/hypercms/src/views/inline-layer.js +421 -0
  448. package/examples/components/reference/sources/hypercms/src/views/inline.js +1072 -0
  449. package/examples/components/reference/sources/hypercms/src/views/sidebar.js +97 -0
  450. package/examples/components/reference/sources/modal-reference.html +301 -0
  451. package/examples/components/reference/sources/quickcrop/quickcrop.css +124 -0
  452. package/examples/components/reference/sources/quickcrop/quickcrop.js +400 -0
  453. package/examples/components/reference/sources/richclay/richclay.css +416 -0
  454. package/examples/components/reference/sources/richclay/richclay.js +2962 -0
  455. package/examples/components/reference/sources/richclay/src/a11y.js +67 -0
  456. package/examples/components/reference/sources/richclay/src/browser-global.js +7 -0
  457. package/examples/components/reference/sources/richclay/src/buttons.js +254 -0
  458. package/examples/components/reference/sources/richclay/src/hyperclay.js +470 -0
  459. package/examples/components/reference/sources/richclay/src/normalize.js +380 -0
  460. package/examples/components/reference/sources/richclay/src/richclay.js +1356 -0
  461. package/examples/components/reference/sources/richclay/src/sanitize.js +189 -0
  462. package/examples/components/reference/sources/richclay/src/styles.js +23 -0
  463. package/examples/components/reference/sources/richclay/src/toolbar-float.js +175 -0
  464. package/examples/components/reference/sources/richclay/src/toolbar.js +394 -0
  465. package/examples/components/reference/sources/richclay/src/vendor-bundle.js +15 -0
  466. package/examples/components/reference/sources/richclay/vendor/DOMPURIFY-LICENSE +202 -0
  467. package/examples/components/reference/sources/richclay/vendor/DOMPURIFY-LICENSE-MPL +373 -0
  468. package/examples/components/reference/sources/richclay/vendor/SQUIRE-LICENSE +21 -0
  469. package/examples/components/reference/sources/richclay/vendor/purify.es.mjs +2249 -0
  470. package/examples/components/reference/sources/richclay/vendor/purify.min.js +3 -0
  471. package/examples/components/reference/sources/richclay/vendor/squire.js +22 -0
  472. package/examples/components/reference/sources/richclay/vendor/squire.mjs +22 -0
  473. package/examples/components/reference/sources/richclay/vendor/squire.provenance.json +19 -0
  474. package/examples/components/rich-text-toolbar.html +43 -0
  475. package/examples/components/save-status-indicator.html +11 -0
  476. package/examples/components/selectable-cards.html +9 -0
  477. package/examples/components/settings-rows.html +10 -0
  478. package/examples/components/sidebar-nav.html +9 -0
  479. package/examples/components/small-charts.html +21 -0
  480. package/examples/components/split-button.html +13 -0
  481. package/examples/components/split-panes-editor.html +6 -0
  482. package/examples/components/split-panes.html +9 -0
  483. package/examples/components/stat-tiles.html +23 -0
  484. package/examples/components/toast-stack.html +21 -0
  485. package/examples/components/tree-view.html +9 -0
  486. package/examples/components/upload-dropzone.html +88 -0
  487. package/examples/components/wizard-shell.html +50 -0
  488. package/examples/datastar/index.html +38 -0
  489. package/examples/datastar/server.mjs +31 -0
  490. package/examples/fixtures/field-guide.pdf +14 -0
  491. package/examples/fixtures/workspace.jpg +0 -0
  492. package/examples/setup-peers.mjs +35 -0
  493. package/fonts/LICENSE-ibm-plex-mono.txt +93 -0
  494. package/fonts/LICENSE-ibm-plex-sans.txt +93 -0
  495. package/fonts/LICENSE-newsreader.txt +93 -0
  496. package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  497. package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  498. package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  499. package/fonts/ibm-plex-sans-latin-400-italic.woff2 +0 -0
  500. package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  501. package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  502. package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  503. package/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  504. package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  505. package/icons/LICENSE +21 -0
  506. package/icons/house.json +71 -0
  507. package/icons/icons.d.ts +17 -0
  508. package/icons/icons.js +648 -0
  509. package/icons/icons.svg +126 -0
  510. package/icons/marks/build-tray.source.mjs +45 -0
  511. package/icons/marks/hyperclay-local.svg +11 -0
  512. package/icons/phosphor.json +53 -0
  513. package/icons/round.json +8 -0
  514. package/integrations/datastar.js +202 -0
  515. package/integrations/quickcrop.css +26 -0
  516. package/integrations/quickcrop.js +56 -0
  517. package/integrations/richclay.css +100 -0
  518. package/integrations/richclay.js +131 -0
  519. package/package.json +630 -0
  520. package/react/README.md +244 -0
  521. package/src/README.md +83 -0
  522. package/vendor/datastar/datastar-1.0.3.js +10 -0
  523. package/vendor/datastar/provenance.json +6 -0
  524. package/vendor/floating-ui/LICENSE-core +20 -0
  525. package/vendor/floating-ui/LICENSE-dom +20 -0
  526. package/vendor/floating-ui/floating-ui.core.umd.min.js +1 -0
  527. package/vendor/floating-ui/floating-ui.dom.umd.min.js +1 -0
  528. package/vendor/floating-ui/provenance.json +17 -0
  529. package/vendor/richclay/LICENSE +16 -0
  530. package/vendor/richclay/THIRD-PARTY-NOTICES.md +45 -0
  531. package/vendor/richclay/provenance.json +17 -0
  532. package/vendor/richclay/richclay.css +404 -0
  533. package/vendor/richclay/richclay.js +2940 -0
  534. package/vendor/richclay/vendor/DOMPURIFY-LICENSE +202 -0
  535. package/vendor/richclay/vendor/DOMPURIFY-LICENSE-MPL +373 -0
  536. package/vendor/richclay/vendor/SQUIRE-LICENSE +21 -0
  537. package/vendor/richclay/vendor/purify.min.js +3 -0
  538. package/vendor/richclay/vendor/squire.js +22 -0
  539. package/vendor/richclay/vendor/squire.provenance.json +30 -0
@@ -0,0 +1,208 @@
1
+ import {
2
+ shapeKindOf,
3
+ findTemplate,
4
+ isInlineTemplate,
5
+ componentForScalarRule,
6
+ winningScalarArrayComponent,
7
+ } from './templates.js'
8
+
9
+ const TAG_PROP_MAP = {
10
+ IMG: 'src',
11
+ A: 'href',
12
+ }
13
+
14
+ export function fieldPropertyFor(el) {
15
+ if (!el) return 'value'
16
+ const tag = (el.tagName || '').toUpperCase()
17
+ if (tag === 'INPUT') {
18
+ const type = (el.getAttribute('type') || 'text').toLowerCase()
19
+ if (type === 'checkbox') return 'checked'
20
+ return 'value'
21
+ }
22
+ if (tag === 'TEXTAREA' || tag === 'SELECT') return 'value'
23
+ if (TAG_PROP_MAP[tag]) return TAG_PROP_MAP[tag]
24
+ // Rich-text surfaces (the @richtext component): the value IS the markup.
25
+ if (el.hasAttribute && el.hasAttribute('contenteditable')) return 'innerHTML'
26
+ return null
27
+ }
28
+
29
+ // Tag-specific scalar field selector. Returns a selector that uniquely
30
+ // identifies the leaf input under data-hcms-field="key" — necessary because
31
+ // data-hcms-field is now stamped on every keyed node (containers + leaves),
32
+ // so a bare `[data-hcms-field="key"]` would match the wrapping container too.
33
+ export function fieldSelectorFor(el, key) {
34
+ const tag = (el.tagName || '').toUpperCase()
35
+ const type = (el.getAttribute && el.getAttribute('type') || '').toLowerCase()
36
+ const prop = fieldPropertyFor(el)
37
+ const tagSel = tagSelectorFor(tag, type)
38
+ const fieldSel = `${tagSel}[data-hcms-field="${cssEscape(key)}"]`
39
+ if (tag === 'INPUT' && type === 'radio') {
40
+ return `${fieldSel}:checked@value`
41
+ }
42
+ if (prop) return `${fieldSel}@${prop}`
43
+ return fieldSel
44
+ }
45
+
46
+ // Build a per-item leaf selector with no key qualifier — used by scalar-array
47
+ // rules where the leaf is scoped by the surrounding per-item context, so the
48
+ // data-hcms-field key isn't needed (and items don't have a stable key anyway).
49
+ function keylessFieldSelectorFor(el) {
50
+ const tag = (el.tagName || '').toUpperCase()
51
+ const type = (el.getAttribute && el.getAttribute('type') || '').toLowerCase()
52
+ const prop = fieldPropertyFor(el)
53
+ const tagSel = tagSelectorFor(tag, type)
54
+ const base = `${tagSel}[data-hcms-field]`
55
+ if (tag === 'INPUT' && type === 'radio') return `${base}:checked@value`
56
+ if (prop) return `${base}@${prop}`
57
+ return base
58
+ }
59
+
60
+ function tagSelectorFor(tag, type) {
61
+ if (tag === 'INPUT') {
62
+ if (type) return `input[type="${type}"]`
63
+ return 'input'
64
+ }
65
+ if (tag === 'TEXTAREA') return 'textarea'
66
+ if (tag === 'SELECT') return 'select'
67
+ if (tag === 'IMG') return 'img'
68
+ if (tag === 'A') return 'a'
69
+ // Custom/unknown element (e.g. contenteditable div) — match by attribute
70
+ // and exclude every container shape (scalar wrapper too) so the wrapping
71
+ // node never shadows the inner leaf. v0.3 stamps data-hcms-field on the
72
+ // scalar wrapper as well, so omitting `scalar` here would otherwise match
73
+ // the wrapper first and extract its full text instead of the leaf's.
74
+ return ':not([data-hcms-shape="scalar"]):not([data-hcms-shape="object"]):not([data-hcms-shape="object-array"]):not([data-hcms-shape="scalar-array"])'
75
+ }
76
+
77
+ function cssEscape(value) {
78
+ if (typeof CSS !== 'undefined' && CSS.escape) return CSS.escape(value)
79
+ return String(value).replace(/[^a-zA-Z0-9_\-.*]/g, (c) => '\\' + c)
80
+ }
81
+
82
+ const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype'])
83
+
84
+ export function deriveFormRules(pageRules, doc) {
85
+ return derive(pageRules, [])
86
+
87
+ function derive(rule, pathArr) {
88
+ const kind = shapeKindOf(rule)
89
+ if (kind === 'scalar') return scalarRule(rule, pathArr)
90
+ if (kind === 'scalar-array') return scalarArrayRule(rule, pathArr)
91
+ if (kind === 'object-array') return objectArrayRule(rule, pathArr)
92
+ if (kind === 'object') {
93
+ const out = Object.create(null)
94
+ for (const [k, child] of Object.entries(rule)) {
95
+ if (FORBIDDEN_KEYS.has(k)) {
96
+ throw new Error(`hypercms: rule key "${k}" is forbidden at "${pathArr.join('.') || '<root>'}"`)
97
+ }
98
+ out[k] = derive(child, [...pathArr, k])
99
+ }
100
+ return out
101
+ }
102
+ return null
103
+ }
104
+
105
+ function scalarRule(rule, pathArr) {
106
+ const key = pathArr.length ? pathArr[pathArr.length - 1] : null
107
+ const fieldKey = typeof key === 'string' ? key : '__value'
108
+ const inlineEl = findInlineFieldEl(pathArr, fieldKey)
109
+ if (inlineEl) return fieldSelectorFor(inlineEl, fieldKey)
110
+ // No path-bound override: resolve the component this rule opts into
111
+ // (@scalar / @image / @file) and derive the selector from THAT template's
112
+ // leaf tag, so an @image field reads img@src and an @file field reads
113
+ // a@href, while a plain scalar still respects a global @scalar override
114
+ // (e.g. a <textarea>) instead of assuming <input>.
115
+ const shapeEl = findShapeFieldEl(componentForScalarRule(rule, doc, pathArr, pageRules), fieldKey)
116
+ if (shapeEl) return fieldSelectorFor(shapeEl, fieldKey)
117
+ return `input[data-hcms-field="${cssEscape(fieldKey)}"]@value`
118
+ }
119
+
120
+ function scalarArrayRule(rule, pathArr) {
121
+ // The engine's "selector[]" form is text-only; for an editable form we need
122
+ // per-item @value reads, so emit the [selector, shape] form with a scalar
123
+ // shape that targets the inner field's value property. If the site
124
+ // overrides @scalar-array-item with a non-<input> leaf (textarea, etc.),
125
+ // derive the inner-leaf selector from the override. A component array
126
+ // (chips) derives from its own item template first — but only when the
127
+ // chips template actually wins at this path (winningScalarArrayComponent
128
+ // is the same verdict buildScalarArray uses, so the derived item selector
129
+ // always matches the rendered item leaf).
130
+ const comp = winningScalarArrayComponent(rule, pathArr, doc)
131
+ const shapeEl =
132
+ (comp && findShapeFieldEl(comp.item, null)) || findShapeFieldEl('@scalar-array-item', null)
133
+ const itemSel = shapeEl ? keylessFieldSelectorFor(shapeEl) : 'input[data-hcms-field]@value'
134
+ return [itemSelectorFor(pathArr, '[data-hcms-array-item]'), itemSel]
135
+ }
136
+
137
+ function objectArrayRule(rule, pathArr) {
138
+ const [, itemShape] = rule
139
+ const itemPath = [...pathArr, '*']
140
+ const itemSelector = itemSelectorFor(pathArr, '[data-hcms-card]')
141
+ if (itemShape && typeof itemShape === 'object' && !Array.isArray(itemShape)) {
142
+ const obj = Object.create(null)
143
+ for (const [k, child] of Object.entries(itemShape)) {
144
+ if (FORBIDDEN_KEYS.has(k)) {
145
+ throw new Error(`hypercms: rule key "${k}" is forbidden at "${itemPath.join('.')}"`)
146
+ }
147
+ obj[k] = derive(child, [...itemPath, k])
148
+ }
149
+ return [itemSelector, obj]
150
+ }
151
+ return [itemSelector, derive(itemShape, [...itemPath, 0])]
152
+ }
153
+
154
+ // Build a selector that scopes item matching to the right array container.
155
+ // Top-level paths use exact data-hcms-path match. Nested paths use the
156
+ // stable data-hcms-field key (which the form-builder stamps on every keyed
157
+ // container) — this disambiguates sibling arrays that share a terminal key
158
+ // (e.g. products.*.primary.variants vs products.*.secondary.variants would
159
+ // both have a `variants` array with the same last segment).
160
+ function itemSelectorFor(pathArr, itemTag) {
161
+ const lastKey = pathArr.length ? pathArr[pathArr.length - 1] : ''
162
+ const hasWildcard = pathArr.some((s) => s === '*')
163
+ const pathStr = pathArr.join('.')
164
+ const containerSel = hasWildcard
165
+ ? `[data-hcms-field="${cssEscape(lastKey)}"]`
166
+ : `[data-hcms-path="${cssEscape(pathStr)}"]`
167
+ return `${containerSel} > .hcms-array-items > ${itemTag}`
168
+ }
169
+
170
+ // Look up the field leaf inside a shape-default template (@scalar,
171
+ // @scalar-array-item, etc.). Sites can override these globally, so the
172
+ // selector emitted must reflect the actual leaf tag/type, not assume input.
173
+ // Returns null if the template isn't found or has no [data-hcms-field].
174
+ function findShapeFieldEl(shapeKey, fieldKey) {
175
+ if (!doc) return null
176
+ const tpl = findTemplate(doc, shapeKey)
177
+ if (!tpl) return null
178
+ const content = tpl.content || tpl
179
+ if (fieldKey) {
180
+ const keyed = content.querySelector(`[data-hcms-field="${cssEscape(fieldKey)}"]`)
181
+ if (keyed) return keyed
182
+ }
183
+ return content.querySelector('[data-hcms-field]')
184
+ }
185
+
186
+ function findInlineFieldEl(pathArr, fieldKey) {
187
+ if (!doc) return null
188
+ const wildcardKey = pathArr.map((s) => (typeof s === 'number' ? '*' : s)).join('.')
189
+ const pathStr = pathArr.join('.')
190
+ const candidates = [pathStr, wildcardKey]
191
+ for (let i = pathArr.length - 1; i >= 0; i--) {
192
+ const slice = pathArr.slice(0, i).map((s) => (typeof s === 'number' ? '*' : s))
193
+ slice.push('*')
194
+ candidates.push(slice.join('.'))
195
+ }
196
+ for (const key of candidates) {
197
+ if (!key) continue
198
+ const tpl = findTemplate(doc, key)
199
+ if (!tpl || !isInlineTemplate(tpl)) continue
200
+ const content = tpl.content || tpl
201
+ const el =
202
+ content.querySelector(`[data-hcms-field="${cssEscape(fieldKey)}"]`) ||
203
+ content.querySelector('[data-hcms-field]')
204
+ if (el) return el
205
+ }
206
+ return null
207
+ }
208
+ }
@@ -0,0 +1,100 @@
1
+ // The two clone hooks hypercms installs on the host client, and the retry that
2
+ // covers a client arriving after hypercms does.
3
+ //
4
+ // Their own module rather than private state inside hypercms.js, because the
5
+ // inline view has to install the snapshot hook too: by the time someone clicks
6
+ // a heading the client is certainly loaded, which makes the bind the reliable
7
+ // second trigger for a page that lost the readiness race. Importing that back
8
+ // out of the entry module would have made the entry module part of a cycle.
9
+
10
+ import { platform, onPlatformEvent, PLATFORM_READY } from './platform.js'
11
+ import { BOUND_ID_ATTR, cleanRichClayFromSnapshot, pristineMarkupFor } from './richclay-bridge.js'
12
+ import { SESSION_OPEN_CLASS } from './shell.js'
13
+
14
+ // Strip hypercms's own body chrome from the SAVE clone so it never reaches disk
15
+ // (the shell element itself is [save-remove], but the hcms-open class lives on
16
+ // <body>, outside the stripped subtree). Registered once at first open():
17
+ // onPrepareForSave runs save-only, on a clone, so it never touches the live DOM,
18
+ // and removing absent classes is a no-op when the shell is closed. Guarded so it
19
+ // degrades cleanly when the host save pipeline (hyperclayjs) isn't present.
20
+ let prepareHookInstalled = false
21
+ export function installSavePrepareHook() {
22
+ if (prepareHookInstalled) return
23
+ const onPrepareForSave = platform('onPrepareForSave')
24
+ if (typeof onPrepareForSave !== 'function') return
25
+ onPrepareForSave((clonedDocEl) => {
26
+ restorePristineMarkup(clonedDocEl)
27
+ stripSessionChrome(clonedDocEl)
28
+ })
29
+ prepareHookInstalled = true
30
+ }
31
+
32
+ // hypercms's own chrome lives on <body>, outside the [save-remove] shell, so it
33
+ // has to be taken off the clone by hand. Both hooks call this: the save hook
34
+ // because a client may offer onPrepareForSave and no onSnapshot, and the
35
+ // snapshot hook because that is the only one live sync reaches, and without it
36
+ // the session's own classes are broadcast to every other browser. Removing an
37
+ // absent class is a no-op, so running twice on a save costs nothing.
38
+ function stripSessionChrome(clonedDocEl) {
39
+ const b = clonedDocEl && clonedDocEl.querySelector && clonedDocEl.querySelector('body')
40
+ if (b) b.classList.remove('hcms-open', 'hcms-overlay', 'hcms-side-left', SESSION_OPEN_CLASS)
41
+ }
42
+
43
+ // A save can land while somebody is mid-session with an editor open on a
44
+ // heading. richclay rewrites a block's markup simply by being attached to it, so
45
+ // without this the file records that rewriting as though the author had typed
46
+ // it. The live editors are deliberately untouched: this is the clone, and the
47
+ // person editing keeps their caret and their selection.
48
+ function restorePristineMarkup(clonedDocEl) {
49
+ if (!clonedDocEl || typeof clonedDocEl.querySelectorAll !== 'function') return
50
+ for (const el of clonedDocEl.querySelectorAll(`[${BOUND_ID_ATTR}]`)) {
51
+ const html = pristineMarkupFor(el)
52
+ if (html !== null) el.innerHTML = html
53
+ el.removeAttribute(BOUND_ID_ATTR)
54
+ }
55
+ }
56
+
57
+ // Clean up after the richclay instances hypercms itself creates on page
58
+ // elements. Installed once at first open, alongside the save-prepare hook above,
59
+ // and for the same reason: the host client may not be loaded yet at module
60
+ // evaluation. onSnapshot rather than onPrepareForSave, because this has to run
61
+ // for live sync too — a hook on the save-only path fires after the editor's
62
+ // leftovers have already been broadcast to every other browser.
63
+ let snapshotHookInstalled = false
64
+ export function installSnapshotHook() {
65
+ if (snapshotHookInstalled) return
66
+ const onSnapshot = platform('onSnapshot')
67
+ if (typeof onSnapshot !== 'function') return
68
+ onSnapshot((clonedDocEl) => {
69
+ restorePristineMarkup(clonedDocEl)
70
+ cleanRichClayFromSnapshot(clonedDocEl, typeof window !== 'undefined' ? window : null)
71
+ stripSessionChrome(clonedDocEl)
72
+ })
73
+ snapshotHookInstalled = true
74
+ }
75
+
76
+ // What open() calls: install both, and arm the retry when either could not be
77
+ // installed yet.
78
+ export function installHooks() {
79
+ installSavePrepareHook()
80
+ installSnapshotHook()
81
+ if (!prepareHookInstalled || !snapshotHookInstalled) armHookRetry()
82
+ }
83
+
84
+ // Both hooks read a capability that can arrive after hypercms does, and both
85
+ // return without recording success when it is missing. Retry on the clients'
86
+ // readiness pair and disarm once both are in, so a page that lost the import
87
+ // race still gets its clone cleanup.
88
+ let offPlatformReady = null
89
+ function armHookRetry() {
90
+ if (offPlatformReady || typeof document === 'undefined') return
91
+ const retry = () => {
92
+ installSavePrepareHook()
93
+ installSnapshotHook()
94
+ if (prepareHookInstalled && snapshotHookInstalled) {
95
+ offPlatformReady?.()
96
+ offPlatformReady = null
97
+ }
98
+ }
99
+ offPlatformReady = onPlatformEvent(document, PLATFORM_READY, retry)
100
+ }
@@ -0,0 +1,10 @@
1
+ import * as core from './hypercms.js'
2
+ import themeText from './theme.generated.css'
3
+ // Install mirk's delegated component runtime (tags chips, number stepper, etc.)
4
+ // as a side effect. Idempotent + document-delegated, so safe to include once.
5
+ import './vendor/mirk.vendor.js'
6
+
7
+ core.installStyles(themeText)
8
+
9
+ export const cms = core.cms
10
+ export default { cms: core.cms }
@@ -0,0 +1,347 @@
1
+ import { engine } from 'hyper-html-api'
2
+ import * as pathUtil from './path.js'
3
+ import { scaffold } from './scaffold.js'
4
+ import { morphForm } from './morph.js'
5
+ import {
6
+ commit,
7
+ commitWithUndo,
8
+ onAdd as evOnAdd,
9
+ onRemove as evOnRemove,
10
+ extractFormData,
11
+ restampAllSiblings,
12
+ } from './events.js'
13
+ import { setShellStyles, markStylesBundled } from './shell.js'
14
+ import { createSidebarView } from './views/sidebar.js'
15
+ import { createInlineView } from './views/inline.js'
16
+ import {
17
+ state,
18
+ createSession,
19
+ startSession,
20
+ teardownSession,
21
+ nextSearchAfterClose,
22
+ shouldAutoOpenFromSearch,
23
+ } from './session.js'
24
+ import { findLeafField, writeFieldValue } from './events.js'
25
+ import { maybeInjectToggle } from './toggle.js'
26
+ import { platform, onPlatformEvent, MUTATION_READY } from './platform.js'
27
+ import { installHooks } from './hooks.js'
28
+
29
+ export { nextSearchAfterClose, shouldAutoOpenFromSearch }
30
+ // The leaf-write pair api.setValue is built on. Exported because the inline
31
+ // view's text commit writes the same form leaf from the page element, and a
32
+ // second copy of this logic is how the two paths drift apart.
33
+ export { findLeafField, writeFieldValue }
34
+
35
+ export function installStyles(text) {
36
+ setShellStyles(text)
37
+ }
38
+
39
+ export function markBundledStyles(doc) {
40
+ markStylesBundled(doc)
41
+ }
42
+
43
+ // The views a session can be rendered through. A name that is not in here is a
44
+ // caller error, never a fallback: silently opening a sidebar when someone asked
45
+ // for the inline editor would read as the inline editor being broken.
46
+ const VIEWS = {
47
+ sidebar: createSidebarView,
48
+ inline: createInlineView,
49
+ }
50
+
51
+ export function open(opts = {}) {
52
+ // Everything that can be rejected is rejected before anything is destroyed. A
53
+ // bad view name or a missing page must not leave a caller with the session
54
+ // they had already torn down.
55
+ // A bare open() means "make sure the editor is up", not "put it in the
56
+ // sidebar". Defaulting to sidebar turned every such call into a view switch.
57
+ const viewName = opts.view || (state.isOpen ? state.ctx.view.name : 'sidebar')
58
+ const makeView = VIEWS[viewName]
59
+ if (!makeView) {
60
+ throw new Error(`hypercms: unknown view "${viewName}" (expected ${Object.keys(VIEWS).join(' or ')})`)
61
+ }
62
+ // A switch changes the presentation of one session, not the session. Someone
63
+ // calling open({ view: 'sidebar' }) means "show me this in the sidebar"; they
64
+ // are not restating the pageRoot, the rules and the callbacks they opened
65
+ // with. Resolving those from this call alone silently retargets the CMS at
66
+ // document.body with the default rules tag and stops calling onChange.
67
+ // state.opts holds the active session's options and teardown clears it, so it
68
+ // has to be read here, before anything is torn down.
69
+ const effective = state.isOpen ? { ...state.opts, ...opts, view: viewName } : opts
70
+
71
+ const pageRoot = effective.pageRoot || (typeof document !== 'undefined' ? document.body : null)
72
+ if (!pageRoot) throw new Error('hypercms: no pageRoot available')
73
+ const doc = pageRoot.ownerDocument || (typeof document !== 'undefined' ? document : null)
74
+ if (!doc) throw new Error('hypercms: no document available')
75
+
76
+ // Reopening the view that is already up is a silent no-op, not a warning: a
77
+ // host wiring a button to open({ view }) should be able to call it without
78
+ // first asking what is open. Switching views tears the old one down WITHOUT
79
+ // the close-only side effects — no hcms:close URL rewrite, no focus restore —
80
+ // because a switch is one continuous session from the person's side, not a
81
+ // close followed by an open.
82
+ let carriedFocus = null
83
+ let previous = null
84
+ if (state.isOpen) {
85
+ if (state.ctx.view.name === viewName) return
86
+ carriedFocus = state.ctx.previouslyFocused
87
+ previous = state.ctx.view.name
88
+ teardownSession(state.ctx, { restoreFocus: false, updateUrl: false, reason: 'switch' })
89
+ }
90
+
91
+ installHooks()
92
+
93
+ const view = makeView({ doc, pageRoot, opts: effective })
94
+ const ctx = createSession({
95
+ view,
96
+ doc,
97
+ pageRoot,
98
+ opts: effective,
99
+ onCloseRequested: () => close(),
100
+ onViewRequested: (name) => open({ view: name }),
101
+ })
102
+ // Where focus was before the FIRST open, carried across the switch. Closing
103
+ // the second view should return the person where they started, not to
104
+ // whatever held focus after the first view was pulled out from under them.
105
+ if (carriedFocus) ctx.previouslyFocused = carriedFocus
106
+
107
+ // Everything past here touches the live DOM, the module-level state, and
108
+ // external subscriptions (observer, undo, livesync). A throw in here — most
109
+ // notably installObserver when window.hyperclay.Mutation isn't loaded — would
110
+ // otherwise strand the mounted view in the DOM with no way to close it (close()
111
+ // early-returns while state.isOpen is false). Tear down whatever was wired,
112
+ // without the close-only side effects (no hcms:close, no URL rewrite, no focus
113
+ // restore), then rethrow so host misuse still fails loudly.
114
+ try {
115
+ view.mount(ctx.initialData)
116
+ startSession(ctx)
117
+ view.focusOnOpen()
118
+
119
+ state.isOpen = true
120
+ state.ctx = ctx
121
+ state.opts = effective
122
+
123
+ ctx.dispatch('hcms:open', { pageRoot, previous })
124
+ } catch (err) {
125
+ // A failed switch already destroyed the view that was up, so nothing is
126
+ // mounted and nothing can be reopened; the least bad outcome is handing
127
+ // focus back where it started. On a first open focus never moved, so
128
+ // restoring it there would be a no-op either way.
129
+ teardownSession(ctx, { dispatch: false, restoreFocus: !!carriedFocus, updateUrl: false })
130
+ throw err
131
+ }
132
+ }
133
+
134
+ export function close() {
135
+ if (!state.isOpen) return
136
+ teardownSession(state.ctx)
137
+ }
138
+
139
+ export function refresh() {
140
+ if (!state.isOpen) return
141
+ state.ctx.view.refresh('api')
142
+ }
143
+
144
+ export function isOpen() {
145
+ return state.isOpen
146
+ }
147
+
148
+ // Which view owns the active session. Nothing may infer this from
149
+ // body.hcms-open, which describes only the sidebar's page shift and says nothing
150
+ // about an inline session.
151
+ export function currentView() {
152
+ return state.isOpen && state.ctx ? state.ctx.view.name : null
153
+ }
154
+
155
+ export const api = {
156
+ getData() {
157
+ if (!state.isOpen) return null
158
+ return extractFormData(state.ctx)
159
+ },
160
+ setValue(path, value) {
161
+ if (!state.isOpen) throw new Error('hypercms: cms is not open')
162
+ const ctx = state.ctx
163
+ const pathArr = pathUtil.fromString(path)
164
+ const rule = pathUtil.getRuleAtPath(ctx.pageRules, pathArr)
165
+ if (rule === undefined) throw new Error(`hypercms: no rule at path "${path}"`)
166
+ if (typeof rule !== 'string' || rule.endsWith('[]')) {
167
+ throw new Error(`hypercms: setValue requires a leaf scalar path; "${path}" is not a leaf`)
168
+ }
169
+ const field = findLeafField(ctx.formRoot, path)
170
+ if (!field) throw new Error(`hypercms: no field element at path "${path}"`)
171
+ writeFieldValue(field, value, ctx.formRoot, path)
172
+ commit(extractFormData(ctx), { path, structural: false }, ctx)
173
+ },
174
+ addItem(arrayPath) {
175
+ if (!state.isOpen) throw new Error('hypercms: cms is not open')
176
+ evOnAdd(arrayPath, state.ctx)
177
+ },
178
+ removeItem(itemPath) {
179
+ if (!state.isOpen) throw new Error('hypercms: cms is not open')
180
+ const ctx = state.ctx
181
+ const pathArr = pathUtil.fromString(itemPath)
182
+ // Validate path: must be an array item — last segment is a non-negative
183
+ // integer, parent rule must be an array (object-array or scalar-array).
184
+ const lastSeg = pathArr[pathArr.length - 1]
185
+ if (typeof lastSeg !== 'number') {
186
+ throw new Error(`hypercms: removeItem requires an item path; "${itemPath}" is not an array index`)
187
+ }
188
+ const parentRule = pathUtil.getRuleAtPath(ctx.pageRules, pathArr.slice(0, -1))
189
+ const parentIsArray = Array.isArray(parentRule) || (typeof parentRule === 'string' && parentRule.endsWith('[]'))
190
+ if (!parentIsArray) {
191
+ throw new Error(`hypercms: removeItem requires an item path; parent of "${itemPath}" is not an array`)
192
+ }
193
+ const itemEl = ctx.formRoot.querySelector(`[data-hcms-path="${cssEscape(itemPath)}"]`)
194
+ if (!itemEl) throw new Error(`hypercms: no element at path "${itemPath}"`)
195
+ evOnRemove(itemEl, ctx)
196
+ },
197
+ refresh,
198
+ _commit() {
199
+ if (!state.isOpen) return
200
+ const ctx = state.ctx
201
+ restampAllSiblings(ctx.formRoot)
202
+ return commitWithUndo('Update', () =>
203
+ commit(extractFormData(ctx), { path: '', structural: true }, ctx)
204
+ )
205
+ },
206
+ }
207
+
208
+ // ?cms=true auto-open. When the page URL carries cms=true at load, open the CMS
209
+ // for everyone (no gating — this is a feature showcase; a non-admin's edits stay
210
+ // DOM-local and the host save path shows its view-mode notice). open() is
211
+ // idempotent-safe (it returns quietly if the same view is already open), so a
212
+ // host that also calls open() never double-mounts. No special opts — host
213
+ // defaults apply.
214
+ //
215
+ // Patience matters here: this runs at module load from the dist IIFE, which a
216
+ // host typically loads BEFORE installing window.hyperclay.Mutation (the Mutation
217
+ // install often sits behind an `await import(...)` that resumes after this script,
218
+ // even after DOMContentLoaded). open() needs <body> AND Mutation, so we poll for
219
+ // both rather than firing eagerly. Firing without Mutation would throw out of
220
+ // installObserver, escape module evaluation, and abort esbuild's
221
+ // window.hypercms assignment — taking the host's own wiring down with it.
222
+ const SAFETY_POLL_MS = 250
223
+ const AUTO_OPEN_DEADLINE_MS = 10000
224
+ export function maybeAutoOpen() {
225
+ if (typeof window === 'undefined' || typeof document === 'undefined') return
226
+ if (!shouldAutoOpenFromSearch(window.location ? window.location.search : '')) return
227
+ if (state.isOpen) return
228
+ whenReady(() => {
229
+ if (state.isOpen) return
230
+ try {
231
+ open()
232
+ } catch (err) {
233
+ console.warn('hypercms: auto-open failed', err)
234
+ }
235
+ })
236
+ }
237
+
238
+ // Auto-open is ready only once <body> is parsed AND window.hyperclay.Mutation is
239
+ // installed (open() needs both). NEVER throw out of module scope.
240
+ function autoOpenReady() {
241
+ return !!document.body && !!platform('Mutation')
242
+ }
243
+
244
+ // Three layers, in order of preference:
245
+ // 1. Sync fast-path: if <body> and Mutation are already here, fire now.
246
+ // 2. Event: both clients dispatch a mutation-ready signal the instant they
247
+ // install the mutation hub, under two spellings ('clay:mutation-ready' and
248
+ // 'hyperclay:mutation-ready'), so hypercms listens for both and dedups. On
249
+ // loader pages this is moot — the loader's first wave evaluates mutation
250
+ // before hypercms, so the fast-path wins — but the event catches any late
251
+ // install (the deferred `await import()` ordering). The body gate is
252
+ // re-checked in the handler because the event can land before <body> is
253
+ // parsed.
254
+ // 3. Backstop: a slow self-cancelling 250ms poll for exotic hosts that hand-roll
255
+ // window.hyperclay.Mutation by direct assignment WITHOUT dispatching the
256
+ // event. It cancels the moment the event fires, open succeeds, or the
257
+ // deadline is hit (warn, never throw).
258
+ function whenReady(fn) {
259
+ if (autoOpenReady()) {
260
+ // Defer one microtask. On the bundled entry (hypercms-bundle.js), installStyles()
261
+ // runs AFTER this module evaluates, so firing open() synchronously here would
262
+ // mount the shell with cssText still empty (an unstyled sidebar). A microtask
263
+ // runs only after the synchronous bundle evaluation completes, by which point
264
+ // styles are installed. The event/backstop paths below already fire post-eval,
265
+ // so only this fast path needed deferring.
266
+ queueMicrotask(fn)
267
+ return
268
+ }
269
+ const deadline = Date.now() + AUTO_OPEN_DEADLINE_MS
270
+ let done = false
271
+ let timer = null
272
+ let offMutationReady = null
273
+ const finish = () => {
274
+ if (done) return
275
+ done = true
276
+ if (timer !== null) clearInterval(timer)
277
+ if (offMutationReady) offMutationReady()
278
+ }
279
+ function onMutationReady() {
280
+ if (state.isOpen) {
281
+ finish()
282
+ return
283
+ }
284
+ if (autoOpenReady()) {
285
+ finish()
286
+ fn()
287
+ }
288
+ // Body may still be pending. Unlike the old one-shot listener this stays
289
+ // subscribed, so a later dispatch is still caught and the backstop is no
290
+ // longer the only remaining path.
291
+ }
292
+ offMutationReady = onPlatformEvent(document, MUTATION_READY, onMutationReady)
293
+ timer = setInterval(() => {
294
+ if (state.isOpen) {
295
+ finish()
296
+ return
297
+ }
298
+ if (autoOpenReady()) {
299
+ finish()
300
+ fn()
301
+ return
302
+ }
303
+ if (Date.now() >= deadline) {
304
+ finish()
305
+ console.warn(
306
+ 'hypercms: ?cms=true auto-open gave up — no mutation hub appeared. ' +
307
+ 'Load clayjs or hyperclayjs (or just the mutation utility) so the CMS can initialize.'
308
+ )
309
+ }
310
+ }, SAFETY_POLL_MS)
311
+ }
312
+
313
+ // Auto-open at module load (browser only — node tests import this without a real
314
+ // window, and the guards above no-op there).
315
+ maybeAutoOpen()
316
+
317
+ // Floating edit-mode toggle at module load (same browser-only no-op in node).
318
+ maybeInjectToggle({
319
+ open,
320
+ close,
321
+ isOpen,
322
+ // The same map open() resolves a view name against, so the split button
323
+ // offers exactly the views this build can actually render.
324
+ views: Object.keys(VIEWS),
325
+ hasRules: (doc) => !!engine.findRules(doc, 'cms'),
326
+ })
327
+
328
+ const cms = {
329
+ open,
330
+ close,
331
+ refresh,
332
+ api,
333
+ get isOpen() { return state.isOpen },
334
+ currentView,
335
+ // Power-user exports (mostly for testing + advanced integration)
336
+ path: pathUtil,
337
+ scaffold,
338
+ morphForm,
339
+ }
340
+
341
+ function cssEscape(value) {
342
+ if (typeof CSS !== 'undefined' && CSS.escape) return CSS.escape(value)
343
+ return String(value).replace(/[^a-zA-Z0-9_\-.*]/g, (c) => '\\' + c)
344
+ }
345
+
346
+ export { cms }
347
+ export default { cms }
@@ -0,0 +1,10 @@
1
+ import HyperMorph from 'hyper-morph'
2
+
3
+ export function morphForm(formRoot, newFragment, { ignoreActiveValue = true } = {}) {
4
+ HyperMorph.morph(formRoot, newFragment, {
5
+ morphStyle: 'innerHTML',
6
+ ignoreActiveValue,
7
+ restoreFocus: true,
8
+ formStateSync: 'property',
9
+ })
10
+ }