nebula-cms 0.1.3 → 0.1.5

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 (381) hide show
  1. package/README.md +3 -1
  2. package/dist/astro/index.d.ts +43 -0
  3. package/dist/astro/index.d.ts.map +1 -0
  4. package/dist/astro/index.js +223 -0
  5. package/{src → dist}/client/Admin.svelte +20 -11
  6. package/dist/client/Admin.svelte.d.ts +11 -0
  7. package/dist/client/Admin.svelte.d.ts.map +1 -0
  8. package/dist/client/components/BackendPicker.svelte.d.ts +4 -0
  9. package/dist/client/components/BackendPicker.svelte.d.ts.map +1 -0
  10. package/dist/client/components/DraftChip.svelte.d.ts +10 -0
  11. package/dist/client/components/DraftChip.svelte.d.ts.map +1 -0
  12. package/dist/client/components/MetadataForm.svelte.d.ts +12 -0
  13. package/dist/client/components/MetadataForm.svelte.d.ts.map +1 -0
  14. package/dist/client/components/ThemeToggle.svelte.d.ts +19 -0
  15. package/dist/client/components/ThemeToggle.svelte.d.ts.map +1 -0
  16. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts +11 -0
  17. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts.map +1 -0
  18. package/{src → dist}/client/components/dialogs/FilenameDialog.svelte +1 -2
  19. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts +13 -0
  20. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts.map +1 -0
  21. package/dist/client/components/editor/EditorPane.svelte.d.ts +4 -0
  22. package/dist/client/components/editor/EditorPane.svelte.d.ts.map +1 -0
  23. package/dist/client/components/editor/EditorTabs.svelte.d.ts +8 -0
  24. package/dist/client/components/editor/EditorTabs.svelte.d.ts.map +1 -0
  25. package/dist/client/components/editor/EditorToolbar.svelte.d.ts +4 -0
  26. package/dist/client/components/editor/EditorToolbar.svelte.d.ts.map +1 -0
  27. package/dist/client/components/editor/FormatSelector.svelte.d.ts +4 -0
  28. package/dist/client/components/editor/FormatSelector.svelte.d.ts.map +1 -0
  29. package/dist/client/components/editor/Toolbar.svelte.d.ts +19 -0
  30. package/dist/client/components/editor/Toolbar.svelte.d.ts.map +1 -0
  31. package/dist/client/components/fields/ArrayField.svelte.d.ts +15 -0
  32. package/dist/client/components/fields/ArrayField.svelte.d.ts.map +1 -0
  33. package/dist/client/components/fields/ArrayItem.svelte.d.ts +28 -0
  34. package/dist/client/components/fields/ArrayItem.svelte.d.ts.map +1 -0
  35. package/dist/client/components/fields/BooleanField.svelte.d.ts +16 -0
  36. package/dist/client/components/fields/BooleanField.svelte.d.ts.map +1 -0
  37. package/dist/client/components/fields/DateField.svelte.d.ts +16 -0
  38. package/dist/client/components/fields/DateField.svelte.d.ts.map +1 -0
  39. package/dist/client/components/fields/EnumField.svelte.d.ts +17 -0
  40. package/dist/client/components/fields/EnumField.svelte.d.ts.map +1 -0
  41. package/dist/client/components/fields/FieldWrapper.svelte.d.ts +18 -0
  42. package/dist/client/components/fields/FieldWrapper.svelte.d.ts.map +1 -0
  43. package/dist/client/components/fields/NumberField.svelte.d.ts +16 -0
  44. package/dist/client/components/fields/NumberField.svelte.d.ts.map +1 -0
  45. package/dist/client/components/fields/ObjectField.svelte.d.ts +16 -0
  46. package/dist/client/components/fields/ObjectField.svelte.d.ts.map +1 -0
  47. package/dist/client/components/fields/SchemaField.svelte.d.ts +16 -0
  48. package/dist/client/components/fields/SchemaField.svelte.d.ts.map +1 -0
  49. package/dist/client/components/fields/StringField.svelte.d.ts +16 -0
  50. package/dist/client/components/fields/StringField.svelte.d.ts.map +1 -0
  51. package/{src → dist}/client/components/sidebar/AdminSidebar.svelte +2 -4
  52. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts +19 -0
  53. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts.map +1 -0
  54. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts +12 -0
  55. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts.map +1 -0
  56. package/dist/client/css/icons.css +29 -0
  57. package/dist/client/index.d.ts +2 -0
  58. package/dist/client/index.d.ts.map +1 -0
  59. package/dist/client/js/drafts/merge.svelte.d.ts +24 -0
  60. package/dist/client/js/drafts/merge.svelte.d.ts.map +1 -0
  61. package/dist/client/js/drafts/merge.svelte.js +106 -0
  62. package/dist/client/js/drafts/ops.svelte.d.ts +31 -0
  63. package/dist/client/js/drafts/ops.svelte.d.ts.map +1 -0
  64. package/dist/client/js/drafts/ops.svelte.js +182 -0
  65. package/dist/client/js/drafts/storage.d.ts +45 -0
  66. package/dist/client/js/drafts/storage.d.ts.map +1 -0
  67. package/dist/client/js/drafts/storage.js +76 -0
  68. package/dist/client/js/drafts/workers/diff.d.ts +2 -0
  69. package/dist/client/js/drafts/workers/diff.d.ts.map +1 -0
  70. package/dist/client/js/drafts/workers/diff.js +20 -0
  71. package/dist/client/js/editor/editor.svelte.d.ts +124 -0
  72. package/dist/client/js/editor/editor.svelte.d.ts.map +1 -0
  73. package/dist/client/js/editor/editor.svelte.js +294 -0
  74. package/dist/client/js/editor/languages.d.ts +11 -0
  75. package/dist/client/js/editor/languages.d.ts.map +1 -0
  76. package/dist/client/js/editor/languages.js +93 -0
  77. package/dist/client/js/editor/link-wrap.d.ts +6 -0
  78. package/dist/client/js/editor/link-wrap.d.ts.map +1 -0
  79. package/{src/client/js/editor/link-wrap.ts → dist/client/js/editor/link-wrap.js} +17 -24
  80. package/dist/client/js/editor/markdown-shortcuts.d.ts +4 -0
  81. package/dist/client/js/editor/markdown-shortcuts.d.ts.map +1 -0
  82. package/dist/client/js/editor/markdown-shortcuts.js +219 -0
  83. package/dist/client/js/handlers/admin.d.ts +64 -0
  84. package/dist/client/js/handlers/admin.d.ts.map +1 -0
  85. package/dist/client/js/handlers/admin.js +186 -0
  86. package/dist/client/js/state/dialogs.svelte.d.ts +16 -0
  87. package/dist/client/js/state/dialogs.svelte.d.ts.map +1 -0
  88. package/dist/client/js/state/dialogs.svelte.js +28 -0
  89. package/dist/client/js/state/router.svelte.d.ts +44 -0
  90. package/dist/client/js/state/router.svelte.d.ts.map +1 -0
  91. package/dist/client/js/state/router.svelte.js +141 -0
  92. package/dist/client/js/state/schema.svelte.d.ts +51 -0
  93. package/dist/client/js/state/schema.svelte.d.ts.map +1 -0
  94. package/{src/client/js/state/schema.svelte.ts → dist/client/js/state/schema.svelte.js} +55 -70
  95. package/dist/client/js/state/state.svelte.d.ts +68 -0
  96. package/dist/client/js/state/state.svelte.d.ts.map +1 -0
  97. package/dist/client/js/state/state.svelte.js +300 -0
  98. package/dist/client/js/state/theme.svelte.d.ts +24 -0
  99. package/dist/client/js/state/theme.svelte.d.ts.map +1 -0
  100. package/{src/client/js/state/theme.svelte.ts → dist/client/js/state/theme.svelte.js} +54 -91
  101. package/dist/client/js/storage/adapter.d.ts +130 -0
  102. package/dist/client/js/storage/adapter.d.ts.map +1 -0
  103. package/dist/client/js/storage/adapter.js +5 -0
  104. package/dist/client/js/storage/client.d.ts +72 -0
  105. package/dist/client/js/storage/client.d.ts.map +1 -0
  106. package/dist/client/js/storage/client.js +121 -0
  107. package/dist/client/js/storage/db.d.ts +8 -0
  108. package/dist/client/js/storage/db.d.ts.map +1 -0
  109. package/dist/client/js/storage/db.js +35 -0
  110. package/dist/client/js/storage/fsa.d.ts +51 -0
  111. package/dist/client/js/storage/fsa.d.ts.map +1 -0
  112. package/dist/client/js/storage/fsa.js +91 -0
  113. package/dist/client/js/storage/github.d.ts +62 -0
  114. package/dist/client/js/storage/github.d.ts.map +1 -0
  115. package/dist/client/js/storage/github.js +216 -0
  116. package/dist/client/js/storage/storage.d.ts +32 -0
  117. package/dist/client/js/storage/storage.d.ts.map +1 -0
  118. package/dist/client/js/storage/storage.js +68 -0
  119. package/dist/client/js/storage/workers/frontmatter.d.ts +2 -0
  120. package/dist/client/js/storage/workers/frontmatter.d.ts.map +1 -0
  121. package/dist/client/js/storage/workers/frontmatter.js +253 -0
  122. package/dist/client/js/storage/workers/storage.d.ts +2 -0
  123. package/dist/client/js/storage/workers/storage.d.ts.map +1 -0
  124. package/dist/client/js/storage/workers/storage.js +167 -0
  125. package/dist/client/js/storage/workers/toml-parser.d.ts +2 -0
  126. package/dist/client/js/storage/workers/toml-parser.d.ts.map +1 -0
  127. package/dist/client/js/storage/workers/toml-parser.js +75 -0
  128. package/dist/client/js/storage/workers/yaml-parser.d.ts +2 -0
  129. package/dist/client/js/storage/workers/yaml-parser.d.ts.map +1 -0
  130. package/dist/client/js/storage/workers/yaml-parser.js +100 -0
  131. package/dist/client/js/utils/file-types.d.ts +58 -0
  132. package/dist/client/js/utils/file-types.d.ts.map +1 -0
  133. package/{src/client/js/utils/file-types.ts → dist/client/js/utils/file-types.js} +75 -107
  134. package/dist/client/js/utils/format.d.ts +8 -0
  135. package/dist/client/js/utils/format.d.ts.map +1 -0
  136. package/{src/client/js/utils/format.ts → dist/client/js/utils/format.js} +5 -6
  137. package/dist/client/js/utils/frontmatter.d.ts +12 -0
  138. package/dist/client/js/utils/frontmatter.d.ts.map +1 -0
  139. package/dist/client/js/utils/frontmatter.js +29 -0
  140. package/dist/client/js/utils/schema-utils.d.ts +110 -0
  141. package/dist/client/js/utils/schema-utils.d.ts.map +1 -0
  142. package/dist/client/js/utils/schema-utils.js +242 -0
  143. package/dist/client/js/utils/slug.d.ts +8 -0
  144. package/dist/client/js/utils/slug.d.ts.map +1 -0
  145. package/{src/client/js/utils/slug.ts → dist/client/js/utils/slug.js} +6 -7
  146. package/dist/client/js/utils/sort.d.ts +41 -0
  147. package/dist/client/js/utils/sort.d.ts.map +1 -0
  148. package/dist/client/js/utils/sort.js +65 -0
  149. package/dist/client/js/utils/stable-stringify.d.ts +8 -0
  150. package/dist/client/js/utils/stable-stringify.d.ts.map +1 -0
  151. package/dist/client/js/utils/stable-stringify.js +23 -0
  152. package/dist/client/js/utils/url-utils.d.ts +11 -0
  153. package/dist/client/js/utils/url-utils.d.ts.map +1 -0
  154. package/{src/client/js/utils/url-utils.ts → dist/client/js/utils/url-utils.js} +22 -23
  155. package/dist/client/types/browser-apis.d.ts +39 -0
  156. package/dist/types.d.ts +22 -0
  157. package/dist/types.d.ts.map +1 -0
  158. package/dist/types.js +1 -0
  159. package/package.json +13 -3
  160. package/.github/workflows/ci.yml +0 -27
  161. package/.github/workflows/publish.yml +0 -34
  162. package/.mcp.json +0 -12
  163. package/.prettierignore +0 -5
  164. package/.prettierrc.cjs +0 -22
  165. package/AGENTS.md +0 -183
  166. package/playground/astro.config.mjs +0 -7
  167. package/playground/node_modules/.bin/astro +0 -21
  168. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +0 -85
  169. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +0 -7
  170. package/playground/node_modules/.vite/deps/_metadata.json +0 -184
  171. package/playground/node_modules/.vite/deps/astro___aria-query.js +0 -6776
  172. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
  173. package/playground/node_modules/.vite/deps/astro___axobject-query.js +0 -3754
  174. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
  175. package/playground/node_modules/.vite/deps/astro___html-escaper.js +0 -34
  176. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
  177. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  178. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +0 -7
  179. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +0 -8
  180. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +0 -7
  181. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +0 -21
  182. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +0 -7
  183. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +0 -223
  184. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +0 -7
  185. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js +0 -27
  186. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js.map +0 -7
  187. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js +0 -1005
  188. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js.map +0 -7
  189. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js +0 -204
  190. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js.map +0 -7
  191. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js +0 -688
  192. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js.map +0 -7
  193. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js +0 -5099
  194. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js.map +0 -7
  195. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js +0 -4376
  196. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js.map +0 -7
  197. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +0 -23
  198. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +0 -7
  199. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +0 -148
  200. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +0 -7
  201. package/playground/node_modules/.vite/deps/package.json +0 -3
  202. package/playground/node_modules/.vite/deps/smol-toml.js +0 -843
  203. package/playground/node_modules/.vite/deps/smol-toml.js.map +0 -7
  204. package/playground/node_modules/.vite/deps/svelte.js +0 -55
  205. package/playground/node_modules/.vite/deps/svelte.js.map +0 -7
  206. package/playground/node_modules/.vite/deps/svelte___clsx.js +0 -9
  207. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +0 -7
  208. package/playground/node_modules/.vite/deps/svelte_animate.js +0 -57
  209. package/playground/node_modules/.vite/deps/svelte_animate.js.map +0 -7
  210. package/playground/node_modules/.vite/deps/svelte_attachments.js +0 -15
  211. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +0 -7
  212. package/playground/node_modules/.vite/deps/svelte_easing.js +0 -67
  213. package/playground/node_modules/.vite/deps/svelte_easing.js.map +0 -7
  214. package/playground/node_modules/.vite/deps/svelte_events.js +0 -11
  215. package/playground/node_modules/.vite/deps/svelte_events.js.map +0 -7
  216. package/playground/node_modules/.vite/deps/svelte_internal.js +0 -5
  217. package/playground/node_modules/.vite/deps/svelte_internal.js.map +0 -7
  218. package/playground/node_modules/.vite/deps/svelte_internal_client.js +0 -402
  219. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +0 -7
  220. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +0 -10
  221. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +0 -7
  222. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +0 -8
  223. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +0 -7
  224. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +0 -8
  225. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +0 -7
  226. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +0 -8
  227. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +0 -7
  228. package/playground/node_modules/.vite/deps/svelte_legacy.js +0 -35
  229. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +0 -7
  230. package/playground/node_modules/.vite/deps/svelte_motion.js +0 -545
  231. package/playground/node_modules/.vite/deps/svelte_motion.js.map +0 -7
  232. package/playground/node_modules/.vite/deps/svelte_reactivity.js +0 -29
  233. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +0 -7
  234. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +0 -127
  235. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +0 -7
  236. package/playground/node_modules/.vite/deps/svelte_store.js +0 -103
  237. package/playground/node_modules/.vite/deps/svelte_store.js.map +0 -7
  238. package/playground/node_modules/.vite/deps/svelte_transition.js +0 -208
  239. package/playground/node_modules/.vite/deps/svelte_transition.js.map +0 -7
  240. package/playground/package.json +0 -16
  241. package/playground/pnpm-lock.yaml +0 -3167
  242. package/playground/src/content/authors/jane-doe.json +0 -8
  243. package/playground/src/content/config/build.toml +0 -2
  244. package/playground/src/content/courses/web-fundamentals.json +0 -29
  245. package/playground/src/content/docs/advanced.mdx +0 -6
  246. package/playground/src/content/docs/intro.md +0 -6
  247. package/playground/src/content/guides/getting-started.mdx +0 -6
  248. package/playground/src/content/posts/hello-world.md +0 -7
  249. package/playground/src/content/products/t-shirt.json +0 -16
  250. package/playground/src/content/recipes/pancakes.mdoc +0 -8
  251. package/playground/src/content/settings/site.yml +0 -2
  252. package/playground/src/content.config.ts +0 -198
  253. package/playground/src/env.d.ts +0 -1
  254. package/playground/src/pages/index.astro +0 -11
  255. package/playground/src/pages/nebula.astro +0 -14
  256. package/pnpm-workspace.yaml +0 -2
  257. package/scripts/subset-icons.mjs +0 -178
  258. package/src/astro/index.ts +0 -295
  259. package/src/client/js/drafts/merge.svelte.ts +0 -121
  260. package/src/client/js/drafts/ops.svelte.ts +0 -227
  261. package/src/client/js/drafts/storage.ts +0 -108
  262. package/src/client/js/drafts/workers/diff.ts +0 -40
  263. package/src/client/js/editor/editor.svelte.ts +0 -343
  264. package/src/client/js/editor/languages.ts +0 -98
  265. package/src/client/js/editor/markdown-shortcuts.ts +0 -261
  266. package/src/client/js/handlers/admin.ts +0 -246
  267. package/src/client/js/state/dialogs.svelte.ts +0 -35
  268. package/src/client/js/state/router.svelte.ts +0 -156
  269. package/src/client/js/state/state.svelte.ts +0 -334
  270. package/src/client/js/storage/adapter.ts +0 -102
  271. package/src/client/js/storage/client.ts +0 -150
  272. package/src/client/js/storage/db.ts +0 -36
  273. package/src/client/js/storage/fsa.ts +0 -110
  274. package/src/client/js/storage/github.ts +0 -297
  275. package/src/client/js/storage/storage.ts +0 -83
  276. package/src/client/js/storage/workers/frontmatter.ts +0 -320
  277. package/src/client/js/storage/workers/storage.ts +0 -177
  278. package/src/client/js/storage/workers/toml-parser.ts +0 -106
  279. package/src/client/js/storage/workers/yaml-parser.ts +0 -132
  280. package/src/client/js/utils/frontmatter.ts +0 -38
  281. package/src/client/js/utils/schema-utils.ts +0 -295
  282. package/src/client/js/utils/sort.ts +0 -84
  283. package/src/client/js/utils/stable-stringify.ts +0 -27
  284. package/src/types.ts +0 -25
  285. package/svelte.config.js +0 -4
  286. package/tests/astro/build.test.ts +0 -63
  287. package/tests/astro/index.test.ts +0 -689
  288. package/tests/client/components/Admin.test.ts +0 -446
  289. package/tests/client/components/BackendPicker.test.ts +0 -239
  290. package/tests/client/components/DraftChip.test.ts +0 -53
  291. package/tests/client/components/MetadataForm.test.ts +0 -164
  292. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +0 -91
  293. package/tests/client/components/dialogs/FilenameDialog.test.ts +0 -209
  294. package/tests/client/components/dialogs/dialog-stubs.ts +0 -19
  295. package/tests/client/components/editor/EditorPane.test.ts +0 -100
  296. package/tests/client/components/editor/EditorTabs.test.ts +0 -253
  297. package/tests/client/components/editor/EditorToolbar.test.ts +0 -252
  298. package/tests/client/components/editor/fixtures.ts +0 -31
  299. package/tests/client/components/fields/ArrayField.test.ts +0 -197
  300. package/tests/client/components/fields/BooleanField.test.ts +0 -206
  301. package/tests/client/components/fields/DateField.test.ts +0 -210
  302. package/tests/client/components/fields/EnumField.test.ts +0 -246
  303. package/tests/client/components/fields/NumberField.test.ts +0 -240
  304. package/tests/client/components/fields/ObjectField.test.ts +0 -157
  305. package/tests/client/components/fields/SchemaField.test.ts +0 -190
  306. package/tests/client/components/fields/StringField.test.ts +0 -223
  307. package/tests/client/components/sidebar/AdminSidebar.test.ts +0 -285
  308. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +0 -135
  309. package/tests/client/components/sidebar/sort-mock.ts +0 -23
  310. package/tests/client/js/drafts/fixtures.ts +0 -22
  311. package/tests/client/js/drafts/merge.test.ts +0 -282
  312. package/tests/client/js/drafts/ops.test.ts +0 -658
  313. package/tests/client/js/drafts/storage.test.ts +0 -200
  314. package/tests/client/js/drafts/workers/diff.test.ts +0 -165
  315. package/tests/client/js/editor/editor.test.ts +0 -616
  316. package/tests/client/js/editor/link-wrap.test.ts +0 -225
  317. package/tests/client/js/editor/markdown-shortcuts.test.ts +0 -370
  318. package/tests/client/js/handlers/admin.test.ts +0 -467
  319. package/tests/client/js/state/router.test.ts +0 -619
  320. package/tests/client/js/state/schema.test.ts +0 -266
  321. package/tests/client/js/state/state.test.ts +0 -328
  322. package/tests/client/js/storage/adapter.test.ts +0 -115
  323. package/tests/client/js/storage/client.test.ts +0 -250
  324. package/tests/client/js/storage/db.test.ts +0 -59
  325. package/tests/client/js/storage/fsa.test.ts +0 -284
  326. package/tests/client/js/storage/github.test.ts +0 -349
  327. package/tests/client/js/storage/mock-port.ts +0 -95
  328. package/tests/client/js/storage/storage.test.ts +0 -77
  329. package/tests/client/js/storage/workers/frontmatter.test.ts +0 -479
  330. package/tests/client/js/storage/workers/storage.test.ts +0 -299
  331. package/tests/client/js/storage/workers/toml-parser.test.ts +0 -169
  332. package/tests/client/js/storage/workers/yaml-parser.test.ts +0 -168
  333. package/tests/client/js/utils/file-types.test.ts +0 -268
  334. package/tests/client/js/utils/frontmatter.test.ts +0 -87
  335. package/tests/client/js/utils/schema-utils.test.ts +0 -318
  336. package/tests/client/js/utils/slug.test.ts +0 -58
  337. package/tests/client/js/utils/sort.test.ts +0 -276
  338. package/tests/client/js/utils/stable-stringify.test.ts +0 -68
  339. package/tests/client/js/utils/url-utils.test.ts +0 -70
  340. package/tests/e2e/backend-connection.test.ts +0 -301
  341. package/tests/e2e/draft-lifecycle.test.ts +0 -388
  342. package/tests/e2e/editing.test.ts +0 -355
  343. package/tests/e2e/github-adapter.test.ts +0 -330
  344. package/tests/e2e/helpers/mock-adapter.ts +0 -166
  345. package/tests/e2e/helpers/test-app.ts +0 -155
  346. package/tests/e2e/navigation.test.ts +0 -358
  347. package/tests/e2e/publishing.test.ts +0 -345
  348. package/tests/e2e/unsaved-changes.test.ts +0 -317
  349. package/tests/setup.ts +0 -2
  350. package/tests/stubs/codemirror.ts +0 -197
  351. package/tsconfig.json +0 -19
  352. package/vitest.config.ts +0 -178
  353. /package/{src → dist}/client/components/BackendPicker.svelte +0 -0
  354. /package/{src → dist}/client/components/DraftChip.svelte +0 -0
  355. /package/{src → dist}/client/components/MetadataForm.svelte +0 -0
  356. /package/{src → dist}/client/components/ThemeToggle.svelte +0 -0
  357. /package/{src → dist}/client/components/dialogs/DeleteDraftDialog.svelte +0 -0
  358. /package/{src → dist}/client/components/editor/EditorPane.svelte +0 -0
  359. /package/{src → dist}/client/components/editor/EditorTabs.svelte +0 -0
  360. /package/{src → dist}/client/components/editor/EditorToolbar.svelte +0 -0
  361. /package/{src → dist}/client/components/editor/FormatSelector.svelte +0 -0
  362. /package/{src → dist}/client/components/editor/Toolbar.svelte +0 -0
  363. /package/{src → dist}/client/components/fields/ArrayField.svelte +0 -0
  364. /package/{src → dist}/client/components/fields/ArrayItem.svelte +0 -0
  365. /package/{src → dist}/client/components/fields/BooleanField.svelte +0 -0
  366. /package/{src → dist}/client/components/fields/DateField.svelte +0 -0
  367. /package/{src → dist}/client/components/fields/EnumField.svelte +0 -0
  368. /package/{src → dist}/client/components/fields/FieldWrapper.svelte +0 -0
  369. /package/{src → dist}/client/components/fields/NumberField.svelte +0 -0
  370. /package/{src → dist}/client/components/fields/ObjectField.svelte +0 -0
  371. /package/{src → dist}/client/components/fields/SchemaField.svelte +0 -0
  372. /package/{src → dist}/client/components/fields/StringField.svelte +0 -0
  373. /package/{src → dist}/client/components/sidebar/AdminSidebarSort.svelte +0 -0
  374. /package/{src → dist}/client/css/a11y.css +0 -0
  375. /package/{src → dist}/client/css/btn.css +0 -0
  376. /package/{src → dist}/client/css/dialog.css +0 -0
  377. /package/{src → dist}/client/css/field-input.css +0 -0
  378. /package/{src → dist}/client/css/reset.css +0 -0
  379. /package/{src → dist}/client/css/theme.css +0 -0
  380. /package/{src/client/index.ts → dist/client/index.js} +0 -0
  381. /package/{src → dist}/virtual.d.ts +0 -0
