@velastack/cms 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 (327) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +644 -0
  3. package/dist/cms.d.ts +6 -0
  4. package/dist/cms.js +30 -0
  5. package/dist/components/admin-bar/admin-bar-internal.svelte +1697 -0
  6. package/dist/components/admin-bar/admin-bar-internal.svelte.d.ts +12 -0
  7. package/dist/components/admin-bar/admin-bar.css +5 -0
  8. package/dist/components/admin-bar/admin-bar.svelte +355 -0
  9. package/dist/components/admin-bar/admin-bar.svelte.d.ts +7 -0
  10. package/dist/components/admin-bar/click-outside.d.ts +8 -0
  11. package/dist/components/admin-bar/click-outside.js +17 -0
  12. package/dist/components/admin-bar/css-root.svelte +23 -0
  13. package/dist/components/admin-bar/css-root.svelte.d.ts +19 -0
  14. package/dist/components/admin-bar/delete-page-dialog.svelte +326 -0
  15. package/dist/components/admin-bar/delete-page-dialog.svelte.d.ts +32 -0
  16. package/dist/components/admin-bar/history-panel.svelte +264 -0
  17. package/dist/components/admin-bar/history-panel.svelte.d.ts +8 -0
  18. package/dist/components/admin-bar/icons/bold.svelte +13 -0
  19. package/dist/components/admin-bar/icons/bold.svelte.d.ts +3 -0
  20. package/dist/components/admin-bar/icons/check.svelte +11 -0
  21. package/dist/components/admin-bar/icons/check.svelte.d.ts +3 -0
  22. package/dist/components/admin-bar/icons/chevron-down.svelte +11 -0
  23. package/dist/components/admin-bar/icons/chevron-down.svelte.d.ts +3 -0
  24. package/dist/components/admin-bar/icons/chevron-right.svelte +11 -0
  25. package/dist/components/admin-bar/icons/chevron-right.svelte.d.ts +3 -0
  26. package/dist/components/admin-bar/icons/chevron-up.svelte +11 -0
  27. package/dist/components/admin-bar/icons/chevron-up.svelte.d.ts +3 -0
  28. package/dist/components/admin-bar/icons/code.svelte +14 -0
  29. package/dist/components/admin-bar/icons/code.svelte.d.ts +3 -0
  30. package/dist/components/admin-bar/icons/external-link.svelte +15 -0
  31. package/dist/components/admin-bar/icons/external-link.svelte.d.ts +3 -0
  32. package/dist/components/admin-bar/icons/file.svelte +19 -0
  33. package/dist/components/admin-bar/icons/file.svelte.d.ts +3 -0
  34. package/dist/components/admin-bar/icons/heading-2.svelte +16 -0
  35. package/dist/components/admin-bar/icons/heading-2.svelte.d.ts +3 -0
  36. package/dist/components/admin-bar/icons/heading-3.svelte +17 -0
  37. package/dist/components/admin-bar/icons/heading-3.svelte.d.ts +3 -0
  38. package/dist/components/admin-bar/icons/icon.svelte +41 -0
  39. package/dist/components/admin-bar/icons/icon.svelte.d.ts +21 -0
  40. package/dist/components/admin-bar/icons/images.svelte +16 -0
  41. package/dist/components/admin-bar/icons/images.svelte.d.ts +3 -0
  42. package/dist/components/admin-bar/icons/italic.svelte +15 -0
  43. package/dist/components/admin-bar/icons/italic.svelte.d.ts +3 -0
  44. package/dist/components/admin-bar/icons/link-2-off.svelte +16 -0
  45. package/dist/components/admin-bar/icons/link-2-off.svelte.d.ts +3 -0
  46. package/dist/components/admin-bar/icons/link.svelte +14 -0
  47. package/dist/components/admin-bar/icons/link.svelte.d.ts +3 -0
  48. package/dist/components/admin-bar/icons/list-ordered.svelte +18 -0
  49. package/dist/components/admin-bar/icons/list-ordered.svelte.d.ts +3 -0
  50. package/dist/components/admin-bar/icons/list.svelte +18 -0
  51. package/dist/components/admin-bar/icons/list.svelte.d.ts +3 -0
  52. package/dist/components/admin-bar/icons/loader-circle.svelte +11 -0
  53. package/dist/components/admin-bar/icons/loader-circle.svelte.d.ts +3 -0
  54. package/dist/components/admin-bar/icons/minus.svelte +11 -0
  55. package/dist/components/admin-bar/icons/minus.svelte.d.ts +3 -0
  56. package/dist/components/admin-bar/icons/quote.svelte +24 -0
  57. package/dist/components/admin-bar/icons/quote.svelte.d.ts +3 -0
  58. package/dist/components/admin-bar/icons/search.svelte +14 -0
  59. package/dist/components/admin-bar/icons/search.svelte.d.ts +3 -0
  60. package/dist/components/admin-bar/icons/trash-2.svelte +17 -0
  61. package/dist/components/admin-bar/icons/trash-2.svelte.d.ts +3 -0
  62. package/dist/components/admin-bar/icons/triangle-alert.svelte +15 -0
  63. package/dist/components/admin-bar/icons/triangle-alert.svelte.d.ts +3 -0
  64. package/dist/components/admin-bar/icons/types.d.ts +7 -0
  65. package/dist/components/admin-bar/icons/types.js +1 -0
  66. package/dist/components/admin-bar/icons/undo-2.svelte +14 -0
  67. package/dist/components/admin-bar/icons/undo-2.svelte.d.ts +3 -0
  68. package/dist/components/admin-bar/icons/upload.svelte +15 -0
  69. package/dist/components/admin-bar/icons/upload.svelte.d.ts +3 -0
  70. package/dist/components/admin-bar/icons/x.svelte +14 -0
  71. package/dist/components/admin-bar/icons/x.svelte.d.ts +3 -0
  72. package/dist/components/admin-bar/keyboard-shortcuts-dialog.svelte +96 -0
  73. package/dist/components/admin-bar/keyboard-shortcuts-dialog.svelte.d.ts +7 -0
  74. package/dist/components/admin-bar/locales-panel.svelte +137 -0
  75. package/dist/components/admin-bar/locales-panel.svelte.d.ts +9 -0
  76. package/dist/components/admin-bar/media-panel.svelte +282 -0
  77. package/dist/components/admin-bar/media-panel.svelte.d.ts +7 -0
  78. package/dist/components/admin-bar/new-page-chooser-dialog.svelte +56 -0
  79. package/dist/components/admin-bar/new-page-chooser-dialog.svelte.d.ts +10 -0
  80. package/dist/components/admin-bar/new-page-dialog.svelte +126 -0
  81. package/dist/components/admin-bar/new-page-dialog.svelte.d.ts +14 -0
  82. package/dist/components/admin-bar/page-config.d.ts +61 -0
  83. package/dist/components/admin-bar/page-config.js +24 -0
  84. package/dist/components/admin-bar/pages-panel.svelte +639 -0
  85. package/dist/components/admin-bar/pages-panel.svelte.d.ts +13 -0
  86. package/dist/components/admin-bar/panel-footer.svelte +21 -0
  87. package/dist/components/admin-bar/panel-footer.svelte.d.ts +8 -0
  88. package/dist/components/admin-bar/panel-header.svelte +29 -0
  89. package/dist/components/admin-bar/panel-header.svelte.d.ts +10 -0
  90. package/dist/components/admin-bar/panel.svelte +41 -0
  91. package/dist/components/admin-bar/panel.svelte.d.ts +10 -0
  92. package/dist/components/admin-bar/publish-dialog.svelte +387 -0
  93. package/dist/components/admin-bar/publish-dialog.svelte.d.ts +16 -0
  94. package/dist/components/admin-bar/resolve-route.d.ts +20 -0
  95. package/dist/components/admin-bar/resolve-route.js +50 -0
  96. package/dist/components/admin-bar/seo-panel.svelte +387 -0
  97. package/dist/components/admin-bar/seo-panel.svelte.d.ts +7 -0
  98. package/dist/components/admin-bar/share-preview-link-dialog.svelte +97 -0
  99. package/dist/components/admin-bar/share-preview-link-dialog.svelte.d.ts +9 -0
  100. package/dist/components/admin-bar/site-settings-panel.svelte +225 -0
  101. package/dist/components/admin-bar/site-settings-panel.svelte.d.ts +7 -0
  102. package/dist/components/admin-bar/status-pill.svelte +49 -0
  103. package/dist/components/admin-bar/status-pill.svelte.d.ts +11 -0
  104. package/dist/components/admin-bar/theme.svelte.d.ts +16 -0
  105. package/dist/components/admin-bar/theme.svelte.js +39 -0
  106. package/dist/components/admin-bar/ui/badge/badge.svelte +57 -0
  107. package/dist/components/admin-bar/ui/badge/badge.svelte.d.ts +16 -0
  108. package/dist/components/admin-bar/ui/badge/index.d.ts +4 -0
  109. package/dist/components/admin-bar/ui/badge/index.js +4 -0
  110. package/dist/components/admin-bar/ui/button/button.svelte +73 -0
  111. package/dist/components/admin-bar/ui/button/button.svelte.d.ts +16 -0
  112. package/dist/components/admin-bar/ui/button/index.d.ts +4 -0
  113. package/dist/components/admin-bar/ui/button/index.js +4 -0
  114. package/dist/components/admin-bar/ui/collapsible/collapsible-content.svelte +7 -0
  115. package/dist/components/admin-bar/ui/collapsible/collapsible-content.svelte.d.ts +4 -0
  116. package/dist/components/admin-bar/ui/collapsible/collapsible-trigger.svelte +7 -0
  117. package/dist/components/admin-bar/ui/collapsible/collapsible-trigger.svelte.d.ts +4 -0
  118. package/dist/components/admin-bar/ui/collapsible/collapsible.svelte +7 -0
  119. package/dist/components/admin-bar/ui/collapsible/collapsible.svelte.d.ts +4 -0
  120. package/dist/components/admin-bar/ui/collapsible/index.d.ts +4 -0
  121. package/dist/components/admin-bar/ui/collapsible/index.js +6 -0
  122. package/dist/components/admin-bar/ui/dialog/dialog-close.svelte +7 -0
  123. package/dist/components/admin-bar/ui/dialog/dialog-close.svelte.d.ts +4 -0
  124. package/dist/components/admin-bar/ui/dialog/dialog-content.svelte +46 -0
  125. package/dist/components/admin-bar/ui/dialog/dialog-content.svelte.d.ts +11 -0
  126. package/dist/components/admin-bar/ui/dialog/dialog-description.svelte +17 -0
  127. package/dist/components/admin-bar/ui/dialog/dialog-description.svelte.d.ts +4 -0
  128. package/dist/components/admin-bar/ui/dialog/dialog-footer.svelte +23 -0
  129. package/dist/components/admin-bar/ui/dialog/dialog-footer.svelte.d.ts +5 -0
  130. package/dist/components/admin-bar/ui/dialog/dialog-header.svelte +20 -0
  131. package/dist/components/admin-bar/ui/dialog/dialog-header.svelte.d.ts +5 -0
  132. package/dist/components/admin-bar/ui/dialog/dialog-overlay.svelte +20 -0
  133. package/dist/components/admin-bar/ui/dialog/dialog-overlay.svelte.d.ts +4 -0
  134. package/dist/components/admin-bar/ui/dialog/dialog-portal.svelte +7 -0
  135. package/dist/components/admin-bar/ui/dialog/dialog-portal.svelte.d.ts +3 -0
  136. package/dist/components/admin-bar/ui/dialog/dialog-title.svelte +17 -0
  137. package/dist/components/admin-bar/ui/dialog/dialog-title.svelte.d.ts +4 -0
  138. package/dist/components/admin-bar/ui/dialog/dialog-trigger.svelte +7 -0
  139. package/dist/components/admin-bar/ui/dialog/dialog-trigger.svelte.d.ts +4 -0
  140. package/dist/components/admin-bar/ui/dialog/dialog.svelte +7 -0
  141. package/dist/components/admin-bar/ui/dialog/dialog.svelte.d.ts +3 -0
  142. package/dist/components/admin-bar/ui/dialog/index.d.ts +11 -0
  143. package/dist/components/admin-bar/ui/dialog/index.js +13 -0
  144. package/dist/components/admin-bar/ui/input/index.d.ts +3 -0
  145. package/dist/components/admin-bar/ui/input/index.js +3 -0
  146. package/dist/components/admin-bar/ui/input/input.svelte +24 -0
  147. package/dist/components/admin-bar/ui/input/input.svelte.d.ts +5 -0
  148. package/dist/components/admin-bar/ui/kbd/index.d.ts +4 -0
  149. package/dist/components/admin-bar/ui/kbd/index.js +6 -0
  150. package/dist/components/admin-bar/ui/kbd/kbd-group.svelte +20 -0
  151. package/dist/components/admin-bar/ui/kbd/kbd-group.svelte.d.ts +5 -0
  152. package/dist/components/admin-bar/ui/kbd/kbd-shortcut.svelte +52 -0
  153. package/dist/components/admin-bar/ui/kbd/kbd-shortcut.svelte.d.ts +8 -0
  154. package/dist/components/admin-bar/ui/kbd/kbd.svelte +27 -0
  155. package/dist/components/admin-bar/ui/kbd/kbd.svelte.d.ts +5 -0
  156. package/dist/components/admin-bar/ui/label/index.d.ts +3 -0
  157. package/dist/components/admin-bar/ui/label/index.js +3 -0
  158. package/dist/components/admin-bar/ui/label/label.svelte +20 -0
  159. package/dist/components/admin-bar/ui/label/label.svelte.d.ts +4 -0
  160. package/dist/components/admin-bar/ui/menubar/index.d.ts +18 -0
  161. package/dist/components/admin-bar/ui/menubar/index.js +20 -0
  162. package/dist/components/admin-bar/ui/menubar/menubar-checkbox-item.svelte +46 -0
  163. package/dist/components/admin-bar/ui/menubar/menubar-checkbox-item.svelte.d.ts +10 -0
  164. package/dist/components/admin-bar/ui/menubar/menubar-content.svelte +28 -0
  165. package/dist/components/admin-bar/ui/menubar/menubar-content.svelte.d.ts +4 -0
  166. package/dist/components/admin-bar/ui/menubar/menubar-group-heading.svelte +25 -0
  167. package/dist/components/admin-bar/ui/menubar/menubar-group-heading.svelte.d.ts +8 -0
  168. package/dist/components/admin-bar/ui/menubar/menubar-group.svelte +12 -0
  169. package/dist/components/admin-bar/ui/menubar/menubar-group.svelte.d.ts +7 -0
  170. package/dist/components/admin-bar/ui/menubar/menubar-item.svelte +27 -0
  171. package/dist/components/admin-bar/ui/menubar/menubar-item.svelte.d.ts +8 -0
  172. package/dist/components/admin-bar/ui/menubar/menubar-label.svelte +25 -0
  173. package/dist/components/admin-bar/ui/menubar/menubar-label.svelte.d.ts +8 -0
  174. package/dist/components/admin-bar/ui/menubar/menubar-menu.svelte +7 -0
  175. package/dist/components/admin-bar/ui/menubar/menubar-menu.svelte.d.ts +3 -0
  176. package/dist/components/admin-bar/ui/menubar/menubar-portal.svelte +7 -0
  177. package/dist/components/admin-bar/ui/menubar/menubar-portal.svelte.d.ts +3 -0
  178. package/dist/components/admin-bar/ui/menubar/menubar-radio-group.svelte +11 -0
  179. package/dist/components/admin-bar/ui/menubar/menubar-radio-group.svelte.d.ts +4 -0
  180. package/dist/components/admin-bar/ui/menubar/menubar-radio-item.svelte +37 -0
  181. package/dist/components/admin-bar/ui/menubar/menubar-radio-item.svelte.d.ts +8 -0
  182. package/dist/components/admin-bar/ui/menubar/menubar-separator.svelte +17 -0
  183. package/dist/components/admin-bar/ui/menubar/menubar-separator.svelte.d.ts +4 -0
  184. package/dist/components/admin-bar/ui/menubar/menubar-shortcut.svelte +23 -0
  185. package/dist/components/admin-bar/ui/menubar/menubar-shortcut.svelte.d.ts +5 -0
  186. package/dist/components/admin-bar/ui/menubar/menubar-sub-content.svelte +20 -0
  187. package/dist/components/admin-bar/ui/menubar/menubar-sub-content.svelte.d.ts +4 -0
  188. package/dist/components/admin-bar/ui/menubar/menubar-sub-trigger.svelte +29 -0
  189. package/dist/components/admin-bar/ui/menubar/menubar-sub-trigger.svelte.d.ts +8 -0
  190. package/dist/components/admin-bar/ui/menubar/menubar-sub.svelte +7 -0
  191. package/dist/components/admin-bar/ui/menubar/menubar-sub.svelte.d.ts +4 -0
  192. package/dist/components/admin-bar/ui/menubar/menubar-trigger.svelte +20 -0
  193. package/dist/components/admin-bar/ui/menubar/menubar-trigger.svelte.d.ts +4 -0
  194. package/dist/components/admin-bar/ui/menubar/menubar.svelte +20 -0
  195. package/dist/components/admin-bar/ui/menubar/menubar.svelte.d.ts +4 -0
  196. package/dist/components/admin-bar/ui/select/index.d.ts +12 -0
  197. package/dist/components/admin-bar/ui/select/index.js +14 -0
  198. package/dist/components/admin-bar/ui/select/select-content.svelte +45 -0
  199. package/dist/components/admin-bar/ui/select/select-content.svelte.d.ts +11 -0
  200. package/dist/components/admin-bar/ui/select/select-group-heading.svelte +21 -0
  201. package/dist/components/admin-bar/ui/select/select-group-heading.svelte.d.ts +10 -0
  202. package/dist/components/admin-bar/ui/select/select-group.svelte +17 -0
  203. package/dist/components/admin-bar/ui/select/select-group.svelte.d.ts +4 -0
  204. package/dist/components/admin-bar/ui/select/select-item.svelte +40 -0
  205. package/dist/components/admin-bar/ui/select/select-item.svelte.d.ts +5 -0
  206. package/dist/components/admin-bar/ui/select/select-label.svelte +20 -0
  207. package/dist/components/admin-bar/ui/select/select-label.svelte.d.ts +6 -0
  208. package/dist/components/admin-bar/ui/select/select-portal.svelte +7 -0
  209. package/dist/components/admin-bar/ui/select/select-portal.svelte.d.ts +3 -0
  210. package/dist/components/admin-bar/ui/select/select-scroll-down-button.svelte +23 -0
  211. package/dist/components/admin-bar/ui/select/select-scroll-down-button.svelte.d.ts +5 -0
  212. package/dist/components/admin-bar/ui/select/select-scroll-up-button.svelte +23 -0
  213. package/dist/components/admin-bar/ui/select/select-scroll-up-button.svelte.d.ts +5 -0
  214. package/dist/components/admin-bar/ui/select/select-separator.svelte +21 -0
  215. package/dist/components/admin-bar/ui/select/select-separator.svelte.d.ts +4 -0
  216. package/dist/components/admin-bar/ui/select/select-trigger.svelte +31 -0
  217. package/dist/components/admin-bar/ui/select/select-trigger.svelte.d.ts +8 -0
  218. package/dist/components/admin-bar/ui/select/select.svelte +11 -0
  219. package/dist/components/admin-bar/ui/select/select.svelte.d.ts +3 -0
  220. package/dist/components/admin-bar/ui/separator/index.d.ts +2 -0
  221. package/dist/components/admin-bar/ui/separator/index.js +4 -0
  222. package/dist/components/admin-bar/ui/separator/separator.svelte +21 -0
  223. package/dist/components/admin-bar/ui/separator/separator.svelte.d.ts +4 -0
  224. package/dist/components/admin-bar/ui/textarea/index.d.ts +3 -0
  225. package/dist/components/admin-bar/ui/textarea/index.js +3 -0
  226. package/dist/components/admin-bar/ui/textarea/textarea.svelte +21 -0
  227. package/dist/components/admin-bar/ui/textarea/textarea.svelte.d.ts +5 -0
  228. package/dist/components/admin-bar/user-avatar.svelte +29 -0
  229. package/dist/components/admin-bar/user-avatar.svelte.d.ts +7 -0
  230. package/dist/components/admin-bar/utils.d.ts +10 -0
  231. package/dist/components/admin-bar/utils.js +46 -0
  232. package/dist/components/admin-bar/variants.d.ts +20 -0
  233. package/dist/components/admin-bar/variants.js +14 -0
  234. package/dist/components/cms/cms-boolean.svelte +102 -0
  235. package/dist/components/cms/cms-boolean.svelte.d.ts +10 -0
  236. package/dist/components/cms/cms-date-time-editable.svelte +66 -0
  237. package/dist/components/cms/cms-date-time-editable.svelte.d.ts +11 -0
  238. package/dist/components/cms/cms-date-time.svelte +67 -0
  239. package/dist/components/cms/cms-date-time.svelte.d.ts +19 -0
  240. package/dist/components/cms/cms-entries.svelte +24 -0
  241. package/dist/components/cms/cms-entries.svelte.d.ts +31 -0
  242. package/dist/components/cms/cms-image-editable.svelte +439 -0
  243. package/dist/components/cms/cms-image-editable.svelte.d.ts +11 -0
  244. package/dist/components/cms/cms-image.svelte +78 -0
  245. package/dist/components/cms/cms-image.svelte.d.ts +18 -0
  246. package/dist/components/cms/cms-link-editable.svelte +429 -0
  247. package/dist/components/cms/cms-link-editable.svelte.d.ts +13 -0
  248. package/dist/components/cms/cms-link.svelte +92 -0
  249. package/dist/components/cms/cms-link.svelte.d.ts +25 -0
  250. package/dist/components/cms/cms-markdown-editable.svelte +74 -0
  251. package/dist/components/cms/cms-markdown-editable.svelte.d.ts +9 -0
  252. package/dist/components/cms/cms-markdown.svelte +56 -0
  253. package/dist/components/cms/cms-markdown.svelte.d.ts +10 -0
  254. package/dist/components/cms/cms-media-picker.svelte +150 -0
  255. package/dist/components/cms/cms-media-picker.svelte.d.ts +8 -0
  256. package/dist/components/cms/cms-number-editable.svelte +77 -0
  257. package/dist/components/cms/cms-number-editable.svelte.d.ts +13 -0
  258. package/dist/components/cms/cms-number.svelte +63 -0
  259. package/dist/components/cms/cms-number.svelte.d.ts +16 -0
  260. package/dist/components/cms/cms-repeater-editable.svelte +137 -0
  261. package/dist/components/cms/cms-repeater-editable.svelte.d.ts +31 -0
  262. package/dist/components/cms/cms-repeater.svelte +55 -0
  263. package/dist/components/cms/cms-repeater.svelte.d.ts +30 -0
  264. package/dist/components/cms/cms-rich-text-editable.svelte +101 -0
  265. package/dist/components/cms/cms-rich-text-editable.svelte.d.ts +9 -0
  266. package/dist/components/cms/cms-rich-text-toolbar.svelte +246 -0
  267. package/dist/components/cms/cms-rich-text-toolbar.svelte.d.ts +9 -0
  268. package/dist/components/cms/cms-rich-text.svelte +74 -0
  269. package/dist/components/cms/cms-rich-text.svelte.d.ts +10 -0
  270. package/dist/components/cms/cms-store.svelte.d.ts +294 -0
  271. package/dist/components/cms/cms-store.svelte.js +625 -0
  272. package/dist/components/cms/cms-text.svelte +131 -0
  273. package/dist/components/cms/cms-text.svelte.d.ts +11 -0
  274. package/dist/components/cms/index.d.ts +13 -0
  275. package/dist/components/cms/index.js +11 -0
  276. package/dist/components/cms/install-scope.svelte.d.ts +7 -0
  277. package/dist/components/cms/install-scope.svelte.js +33 -0
  278. package/dist/components/cms/locale-merge.d.ts +41 -0
  279. package/dist/components/cms/locale-merge.js +77 -0
  280. package/dist/components/cms/overlay-sync.d.ts +82 -0
  281. package/dist/components/cms/overlay-sync.js +76 -0
  282. package/dist/components/cms/path.d.ts +30 -0
  283. package/dist/components/cms/path.js +156 -0
  284. package/dist/components/cms/scope.d.ts +120 -0
  285. package/dist/components/cms/scope.js +23 -0
  286. package/dist/index.d.ts +27 -0
  287. package/dist/index.js +28 -0
  288. package/dist/srv/README.md +341 -0
  289. package/dist/srv/api-adapter.d.ts +20 -0
  290. package/dist/srv/api-adapter.js +178 -0
  291. package/dist/srv/cms.d.ts +62 -0
  292. package/dist/srv/cms.js +35 -0
  293. package/dist/srv/index.d.ts +5 -0
  294. package/dist/srv/index.js +4 -0
  295. package/dist/srv/load-cms.d.ts +105 -0
  296. package/dist/srv/load-cms.js +227 -0
  297. package/dist/srv/mock-adapter.d.ts +96 -0
  298. package/dist/srv/mock-adapter.js +171 -0
  299. package/dist/srv/types.d.ts +114 -0
  300. package/dist/srv/types.js +1 -0
  301. package/package.json +116 -0
  302. package/plugin/README.md +137 -0
  303. package/plugin/dist/adapter.d.ts +35 -0
  304. package/plugin/dist/adapter.js +346 -0
  305. package/plugin/dist/adapter.js.map +1 -0
  306. package/plugin/dist/build-state.d.ts +10 -0
  307. package/plugin/dist/build-state.js +26 -0
  308. package/plugin/dist/build-state.js.map +1 -0
  309. package/plugin/dist/fallback-shim.js +194 -0
  310. package/plugin/dist/index.d.ts +56 -0
  311. package/plugin/dist/index.js +396 -0
  312. package/plugin/dist/index.js.map +1 -0
  313. package/plugin/dist/manifest.d.ts +76 -0
  314. package/plugin/dist/manifest.js +367 -0
  315. package/plugin/dist/manifest.js.map +1 -0
  316. package/plugin/dist/media.d.ts +36 -0
  317. package/plugin/dist/media.js +153 -0
  318. package/plugin/dist/media.js.map +1 -0
  319. package/plugin/dist/parse-svelte.d.ts +35 -0
  320. package/plugin/dist/parse-svelte.js +120 -0
  321. package/plugin/dist/parse-svelte.js.map +1 -0
  322. package/plugin/dist/route-tree.d.ts +27 -0
  323. package/plugin/dist/route-tree.js +69 -0
  324. package/plugin/dist/route-tree.js.map +1 -0
  325. package/plugin/dist/transforms.d.ts +42 -0
  326. package/plugin/dist/transforms.js +95 -0
  327. package/plugin/dist/transforms.js.map +1 -0
