nebula-cms 0.1.3 → 0.1.4

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 (380) hide show
  1. package/README.md +2 -0
  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/dist/client/Admin.svelte.d.ts +11 -0
  6. package/dist/client/Admin.svelte.d.ts.map +1 -0
  7. package/dist/client/components/BackendPicker.svelte.d.ts +4 -0
  8. package/dist/client/components/BackendPicker.svelte.d.ts.map +1 -0
  9. package/dist/client/components/DraftChip.svelte.d.ts +10 -0
  10. package/dist/client/components/DraftChip.svelte.d.ts.map +1 -0
  11. package/dist/client/components/MetadataForm.svelte.d.ts +12 -0
  12. package/dist/client/components/MetadataForm.svelte.d.ts.map +1 -0
  13. package/dist/client/components/ThemeToggle.svelte.d.ts +19 -0
  14. package/dist/client/components/ThemeToggle.svelte.d.ts.map +1 -0
  15. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts +11 -0
  16. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts.map +1 -0
  17. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts +13 -0
  18. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts.map +1 -0
  19. package/dist/client/components/editor/EditorPane.svelte.d.ts +4 -0
  20. package/dist/client/components/editor/EditorPane.svelte.d.ts.map +1 -0
  21. package/dist/client/components/editor/EditorTabs.svelte.d.ts +8 -0
  22. package/dist/client/components/editor/EditorTabs.svelte.d.ts.map +1 -0
  23. package/dist/client/components/editor/EditorToolbar.svelte.d.ts +4 -0
  24. package/dist/client/components/editor/EditorToolbar.svelte.d.ts.map +1 -0
  25. package/dist/client/components/editor/FormatSelector.svelte.d.ts +4 -0
  26. package/dist/client/components/editor/FormatSelector.svelte.d.ts.map +1 -0
  27. package/dist/client/components/editor/Toolbar.svelte.d.ts +19 -0
  28. package/dist/client/components/editor/Toolbar.svelte.d.ts.map +1 -0
  29. package/dist/client/components/fields/ArrayField.svelte.d.ts +15 -0
  30. package/dist/client/components/fields/ArrayField.svelte.d.ts.map +1 -0
  31. package/dist/client/components/fields/ArrayItem.svelte.d.ts +28 -0
  32. package/dist/client/components/fields/ArrayItem.svelte.d.ts.map +1 -0
  33. package/dist/client/components/fields/BooleanField.svelte.d.ts +16 -0
  34. package/dist/client/components/fields/BooleanField.svelte.d.ts.map +1 -0
  35. package/dist/client/components/fields/DateField.svelte.d.ts +16 -0
  36. package/dist/client/components/fields/DateField.svelte.d.ts.map +1 -0
  37. package/dist/client/components/fields/EnumField.svelte.d.ts +17 -0
  38. package/dist/client/components/fields/EnumField.svelte.d.ts.map +1 -0
  39. package/dist/client/components/fields/FieldWrapper.svelte.d.ts +18 -0
  40. package/dist/client/components/fields/FieldWrapper.svelte.d.ts.map +1 -0
  41. package/dist/client/components/fields/NumberField.svelte.d.ts +16 -0
  42. package/dist/client/components/fields/NumberField.svelte.d.ts.map +1 -0
  43. package/dist/client/components/fields/ObjectField.svelte.d.ts +16 -0
  44. package/dist/client/components/fields/ObjectField.svelte.d.ts.map +1 -0
  45. package/dist/client/components/fields/SchemaField.svelte.d.ts +16 -0
  46. package/dist/client/components/fields/SchemaField.svelte.d.ts.map +1 -0
  47. package/dist/client/components/fields/StringField.svelte.d.ts +16 -0
  48. package/dist/client/components/fields/StringField.svelte.d.ts.map +1 -0
  49. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts +19 -0
  50. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts.map +1 -0
  51. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts +12 -0
  52. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts.map +1 -0
  53. package/dist/client/css/icons.css +29 -0
  54. package/dist/client/index.d.ts +2 -0
  55. package/dist/client/index.d.ts.map +1 -0
  56. package/dist/client/js/drafts/merge.svelte.d.ts +24 -0
  57. package/dist/client/js/drafts/merge.svelte.d.ts.map +1 -0
  58. package/dist/client/js/drafts/merge.svelte.js +106 -0
  59. package/dist/client/js/drafts/ops.svelte.d.ts +31 -0
  60. package/dist/client/js/drafts/ops.svelte.d.ts.map +1 -0
  61. package/dist/client/js/drafts/ops.svelte.js +182 -0
  62. package/dist/client/js/drafts/storage.d.ts +45 -0
  63. package/dist/client/js/drafts/storage.d.ts.map +1 -0
  64. package/dist/client/js/drafts/storage.js +76 -0
  65. package/dist/client/js/drafts/workers/diff.d.ts +2 -0
  66. package/dist/client/js/drafts/workers/diff.d.ts.map +1 -0
  67. package/dist/client/js/drafts/workers/diff.js +20 -0
  68. package/dist/client/js/editor/editor.svelte.d.ts +124 -0
  69. package/dist/client/js/editor/editor.svelte.d.ts.map +1 -0
  70. package/dist/client/js/editor/editor.svelte.js +294 -0
  71. package/dist/client/js/editor/languages.d.ts +11 -0
  72. package/dist/client/js/editor/languages.d.ts.map +1 -0
  73. package/dist/client/js/editor/languages.js +93 -0
  74. package/dist/client/js/editor/link-wrap.d.ts +6 -0
  75. package/dist/client/js/editor/link-wrap.d.ts.map +1 -0
  76. package/{src/client/js/editor/link-wrap.ts → dist/client/js/editor/link-wrap.js} +17 -24
  77. package/dist/client/js/editor/markdown-shortcuts.d.ts +4 -0
  78. package/dist/client/js/editor/markdown-shortcuts.d.ts.map +1 -0
  79. package/dist/client/js/editor/markdown-shortcuts.js +219 -0
  80. package/dist/client/js/handlers/admin.d.ts +64 -0
  81. package/dist/client/js/handlers/admin.d.ts.map +1 -0
  82. package/dist/client/js/handlers/admin.js +186 -0
  83. package/dist/client/js/state/dialogs.svelte.d.ts +16 -0
  84. package/dist/client/js/state/dialogs.svelte.d.ts.map +1 -0
  85. package/dist/client/js/state/dialogs.svelte.js +28 -0
  86. package/dist/client/js/state/router.svelte.d.ts +44 -0
  87. package/dist/client/js/state/router.svelte.d.ts.map +1 -0
  88. package/dist/client/js/state/router.svelte.js +135 -0
  89. package/dist/client/js/state/schema.svelte.d.ts +51 -0
  90. package/dist/client/js/state/schema.svelte.d.ts.map +1 -0
  91. package/{src/client/js/state/schema.svelte.ts → dist/client/js/state/schema.svelte.js} +55 -70
  92. package/dist/client/js/state/state.svelte.d.ts +68 -0
  93. package/dist/client/js/state/state.svelte.d.ts.map +1 -0
  94. package/dist/client/js/state/state.svelte.js +291 -0
  95. package/dist/client/js/state/theme.svelte.d.ts +24 -0
  96. package/dist/client/js/state/theme.svelte.d.ts.map +1 -0
  97. package/{src/client/js/state/theme.svelte.ts → dist/client/js/state/theme.svelte.js} +54 -91
  98. package/dist/client/js/storage/adapter.d.ts +130 -0
  99. package/dist/client/js/storage/adapter.d.ts.map +1 -0
  100. package/dist/client/js/storage/adapter.js +5 -0
  101. package/dist/client/js/storage/client.d.ts +72 -0
  102. package/dist/client/js/storage/client.d.ts.map +1 -0
  103. package/dist/client/js/storage/client.js +121 -0
  104. package/dist/client/js/storage/db.d.ts +8 -0
  105. package/dist/client/js/storage/db.d.ts.map +1 -0
  106. package/dist/client/js/storage/db.js +35 -0
  107. package/dist/client/js/storage/fsa.d.ts +51 -0
  108. package/dist/client/js/storage/fsa.d.ts.map +1 -0
  109. package/dist/client/js/storage/fsa.js +91 -0
  110. package/dist/client/js/storage/github.d.ts +62 -0
  111. package/dist/client/js/storage/github.d.ts.map +1 -0
  112. package/dist/client/js/storage/github.js +216 -0
  113. package/dist/client/js/storage/storage.d.ts +32 -0
  114. package/dist/client/js/storage/storage.d.ts.map +1 -0
  115. package/dist/client/js/storage/storage.js +68 -0
  116. package/dist/client/js/storage/workers/frontmatter.d.ts +2 -0
  117. package/dist/client/js/storage/workers/frontmatter.d.ts.map +1 -0
  118. package/dist/client/js/storage/workers/frontmatter.js +253 -0
  119. package/dist/client/js/storage/workers/storage.d.ts +2 -0
  120. package/dist/client/js/storage/workers/storage.d.ts.map +1 -0
  121. package/dist/client/js/storage/workers/storage.js +167 -0
  122. package/dist/client/js/storage/workers/toml-parser.d.ts +2 -0
  123. package/dist/client/js/storage/workers/toml-parser.d.ts.map +1 -0
  124. package/dist/client/js/storage/workers/toml-parser.js +75 -0
  125. package/dist/client/js/storage/workers/yaml-parser.d.ts +2 -0
  126. package/dist/client/js/storage/workers/yaml-parser.d.ts.map +1 -0
  127. package/dist/client/js/storage/workers/yaml-parser.js +100 -0
  128. package/dist/client/js/utils/file-types.d.ts +58 -0
  129. package/dist/client/js/utils/file-types.d.ts.map +1 -0
  130. package/{src/client/js/utils/file-types.ts → dist/client/js/utils/file-types.js} +75 -107
  131. package/dist/client/js/utils/format.d.ts +8 -0
  132. package/dist/client/js/utils/format.d.ts.map +1 -0
  133. package/{src/client/js/utils/format.ts → dist/client/js/utils/format.js} +5 -6
  134. package/dist/client/js/utils/frontmatter.d.ts +12 -0
  135. package/dist/client/js/utils/frontmatter.d.ts.map +1 -0
  136. package/dist/client/js/utils/frontmatter.js +29 -0
  137. package/dist/client/js/utils/schema-utils.d.ts +110 -0
  138. package/dist/client/js/utils/schema-utils.d.ts.map +1 -0
  139. package/dist/client/js/utils/schema-utils.js +242 -0
  140. package/dist/client/js/utils/slug.d.ts +8 -0
  141. package/dist/client/js/utils/slug.d.ts.map +1 -0
  142. package/{src/client/js/utils/slug.ts → dist/client/js/utils/slug.js} +6 -7
  143. package/dist/client/js/utils/sort.d.ts +41 -0
  144. package/dist/client/js/utils/sort.d.ts.map +1 -0
  145. package/dist/client/js/utils/sort.js +65 -0
  146. package/dist/client/js/utils/stable-stringify.d.ts +8 -0
  147. package/dist/client/js/utils/stable-stringify.d.ts.map +1 -0
  148. package/dist/client/js/utils/stable-stringify.js +23 -0
  149. package/dist/client/js/utils/url-utils.d.ts +11 -0
  150. package/dist/client/js/utils/url-utils.d.ts.map +1 -0
  151. package/{src/client/js/utils/url-utils.ts → dist/client/js/utils/url-utils.js} +22 -23
  152. package/dist/types.d.ts +22 -0
  153. package/dist/types.d.ts.map +1 -0
  154. package/dist/types.js +1 -0
  155. package/package.json +4 -1
  156. package/.github/workflows/ci.yml +0 -27
  157. package/.github/workflows/publish.yml +0 -34
  158. package/.mcp.json +0 -12
  159. package/.prettierignore +0 -5
  160. package/.prettierrc.cjs +0 -22
  161. package/AGENTS.md +0 -183
  162. package/playground/astro.config.mjs +0 -7
  163. package/playground/node_modules/.bin/astro +0 -21
  164. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +0 -85
  165. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +0 -7
  166. package/playground/node_modules/.vite/deps/_metadata.json +0 -184
  167. package/playground/node_modules/.vite/deps/astro___aria-query.js +0 -6776
  168. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
  169. package/playground/node_modules/.vite/deps/astro___axobject-query.js +0 -3754
  170. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
  171. package/playground/node_modules/.vite/deps/astro___html-escaper.js +0 -34
  172. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
  173. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  174. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +0 -7
  175. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +0 -8
  176. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +0 -7
  177. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +0 -21
  178. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +0 -7
  179. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +0 -223
  180. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +0 -7
  181. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js +0 -27
  182. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js.map +0 -7
  183. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js +0 -1005
  184. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js.map +0 -7
  185. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js +0 -204
  186. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js.map +0 -7
  187. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js +0 -688
  188. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js.map +0 -7
  189. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js +0 -5099
  190. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js.map +0 -7
  191. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js +0 -4376
  192. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js.map +0 -7
  193. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +0 -23
  194. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +0 -7
  195. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +0 -148
  196. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +0 -7
  197. package/playground/node_modules/.vite/deps/package.json +0 -3
  198. package/playground/node_modules/.vite/deps/smol-toml.js +0 -843
  199. package/playground/node_modules/.vite/deps/smol-toml.js.map +0 -7
  200. package/playground/node_modules/.vite/deps/svelte.js +0 -55
  201. package/playground/node_modules/.vite/deps/svelte.js.map +0 -7
  202. package/playground/node_modules/.vite/deps/svelte___clsx.js +0 -9
  203. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +0 -7
  204. package/playground/node_modules/.vite/deps/svelte_animate.js +0 -57
  205. package/playground/node_modules/.vite/deps/svelte_animate.js.map +0 -7
  206. package/playground/node_modules/.vite/deps/svelte_attachments.js +0 -15
  207. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +0 -7
  208. package/playground/node_modules/.vite/deps/svelte_easing.js +0 -67
  209. package/playground/node_modules/.vite/deps/svelte_easing.js.map +0 -7
  210. package/playground/node_modules/.vite/deps/svelte_events.js +0 -11
  211. package/playground/node_modules/.vite/deps/svelte_events.js.map +0 -7
  212. package/playground/node_modules/.vite/deps/svelte_internal.js +0 -5
  213. package/playground/node_modules/.vite/deps/svelte_internal.js.map +0 -7
  214. package/playground/node_modules/.vite/deps/svelte_internal_client.js +0 -402
  215. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +0 -7
  216. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +0 -10
  217. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +0 -7
  218. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +0 -8
  219. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +0 -7
  220. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +0 -8
  221. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +0 -7
  222. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +0 -8
  223. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +0 -7
  224. package/playground/node_modules/.vite/deps/svelte_legacy.js +0 -35
  225. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +0 -7
  226. package/playground/node_modules/.vite/deps/svelte_motion.js +0 -545
  227. package/playground/node_modules/.vite/deps/svelte_motion.js.map +0 -7
  228. package/playground/node_modules/.vite/deps/svelte_reactivity.js +0 -29
  229. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +0 -7
  230. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +0 -127
  231. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +0 -7
  232. package/playground/node_modules/.vite/deps/svelte_store.js +0 -103
  233. package/playground/node_modules/.vite/deps/svelte_store.js.map +0 -7
  234. package/playground/node_modules/.vite/deps/svelte_transition.js +0 -208
  235. package/playground/node_modules/.vite/deps/svelte_transition.js.map +0 -7
  236. package/playground/package.json +0 -16
  237. package/playground/pnpm-lock.yaml +0 -3167
  238. package/playground/src/content/authors/jane-doe.json +0 -8
  239. package/playground/src/content/config/build.toml +0 -2
  240. package/playground/src/content/courses/web-fundamentals.json +0 -29
  241. package/playground/src/content/docs/advanced.mdx +0 -6
  242. package/playground/src/content/docs/intro.md +0 -6
  243. package/playground/src/content/guides/getting-started.mdx +0 -6
  244. package/playground/src/content/posts/hello-world.md +0 -7
  245. package/playground/src/content/products/t-shirt.json +0 -16
  246. package/playground/src/content/recipes/pancakes.mdoc +0 -8
  247. package/playground/src/content/settings/site.yml +0 -2
  248. package/playground/src/content.config.ts +0 -198
  249. package/playground/src/env.d.ts +0 -1
  250. package/playground/src/pages/index.astro +0 -11
  251. package/playground/src/pages/nebula.astro +0 -14
  252. package/pnpm-workspace.yaml +0 -2
  253. package/scripts/subset-icons.mjs +0 -178
  254. package/src/astro/index.ts +0 -295
  255. package/src/client/js/drafts/merge.svelte.ts +0 -121
  256. package/src/client/js/drafts/ops.svelte.ts +0 -227
  257. package/src/client/js/drafts/storage.ts +0 -108
  258. package/src/client/js/drafts/workers/diff.ts +0 -40
  259. package/src/client/js/editor/editor.svelte.ts +0 -343
  260. package/src/client/js/editor/languages.ts +0 -98
  261. package/src/client/js/editor/markdown-shortcuts.ts +0 -261
  262. package/src/client/js/handlers/admin.ts +0 -246
  263. package/src/client/js/state/dialogs.svelte.ts +0 -35
  264. package/src/client/js/state/router.svelte.ts +0 -156
  265. package/src/client/js/state/state.svelte.ts +0 -334
  266. package/src/client/js/storage/adapter.ts +0 -102
  267. package/src/client/js/storage/client.ts +0 -150
  268. package/src/client/js/storage/db.ts +0 -36
  269. package/src/client/js/storage/fsa.ts +0 -110
  270. package/src/client/js/storage/github.ts +0 -297
  271. package/src/client/js/storage/storage.ts +0 -83
  272. package/src/client/js/storage/workers/frontmatter.ts +0 -320
  273. package/src/client/js/storage/workers/storage.ts +0 -177
  274. package/src/client/js/storage/workers/toml-parser.ts +0 -106
  275. package/src/client/js/storage/workers/yaml-parser.ts +0 -132
  276. package/src/client/js/utils/frontmatter.ts +0 -38
  277. package/src/client/js/utils/schema-utils.ts +0 -295
  278. package/src/client/js/utils/sort.ts +0 -84
  279. package/src/client/js/utils/stable-stringify.ts +0 -27
  280. package/src/types.ts +0 -25
  281. package/svelte.config.js +0 -4
  282. package/tests/astro/build.test.ts +0 -63
  283. package/tests/astro/index.test.ts +0 -689
  284. package/tests/client/components/Admin.test.ts +0 -446
  285. package/tests/client/components/BackendPicker.test.ts +0 -239
  286. package/tests/client/components/DraftChip.test.ts +0 -53
  287. package/tests/client/components/MetadataForm.test.ts +0 -164
  288. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +0 -91
  289. package/tests/client/components/dialogs/FilenameDialog.test.ts +0 -209
  290. package/tests/client/components/dialogs/dialog-stubs.ts +0 -19
  291. package/tests/client/components/editor/EditorPane.test.ts +0 -100
  292. package/tests/client/components/editor/EditorTabs.test.ts +0 -253
  293. package/tests/client/components/editor/EditorToolbar.test.ts +0 -252
  294. package/tests/client/components/editor/fixtures.ts +0 -31
  295. package/tests/client/components/fields/ArrayField.test.ts +0 -197
  296. package/tests/client/components/fields/BooleanField.test.ts +0 -206
  297. package/tests/client/components/fields/DateField.test.ts +0 -210
  298. package/tests/client/components/fields/EnumField.test.ts +0 -246
  299. package/tests/client/components/fields/NumberField.test.ts +0 -240
  300. package/tests/client/components/fields/ObjectField.test.ts +0 -157
  301. package/tests/client/components/fields/SchemaField.test.ts +0 -190
  302. package/tests/client/components/fields/StringField.test.ts +0 -223
  303. package/tests/client/components/sidebar/AdminSidebar.test.ts +0 -285
  304. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +0 -135
  305. package/tests/client/components/sidebar/sort-mock.ts +0 -23
  306. package/tests/client/js/drafts/fixtures.ts +0 -22
  307. package/tests/client/js/drafts/merge.test.ts +0 -282
  308. package/tests/client/js/drafts/ops.test.ts +0 -658
  309. package/tests/client/js/drafts/storage.test.ts +0 -200
  310. package/tests/client/js/drafts/workers/diff.test.ts +0 -165
  311. package/tests/client/js/editor/editor.test.ts +0 -616
  312. package/tests/client/js/editor/link-wrap.test.ts +0 -225
  313. package/tests/client/js/editor/markdown-shortcuts.test.ts +0 -370
  314. package/tests/client/js/handlers/admin.test.ts +0 -467
  315. package/tests/client/js/state/router.test.ts +0 -619
  316. package/tests/client/js/state/schema.test.ts +0 -266
  317. package/tests/client/js/state/state.test.ts +0 -328
  318. package/tests/client/js/storage/adapter.test.ts +0 -115
  319. package/tests/client/js/storage/client.test.ts +0 -250
  320. package/tests/client/js/storage/db.test.ts +0 -59
  321. package/tests/client/js/storage/fsa.test.ts +0 -284
  322. package/tests/client/js/storage/github.test.ts +0 -349
  323. package/tests/client/js/storage/mock-port.ts +0 -95
  324. package/tests/client/js/storage/storage.test.ts +0 -77
  325. package/tests/client/js/storage/workers/frontmatter.test.ts +0 -479
  326. package/tests/client/js/storage/workers/storage.test.ts +0 -299
  327. package/tests/client/js/storage/workers/toml-parser.test.ts +0 -169
  328. package/tests/client/js/storage/workers/yaml-parser.test.ts +0 -168
  329. package/tests/client/js/utils/file-types.test.ts +0 -268
  330. package/tests/client/js/utils/frontmatter.test.ts +0 -87
  331. package/tests/client/js/utils/schema-utils.test.ts +0 -318
  332. package/tests/client/js/utils/slug.test.ts +0 -58
  333. package/tests/client/js/utils/sort.test.ts +0 -276
  334. package/tests/client/js/utils/stable-stringify.test.ts +0 -68
  335. package/tests/client/js/utils/url-utils.test.ts +0 -70
  336. package/tests/e2e/backend-connection.test.ts +0 -301
  337. package/tests/e2e/draft-lifecycle.test.ts +0 -388
  338. package/tests/e2e/editing.test.ts +0 -355
  339. package/tests/e2e/github-adapter.test.ts +0 -330
  340. package/tests/e2e/helpers/mock-adapter.ts +0 -166
  341. package/tests/e2e/helpers/test-app.ts +0 -155
  342. package/tests/e2e/navigation.test.ts +0 -358
  343. package/tests/e2e/publishing.test.ts +0 -345
  344. package/tests/e2e/unsaved-changes.test.ts +0 -317
  345. package/tests/setup.ts +0 -2
  346. package/tests/stubs/codemirror.ts +0 -197
  347. package/tsconfig.json +0 -19
  348. package/vitest.config.ts +0 -178
  349. /package/{src → dist}/client/Admin.svelte +0 -0
  350. /package/{src → dist}/client/components/BackendPicker.svelte +0 -0
  351. /package/{src → dist}/client/components/DraftChip.svelte +0 -0
  352. /package/{src → dist}/client/components/MetadataForm.svelte +0 -0
  353. /package/{src → dist}/client/components/ThemeToggle.svelte +0 -0
  354. /package/{src → dist}/client/components/dialogs/DeleteDraftDialog.svelte +0 -0
  355. /package/{src → dist}/client/components/dialogs/FilenameDialog.svelte +0 -0
  356. /package/{src → dist}/client/components/editor/EditorPane.svelte +0 -0
  357. /package/{src → dist}/client/components/editor/EditorTabs.svelte +0 -0
  358. /package/{src → dist}/client/components/editor/EditorToolbar.svelte +0 -0
  359. /package/{src → dist}/client/components/editor/FormatSelector.svelte +0 -0
  360. /package/{src → dist}/client/components/editor/Toolbar.svelte +0 -0
  361. /package/{src → dist}/client/components/fields/ArrayField.svelte +0 -0
  362. /package/{src → dist}/client/components/fields/ArrayItem.svelte +0 -0
  363. /package/{src → dist}/client/components/fields/BooleanField.svelte +0 -0
  364. /package/{src → dist}/client/components/fields/DateField.svelte +0 -0
  365. /package/{src → dist}/client/components/fields/EnumField.svelte +0 -0
  366. /package/{src → dist}/client/components/fields/FieldWrapper.svelte +0 -0
  367. /package/{src → dist}/client/components/fields/NumberField.svelte +0 -0
  368. /package/{src → dist}/client/components/fields/ObjectField.svelte +0 -0
  369. /package/{src → dist}/client/components/fields/SchemaField.svelte +0 -0
  370. /package/{src → dist}/client/components/fields/StringField.svelte +0 -0
  371. /package/{src → dist}/client/components/sidebar/AdminSidebar.svelte +0 -0
  372. /package/{src → dist}/client/components/sidebar/AdminSidebarSort.svelte +0 -0
  373. /package/{src → dist}/client/css/a11y.css +0 -0
  374. /package/{src → dist}/client/css/btn.css +0 -0
  375. /package/{src → dist}/client/css/dialog.css +0 -0
  376. /package/{src → dist}/client/css/field-input.css +0 -0
  377. /package/{src → dist}/client/css/reset.css +0 -0
  378. /package/{src → dist}/client/css/theme.css +0 -0
  379. /package/{src/client/index.ts → dist/client/index.js} +0 -0
  380. /package/{src → dist}/virtual.d.ts +0 -0
