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
@@ -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
- });