@@ -0,0 +1,76 @@
1
+ /*
2
+ * Draft persistence layer backed by IndexedDB.
3
+ * CRUD operations for draft content entries, used by the editor and merge logic.
4
+ */
5
+ import { openDB } from '../storage/db';
6
+ // Object store name for drafts
7
+ const STORE = 'drafts';
8
+ /**
9
+ * Saves or updates a draft in IndexedDB.
10
+ * @param {Draft} draft - The draft record to persist
11
+ * @return {Promise<void>}
12
+ */
13
+ export async function saveDraft(draft) {
14
+ const db = await openDB();
15
+ return new Promise((resolve, reject) => {
16
+ const tx = db.transaction(STORE, 'readwrite');
17
+ tx.objectStore(STORE).put(draft);
18
+ tx.oncomplete = () => resolve();
19
+ tx.onerror = () => reject(tx.error);
20
+ });
21
+ }
22
+ /**
23
+ * Loads all drafts for a given collection.
24
+ * @param {string} collection - The collection name to filter by
25
+ * @return {Promise<Draft[]>} All drafts belonging to the collection
26
+ */
27
+ export async function loadDrafts(collection) {
28
+ const db = await openDB();
29
+ return new Promise((resolve, reject) => {
30
+ const tx = db.transaction(STORE, 'readonly');
31
+ const request = tx.objectStore(STORE).getAll();
32
+ request.onsuccess = () => {
33
+ const all = request.result;
34
+ resolve(all.filter((d) => d.collection === collection));
35
+ };
36
+ request.onerror = () => reject(request.error);
37
+ });
38
+ }
39
+ /**
40
+ * Loads a single draft by ID.
41
+ * @param {string} id - The draft UUID
42
+ * @return {Promise<Draft | null>} The draft, or null if not found
43
+ */
44
+ export async function loadDraft(id) {
45
+ const db = await openDB();
46
+ return new Promise((resolve, reject) => {
47
+ const tx = db.transaction(STORE, 'readonly');
48
+ const request = tx.objectStore(STORE).get(id);
49
+ request.onsuccess = () => resolve(request.result ?? null);
50
+ request.onerror = () => reject(request.error);
51
+ });
52
+ }
53
+ /**
54
+ * Deletes a draft from IndexedDB by ID.
55
+ * @param {string} id - The draft UUID to delete
56
+ * @return {Promise<void>}
57
+ */
58
+ export async function deleteDraft(id) {
59
+ const db = await openDB();
60
+ return new Promise((resolve, reject) => {
61
+ const tx = db.transaction(STORE, 'readwrite');
62
+ tx.objectStore(STORE).delete(id);
63
+ tx.oncomplete = () => resolve();
64
+ tx.onerror = () => reject(tx.error);
65
+ });
66
+ }
67
+ /**
68
+ * Finds a draft linked to a specific live file.
69
+ * @param {string} collection - The collection name
70
+ * @param {string} filename - The live file's filename
71
+ * @return {Promise<Draft | null>} The matching draft, or null if none exists
72
+ */
73
+ export async function getDraftByFile(collection, filename) {
74
+ const drafts = await loadDrafts(collection);
75
+ return drafts.find((d) => !d.isNew && d.filename === filename) ?? null;
76
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../../../src/client/js/drafts/workers/diff.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import { stableStringify } from '../../utils/stable-stringify';
2
+ /*
3
+ * Listens for diff requests, compares each draft's snapshot against current
4
+ * live content, and returns a map of draftId to isOutdated.
5
+ */
6
+ self.addEventListener('message', (event) => {
7
+ const { type, entries } = event.data;
8
+ if (type !== 'diff')
9
+ return;
10
+ const results = {};
11
+ for (const entry of entries) {
12
+ // Reconstruct the comparable string using the same format as snapshot creation
13
+ const liveString = stableStringify({
14
+ formData: entry.liveFormData,
15
+ body: entry.liveBody,
16
+ });
17
+ results[entry.draftId] = liveString !== entry.snapshot;
18
+ }
19
+ self.postMessage({ type: 'diff-result', results });
20
+ });
@@ -0,0 +1,124 @@
1
+ import { type PathSegment } from '../utils/schema-utils';
2
+ export type EditorFile = {
3
+ body: string;
4
+ formData: Record<string, unknown>;
5
+ dirty: boolean;
6
+ saving: boolean;
7
+ filename: string;
8
+ bodyLoaded: boolean;
9
+ draftId: string | null;
10
+ isNewDraft: boolean;
11
+ };
12
+ export type EditorStateConfig = {
13
+ body: string;
14
+ formData: Record<string, unknown>;
15
+ filename: string;
16
+ bodyLoaded: boolean;
17
+ draftId: string | null;
18
+ isNewDraft: boolean;
19
+ snapshot: string | null;
20
+ collection: string;
21
+ draftCreatedAt: string | null;
22
+ };
23
+ export declare const editor: {
24
+ readonly data: Record<string, unknown>;
25
+ readonly tab: string;
26
+ readonly originalFilename: string;
27
+ };
28
+ /**
29
+ * Applies a full set of editor state values, resetting dirty/saving flags and updating save baselines.
30
+ * @param {EditorStateConfig} c - All editor state fields to apply
31
+ * @param {boolean} open - Whether to mark the file as open
32
+ * @return {void}
33
+ */
34
+ export declare function applyEditorState(c: EditorStateConfig, open: boolean): void;
35
+ /**
36
+ * Returns a snapshot of draft-related internal state for use by editor-draft-ops. Exposes private module state without leaking $state reactivity.
37
+ * @return {object} Snapshot of all draft-related editor state
38
+ */
39
+ export declare function _getDraftState(): {
40
+ saving: boolean;
41
+ draftId: string | null;
42
+ isNewDraft: boolean;
43
+ snapshot: string | null;
44
+ currentCollection: string;
45
+ draftCreatedAt: string | null;
46
+ lastSavedFormData: string;
47
+ lastSavedBody: string;
48
+ formData: Record<string, unknown>;
49
+ body: string;
50
+ filename: string;
51
+ originalFilename: string;
52
+ dirty: boolean;
53
+ };
54
+ /**
55
+ * Applies draft-related state mutations from editor-draft-ops back into the reactive module state.
56
+ * @param {Partial<ReturnType<typeof _getDraftState>>} updates - Fields to update
57
+ * @return {void}
58
+ */
59
+ export declare function _setDraftState(updates: Partial<ReturnType<typeof _getDraftState>>): void;
60
+ /**
61
+ * Returns the current editor file state, or null if no file is open.
62
+ * @return {EditorFile | null} The current editor file state, or null
63
+ */
64
+ export declare function getEditorFile(): EditorFile | null;
65
+ /**
66
+ * Sets the active editor tab.
67
+ * @param {string} tab - The tab identifier to activate
68
+ * @return {void}
69
+ */
70
+ export declare function setActiveTab(tab: string): void;
71
+ /**
72
+ * Updates a single field within formData by path and recomputes dirty state.
73
+ * @param {PathSegment[]} path - Ordered path segments addressing the field
74
+ * @param {unknown} value - The new value to assign at the given path
75
+ * @return {void}
76
+ */
77
+ export declare function updateFormField(path: PathSegment[], value: unknown): void;
78
+ /**
79
+ * Populates the editor with metadata so the UI renders without waiting for the async file read. Checks IndexedDB for an existing draft first — if found, loads draft data instead.
80
+ * @param {string} collection - The collection this file belongs to
81
+ * @param {string} itemFilename - The content file's name
82
+ * @param {Record<string, unknown>} data - Pre-parsed frontmatter data
83
+ * @return {Promise<void>}
84
+ */
85
+ export declare function preloadFile(collection: string, itemFilename: string, data: Record<string, unknown>): Promise<void>;
86
+ /**
87
+ * Sets the filename for the current editor file. Used by the filename dialog.
88
+ * @param {string} newFilename - The new filename to set
89
+ * @return {void}
90
+ */
91
+ export declare function setFilename(newFilename: string): void;
92
+ /**
93
+ * Changes the file format by swapping the filename extension to the new type's default. Preserves the slug (base filename without extension) and leaves originalFilename untouched so publishFile can detect the rename and delete the old file on disk.
94
+ * @param {string} newType - The type identifier to switch to (e.g. 'md', 'json')
95
+ * @return {void}
96
+ */
97
+ export declare function changeFileFormat(newType: string): void;
98
+ /**
99
+ * Sets a default file format for a new draft based on the collection's supported file types. Only applies when the editor has no filename yet (new draft). Sets the activeTab to 'body' if the format supports body editing.
100
+ * @param {string[]} fileTypes - Type identifiers from the schema's files array
101
+ * @return {void}
102
+ */
103
+ export declare function setDefaultFormat(fileTypes: string[]): void;
104
+ /**
105
+ * Loads body content via StorageClient for an already-preloaded file, completing the two-phase load.
106
+ * @param {string} collection - The collection the file belongs to
107
+ * @param {string} filename - The filename to read within the collection
108
+ * @return {Promise<void>}
109
+ */
110
+ export declare function loadFileBody(collection: string, filename: string): Promise<void>;
111
+ /**
112
+ * Updates the editor body content and recomputes dirty state.
113
+ * Only compares body against its saved snapshot — avoids serializing
114
+ * formData to JSON on every keystroke from CodeMirror's update listener.
115
+ * @param {string} content - The new body content
116
+ * @return {void}
117
+ */
118
+ export declare function updateBody(content: string): void;
119
+ /**
120
+ * Resets all editor state including draft-specific fields.
121
+ * @return {void}
122
+ */
123
+ export declare function clearEditor(): void;
124
+ //# sourceMappingURL=editor.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.svelte.d.ts","sourceRoot":"","sources":["../../../../src/client/js/editor/editor.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAWpE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAGF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAqBF,eAAO,MAAM,MAAM;mBAEL,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;kBAIxB,MAAM;+BAIO,MAAM;CAG/B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAkB1E;AAeD;;;GAGG;AACH,wBAAgB,cAAc;;;;;;;;;;;;;;EAgB7B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,GAClD,IAAI,CAYN;AACD;;;GAGG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAYjD;AACD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AACD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAIzE;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAwCf;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAErD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAQtD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAS1D;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAelC"}
@@ -0,0 +1,294 @@
1
+ /*
2
+ * Reactive editor state for the file editing view.
3
+ * Manages loading, saving, and dirty-checking of content files and drafts.
4
+ */
5
+ import { registerDirtyChecker } from '../state/router.svelte';
6
+ import { splitFrontmatter } from '../utils/frontmatter';
7
+ import { setByPath } from '../utils/schema-utils';
8
+ import { getDraftByFile } from '../drafts/storage';
9
+ import { storageClient } from '../state/state.svelte';
10
+ import { getFileCategory, stripExtension, getDefaultExtension, FILE_TYPES, } from '../utils/file-types';
11
+ let body = $state('');
12
+ let formData = $state({});
13
+ let dirty = $state(false);
14
+ let saving = $state(false);
15
+ let lastSavedBody = '';
16
+ let lastSavedFormData = '{}';
17
+ let filename = $state('');
18
+ let fileOpen = $state(false);
19
+ let activeTab = $state('metadata');
20
+ let bodyLoaded = $state(false);
21
+ let originalFilename = $state(''); // filename at load time — publish uses this to detect renames
22
+ // Draft-specific state
23
+ let draftId = $state(null);
24
+ let isNewDraft = $state(false);
25
+ let snapshot = $state(null);
26
+ let currentCollection = $state('');
27
+ let draftCreatedAt = $state(null);
28
+ registerDirtyChecker(() => dirty);
29
+ export const editor = {
30
+ // Current structured form data for the open file.
31
+ get data() {
32
+ return formData;
33
+ },
34
+ // Currently active editor tab identifier.
35
+ get tab() {
36
+ return activeTab;
37
+ },
38
+ // Filename at load time — publish uses this to detect renames.
39
+ get originalFilename() {
40
+ return originalFilename;
41
+ },
42
+ };
43
+ /**
44
+ * Applies a full set of editor state values, resetting dirty/saving flags and updating save baselines.
45
+ * @param {EditorStateConfig} c - All editor state fields to apply
46
+ * @param {boolean} open - Whether to mark the file as open
47
+ * @return {void}
48
+ */
49
+ export function applyEditorState(c, open) {
50
+ formData = c.formData;
51
+ lastSavedFormData = JSON.stringify(c.formData);
52
+ body = c.body;
53
+ lastSavedBody = c.body;
54
+ dirty = false;
55
+ formDataDirty = false;
56
+ saving = false;
57
+ filename = c.filename;
58
+ originalFilename = c.filename;
59
+ bodyLoaded = c.bodyLoaded;
60
+ activeTab = 'metadata';
61
+ fileOpen = open;
62
+ draftId = c.draftId;
63
+ isNewDraft = c.isNewDraft;
64
+ snapshot = c.snapshot;
65
+ currentCollection = c.collection;
66
+ draftCreatedAt = c.draftCreatedAt;
67
+ }
68
+ /*
69
+ * Tracks whether formData has diverged from its saved snapshot.
70
+ * Updated only by updateFormField to avoid re-serializing on every body keystroke.
71
+ */
72
+ let formDataDirty = false;
73
+ /**
74
+ * Recomputes dirty state from body comparison and the cached formData flag.
75
+ * @return {void}
76
+ */
77
+ function recomputeDirty() {
78
+ dirty = body !== lastSavedBody || formDataDirty;
79
+ }
80
+ /**
81
+ * Returns a snapshot of draft-related internal state for use by editor-draft-ops. Exposes private module state without leaking $state reactivity.
82
+ * @return {object} Snapshot of all draft-related editor state
83
+ */
84
+ export function _getDraftState() {
85
+ return {
86
+ saving,
87
+ draftId,
88
+ isNewDraft,
89
+ snapshot,
90
+ currentCollection,
91
+ draftCreatedAt,
92
+ lastSavedFormData,
93
+ lastSavedBody,
94
+ formData,
95
+ body,
96
+ filename,
97
+ originalFilename,
98
+ dirty,
99
+ };
100
+ }
101
+ /**
102
+ * Applies draft-related state mutations from editor-draft-ops back into the reactive module state.
103
+ * @param {Partial<ReturnType<typeof _getDraftState>>} updates - Fields to update
104
+ * @return {void}
105
+ */
106
+ export function _setDraftState(updates) {
107
+ if ('saving' in updates)
108
+ saving = updates.saving;
109
+ if ('draftId' in updates)
110
+ draftId = updates.draftId;
111
+ if ('isNewDraft' in updates)
112
+ isNewDraft = updates.isNewDraft;
113
+ if ('snapshot' in updates)
114
+ snapshot = updates.snapshot;
115
+ if ('draftCreatedAt' in updates)
116
+ draftCreatedAt = updates.draftCreatedAt;
117
+ if ('lastSavedFormData' in updates)
118
+ lastSavedFormData = updates.lastSavedFormData;
119
+ if ('lastSavedBody' in updates)
120
+ lastSavedBody = updates.lastSavedBody;
121
+ if ('dirty' in updates)
122
+ dirty = updates.dirty;
123
+ if ('originalFilename' in updates)
124
+ originalFilename = updates.originalFilename;
125
+ }
126
+ /**
127
+ * Returns the current editor file state, or null if no file is open.
128
+ * @return {EditorFile | null} The current editor file state, or null
129
+ */
130
+ export function getEditorFile() {
131
+ if (!fileOpen)
132
+ return null;
133
+ return {
134
+ body,
135
+ formData,
136
+ dirty,
137
+ saving,
138
+ filename,
139
+ bodyLoaded,
140
+ draftId,
141
+ isNewDraft,
142
+ };
143
+ }
144
+ /**
145
+ * Sets the active editor tab.
146
+ * @param {string} tab - The tab identifier to activate
147
+ * @return {void}
148
+ */
149
+ export function setActiveTab(tab) {
150
+ activeTab = tab;
151
+ }
152
+ /**
153
+ * Updates a single field within formData by path and recomputes dirty state.
154
+ * @param {PathSegment[]} path - Ordered path segments addressing the field
155
+ * @param {unknown} value - The new value to assign at the given path
156
+ * @return {void}
157
+ */
158
+ export function updateFormField(path, value) {
159
+ setByPath(formData, path, value);
160
+ formDataDirty = JSON.stringify(formData) !== lastSavedFormData;
161
+ recomputeDirty();
162
+ }
163
+ /**
164
+ * Populates the editor with metadata so the UI renders without waiting for the async file read. Checks IndexedDB for an existing draft first — if found, loads draft data instead.
165
+ * @param {string} collection - The collection this file belongs to
166
+ * @param {string} itemFilename - The content file's name
167
+ * @param {Record<string, unknown>} data - Pre-parsed frontmatter data
168
+ * @return {Promise<void>}
169
+ */
170
+ export async function preloadFile(collection, itemFilename, data) {
171
+ // Compare slugs so a format change (e.g. .md → .mdx) doesn't trigger a full reload
172
+ if (stripExtension(filename) === stripExtension(itemFilename) && fileOpen)
173
+ return;
174
+ // Check IndexedDB for an existing draft of this live file
175
+ const d = await getDraftByFile(collection, itemFilename);
176
+ if (d) {
177
+ // Draft already contains body content, no disk read needed
178
+ applyEditorState({
179
+ body: d.body,
180
+ formData: d.formData,
181
+ filename: itemFilename,
182
+ bodyLoaded: true,
183
+ draftId: d.id,
184
+ isNewDraft: d.isNew,
185
+ snapshot: d.snapshot,
186
+ collection,
187
+ draftCreatedAt: d.createdAt,
188
+ }, true);
189
+ return;
190
+ }
191
+ // No draft — load live data; $state.snapshot strips Svelte reactive proxies
192
+ applyEditorState({
193
+ body: '',
194
+ formData: $state.snapshot(data),
195
+ filename: itemFilename,
196
+ bodyLoaded: false,
197
+ draftId: null,
198
+ isNewDraft: false,
199
+ snapshot: null,
200
+ collection,
201
+ draftCreatedAt: null,
202
+ }, true);
203
+ }
204
+ /**
205
+ * Sets the filename for the current editor file. Used by the filename dialog.
206
+ * @param {string} newFilename - The new filename to set
207
+ * @return {void}
208
+ */
209
+ export function setFilename(newFilename) {
210
+ filename = newFilename;
211
+ }
212
+ /**
213
+ * Changes the file format by swapping the filename extension to the new type's default. Preserves the slug (base filename without extension) and leaves originalFilename untouched so publishFile can detect the rename and delete the old file on disk.
214
+ * @param {string} newType - The type identifier to switch to (e.g. 'md', 'json')
215
+ * @return {void}
216
+ */
217
+ export function changeFileFormat(newType) {
218
+ const ext = getDefaultExtension(newType);
219
+ if (!ext)
220
+ return;
221
+ const slug = filename ? stripExtension(filename) : '';
222
+ filename = slug ? slug + ext : '';
223
+ // Switch to metadata tab if the new format has no body editor
224
+ const config = FILE_TYPES[newType];
225
+ if (config && !config.hasBody && activeTab === 'body')
226
+ activeTab = 'metadata';
227
+ }
228
+ /**
229
+ * Sets a default file format for a new draft based on the collection's supported file types. Only applies when the editor has no filename yet (new draft). Sets the activeTab to 'body' if the format supports body editing.
230
+ * @param {string[]} fileTypes - Type identifiers from the schema's files array
231
+ * @return {void}
232
+ */
233
+ export function setDefaultFormat(fileTypes) {
234
+ if (filename || fileTypes.length === 0)
235
+ return;
236
+ const defaultType = fileTypes[0];
237
+ const ext = getDefaultExtension(defaultType);
238
+ if (!ext)
239
+ return;
240
+ // Set just the extension so EditorTabs and FormatSelector derive the correct type
241
+ filename = ext;
242
+ // Activate body tab for content types that support it
243
+ if (FILE_TYPES[defaultType]?.hasBody)
244
+ activeTab = 'body';
245
+ }
246
+ /**
247
+ * Loads body content via StorageClient for an already-preloaded file, completing the two-phase load.
248
+ * @param {string} collection - The collection the file belongs to
249
+ * @param {string} filename - The filename to read within the collection
250
+ * @return {Promise<void>}
251
+ */
252
+ export async function loadFileBody(collection, filename) {
253
+ const category = getFileCategory(filename);
254
+ if (category === 'data') {
255
+ // Data files have no body — all content was parsed as formData during preload
256
+ bodyLoaded = true;
257
+ return;
258
+ }
259
+ if (!storageClient)
260
+ return;
261
+ const text = await storageClient.readFile(collection, filename);
262
+ const split = splitFrontmatter(text);
263
+ // Strip leading/trailing newlines from body; added back on save when reconstituting the file
264
+ body = lastSavedBody = split.body.replace(/^\n+/, '').replace(/\n+$/, '');
265
+ bodyLoaded = true;
266
+ }
267
+ /**
268
+ * Updates the editor body content and recomputes dirty state.
269
+ * Only compares body against its saved snapshot — avoids serializing
270
+ * formData to JSON on every keystroke from CodeMirror's update listener.
271
+ * @param {string} content - The new body content
272
+ * @return {void}
273
+ */
274
+ export function updateBody(content) {
275
+ body = content;
276
+ recomputeDirty();
277
+ }
278
+ /**
279
+ * Resets all editor state including draft-specific fields.
280
+ * @return {void}
281
+ */
282
+ export function clearEditor() {
283
+ applyEditorState({
284
+ body: '',
285
+ formData: {},
286
+ filename: '',
287
+ bodyLoaded: false,
288
+ draftId: null,
289
+ isNewDraft: false,
290
+ snapshot: null,
291
+ collection: '',
292
+ draftCreatedAt: null,
293
+ }, false);
294
+ }
@@ -0,0 +1,11 @@
1
+ import type { Extension } from '@codemirror/state';
2
+ /**
3
+ * Returns the composed language extension (parser + syntax highlighting)
4
+ * for a given file type. Results are cached per type so repeated calls
5
+ * return the same reference. Today all body formats use the same markdown
6
+ * extension — individual entries can diverge when custom parsers are added.
7
+ * @param {string} fileType - Type identifier from the file type registry (e.g. 'md', 'mdx', 'markdoc')
8
+ * @return {Extension} The composed CodeMirror language extension
9
+ */
10
+ export declare function getLanguageExtension(fileType: string): Extension;
11
+ //# sourceMappingURL=languages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"languages.d.ts","sourceRoot":"","sources":["../../../../src/client/js/editor/languages.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AA4DnD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAiBhE"}
@@ -0,0 +1,93 @@
1
+ /*
2
+ * Language extension registry for the CodeMirror editor.
3
+ * Provides lazy-loaded language extensions keyed by file type identifier.
4
+ * Today all body formats use the same markdown parser — the registry
5
+ * exists so MDX/Markdoc-specific parsers can drop in later without
6
+ * changing EditorPane.
7
+ */
8
+ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
9
+ import { languages } from '@codemirror/language-data';
10
+ import { syntaxHighlighting, HighlightStyle } from '@codemirror/language';
11
+ import { tags as t } from '@lezer/highlight';
12
+ // Highlight style for the editor — headings are sized, syntax markers are dimmed.
13
+ const editorHighlight = HighlightStyle.define([
14
+ // Headings — larger, bold
15
+ {
16
+ tag: t.heading1,
17
+ fontSize: '1.5rem',
18
+ fontWeight: 'bold',
19
+ color: 'var(--cms-fg)',
20
+ },
21
+ {
22
+ tag: t.heading2,
23
+ fontSize: '1.25rem',
24
+ fontWeight: 'bold',
25
+ color: 'var(--cms-fg)',
26
+ },
27
+ {
28
+ tag: t.heading3,
29
+ fontSize: '1rem',
30
+ fontWeight: 'bold',
31
+ color: 'var(--cms-fg)',
32
+ },
33
+ // Emphasis
34
+ { tag: t.strong, fontWeight: 'bold', color: 'var(--cms-fg)' },
35
+ { tag: t.emphasis, fontStyle: 'italic', color: 'var(--cms-fg)' },
36
+ // Inline code
37
+ { tag: t.monospace, color: 'var(--light-orange)' },
38
+ // Links
39
+ { tag: t.link, color: 'var(--light-teal)', textDecoration: 'underline' },
40
+ { tag: t.url, color: 'var(--light-green)' },
41
+ // Syntax markers — dimmed
42
+ { tag: t.processingInstruction, color: 'var(--cms-muted)' },
43
+ { tag: t.labelName, color: 'var(--light-teal)' },
44
+ // Code block language tag
45
+ { tag: t.tagName, color: 'var(--light-purple)' },
46
+ // Lists
47
+ { tag: t.list, color: 'var(--light-teal)' },
48
+ // Blockquotes
49
+ { tag: t.quote, color: 'var(--cms-muted)', fontStyle: 'italic' },
50
+ // Code block contents — language-specific highlighting
51
+ { tag: t.keyword, color: 'var(--light-plum)' },
52
+ { tag: t.string, color: 'var(--light-orange)' },
53
+ { tag: t.variableName, color: 'var(--light-teal)' },
54
+ { tag: t.function(t.variableName), color: 'var(--gold)' },
55
+ { tag: t.typeName, color: 'var(--light-green)' },
56
+ { tag: t.number, color: 'var(--light-purple)' },
57
+ { tag: t.bool, color: 'var(--light-purple)' },
58
+ { tag: t.comment, color: 'var(--cms-muted)', fontStyle: 'italic' },
59
+ { tag: t.operator, color: 'var(--light-red)' },
60
+ { tag: t.punctuation, color: 'var(--cms-muted)' },
61
+ { tag: t.meta, color: 'var(--cms-muted)' },
62
+ ]);
63
+ /*
64
+ * Cached extensions per file type — avoids re-creating parser instances and
65
+ * lets CodeMirror short-circuit its extension diff via reference equality.
66
+ */
67
+ const cache = new Map();
68
+ /**
69
+ * Returns the composed language extension (parser + syntax highlighting)
70
+ * for a given file type. Results are cached per type so repeated calls
71
+ * return the same reference. Today all body formats use the same markdown
72
+ * extension — individual entries can diverge when custom parsers are added.
73
+ * @param {string} fileType - Type identifier from the file type registry (e.g. 'md', 'mdx', 'markdoc')
74
+ * @return {Extension} The composed CodeMirror language extension
75
+ */
76
+ export function getLanguageExtension(fileType) {
77
+ let ext = cache.get(fileType);
78
+ if (ext)
79
+ return ext;
80
+ /*
81
+ * All body formats currently use the same markdown parser.
82
+ * When MDX/Markdoc-specific parsers are built, add branches here.
83
+ */
84
+ ext = [
85
+ markdown({
86
+ base: markdownLanguage,
87
+ codeLanguages: languages,
88
+ }),
89
+ syntaxHighlighting(editorHighlight),
90
+ ];
91
+ cache.set(fileType, ext);
92
+ return ext;
93
+ }
@@ -0,0 +1,6 @@
1
+ import { ViewPlugin, type DecorationSet } from '@codemirror/view';
2
+ export declare const linkWrapPlugin: ViewPlugin<{
3
+ decorations: DecorationSet;
4
+ update(update: import("@codemirror/view").ViewUpdate): void;
5
+ }, undefined>;
6
+ //# sourceMappingURL=link-wrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-wrap.d.ts","sourceRoot":"","sources":["../../../../src/client/js/editor/link-wrap.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAc,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA6B9E,eAAO,MAAM,cAAc;;;aAU1B,CAAC"}