@@ -1,166 +0,0 @@
1
- import { vi } from 'vitest';
2
- import type {
3
- FileEntry,
4
- FileWrite,
5
- StorageAdapter,
6
- } from '../../../src/client/js/storage/adapter';
7
-
8
- //////////////////////////////
9
- // Sample content for pre-populated collections
10
- //////////////////////////////
11
-
12
- /** Sample posts used to pre-populate the mock adapter. */
13
- const SAMPLE_POSTS: FileEntry[] = [
14
- {
15
- filename: 'hello-world.md',
16
- content: [
17
- '---',
18
- 'title: Hello World',
19
- 'published: 2024-01-15T10:00:00Z',
20
- 'draft: false',
21
- '---',
22
- '',
23
- 'Welcome to the blog. This is the first post.',
24
- ].join('\n'),
25
- },
26
- {
27
- filename: 'second-post.md',
28
- content: [
29
- '---',
30
- 'title: Second Post',
31
- 'published: 2024-02-20T14:30:00Z',
32
- 'draft: false',
33
- '---',
34
- '',
35
- 'This is the second post with more content.',
36
- ].join('\n'),
37
- },
38
- {
39
- filename: 'draft-ideas.md',
40
- content: [
41
- '---',
42
- 'title: Draft Ideas',
43
- 'published: 2024-03-01T09:00:00Z',
44
- 'draft: true',
45
- '---',
46
- '',
47
- 'Some ideas for future posts.',
48
- ].join('\n'),
49
- },
50
- ];
51
-
52
- /** Sample pages for a second collection. */
53
- const SAMPLE_PAGES: FileEntry[] = [
54
- {
55
- filename: 'about.md',
56
- content: ['---', 'title: About', '---', '', 'This is the about page.'].join(
57
- '\n',
58
- ),
59
- },
60
- ];
61
-
62
- /**
63
- * Creates an in-memory StorageAdapter backed by a Map. Pre-populated with
64
- * sample "posts" and "pages" collections containing markdown files with
65
- * YAML frontmatter.
66
- * @return {{ adapter: StorageAdapter, store: Map<string, Map<string, string>> }} The adapter and its backing store for assertions
67
- */
68
- export function createMockAdapter(): {
69
- adapter: StorageAdapter;
70
- store: Map<string, Map<string, string>>;
71
- } {
72
- // Outer map: collection name -> inner map (filename -> content)
73
- const store = new Map<string, Map<string, string>>();
74
-
75
- // Pre-populate posts
76
- const postsMap = new Map<string, string>();
77
- for (const entry of SAMPLE_POSTS) {
78
- postsMap.set(entry.filename, entry.content);
79
- }
80
- store.set('posts', postsMap);
81
-
82
- // Pre-populate pages
83
- const pagesMap = new Map<string, string>();
84
- for (const entry of SAMPLE_PAGES) {
85
- pagesMap.set(entry.filename, entry.content);
86
- }
87
- store.set('pages', pagesMap);
88
-
89
- /**
90
- * Gets or creates the inner map for a collection.
91
- * @param {string} collection - The collection name
92
- * @return {Map<string, string>} The file map for the collection
93
- */
94
- function getCollection(collection: string): Map<string, string> {
95
- let col = store.get(collection);
96
- if (!col) {
97
- col = new Map<string, string>();
98
- store.set(collection, col);
99
- }
100
- return col;
101
- }
102
-
103
- const adapter: StorageAdapter = {
104
- listFiles: vi.fn(
105
- async (
106
- collection: string,
107
- extensions: string[],
108
- ): Promise<FileEntry[]> => {
109
- const col = store.get(collection);
110
- if (!col) return [];
111
- return Array.from(col.entries())
112
- .filter(([filename]) =>
113
- extensions.some((ext) => filename.endsWith(ext)),
114
- )
115
- .map(([filename, content]) => ({
116
- filename,
117
- content,
118
- }));
119
- },
120
- ),
121
-
122
- readFile: vi.fn(
123
- async (collection: string, filename: string): Promise<string> => {
124
- const col = store.get(collection);
125
- if (!col) throw new Error(`Collection "${collection}" not found`);
126
- const content = col.get(filename);
127
- if (content === undefined)
128
- throw new Error(`File "${filename}" not found in "${collection}"`);
129
- return content;
130
- },
131
- ),
132
-
133
- writeFile: vi.fn(
134
- async (
135
- collection: string,
136
- filename: string,
137
- content: string,
138
- ): Promise<void> => {
139
- const col = getCollection(collection);
140
- col.set(filename, content);
141
- },
142
- ),
143
-
144
- writeFiles: vi.fn(async (files: FileWrite[]): Promise<void> => {
145
- for (const file of files) {
146
- const col = getCollection(file.collection);
147
- col.set(file.filename, file.content);
148
- }
149
- }),
150
-
151
- deleteFile: vi.fn(
152
- async (collection: string, filename: string): Promise<void> => {
153
- const col = store.get(collection);
154
- if (!col) throw new Error(`Collection "${collection}" not found`);
155
- if (!col.has(filename))
156
- throw new Error(`File "${filename}" not found in "${collection}"`);
157
- col.delete(filename);
158
- },
159
- ),
160
- };
161
-
162
- return { adapter, store };
163
- }
164
-
165
- /** Exported sample data for test assertions. */
166
- export { SAMPLE_POSTS, SAMPLE_PAGES };
@@ -1,155 +0,0 @@
1
- import type { Mock } from 'vitest';
2
-
3
- //////////////////////////////
4
- // Mock state type
5
- //
6
- // Each test file creates its own mocks via vi.hoisted(). This type
7
- // describes the shape so helper functions can manipulate mock return
8
- // values without being tightly coupled to any single test file.
9
- // Mock names match the getter properties on the exported state objects
10
- // (backend, content, nav, schema, drafts, editor).
11
- //////////////////////////////
12
-
13
- /**
14
- * Shape of the mocks object created by vi.hoisted() in each test file.
15
- * Each field is a Vitest Mock function controlling a reactive export.
16
- */
17
- export type E2EMocks = {
18
- mockBackendReady: Mock<() => boolean>;
19
- mockRoute: Mock;
20
- mockCollections: Mock<() => string[]>;
21
- mockContentList: Mock;
22
- mockLoading: Mock<() => boolean>;
23
- mockError: Mock<() => string | null>;
24
- mockDrafts: Mock;
25
- mockOutdatedMap: Mock<() => Record<string, boolean>>;
26
- mockActiveTab: Mock<() => string>;
27
- mockGetEditorFile: Mock;
28
- mockSchema: Mock;
29
- mockCollectionHasDates: Mock<() => boolean>;
30
- mockComputePublishDisabled: Mock<() => boolean>;
31
- };
32
-
33
- /**
34
- * Resets all mock return values to their disconnected defaults.
35
- * Call in beforeEach() to avoid state leaking between tests.
36
- * @param {E2EMocks} m - The mocks object to reset
37
- * @return {void}
38
- */
39
- export function resetMocks(m: E2EMocks): void {
40
- m.mockBackendReady.mockReturnValue(false);
41
- m.mockRoute.mockReturnValue({ view: 'home' });
42
- m.mockCollections.mockReturnValue([]);
43
- m.mockContentList.mockReturnValue([]);
44
- m.mockLoading.mockReturnValue(false);
45
- m.mockError.mockReturnValue(null);
46
- m.mockDrafts.mockReturnValue([]);
47
- m.mockOutdatedMap.mockReturnValue({});
48
- m.mockActiveTab.mockReturnValue('metadata');
49
- m.mockGetEditorFile.mockReturnValue(null);
50
- m.mockSchema.mockReturnValue(null);
51
- m.mockCollectionHasDates.mockReturnValue(false);
52
- m.mockComputePublishDisabled.mockReturnValue(false);
53
- }
54
-
55
- /**
56
- * Configures mocks to show the backend as connected with collections visible.
57
- * @param {E2EMocks} m - The mocks object
58
- * @param {string[]} collections - Collection names to show
59
- * @return {void}
60
- */
61
- export function configureConnected(
62
- m: E2EMocks,
63
- collections: string[] = ['pages', 'posts'],
64
- ): void {
65
- m.mockBackendReady.mockReturnValue(true);
66
- m.mockCollections.mockReturnValue(collections);
67
- }
68
-
69
- /**
70
- * Configures mocks to show a collection selected with content items.
71
- * @param {E2EMocks} m - The mocks object
72
- * @param {string} collection - The active collection name
73
- * @param {Array<{ filename: string, data: Record<string, unknown> }>} items - Content items
74
- * @return {void}
75
- */
76
- export function configureCollection(
77
- m: E2EMocks,
78
- collection: string,
79
- items: Array<{ filename: string; data: Record<string, unknown> }> = [],
80
- ): void {
81
- configureConnected(m);
82
- m.mockRoute.mockReturnValue({ view: 'collection', collection });
83
- m.mockContentList.mockReturnValue(items);
84
- }
85
-
86
- /**
87
- * Configures mocks to show a file open in the editor.
88
- * @param {E2EMocks} m - The mocks object
89
- * @param {string} collection - The active collection
90
- * @param {string} slug - The file slug (filename without extension)
91
- * @param {{ filename: string, body: string, formData: Record<string, unknown>, dirty?: boolean, draftId?: string | null, isNewDraft?: boolean }} file - Editor file state
92
- * @return {void}
93
- */
94
- export function configureFileOpen(
95
- m: E2EMocks,
96
- collection: string,
97
- slug: string,
98
- file: {
99
- filename: string;
100
- body: string;
101
- formData: Record<string, unknown>;
102
- dirty?: boolean;
103
- draftId?: string | null;
104
- isNewDraft?: boolean;
105
- },
106
- ): void {
107
- configureConnected(m);
108
- m.mockRoute.mockReturnValue({ view: 'file', collection, slug });
109
- m.mockContentList.mockReturnValue([
110
- { filename: file.filename, data: file.formData },
111
- ]);
112
- m.mockGetEditorFile.mockReturnValue({
113
- filename: file.filename,
114
- body: file.body,
115
- formData: file.formData,
116
- dirty: file.dirty ?? false,
117
- saving: false,
118
- bodyLoaded: true,
119
- draftId: file.draftId ?? null,
120
- isNewDraft: file.isNewDraft ?? false,
121
- });
122
- }
123
-
124
- /**
125
- * Configures mocks to show a draft open in the editor.
126
- * @param {E2EMocks} m - The mocks object
127
- * @param {string} collection - The active collection
128
- * @param {string} draftId - The draft UUID
129
- * @param {{ body: string, formData: Record<string, unknown>, dirty?: boolean, filename?: string }} draft - Draft state
130
- * @return {void}
131
- */
132
- export function configureDraftOpen(
133
- m: E2EMocks,
134
- collection: string,
135
- draftId: string,
136
- draft: {
137
- body: string;
138
- formData: Record<string, unknown>;
139
- dirty?: boolean;
140
- filename?: string;
141
- },
142
- ): void {
143
- configureConnected(m);
144
- m.mockRoute.mockReturnValue({ view: 'draft', collection, draftId });
145
- m.mockGetEditorFile.mockReturnValue({
146
- filename: draft.filename ?? '',
147
- body: draft.body,
148
- formData: draft.formData,
149
- dirty: draft.dirty ?? false,
150
- saving: false,
151
- bodyLoaded: true,
152
- draftId,
153
- isNewDraft: true,
154
- });
155
- }
@@ -1,358 +0,0 @@
1
- import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
2
- import { render, cleanup } from '@testing-library/svelte';
3
- import {
4
- resetMocks,
5
- configureConnected,
6
- configureCollection,
7
- configureFileOpen,
8
- } from './helpers/test-app';
9
-
10
- /*
11
- //////////////////////////////
12
- // Hoisted mocks
13
- //////////////////////////////
14
- */
15
-
16
- const mocks = vi.hoisted(() => ({
17
- mockBackendReady: vi.fn(() => false),
18
- mockRoute: vi.fn(() => ({ view: 'home' as const })),
19
- mockCollections: vi.fn(() => [] as string[]),
20
- mockContentList: vi.fn(
21
- () => [] as Array<{ filename: string; data: Record<string, unknown> }>,
22
- ),
23
- mockLoading: vi.fn(() => false),
24
- mockError: vi.fn(() => null as string | null),
25
- mockDrafts: vi.fn(() => []),
26
- mockOutdatedMap: vi.fn(() => ({}) as Record<string, boolean>),
27
- mockActiveTab: vi.fn(() => 'metadata'),
28
- mockGetEditorFile: vi.fn(() => null),
29
- mockSchema: vi.fn(() => null),
30
- mockCollectionHasDates: vi.fn(() => false),
31
- mockComputePublishDisabled: vi.fn(() => false),
32
- }));
33
-
34
- /*
35
- //////////////////////////////
36
- // Module mocks
37
- //////////////////////////////
38
- */
39
-
40
- vi.mock('virtual:nebula/collections', () => ({
41
- default: {
42
- pages: '/collections/pages.schema.json',
43
- posts: '/collections/posts.schema.json',
44
- },
45
- }));
46
- vi.mock('virtual:nebula/config', () => ({
47
- default: { basePath: '/admin', collectionsPath: '/collections' },
48
- }));
49
- vi.mock('../../src/client/js/state/state.svelte', () => ({
50
- backend: {
51
- get type() {
52
- return null;
53
- },
54
- get ready() {
55
- return mocks.mockBackendReady();
56
- },
57
- get permission() {
58
- return 'denied';
59
- },
60
- },
61
- content: {
62
- get list() {
63
- return mocks.mockContentList();
64
- },
65
- get loading() {
66
- return mocks.mockLoading();
67
- },
68
- get error() {
69
- return mocks.mockError();
70
- },
71
- },
72
- get collections() {
73
- return mocks.mockCollections();
74
- },
75
- storageClient: null,
76
- drafts: {
77
- get all() {
78
- return mocks.mockDrafts();
79
- },
80
- get outdated() {
81
- return mocks.mockOutdatedMap();
82
- },
83
- },
84
- restoreBackend: vi.fn(async () => {}),
85
- loadCollection: vi.fn(),
86
- reloadCollection: vi.fn(),
87
- disconnect: vi.fn(),
88
- refreshDrafts: vi.fn(async () => {}),
89
- updateContentItem: vi.fn(),
90
- pickDirectory: vi.fn(),
91
- requestPermission: vi.fn(),
92
- connectGitHub: vi.fn(async () => {}),
93
- }));
94
- vi.mock('../../src/client/js/state/router.svelte', () => ({
95
- initRouter: vi.fn(),
96
- nav: {
97
- get route() {
98
- return mocks.mockRoute();
99
- },
100
- },
101
- navigate: vi.fn(),
102
- registerDirtyChecker: vi.fn(),
103
- adminPath: vi.fn((...segments) =>
104
- segments.length === 0 ? '/admin' : '/admin/' + segments.join('/'),
105
- ),
106
- }));
107
- vi.mock('../../src/client/js/state/schema.svelte', () => ({
108
- fetchSchema: vi.fn(async () => {}),
109
- schema: {
110
- get active() {
111
- return mocks.mockSchema();
112
- },
113
- },
114
- clearSchema: vi.fn(),
115
- prefetchAllSchemas: vi.fn(async () => {}),
116
- collectionHasDates: mocks.mockCollectionHasDates,
117
- getCollectionTitle: vi.fn(() => null),
118
- getCollectionDescription: vi.fn(() => null),
119
- }));
120
- vi.mock('../../src/client/js/editor/editor.svelte', () => ({
121
- preloadFile: vi.fn(async () => {}),
122
- loadFileBody: vi.fn(async () => {}),
123
- clearEditor: vi.fn(),
124
- editor: {
125
- get tab() {
126
- return mocks.mockActiveTab();
127
- },
128
- get data() {
129
- return {};
130
- },
131
- get originalFilename() {
132
- return '';
133
- },
134
- },
135
- setActiveTab: vi.fn(),
136
- getEditorFile: mocks.mockGetEditorFile,
137
- loadDraftById: vi.fn(async () => {}),
138
- setFilename: vi.fn(),
139
- updateBody: vi.fn(),
140
- updateFormField: vi.fn(),
141
- saveDraftToIDB: vi.fn(async () => {}),
142
- saveFile: vi.fn(async () => {}),
143
- publishFile: vi.fn(async () => {}),
144
- deleteCurrentDraft: vi.fn(async () => {}),
145
- applyEditorState: vi.fn(),
146
- _getDraftState: vi.fn(() => ({})),
147
- _setDraftState: vi.fn(),
148
- changeFileFormat: vi.fn(),
149
- setDefaultFormat: vi.fn(),
150
- }));
151
- vi.mock('../../src/client/js/handlers/admin', async (importOriginal) => {
152
- const actual =
153
- await importOriginal<typeof import('../../src/client/js/handlers/admin')>();
154
- return {
155
- ...actual,
156
- handleSave: vi.fn(async () => {}),
157
- handlePublish: vi.fn(async () => ({ status: 'ok' })),
158
- handleDeleteDraft: vi.fn(async () => {}),
159
- handleFilenameConfirm: vi.fn(async () => {}),
160
- computePublishDisabled: mocks.mockComputePublishDisabled,
161
- // Override buildCollectionItems to read from mockCollections — the
162
- // module-level getter for `collections` is not a live binding in
163
- // vitest's browser mode, so the real function would see an empty array.
164
- buildCollectionItems: () =>
165
- mocks.mockCollections().map((name: string) => ({
166
- label: name.charAt(0).toUpperCase() + name.slice(1),
167
- href: '/admin/' + name,
168
- })),
169
- };
170
- });
171
- vi.mock('../../src/client/js/utils/sort', () => ({
172
- toSortDate: vi.fn(() => undefined),
173
- readSortMode: vi.fn(() => 'alpha'),
174
- writeSortMode: vi.fn(),
175
- createComparator: vi.fn(() => () => 0),
176
- SORT_MODES: {
177
- alpha: { icon: 'sort_by_alpha', label: 'Alphabetical' },
178
- 'date-asc': { icon: 'hourglass_arrow_down', label: 'Oldest first' },
179
- 'date-desc': { icon: 'hourglass_arrow_up', label: 'Newest first' },
180
- },
181
- SORT_ORDER: ['alpha', 'date-asc', 'date-desc'],
182
- }));
183
- vi.mock('../../src/client/js/drafts/storage', () => ({
184
- saveDraft: vi.fn(async () => {}),
185
- getDraftByFile: vi.fn(async () => null),
186
- loadDrafts: vi.fn(async () => []),
187
- loadDraft: vi.fn(async () => null),
188
- deleteDraft: vi.fn(async () => {}),
189
- }));
190
- vi.mock('../../src/client/js/utils/schema-utils', () => ({
191
- extractTabs: vi.fn(() => []),
192
- getFieldsForTab: vi.fn(() => []),
193
- resolveFieldType: vi.fn(() => ({ kind: 'string' })),
194
- createDefaultValue: vi.fn(() => ''),
195
- getByPath: vi.fn(),
196
- setByPath: vi.fn(),
197
- isReadOnly: vi.fn(() => false),
198
- isNullable: vi.fn(() => false),
199
- getProperties: vi.fn(
200
- (schema: Record<string, unknown>) => schema['properties'],
201
- ),
202
- getRequiredFields: vi.fn((schema: Record<string, unknown>) =>
203
- Array.isArray(schema['required']) ? schema['required'] : [],
204
- ),
205
- getLabel: vi.fn((schema: Record<string, unknown>, name: string) =>
206
- typeof schema['title'] === 'string' ? schema['title'] : name,
207
- ),
208
- }));
209
- vi.mock('../../src/client/js/drafts/merge.svelte', () => ({
210
- drafts: {
211
- get all() {
212
- return mocks.mockDrafts();
213
- },
214
- get outdated() {
215
- return mocks.mockOutdatedMap();
216
- },
217
- },
218
- mergeDrafts: vi.fn(async () => {}),
219
- refreshDrafts: vi.fn(async () => {}),
220
- resetDraftMerge: vi.fn(),
221
- }));
222
-
223
- vi.mock('../../src/client/js/state/theme.svelte', () => ({
224
- initTheme: vi.fn(() => () => {}),
225
- cycleTheme: vi.fn(),
226
- theme: { resolved: 'dark', icon: 'brightness_auto', label: 'Auto' },
227
- }));
228
- vi.mock('../../src/client/js/state/dialogs.svelte', () => ({
229
- dialog: {
230
- get active() {
231
- return null;
232
- },
233
- open: vi.fn(),
234
- close: vi.fn(),
235
- },
236
- }));
237
-
238
- import Admin from '../../src/client/Admin.svelte';
239
-
240
- afterEach(() => cleanup());
241
- beforeEach(() => resetMocks(mocks));
242
-
243
- describe('Navigation', () => {
244
- it('shows both sidebars when a collection is selected', () => {
245
- configureCollection(mocks, 'posts');
246
-
247
- const { container } = render(Admin);
248
-
249
- const sidebars = container.querySelectorAll('.sidebar');
250
- expect(sidebars.length).toBe(2);
251
- });
252
-
253
- it('does not render editor area in collection view', () => {
254
- configureCollection(mocks, 'posts');
255
-
256
- const { container } = render(Admin);
257
-
258
- expect(container.querySelector('.editor-area')).toBeNull();
259
- });
260
-
261
- it('shows content items in the collection sidebar', () => {
262
- configureCollection(mocks, 'posts', [
263
- { filename: 'hello-world.md', data: { title: 'Hello World' } },
264
- { filename: 'second-post.md', data: { title: 'Second Post' } },
265
- ]);
266
-
267
- const { container } = render(Admin);
268
-
269
- // Second sidebar has the content items
270
- const sidebars = container.querySelectorAll('.sidebar');
271
- const contentSidebar = sidebars[1];
272
- const links = contentSidebar.querySelectorAll('.sidebar-link');
273
- expect(links.length).toBe(2);
274
- });
275
-
276
- it('renders content item titles from frontmatter data', () => {
277
- configureCollection(mocks, 'posts', [
278
- { filename: 'hello.md', data: { title: 'Hello World' } },
279
- { filename: 'bye.md', data: { title: 'Goodbye' } },
280
- ]);
281
-
282
- const { container } = render(Admin);
283
-
284
- const sidebars = container.querySelectorAll('.sidebar');
285
- const links = sidebars[1].querySelectorAll('.sidebar-link');
286
- const labels = Array.from(links).map((el) =>
287
- el.querySelector('.item-label-text')?.textContent?.trim(),
288
- );
289
- expect(labels).toContain('Hello World');
290
- expect(labels).toContain('Goodbye');
291
- });
292
-
293
- it('falls back to filename when title is missing', () => {
294
- configureCollection(mocks, 'posts', [
295
- { filename: 'no-title.md', data: {} },
296
- ]);
297
-
298
- const { container } = render(Admin);
299
-
300
- const sidebars = container.querySelectorAll('.sidebar');
301
- const links = sidebars[1].querySelectorAll('.sidebar-link');
302
- const label = links[0]
303
- .querySelector('.item-label-text')
304
- ?.textContent?.trim();
305
- expect(label).toBe('no-title.md');
306
- });
307
-
308
- it('renders editor area when a file is open', () => {
309
- configureFileOpen(mocks, 'posts', 'hello-world', {
310
- filename: 'hello-world.md',
311
- body: 'Hello content',
312
- formData: { title: 'Hello World' },
313
- });
314
-
315
- const { container } = render(Admin);
316
-
317
- expect(container.querySelector('.editor-area')).not.toBeNull();
318
- });
319
-
320
- it('applies admin--file-open class when editing a file', () => {
321
- configureFileOpen(mocks, 'posts', 'hello-world', {
322
- filename: 'hello-world.md',
323
- body: 'Hello content',
324
- formData: { title: 'Hello World' },
325
- });
326
-
327
- const { container } = render(Admin);
328
-
329
- expect(container.querySelector('.admin--file-open')).not.toBeNull();
330
- });
331
-
332
- it('shows both sidebars and editor area in file view', () => {
333
- configureFileOpen(mocks, 'posts', 'hello-world', {
334
- filename: 'hello-world.md',
335
- body: '',
336
- formData: { title: 'Hello World' },
337
- });
338
-
339
- const { container } = render(Admin);
340
-
341
- const sidebars = container.querySelectorAll('.sidebar');
342
- expect(sidebars.length).toBe(2);
343
- expect(container.querySelector('.editor-area')).not.toBeNull();
344
- });
345
-
346
- it('highlights active collection in the sidebar via aria-current', () => {
347
- configureCollection(mocks, 'posts');
348
- mocks.mockCollections.mockReturnValue(['pages', 'posts']);
349
-
350
- const { container } = render(Admin);
351
-
352
- // Active collection is marked with aria-current="page"
353
- const activeLink = container.querySelector(
354
- '.sidebar-link[aria-current="page"]',
355
- );
356
- expect(activeLink).not.toBeNull();
357
- });
358
- });