@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,61 @@
1
+ export function toString(path) {
2
+ return path.map(String).join('.')
3
+ }
4
+
5
+ export function fromString(str) {
6
+ if (str === '') return []
7
+ return str.split('.').map((seg) => (/^\d+$/.test(seg) ? Number(seg) : seg))
8
+ }
9
+
10
+ export function getRuleAtPath(rules, path) {
11
+ let node = rules
12
+ for (const seg of path) {
13
+ if (node == null) return undefined
14
+ if (typeof node === 'string') {
15
+ // Scalar-array rules like 'li[]' contain leaf scalars at integer indices.
16
+ // Descend by stripping the trailing []; the leaf is treated as a plain
17
+ // scalar string for path queries.
18
+ if (node.endsWith('[]') && (typeof seg === 'number' || seg === '*')) {
19
+ node = node.slice(0, -2)
20
+ continue
21
+ }
22
+ return undefined
23
+ }
24
+ if (Array.isArray(node)) {
25
+ if (typeof seg !== 'number' && seg !== '*') return undefined
26
+ node = node[1]
27
+ continue
28
+ }
29
+ if (typeof node === 'object') {
30
+ if (typeof seg === 'number') return undefined
31
+ if (!(seg in node)) return undefined
32
+ node = node[seg]
33
+ continue
34
+ }
35
+ return undefined
36
+ }
37
+ return node
38
+ }
39
+
40
+ export function getValueAtPath(data, path) {
41
+ let node = data
42
+ for (const seg of path) {
43
+ if (node == null) return undefined
44
+ node = node[seg]
45
+ }
46
+ return node
47
+ }
48
+
49
+ export function setAtPath(obj, path, value) {
50
+ if (path.length === 0) return value
51
+ const [k, ...rest] = path
52
+ if (typeof k === 'number') {
53
+ const next = Array.isArray(obj) ? [...obj] : []
54
+ next[k] = setAtPath(next[k], rest, value)
55
+ return next
56
+ }
57
+ return {
58
+ ...(obj && typeof obj === 'object' ? obj : {}),
59
+ [k]: setAtPath((obj || {})[k], rest, value),
60
+ }
61
+ }
@@ -0,0 +1,131 @@
1
+ // Where to put a floating thing next to a page element.
2
+ //
3
+ // COPIED, deliberately, from richclay/src/toolbar-float.js:1-59. Not imported:
4
+ // hypercms must not gain a hard richclay dependency to lay out a popover on a
5
+ // page that contains no rich text at all. The ladder is the same one richclay's
6
+ // floating toolbar has been using in production, and the copy is verbatim apart
7
+ // from the two notes below, so a fix on either side can be read across.
8
+ //
9
+ // Two differences from the original, both because a popover is not a toolbar:
10
+ //
11
+ // 1. A popover has ONE size. richclay measures a horizontal bar and a vertical
12
+ // rail because its toolbar reflows between them; ours does not. Callers pass
13
+ // the same box as both `bar` and `rail`, which collapses the side-rail rung
14
+ // of the ladder into a plain "put it beside the anchor" without changing a
15
+ // line of the math.
16
+ //
17
+ // 2. The `hidden` rung is a backstop here, not the visibility test. It compares
18
+ // the anchor to the viewport, which cannot see an anchor clipped by an
19
+ // overflow: hidden ancestor while still inside the viewport — the case that
20
+ // floats a control over whatever is visible in its place. The inline view
21
+ // decides visibility from an IntersectionObserver, which accounts for
22
+ // ancestor clipping by construction (measured, plan §3.1.2), and only asks
23
+ // for a placement once it knows the anchor is showing.
24
+
25
+ export const GAP = 16
26
+ export const GAP_LADDER = [16, 8, 0]
27
+ export const VIEWPORT_INSET = 8
28
+ export const PLACEMENT_SLACK = 4
29
+
30
+ // Pure placement ladder: above -> below -> beside -> pinned to viewport top.
31
+ // All coords are viewport-relative (the host is position: fixed). `current` gets
32
+ // PLACEMENT_SLACK of hysteresis so the mode doesn't flip-flop at exact
33
+ // boundaries while the page scrolls.
34
+ export function place({ anchor, bar, rail = bar, viewport, current = null }) {
35
+ const slack = (mode) => (current === mode ? PLACEMENT_SLACK : 0)
36
+
37
+ if (
38
+ anchor.bottom <= 0 ||
39
+ anchor.top >= viewport.height ||
40
+ anchor.right <= 0 ||
41
+ anchor.left >= viewport.width
42
+ ) {
43
+ return { mode: 'hidden', x: 0, y: 0 }
44
+ }
45
+
46
+ const clampX = (width) =>
47
+ Math.max(VIEWPORT_INSET, Math.min(anchor.left, viewport.width - width - VIEWPORT_INSET))
48
+
49
+ const aboveY = anchor.top - GAP - bar.height
50
+ if (aboveY >= VIEWPORT_INSET - slack('above')) {
51
+ return { mode: 'above', x: clampX(bar.width), y: aboveY }
52
+ }
53
+
54
+ const belowY = anchor.bottom + GAP
55
+ if (belowY + bar.height <= viewport.height - VIEWPORT_INSET + slack('below')) {
56
+ return { mode: 'below', x: clampX(bar.width), y: belowY }
57
+ }
58
+
59
+ const rightSpace = viewport.width - anchor.right - VIEWPORT_INSET
60
+ const leftSpace = anchor.left - VIEWPORT_INSET
61
+ const sides =
62
+ rightSpace >= leftSpace
63
+ ? [['rail-right', rightSpace], ['rail-left', leftSpace]]
64
+ : [['rail-left', leftSpace], ['rail-right', rightSpace]]
65
+
66
+ for (const [mode, space] of sides) {
67
+ for (const gap of GAP_LADDER) {
68
+ if (rail.width + gap > space + slack(mode)) continue
69
+ const x =
70
+ mode === 'rail-right'
71
+ ? Math.min(anchor.right + gap, viewport.width - rail.width - VIEWPORT_INSET)
72
+ : Math.max(anchor.left - gap - rail.width, VIEWPORT_INSET)
73
+ const maxY = Math.min(anchor.bottom, viewport.height - VIEWPORT_INSET) - rail.height
74
+ const y = Math.max(VIEWPORT_INSET, Math.min(Math.max(anchor.top, VIEWPORT_INSET), maxY))
75
+ return { mode, x, y, gap }
76
+ }
77
+ }
78
+
79
+ return { mode: 'pinned', x: clampX(bar.width), y: VIEWPORT_INSET }
80
+ }
81
+
82
+ // A handle sits ON its anchor's top-right corner, overlapping it, so it reads as
83
+ // belonging to that element rather than floating near it. No ladder: a handle is
84
+ // small enough that it always fits, and moving it around the anchor would make
85
+ // the page feel like it was shuffling under the pointer.
86
+ //
87
+ // The one thing it does need is the viewport clamp. A card flush against the
88
+ // right edge would otherwise put its handle half off-screen, and a heading at
89
+ // the very top of the document would put it above the fold.
90
+ //
91
+ // `prefer` picks which of the two placements below the caller wants:
92
+ // 'auto' adapts to the anchor's size, 'corner' always takes the corner.
93
+ export function placeHandle({ anchor, handle, viewport, inset = 6, prefer = 'auto' }) {
94
+ // Overlapping only reads as "belongs to this" when there is something left to
95
+ // belong to. On a 108x18 link a 36x31 handle covered 28% of it, including the
96
+ // last word — measured in a real browser. So a small anchor gets its handle
97
+ // set BESIDE it instead: still adjacent, nothing hidden.
98
+ //
99
+ // That rule is about a handle floating near a link. A control that BELONGS to
100
+ // a list row wants the opposite and asks for 'corner': measured in Chrome, a
101
+ // 31px strip beside a 36px card row landed wholly outside the row, and beside
102
+ // a run of 60px-pitch pills the strips covered each other. Such a caller pins
103
+ // the control to the corner and decides for itself whether the row is big
104
+ // enough to carry one at all. The clamp below is shared either way.
105
+ const roomy = anchor.height >= handle.height * 1.5 && anchor.width >= handle.width * 2
106
+ if (prefer === 'corner' || roomy) {
107
+ const x = Math.max(
108
+ VIEWPORT_INSET,
109
+ Math.min(anchor.right - handle.width + inset, viewport.width - handle.width - VIEWPORT_INSET)
110
+ )
111
+ const y = Math.max(
112
+ VIEWPORT_INSET,
113
+ Math.min(anchor.top - inset, viewport.height - handle.height - VIEWPORT_INSET)
114
+ )
115
+ return { x, y, mode: 'over' }
116
+ }
117
+
118
+ // Beside: to the right when it fits, otherwise to the left, vertically
119
+ // centred on the anchor. Both are clamped, so a handle never leaves the
120
+ // viewport even for an anchor flush against an edge.
121
+ const gap = 4
122
+ const fitsRight = anchor.right + gap + handle.width <= viewport.width - VIEWPORT_INSET
123
+ const rawX = fitsRight ? anchor.right + gap : anchor.left - gap - handle.width
124
+ // Only the left edge needs clamping. fitsRight is the test for "the right
125
+ // placement is inside the viewport", so the right branch is already in bounds
126
+ // by construction; the left branch can run off a narrow anchor's left side.
127
+ const x = Math.max(VIEWPORT_INSET, rawX)
128
+ const centred = anchor.top + anchor.height / 2 - handle.height / 2
129
+ const y = Math.max(VIEWPORT_INSET, Math.min(centred, viewport.height - handle.height - VIEWPORT_INSET))
130
+ return { x, y, mode: fitsRight ? 'beside-right' : 'beside-left' }
131
+ }
@@ -0,0 +1,131 @@
1
+ // The optional platform hypercms rides: the mutation hub, undo, the save-prepare
2
+ // hook, rich text, and the two upload helpers. Every one degrades to null, so the
3
+ // CMS still runs in a bare HTML file with no client at all.
4
+ //
5
+ // TWO clients provide this and they disagree on names. hyperclayjs owns
6
+ // window.hyperclay; clayjs owns window.clay and renamed things on the way past
7
+ // (onPrepareForSave -> addDocumentTransform, consent -> confirm, uploadFileBasic ->
8
+ // upload). So this is a capability table rather than a namespace pointer:
9
+ // `window.clay ?? window.hyperclay` would resolve to clay, find no
10
+ // onPrepareForSave, and silently stop stripping the CMS's own chrome out of every
11
+ // save.
12
+ //
13
+ // Reads happen per call, never cached. A client can install a capability after
14
+ // hypercms evaluates — that is the whole reason whenReady() below exists.
15
+ const CAPABILITIES = {
16
+ Mutation: (clay, hyperclay) => clay?.Mutation ?? hyperclay?.Mutation,
17
+ undo: (clay, hyperclay) => clay?.undo ?? hyperclay?.undo,
18
+ onPrepareForSave: (clay, hyperclay) => clay?.addDocumentTransform ?? hyperclay?.onPrepareForSave,
19
+ // Distinct from onPrepareForSave above, and the difference is the whole point:
20
+ // onPrepareForSave is save-only, so a hook there runs AFTER live sync has
21
+ // already broadcast the document. onSnapshot runs on every snapshot, save and
22
+ // sync alike, which is the only place a cleanup can sit and be seen by both.
23
+ onSnapshot: (clay, hyperclay) => clay?.onSnapshot ?? hyperclay?.onSnapshot,
24
+ consent: (clay, hyperclay) => clay?.confirm ?? hyperclay?.consent,
25
+ RichClay: (clay, hyperclay) => clay?.RichClay ?? hyperclay?.RichClay,
26
+ quickcrop: (clay, hyperclay) => clay?.quickcrop ?? hyperclay?.quickcrop,
27
+ upload: (clay, hyperclay) =>
28
+ clay?.upload ?? (hyperclay?.uploadFileBasic ? adaptLegacyUpload(hyperclay.uploadFileBasic) : null),
29
+ }
30
+
31
+ // The one capability where the two clients differ in SHAPE and not just in name,
32
+ // so it is normalized here rather than at the call site. clay.upload answers one
33
+ // envelope for every outcome and never rejects; uploadFileBasic resolves on
34
+ // success and rejects on failure. A pick handler that had to know which client it
35
+ // was riding would grow two of every branch, and the branch that matters most is
36
+ // the one a rejection reads wrong: "this host does not store files" is not a
37
+ // failure, and a caller that catches it as one stops when it should have embedded.
38
+ //
39
+ // The legacy client has no cancellation, so `signal` is honored on the near side
40
+ // only: an upload already in flight runs to completion, and its result is
41
+ // discarded rather than written. That is the same outcome the caller's own
42
+ // ctx.closed check produces, one branch earlier.
43
+ const LEGACY_STATUS_CODES = {
44
+ 402: 'payment-required',
45
+ 413: 'too-large',
46
+ 415: 'unsupported-type',
47
+ 401: 'unauthorized',
48
+ 403: 'forbidden',
49
+ 404: 'not-found',
50
+ }
51
+
52
+ const aborted = () => ({ ok: false, msg: 'Upload cancelled', msgType: 'skipped', code: 'aborted', uploads: [] })
53
+
54
+ function adaptLegacyUpload(uploadFileBasic) {
55
+ return async function upload(file, { onProgress, signal } = {}) {
56
+ if (signal?.aborted) return aborted()
57
+ try {
58
+ // uploadFileBasic reports a bare percent; clay reports {loaded,total,percent}.
59
+ // Callers draw from `percent`, which is the only field both can supply.
60
+ const res = await uploadFileBasic(file, {
61
+ onProgress: (percent) => { onProgress?.({ loaded: null, total: null, percent }) },
62
+ })
63
+ if (signal?.aborted) return aborted()
64
+ const uploads = (res && res.uploads) || []
65
+ if (typeof uploads[0]?.url !== 'string') {
66
+ return { ok: false, msg: 'The host accepted the file but did not say where it put it', msgType: 'error', code: 'bad-response', uploads: [] }
67
+ }
68
+ return { ok: true, msg: res.msg || 'Uploaded', msgType: res.msgType || 'success', code: res.code || null, uploads }
69
+ } catch (err) {
70
+ if (signal?.aborted) return aborted()
71
+ // The rejection carries the HTTP status and the raw body, so the host's own
72
+ // code survives the round trip and the caller branches on the reason rather
73
+ // than pattern-matching a message.
74
+ let body = {}
75
+ try { body = JSON.parse(err?.response || '{}') } catch { body = {} }
76
+ const code = body.code || LEGACY_STATUS_CODES[err?.status] || 'error'
77
+ return { ok: false, msg: (err && err.message) || 'Upload failed', msgType: 'error', code, uploads: [] }
78
+ }
79
+ }
80
+ }
81
+
82
+ // Pass `win` when the caller already holds a realm (a doc.defaultView from a form
83
+ // mounted into another document); it defaults to the ambient one.
84
+ export function platform(name, win) {
85
+ const read = CAPABILITIES[name]
86
+ if (!read) throw new Error(`hypercms: unknown platform capability "${name}"`)
87
+ const scope = win || (typeof window !== 'undefined' ? window : null)
88
+ if (!scope) return null
89
+ return read(scope.clay, scope.hyperclay) || null
90
+ }
91
+
92
+ // Both spellings of the same signal. Each client fires exactly one name: clayjs
93
+ // its own, hyperclayjs the legacy one. Listening for both is what lets one build
94
+ // of hypercms ride either client. The name-keyed guard below stays regardless, so
95
+ // a listener on both names still can't double-fire if a client ever dispatches
96
+ // the pair again.
97
+ export const MUTATION_READY = ['clay:mutation-ready', 'hyperclay:mutation-ready']
98
+ export const LIVESYNC_APPLIED = ['clay:sync-applied', 'hyperclay:livesync-applied']
99
+
100
+ // The readiness pair both clients publish once their public surface is
101
+ // assembled. clayjs fires clay:ready from its loader; hyperclayjs fires
102
+ // hyperclay:ready from exportToWindow. This is the handshake richclay uses for
103
+ // the same race, and it is the only reliable retry signal for a hook whose
104
+ // capability arrives later than hypercms does.
105
+ export const PLATFORM_READY = ['clay:ready', 'hyperclay:ready']
106
+
107
+ // Subscribe to every spelling of one signal, delivering the handler ONCE per
108
+ // occurrence. No client dispatches the pair today, but one that did would make a
109
+ // plain listener on each name fire the handler twice for one event: two form
110
+ // re-extracts, or two open() calls.
111
+ //
112
+ // The first name to arrive claims the tick, and only a DIFFERENT name is suppressed
113
+ // while it holds the claim. That distinction is what makes this exact rather than a
114
+ // heuristic: no client dispatches one name twice for one occurrence, so a repeat of
115
+ // the claiming name is always a second occurrence and always gets through — even
116
+ // when it lands in the same tick, which is precisely what a caller doing two
117
+ // synchronous applies looks like. Guarding on "have I run at all this tick" would
118
+ // swallow that.
119
+ //
120
+ // Returns an unsubscribe that removes every name.
121
+ export function onPlatformEvent(target, names, handler) {
122
+ let claimedBy = null
123
+ const wrapped = (event) => {
124
+ if (claimedBy !== null && claimedBy !== event.type) return
125
+ claimedBy = event.type
126
+ queueMicrotask(() => { claimedBy = null })
127
+ handler(event)
128
+ }
129
+ for (const name of names) target.addEventListener(name, wrapped)
130
+ return () => { for (const name of names) target.removeEventListener(name, wrapped) }
131
+ }
@@ -0,0 +1,87 @@
1
+ import { engine } from 'hyper-html-api'
2
+ import { buildForm } from './form-builder.js'
3
+ import { morphForm } from './morph.js'
4
+ import { injectDefaults, injectComponents } from './templates.js'
5
+ import { deriveFormRules } from './form-rules.js'
6
+ import { coerceBooleans, applyErrorState } from './events.js'
7
+ import { findUnresolved, applyUnresolvedState, stripReadOnly } from './unresolved.js'
8
+ import { enhanceFields } from './enhance.js'
9
+ import { platform } from './platform.js'
10
+ import { rowIdentitySeeder } from './row-identity.js'
11
+
12
+ const ENGINE_OPTS = { skip: '[data-hcms-shell]', templateAttr: 'cms-template' }
13
+
14
+ export function refreshForm(ctx, { ignoreActiveValue } = {}) {
15
+ // Re-resolve rules every refresh, source-aware: an object source returns the
16
+ // same literal (tagNode null); a token source re-resolves the tag so a
17
+ // livesync-replaced rules tag is picked up. Document-scoped via ctx.doc.
18
+ const found = engine.findRules(ctx.doc, ctx.rulesSource || 'cms')
19
+ if (found) {
20
+ // Re-apply the rule upgrade open() performed: the tag holds the author's
21
+ // bare rules, and using them raw would rebind upgraded fields back to
22
+ // textContent mid-session. Route it through the SAME seam open() used
23
+ // (session.js hands found.rules to view.prepareRules) rather than
24
+ // hardcoding one view's upgrade here — that undid a wider upgrade on the
25
+ // first refresh, silently flattening rich text back to plain.
26
+ ctx.pageRules = ctx.view.prepareRules(found.rules)
27
+ ctx.rulesTagNode = found.tagNode
28
+ }
29
+ // Re-derive formRules so schema changes (rule shape, template overrides) flow through.
30
+ injectDefaults(ctx.doc)
31
+ injectComponents(ctx.doc, ctx.pageRules)
32
+ ctx.formRules = deriveFormRules(ctx.pageRules, ctx.doc)
33
+ ctx.writeRules = stripReadOnly(ctx.pageRules)
34
+ // Before the extract below, for the same readable-failure reason as open().
35
+ ctx.unresolved = findUnresolved(ctx.pageRoot, ctx.pageRules)
36
+
37
+ // The morph below can rebuild a form row, which drops its binding to the page
38
+ // row it stood for. Read the pairing off this extract and re-establish it once
39
+ // the morph has settled, so the first operation after a refresh is not left
40
+ // guessing.
41
+ const seeder = rowIdentitySeeder()
42
+ const newData = coerceBooleans(
43
+ engine.extract(ctx.pageRoot, ctx.pageRules, { ...ENGINE_OPTS, ...seeder.hooks }),
44
+ ctx.pageRules
45
+ )
46
+ const fragment = buildForm({
47
+ pageRules: ctx.pageRules,
48
+ formRules: ctx.formRules,
49
+ data: newData,
50
+ doc: ctx.doc,
51
+ })
52
+ morphForm(ctx.formRoot, fragment, { ignoreActiveValue })
53
+ seeder.seed(ctx.formRoot)
54
+ // The morph can rebuild field nodes, dropping autosize heights and richclay
55
+ // instances (instances are per-element); re-enhance whatever is new.
56
+ enhanceFields(ctx.formRoot, ctx.doc, ctx.view?.enhanceFormRichText !== false)
57
+ // morphForm rebuilds the form structure; inline error slots get wiped. Re-apply
58
+ // the last error state so error messages survive across observer-driven refreshes
59
+ // (e.g., the rollback after a failed add).
60
+ applyErrorState(ctx)
61
+ applyUnresolvedState(ctx)
62
+ // Update fingerprint so subsequent commits against the new page state compare correctly.
63
+ if (ctx.updateFingerprint) ctx.updateFingerprint()
64
+ }
65
+
66
+ export function installObserver({ debounce = 100, onRefresh }) {
67
+ const M = platform('Mutation')
68
+ if (!M || typeof M.onAnyChange !== 'function') {
69
+ throw new Error('hypercms: a mutation hub is required (clay.Mutation or hyperclay.Mutation). Load clayjs or hyperclayjs, or just the mutation utility, before initializing hypercms.')
70
+ }
71
+ let paused = 0
72
+ // The hub hands every subscriber the batch of changes it collected
73
+ // ({ type: 'add' | 'remove' | 'attribute', element, parent, attribute,
74
+ // oldValue, newValue }, see clayjs/src/lib/mutation.js). Discarding them was
75
+ // fine while the only view was the sidebar, which refreshes wholesale. A view
76
+ // that edits the page in place needs to know whether a batch is its own doing,
77
+ // so pass them on and let the view decide.
78
+ const unsub = M.onAnyChange({ debounce }, (changes) => {
79
+ if (paused > 0) return
80
+ onRefresh(changes)
81
+ })
82
+ return {
83
+ unsubscribe: typeof unsub === 'function' ? unsub : () => {},
84
+ pause() { paused++ },
85
+ resume() { paused = Math.max(0, paused - 1) },
86
+ }
87
+ }
@@ -0,0 +1,140 @@
1
+ // hypercms's bridge to richclay: how it finds the class, and how it cleans up
2
+ // after the instances it creates.
3
+ //
4
+ // The cleanup exists because hypercms constructs `new RichClay(el, { hyperclay:
5
+ // false })`, and richclay installs its own save bridge only when that option is
6
+ // true (`richclay/src/richclay.js:135`). So on a page whose only richclay usage
7
+ // is hypercms's, NOTHING strips the editor's runtime state out of the document:
8
+ // a contenteditable heading reaches the file and stays editable forever.
9
+ // hypercms created those instances, so hypercms cleans them up.
10
+
11
+ import { platform } from './platform.js'
12
+
13
+ // Written on the live page element at bind time, removed from the snapshot
14
+ // clone. It is the only way to tell an element hypercms made rich from one the
15
+ // author marked up themselves: element identity does not survive cloneNode, so
16
+ // an in-memory Set cannot answer the question on the clone the hook receives.
17
+ //
18
+ // Its VALUE is whether the element held markup before richclay touched it:
19
+ // 'rich' or 'plain'. That has to be frozen here, because Squire wraps the
20
+ // content of a block it binds in a <div>, so from the first click the live DOM
21
+ // can no longer answer whether the author wrote markup in this element.
22
+ export const BOUND_ATTR = 'data-hcms-bound'
23
+
24
+ // The binding's own id, written on the live element and carried into the clone
25
+ // by cloneNode. It is how the save hook, holding a clone, finds the live binding
26
+ // that knows what the author's markup was before richclay rewrote it. Identity
27
+ // cannot cross cloneNode, and the markup is far too big to ride in an attribute.
28
+ export const BOUND_ID_ATTR = 'data-hcms-bound-id'
29
+
30
+ // id -> the live binding. Populated at bind, dropped at unbind, read only by the
31
+ // save hook. Holding the binding rather than a copy of its markup is deliberate:
32
+ // whether the restore should happen at all is a question about the binding's
33
+ // state right now, not about its state when it was created.
34
+ const boundById = new Map()
35
+
36
+ export function registerBinding(id, binding) {
37
+ boundById.set(id, binding)
38
+ }
39
+
40
+ export function releaseBinding(id) {
41
+ boundById.delete(id)
42
+ }
43
+
44
+ // The author's markup for a clone element, or null when the clone must be left
45
+ // exactly as it stands. Null covers all three reasons: no live binding behind
46
+ // it, an editor the author owns, and any content this session has actually
47
+ // changed. See restorable() in inline.js, which asks the same question of the
48
+ // live element at teardown.
49
+ export function pristineMarkupFor(cloneEl) {
50
+ const id = cloneEl.getAttribute(BOUND_ID_ATTR)
51
+ if (!id) return null
52
+ const binding = boundById.get(id)
53
+ if (!binding || !binding.restorable()) return null
54
+ return binding.originalHTML
55
+ }
56
+
57
+ // Written beside the marker when hypercms's own construct call is what put
58
+ // data-richclay on the element. richclay 0.5.0 tracks the same provenance in
59
+ // data-richclay-runtime-marker and strips the attribute for us, but clayjs and
60
+ // hyperclayjs both vendor 0.4.0, which has no marker and leaves it in the file.
61
+ // data-richclay is richclay's mount selector, so left there it turns every
62
+ // heading anyone clicked into a permanent richclay region. hypercms knew the
63
+ // answer before it touched the element; it does not need to ask.
64
+ export const RC_OWNED_ATTR = 'data-hcms-owns-richclay'
65
+
66
+ export function resolveRichClay(win) {
67
+ return (
68
+ (win && win.richclay && win.richclay.RichClay) ||
69
+ platform('RichClay', win) ||
70
+ (win && typeof win.RichClay === 'function' ? win.RichClay : null)
71
+ )
72
+ }
73
+
74
+ export function markBound(el, heldMarkup, richClayIsOurs) {
75
+ if (!el || typeof el.setAttribute !== 'function') return
76
+ el.setAttribute(BOUND_ATTR, heldMarkup ? 'rich' : 'plain')
77
+ if (richClayIsOurs) el.setAttribute(RC_OWNED_ATTR, 'true')
78
+ }
79
+
80
+ // ⚠ THE ORDER IS LOAD-BEARING. richclay's strip iterates
81
+ // "[data-richclay], [richclay], [editable], [clay-editable]". Remove
82
+ // data-richclay first and a bare <h1> stops matching that selector, the strip
83
+ // skips it, and every runtime attribute richclay wrote reaches the file. Strip
84
+ // FIRST, unmark second. Both orders were run and the bytes compared; the other
85
+ // one ships a permanently contenteditable heading.
86
+ export function cleanRichClayFromSnapshot(cloneDocEl, win) {
87
+ if (!cloneDocEl || typeof cloneDocEl.querySelectorAll !== 'function') return
88
+ const bound = cloneDocEl.querySelectorAll(`[${BOUND_ATTR}]`)
89
+ if (!bound.length) return
90
+
91
+ const RichClay = resolveRichClay(win)
92
+ if (RichClay && typeof RichClay.stripFromClone === 'function') {
93
+ try {
94
+ // A second pass over an already-stripped clone is string-identical, so
95
+ // this stays correct on a page that also uses richclay directly and has
96
+ // richclay's own bridge installed.
97
+ RichClay.stripFromClone(cloneDocEl)
98
+ } catch (err) {
99
+ console.warn('[hypercms] richclay strip failed; editor state may reach the save', err)
100
+ }
101
+ }
102
+ for (const el of bound) {
103
+ // Whether data-richclay comes off is decided by who put it there, not by
104
+ // which richclay is loaded. On 0.5.0 stripFromClone has already removed it
105
+ // and this is a no-op; on the 0.4.0 copies clayjs and hyperclayjs vendor,
106
+ // this is the only thing that removes it. An author's own opt-in never
107
+ // carries the marker, so it is never touched.
108
+ if (el.getAttribute(RC_OWNED_ATTR) === 'true') el.removeAttribute('data-richclay')
109
+ el.removeAttribute(RC_OWNED_ATTR)
110
+ el.removeAttribute(BOUND_ATTR)
111
+ }
112
+ }
113
+
114
+ // A page element that carries the CMS marker but has no binding behind it: a
115
+ // clone the engine made of a bound row, or of a bound element it replaced while
116
+ // rolling back a failed apply. destroy() cannot reach it — destroy belongs to an
117
+ // instance and a clone has none — so it is left permanently editable with page
118
+ // undo switched off for it. The snapshot hook still cleans the saved file; this
119
+ // is the live page.
120
+ export function stripOrphanEditorState(el, win) {
121
+ if (!el || typeof el.removeAttribute !== 'function') return
122
+ const RichClay = resolveRichClay(win)
123
+ if (RichClay && typeof RichClay.stripElement === 'function') {
124
+ try {
125
+ RichClay.stripElement(el)
126
+ } catch (err) {
127
+ console.warn('[hypercms] richclay element strip failed; the clone stays editable', err)
128
+ }
129
+ } else {
130
+ // An older richclay with no per-element strip. These two are what actually
131
+ // harm the live page; the rest is cosmetic and the snapshot hook removes it
132
+ // from the file either way.
133
+ el.removeAttribute('contenteditable')
134
+ el.removeAttribute('no-undo')
135
+ }
136
+ if (el.getAttribute(RC_OWNED_ATTR) === 'true') el.removeAttribute('data-richclay')
137
+ el.removeAttribute(RC_OWNED_ATTR)
138
+ el.removeAttribute(BOUND_ATTR)
139
+ el.removeAttribute(BOUND_ID_ATTR)
140
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Row identity supplied to the engine, so two byte-identical rows can still be
3
+ * told apart on a remove or a reorder.
4
+ *
5
+ * The engine matches rows by content, which is exact for every operation the
6
+ * form emits except one: when two rows read identically, "remove row 0" and
7
+ * "remove row 1" produce the same array, and no score can separate them. The
8
+ * form can, because it holds a handle the engine never sees, its own row
9
+ * elements.
10
+ *
11
+ * Those elements are stable across the operations that matter. A keystroke
12
+ * never rebuilds the form (applyWithRollback pauses the mutation observer
13
+ * around apply), and add, remove and move mutate the form DOM in place rather
14
+ * than re-rendering it. By the time commit() runs the form is already in its
15
+ * new shape, so walking its rows in order gives the new item order with each
16
+ * row still carrying the page node it came from. The permutation falls out.
17
+ *
18
+ * refreshForm does re-render the form, through hyper-morph, when the page
19
+ * changes underneath us (livesync, undo, another session). Rows rebuilt there
20
+ * have no entry, identifyRows returns null for them, and the engine's matcher
21
+ * takes over. onRowsApplied re-establishes the pairing on the next apply. That
22
+ * is correct rather than merely tolerable: the form was just built from a fresh
23
+ * read of the page, so index correspondence is exact at that instant.
24
+ */
25
+ const boundNode = new WeakMap()
26
+
27
+ // The engine's trace path as the form writes it in data-hcms-path.
28
+ const pathKey = (path) => path.map(String).join('.')
29
+
30
+ // Rule keys reach the selector verbatim, and a key holding a quote makes
31
+ // querySelector throw. Every other path-into-selector site in this package
32
+ // escapes the same way; this is that function, kept module-local like the rest.
33
+ function cssEscape(value) {
34
+ if (typeof CSS !== 'undefined' && CSS.escape) return CSS.escape(value)
35
+ return String(value).replace(/[^a-zA-Z0-9_\-.*]/g, (c) => '\\' + c)
36
+ }
37
+
38
+ // The form rows for one list, in document order, or null when the form has no
39
+ // such list. Keys look like "products" or "products.0.variants".
40
+ function formRowsAt(formRoot, key) {
41
+ if (!formRoot || !formRoot.querySelector) return null
42
+ const container = formRoot.querySelector(`[data-hcms-path="${cssEscape(key)}"]`)
43
+ const slot = container && container.querySelector('.hcms-array-items')
44
+ if (!slot) return null
45
+ // Direct children, filtered, rather than `:scope > ...`. jsdom's selector
46
+ // engine resolves `:scope >` against the document rather than the slot and
47
+ // returns descendants, so a page with nested lists reported the inner rows as
48
+ // rows of the outer list. The count then failed to match and identity silently
49
+ // fell back to content matching in exactly the shape this exists for.
50
+ return Array.from(slot.children).filter(
51
+ (el) => el.matches && el.matches('[data-hcms-card], [data-hcms-array-item]'),
52
+ )
53
+ }
54
+
55
+ function bind(formRoot, key, nodes) {
56
+ const rows = formRowsAt(formRoot, key)
57
+ if (!rows || rows.length !== nodes.length) return
58
+ rows.forEach((row, i) => {
59
+ if (nodes[i]) boundNode.set(row, nodes[i])
60
+ })
61
+ }
62
+
63
+ /**
64
+ * Establish identity from the extract the form is built from, before any apply
65
+ * has happened.
66
+ *
67
+ * Without this the map is populated only by onRowsApplied, so the FIRST
68
+ * structural operation of a session has nothing to go on and falls back to
69
+ * content matching. Pass `hooks` to the top-level `engine.extract` that feeds
70
+ * buildForm, then call `seed(formRoot)` once the form rows exist. At that
71
+ * moment row i of the form is row i of the page by construction, because the
72
+ * form was built from exactly this read.
73
+ *
74
+ * Never pass these hooks to `apply`: listDiff re-extracts each existing row
75
+ * with a fresh path, which would report nested lists under a truncated one.
76
+ */
77
+ export function rowIdentitySeeder() {
78
+ const readNodes = new Map()
79
+ return {
80
+ hooks: {
81
+ onRowsRead(path, nodes) {
82
+ readNodes.set(pathKey(path), nodes)
83
+ },
84
+ },
85
+ seed(formRoot) {
86
+ for (const [key, nodes] of readNodes) bind(formRoot, key, nodes)
87
+ readNodes.clear()
88
+ },
89
+ }
90
+ }
91
+
92
+ export function rowIdentityHooks(formRoot) {
93
+ return {
94
+ identifyRows(path, items) {
95
+ const rows = formRowsAt(formRoot, pathKey(path))
96
+ // A count mismatch means the form and the data disagree about this list,
97
+ // so the form is not a trustworthy witness for it. Say nothing and let
98
+ // the engine match.
99
+ if (!rows || rows.length !== items.length) return null
100
+ return rows.map((row) => boundNode.get(row) || null)
101
+ },
102
+ onRowsApplied(path, nodes) {
103
+ bind(formRoot, pathKey(path), nodes)
104
+ },
105
+ }
106
+ }
107
+
108
+ // Testing seam only: the WeakMap is module state and a test needs to observe it.
109
+ export function boundNodeFor(formRow) {
110
+ return boundNode.get(formRow) || null
111
+ }