@@ -0,0 +1,625 @@
1
+ /**
2
+ * Reactive edit-mode store for CMS components.
3
+ *
4
+ * Every (scope, locale) owns one tree. `drafts[key].tree` holds the editor's
5
+ * local, uncommitted patch tree; `overlay[key]` holds merged
6
+ * published+release content fetched client-side from `${endpoint}/docs` for
7
+ * that locale. Display components read through `getValue(scope, path)`,
8
+ * which deep-merges `base ∪ overlay ∪ draft` (drafts only when `isEditing`)
9
+ * and returns the value at `path`. The `base` is the request-time doc on
10
+ * `page.data.cms` (already locale-resolved + fallback-merged by the
11
+ * server-side `resolveCmsPayload`); `overlay` and `draft` are scoped to the
12
+ * locale stored on the bucket.
13
+ *
14
+ * `save(endpoint)` flushes draft trees to `${endpoint}/release/items` as
15
+ * `{ items: [{ kind, routeId, [params], locale, tree }] }`. The server
16
+ * deep-merges each tree into the editor's open release, keyed by
17
+ * (scope, locale). Publishing is a separate deliberate step via
18
+ * `/release/publish`.
19
+ *
20
+ * Paths are lodash-style: `welcome.title`, `gallery.0.caption`,
21
+ * `metadata.openGraph.image.url`. Numeric segments index arrays. There is no
22
+ * special envelope for metadata — `metadata` is just a branch on the page
23
+ * scope's tree.
24
+ *
25
+ * Locale on read/write helpers (`getValue`, `setValue`, `hasDraft`,
26
+ * `hasOverlay`) defaults to the request's `page.data.cms.locale`. The
27
+ * Locales panel passes an explicit locale to inspect other locales'
28
+ * working-copy state without switching the page.
29
+ */
30
+ import { page } from '$app/state';
31
+ import { mergeLocaleDocs, mergeLocaleEntries } from './locale-merge.js';
32
+ import { composeKey } from './overlay-sync.js';
33
+ import { get, has, mergeTree, set } from './path.js';
34
+ const scopeKindFromId = (scopeId) => scopeId.startsWith('layout:') ? 'layout' : 'page';
35
+ /**
36
+ * Origin where uploaded media files are served. The backend writes files to
37
+ * `static/uploads/` and returns relative paths like `/uploads/<slug>.png`; the
38
+ * frontend lives on a different origin in dev, so we prefix each URL here at
39
+ * the wire boundary so display components and persisted field values both get
40
+ * a fully-qualified URL.
41
+ */
42
+ export const MEDIA_URL_PREFIX = 'https://velastack.dev';
43
+ const resolveMediaItem = (item) => item.url.startsWith('/') ? { ...item, url: `${MEDIA_URL_PREFIX}${item.url}` } : item;
44
+ const treeHasAnyLeaf = (tree) => {
45
+ for (const v of Object.values(tree)) {
46
+ if (v === undefined)
47
+ continue;
48
+ return true;
49
+ }
50
+ return false;
51
+ };
52
+ /** Read the request-time locale from `page.data.cms`. Empty string until a payload loads. */
53
+ const pageLocale = () => {
54
+ const cms = page.data?.cms;
55
+ return cms?.locale ?? '';
56
+ };
57
+ /**
58
+ * Default (fallback) locale for the project — `cms.locales[0]` by convention
59
+ * (matches `resolveCmsPayload`). Used by `loadAndApply*` to fetch the default
60
+ * locale's content alongside the requested one for `requested → default`
61
+ * fallback merging.
62
+ */
63
+ const pageDefaultLocale = () => {
64
+ const cms = page.data?.cms;
65
+ return cms?.locales?.[0] ?? cms?.locale ?? '';
66
+ };
67
+ class CmsStore {
68
+ isEditing = $state(false);
69
+ drafts = $state({});
70
+ openRelease = $state(null);
71
+ overlay = $state({});
72
+ /** Per-routeId entries overlay populated by `loadAndApplyEntriesOverlay`. */
73
+ entriesOverlay = $state({});
74
+ /**
75
+ * Project-global site bucket. Not localized, not route-bound. `siteOverlay`
76
+ * holds the merged published+open-release tree fetched client-side from
77
+ * `${endpoint}/site`; `siteDraft` holds in-flight Site Settings panel
78
+ * edits awaiting save. `cmsStore.site` returns the merged read of base
79
+ * (load payload) ∪ overlay ∪ draft.
80
+ */
81
+ siteOverlay = $state(undefined);
82
+ siteDraft = $state({});
83
+ /**
84
+ * Active editor preview locale, pushed in by the admin bar from the URL
85
+ * (`?locale=`). Falls back to `page.data.cms.locale` when unset (read-only
86
+ * consumer, or no override). Field components read/write through the
87
+ * default locale below, so editing in `?locale=es` keys drafts to `es`
88
+ * even if the consumer's `getLocale(url)` doesn't honor the param.
89
+ */
90
+ _activeLocale = $state(null);
91
+ overlayFetchToken = 0;
92
+ entriesFetchToken = 0;
93
+ toggleEdit() {
94
+ this.isEditing = !this.isEditing;
95
+ }
96
+ setActiveLocale(locale) {
97
+ this._activeLocale = locale || null;
98
+ }
99
+ effectiveLocale() {
100
+ return this._activeLocale ?? pageLocale();
101
+ }
102
+ /** Base doc (server-rendered) for a scope. Already locale-resolved by `resolveCmsPayload`. */
103
+ baseDoc(scope) {
104
+ const cms = page.data?.cms;
105
+ return (cms?.docs?.[scope.scopeId] ?? {});
106
+ }
107
+ /** Merged read tree: base ∪ overlay (∪ draft when editing) for the given locale. */
108
+ mergedTree(scope, locale) {
109
+ const key = composeKey(scope, locale);
110
+ // Only fold in base when the requested locale matches the page's
111
+ // resolved locale — `page.data.cms.docs` only carries that one.
112
+ const base = locale === pageLocale() ? this.baseDoc(scope) : {};
113
+ const ov = this.overlay[key];
114
+ const draft = this.isEditing ? this.drafts[key]?.tree : undefined;
115
+ return mergeTree(base, ov, draft);
116
+ }
117
+ /** Read the merged value at `path`. Returns `undefined` if missing. */
118
+ getValue(scope, path, locale) {
119
+ return get(this.mergedTree(scope, locale ?? this.effectiveLocale()), path);
120
+ }
121
+ /** Whether the editor's draft tree contains a value at `path`. */
122
+ hasDraft(scope, path, locale) {
123
+ const bucket = this.drafts[composeKey(scope, locale ?? this.effectiveLocale())];
124
+ return bucket ? has(bucket.tree, path) : false;
125
+ }
126
+ /** Whether the client overlay (published+release) contains a value at `path`. */
127
+ hasOverlay(scope, path, locale) {
128
+ const ov = this.overlay[composeKey(scope, locale ?? this.effectiveLocale())];
129
+ return ov ? has(ov, path) : false;
130
+ }
131
+ /** Write a value at `path` into the draft tree. Creates the bucket if needed. */
132
+ setValue(scope, path, value, locale) {
133
+ const lc = locale ?? this.effectiveLocale();
134
+ const key = composeKey(scope, lc);
135
+ const bucket = this.drafts[key] ?? (this.drafts[key] = { scope, locale: lc, tree: {} });
136
+ set(bucket.tree, path, value);
137
+ }
138
+ clearOverlay() {
139
+ this.overlay = {};
140
+ this.entriesOverlay = {};
141
+ this.siteOverlay = undefined;
142
+ }
143
+ /**
144
+ * Reactive merged site tree: base (load payload) ∪ overlay (client-fetched
145
+ * `/site` response with preview overlay) ∪ draft (Site Settings panel
146
+ * edits awaiting save). Reads update live, so a value typed in the panel
147
+ * appears on the page immediately.
148
+ *
149
+ * Drafts are always folded in here (no `isEditing` gate) — the panel
150
+ * commits to the draft via `setSiteValue` only on Save, so any value
151
+ * present is already an intentional staged change.
152
+ */
153
+ get site() {
154
+ const baseSite = page.data?.cms?.site?.tree ?? {};
155
+ return mergeTree(baseSite, this.siteOverlay, this.siteDraft);
156
+ }
157
+ getSiteValue(path) {
158
+ return get(this.site, path);
159
+ }
160
+ setSiteValue(path, value) {
161
+ set(this.siteDraft, path, value);
162
+ }
163
+ hasSiteDraft(path) {
164
+ if (!path)
165
+ return treeHasAnyLeaf(this.siteDraft);
166
+ return has(this.siteDraft, path);
167
+ }
168
+ clearSiteDraft() {
169
+ this.siteDraft = {};
170
+ }
171
+ /**
172
+ * Fetch the merged published+release site tree from `${endpoint}/site`
173
+ * and apply as a client-side overlay. With `previewKey` the response
174
+ * includes the user's open-release site item; without it (or with
175
+ * `versionKey`), the response is published-only / past-snapshot.
176
+ */
177
+ async loadAndApplySiteOverlay(endpoint, previewKey, opts = {}) {
178
+ const versionKey = opts.versionKey ?? null;
179
+ const qs = new URLSearchParams();
180
+ if (versionKey)
181
+ qs.set('version', versionKey);
182
+ else if (previewKey)
183
+ qs.set('preview', previewKey);
184
+ const suffix = qs.toString() ? `?${qs}` : '';
185
+ try {
186
+ const res = await fetch(`${endpoint}/site${suffix}`, { credentials: 'include' });
187
+ if (!res.ok)
188
+ return;
189
+ const data = (await res.json());
190
+ this.siteOverlay = data.contents ?? {};
191
+ }
192
+ catch {
193
+ /* network errors are non-fatal — display falls through to base */
194
+ }
195
+ }
196
+ /**
197
+ * Fetch merged published+release content for each scope on the current
198
+ * route via `${endpoint}/docs` and apply as a client-side overlay.
199
+ *
200
+ * `previewKey` selects the user's open release on the server; pass `null`
201
+ * to fetch published-only content (used post-publish to mask the now-stale
202
+ * `page.data.cms.docs` on static-export sites).
203
+ *
204
+ * `opts.versionKey` (mutually exclusive with `previewKey`) requests the
205
+ * snapshot at a published release's publish time — `?version=…`.
206
+ *
207
+ * `opts.locale` is the BCP-47 locale to request — required, scopes the
208
+ * overlay cache key so different locales don't collide.
209
+ *
210
+ * `opts.defaultLocale` (the consumer's `cms.locales[0]`) enables the
211
+ * fallback fetch: when `locale !== defaultLocale`, we fetch the default
212
+ * locale's content in parallel and `mergeLocaleDocs` it under the
213
+ * requested locale. Adapters return only what's stored for the requested
214
+ * locale (per the mock-adapter contract); the library composes the
215
+ * `requested → default → undefined` chain. Mirrors `resolveCmsPayload`.
216
+ *
217
+ * `opts.reset` replaces the overlay map wholesale (mount with release,
218
+ * post-publish, post-discard). Otherwise we merge — keeps unchanged-layout
219
+ * overlays applied during navigation while new-page scopes load.
220
+ */
221
+ async loadAndApplyOverlay(endpoint, scopes, previewKey, opts) {
222
+ const token = ++this.overlayFetchToken;
223
+ const versionKey = opts.versionKey ?? null;
224
+ const locale = opts.locale;
225
+ const defaultLocale = opts.defaultLocale ?? pageDefaultLocale() ?? locale;
226
+ const needsFallback = !!defaultLocale && locale !== defaultLocale;
227
+ const fetchScope = async (scope, forLocale) => {
228
+ const qs = new URLSearchParams({
229
+ kind: scope.kind,
230
+ routeId: scope.routeId,
231
+ params: JSON.stringify(scope.params),
232
+ locale: forLocale
233
+ });
234
+ if (versionKey)
235
+ qs.set('version', versionKey);
236
+ else if (previewKey)
237
+ qs.set('preview', previewKey);
238
+ const res = await fetch(`${endpoint}/docs?${qs}`, { credentials: 'include' });
239
+ if (!res.ok)
240
+ return null;
241
+ const data = (await res.json());
242
+ return { scope, contents: data.contents };
243
+ };
244
+ const collect = (pairs) => {
245
+ const out = {};
246
+ for (const r of pairs) {
247
+ if (!r)
248
+ continue;
249
+ out[r.scope.scopeId] = r.contents ?? {};
250
+ }
251
+ return out;
252
+ };
253
+ const [requestedPairs, fallbackPairs] = await Promise.all([
254
+ Promise.all(scopes.map((s) => fetchScope(s, locale))),
255
+ needsFallback
256
+ ? Promise.all(scopes.map((s) => fetchScope(s, defaultLocale)))
257
+ : Promise.resolve(null)
258
+ ]);
259
+ if (token !== this.overlayFetchToken)
260
+ return;
261
+ const merged = mergeLocaleDocs(collect(requestedPairs), fallbackPairs ? collect(fallbackPairs) : null);
262
+ const scopeIndex = new Map();
263
+ for (const s of scopes)
264
+ scopeIndex.set(s.scopeId, s);
265
+ const next = {};
266
+ for (const [scopeId, contents] of Object.entries(merged)) {
267
+ const scope = scopeIndex.get(scopeId);
268
+ if (!scope)
269
+ continue;
270
+ const key = composeKey({ scopeId: scope.scopeId, routeId: scope.routeId, params: scope.params }, locale);
271
+ next[key] = contents;
272
+ }
273
+ if (opts.reset) {
274
+ this.overlay = next;
275
+ }
276
+ else {
277
+ this.overlay = { ...this.overlay, ...next };
278
+ }
279
+ }
280
+ /**
281
+ * Refresh the per-routeId entries overlay used by `<CmsEntries>`. One fetch
282
+ * to `${endpoint}/pages?preview=…&locale=…` covers all `routeIds`; we
283
+ * filter the response per route. With `previewKey` set, draft and
284
+ * pending-delete entries are kept; with `previewKey === null`, they're
285
+ * filtered out — used post-publish to mask stale
286
+ * `page.data.cms.entries` on static-export sites.
287
+ *
288
+ * `opts.defaultLocale` enables the fallback fetch: when the requested
289
+ * locale isn't the default, we fetch the default locale's entries in
290
+ * parallel and union by params (requested-locale entries win on metadata
291
+ * for shared params; default-locale-only entries surface as fallbacks).
292
+ */
293
+ async loadAndApplyEntriesOverlay(endpoint, routeIds, previewKey, opts) {
294
+ const token = ++this.entriesFetchToken;
295
+ if (routeIds.length === 0) {
296
+ if (opts.reset)
297
+ this.entriesOverlay = {};
298
+ return;
299
+ }
300
+ const versionKey = opts.versionKey ?? null;
301
+ const defaultLocale = opts.defaultLocale ?? pageDefaultLocale() ?? opts.locale;
302
+ const needsFallback = !!defaultLocale && opts.locale !== defaultLocale;
303
+ const fetchByLocale = async (forLocale) => {
304
+ const qs = new URLSearchParams({ locale: forLocale });
305
+ if (versionKey)
306
+ qs.set('version', versionKey);
307
+ else if (previewKey)
308
+ qs.set('preview', previewKey);
309
+ let res;
310
+ try {
311
+ res = await fetch(`${endpoint}/pages?${qs}`, { credentials: 'include' });
312
+ }
313
+ catch {
314
+ return null;
315
+ }
316
+ if (!res.ok)
317
+ return null;
318
+ const body = (await res.json());
319
+ const out = {};
320
+ const wanted = new Set(routeIds);
321
+ for (const r of body.routes) {
322
+ if (!wanted.has(r.routeId))
323
+ continue;
324
+ // Tombstones (redirect / gone) and pending deletes never appear in
325
+ // consumer-facing `<CmsEntries>` lists. Drafts are visible only in
326
+ // preview / version mode where the editor inspects working state.
327
+ const filtered = r.entries.filter((e) => {
328
+ if (e.redirectTo || e.gone || e.isDeletePending)
329
+ return false;
330
+ if (versionKey || previewKey)
331
+ return true;
332
+ return !e.isDraft;
333
+ });
334
+ out[r.routeId] = filtered.map((e) => ({
335
+ params: e.params,
336
+ metadata: e.metadata ?? {}
337
+ }));
338
+ }
339
+ return out;
340
+ };
341
+ const [requested, fallback] = await Promise.all([
342
+ fetchByLocale(opts.locale),
343
+ needsFallback ? fetchByLocale(defaultLocale) : Promise.resolve(null)
344
+ ]);
345
+ if (token !== this.entriesFetchToken)
346
+ return;
347
+ if (!requested)
348
+ return;
349
+ const merged = mergeLocaleEntries(requested, fallback);
350
+ const next = { ...merged };
351
+ for (const rid of routeIds) {
352
+ if (!(rid in next))
353
+ next[rid] = [];
354
+ }
355
+ if (opts.reset) {
356
+ this.entriesOverlay = next;
357
+ }
358
+ else {
359
+ this.entriesOverlay = { ...this.entriesOverlay, ...next };
360
+ }
361
+ }
362
+ get isDirty() {
363
+ for (const bucket of Object.values(this.drafts)) {
364
+ if (treeHasAnyLeaf(bucket.tree))
365
+ return true;
366
+ }
367
+ if (treeHasAnyLeaf(this.siteDraft))
368
+ return true;
369
+ return false;
370
+ }
371
+ /**
372
+ * Counts distinct page identities and layout routes touched by the open
373
+ * release, NOT raw item count: a page edited in `en` and `es` registers
374
+ * as one. Powers the editor's "X pages draft" mental model — they think
375
+ * "one page is in progress", not "two storage rows exist". For per-locale
376
+ * breakdowns (locales panel "EN: 6, ES: 4"), use `workingCopyCountsByLocale`.
377
+ */
378
+ get workingCopyCounts() {
379
+ const pageKeys = new Set();
380
+ const layoutKeys = new Set();
381
+ let site = 0;
382
+ for (const item of this.openRelease?.items ?? []) {
383
+ if (item.kind === 'site') {
384
+ site = 1;
385
+ }
386
+ else if (item.kind === 'layout') {
387
+ layoutKeys.add(item.routeId);
388
+ }
389
+ else {
390
+ const sortedParams = Object.keys(item.params).sort();
391
+ const qp = sortedParams.map((k) => `${k}=${item.params[k]}`).join('&');
392
+ pageKeys.add(`${item.routeId}?${qp}`);
393
+ }
394
+ }
395
+ return {
396
+ pages: pageKeys.size,
397
+ layouts: layoutKeys.size,
398
+ site,
399
+ total: pageKeys.size + layoutKeys.size + site
400
+ };
401
+ }
402
+ /**
403
+ * Per-locale breakdown of the open release's working-copy items. Powers
404
+ * the Locales panel ("default has 6 edits, es missing 2"). Locales with
405
+ * zero items don't appear in the map; the panel iterates `cms.locales` to
406
+ * surface them as "0 edits".
407
+ */
408
+ get workingCopyCountsByLocale() {
409
+ const out = {};
410
+ for (const item of this.openRelease?.items ?? []) {
411
+ if (item.kind === 'site')
412
+ continue;
413
+ const bucket = (out[item.locale] ??= { pages: 0, layouts: 0, total: 0 });
414
+ if (item.kind === 'layout')
415
+ bucket.layouts += 1;
416
+ else
417
+ bucket.pages += 1;
418
+ bucket.total += 1;
419
+ }
420
+ return out;
421
+ }
422
+ clearDrafts() {
423
+ this.drafts = {};
424
+ this.siteDraft = {};
425
+ }
426
+ setOpenRelease(release) {
427
+ this.openRelease = release;
428
+ }
429
+ async fetchOpenRelease(endpoint) {
430
+ const res = await fetch(`${endpoint}/release`, { credentials: 'include' });
431
+ if (!res.ok) {
432
+ this.openRelease = null;
433
+ return;
434
+ }
435
+ const data = (await res.json());
436
+ this.openRelease = data.release;
437
+ }
438
+ /**
439
+ * Atomically rename a page's slug: stage a draft `page` item at the new
440
+ * params copying content from the published entry, plus a `page-delete`
441
+ * with a redirect outcome on the old params pointing at the new URL. Both
442
+ * items go into the open release as a transactional pair, so undo removes
443
+ * them together. The backend composes this on `POST /pages/rename`.
444
+ */
445
+ async renameSlug(endpoint, args) {
446
+ const res = await fetch(`${endpoint}/pages/rename`, {
447
+ credentials: 'include',
448
+ method: 'POST',
449
+ headers: { 'Content-Type': 'application/json' },
450
+ body: JSON.stringify(args)
451
+ });
452
+ if (!res.ok)
453
+ return { ok: false };
454
+ const data = (await res.json());
455
+ this.openRelease = data.release;
456
+ return { ok: true, release: data.release };
457
+ }
458
+ /**
459
+ * Upload a media file to `${endpoint}/media` and return the full `MediaItem`.
460
+ * The library panel and inline picker both call this so newly-uploaded items
461
+ * can be prepended to local state without a re-list.
462
+ */
463
+ async uploadMedia(endpoint, file) {
464
+ const formData = new FormData();
465
+ formData.append('file', file);
466
+ const res = await fetch(`${endpoint}/media`, {
467
+ method: 'POST',
468
+ body: formData,
469
+ credentials: 'include'
470
+ });
471
+ if (!res.ok) {
472
+ throw new Error(`Upload failed (${res.status})`);
473
+ }
474
+ const item = (await res.json());
475
+ if (typeof item?.url !== 'string') {
476
+ throw new Error('Upload response missing url');
477
+ }
478
+ return resolveMediaItem(item);
479
+ }
480
+ /** Back-compat wrapper around `uploadMedia` that returns just the URL. */
481
+ async uploadImage(endpoint, file) {
482
+ return (await this.uploadMedia(endpoint, file)).url;
483
+ }
484
+ async listMedia(endpoint, params = {}) {
485
+ const qs = new URLSearchParams();
486
+ if (params.offset != null)
487
+ qs.set('offset', String(params.offset));
488
+ if (params.limit != null)
489
+ qs.set('limit', String(params.limit));
490
+ const res = await fetch(`${endpoint}/media?${qs}`, { credentials: 'include' });
491
+ if (!res.ok)
492
+ throw new Error(`List failed (${res.status})`);
493
+ const data = (await res.json());
494
+ return { ...data, items: data.items.map(resolveMediaItem) };
495
+ }
496
+ async deleteMedia(endpoint, id) {
497
+ const res = await fetch(`${endpoint}/media/${id}`, {
498
+ method: 'DELETE',
499
+ credentials: 'include'
500
+ });
501
+ if (!res.ok)
502
+ throw new Error(`Delete failed (${res.status})`);
503
+ }
504
+ /**
505
+ * Flush dirty drafts to the server's open release as one item per
506
+ * (scope, locale), each carrying a partial tree. The server deep-merges
507
+ * each tree into its matching item in the working copy without publishing.
508
+ */
509
+ async save(endpoint) {
510
+ const items = [];
511
+ for (const bucket of Object.values(this.drafts)) {
512
+ if (!treeHasAnyLeaf(bucket.tree))
513
+ continue;
514
+ const kind = scopeKindFromId(bucket.scope.scopeId);
515
+ if (kind === 'page') {
516
+ items.push({
517
+ kind: 'page',
518
+ routeId: bucket.scope.routeId,
519
+ params: bucket.scope.params,
520
+ locale: bucket.locale,
521
+ tree: bucket.tree
522
+ });
523
+ }
524
+ else {
525
+ items.push({
526
+ kind: 'layout',
527
+ routeId: bucket.scope.routeId,
528
+ locale: bucket.locale,
529
+ tree: bucket.tree
530
+ });
531
+ }
532
+ }
533
+ if (treeHasAnyLeaf(this.siteDraft)) {
534
+ items.push({ kind: 'site', tree: this.siteDraft });
535
+ }
536
+ if (items.length === 0)
537
+ return { ok: true, release: this.openRelease ?? undefined };
538
+ const res = await fetch(`${endpoint}/release/items`, {
539
+ credentials: 'include',
540
+ method: 'POST',
541
+ headers: { 'Content-Type': 'application/json' },
542
+ body: JSON.stringify({ items })
543
+ });
544
+ if (!res.ok)
545
+ return { ok: false };
546
+ const data = (await res.json());
547
+ this.openRelease = data.release;
548
+ this.clearDrafts();
549
+ return { ok: true, release: data.release };
550
+ }
551
+ }
552
+ export const cmsStore = new CmsStore();
553
+ /**
554
+ * Public read view of the merged CMS payload: server-loaded `page.data.cms`
555
+ * with `cmsStore.overlay` applied on top, so authed editors see working-copy
556
+ * content without a server reload.
557
+ *
558
+ * Shape mirrors `CmsPayload` directly via getters (`cms.docs`, `cms.metadata`,
559
+ * `cms.page`, …). `metadata` is now a thin alias — it reads the page scope's
560
+ * `metadata` branch from the merged docs.
561
+ */
562
+ class Cms {
563
+ #merged = $derived.by(() => {
564
+ const base = (page.data?.cms ?? null);
565
+ if (!base)
566
+ return null;
567
+ const docs = {};
568
+ for (const [scopeId, scopeEntry] of Object.entries(base.scopes)) {
569
+ const overlayKey = composeKey({
570
+ scopeId: scopeEntry.scopeId,
571
+ routeId: scopeEntry.routeId,
572
+ params: scopeEntry.params
573
+ }, base.locale);
574
+ const overlayBucket = cmsStore.overlay[overlayKey];
575
+ const baseDoc = (base.docs[scopeId] ?? {});
576
+ docs[scopeId] = overlayBucket ? mergeTree(baseDoc, overlayBucket) : baseDoc;
577
+ }
578
+ let metadata = base.metadata;
579
+ if (base.page) {
580
+ const pageDoc = docs[base.page.scopeId];
581
+ const meta = pageDoc?.metadata;
582
+ if (meta && typeof meta === 'object' && !Array.isArray(meta)) {
583
+ metadata = meta;
584
+ }
585
+ }
586
+ const entries = {
587
+ ...(base.entries ?? {}),
588
+ ...cmsStore.entriesOverlay
589
+ };
590
+ return { ...base, docs, metadata, entries };
591
+ });
592
+ get locale() {
593
+ return this.#merged?.locale ?? '';
594
+ }
595
+ get locales() {
596
+ return this.#merged?.locales ?? [];
597
+ }
598
+ get docs() {
599
+ return this.#merged?.docs ?? {};
600
+ }
601
+ get scopes() {
602
+ return this.#merged?.scopes ?? {};
603
+ }
604
+ get metadata() {
605
+ return this.#merged?.metadata ?? {};
606
+ }
607
+ get entries() {
608
+ return this.#merged?.entries ?? {};
609
+ }
610
+ get endpoint() {
611
+ return this.#merged?.endpoint ?? '';
612
+ }
613
+ get page() {
614
+ return this.#merged?.page ?? null;
615
+ }
616
+ /**
617
+ * Project-wide site settings tree, merged through `cmsStore.site` so panel
618
+ * edits show up live in preview. Use this in display components instead of
619
+ * reading `data.cms.site.tree` directly — that's only the SSR seed.
620
+ */
621
+ get site() {
622
+ return cmsStore.site;
623
+ }
624
+ }
625
+ export const cms = new Cms();