nebula-cms 0.1.0 → 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 (386) 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 +8 -1
  156. package/.claude/settings.local.json +0 -42
  157. package/.github/workflows/ci.yml +0 -31
  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/.claude/settings.local.json +0 -5
  163. package/playground/astro.config.mjs +0 -7
  164. package/playground/node_modules/.bin/astro +0 -21
  165. package/playground/node_modules/.bin/rollup +0 -21
  166. package/playground/node_modules/.bin/tsc +0 -21
  167. package/playground/node_modules/.bin/tsserver +0 -21
  168. package/playground/node_modules/.bin/vite +0 -21
  169. package/playground/node_modules/.vite/_svelte_metadata.json +0 -1
  170. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +0 -80
  171. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +0 -7
  172. package/playground/node_modules/.vite/deps/_metadata.json +0 -184
  173. package/playground/node_modules/.vite/deps/astro___aria-query.js +0 -6776
  174. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
  175. package/playground/node_modules/.vite/deps/astro___axobject-query.js +0 -3754
  176. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
  177. package/playground/node_modules/.vite/deps/astro___html-escaper.js +0 -34
  178. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
  179. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  180. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +0 -7
  181. package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js +0 -1005
  182. package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js.map +0 -7
  183. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +0 -8
  184. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +0 -7
  185. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +0 -21
  186. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +0 -7
  187. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +0 -223
  188. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +0 -7
  189. package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js +0 -204
  190. package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js.map +0 -7
  191. package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js +0 -27
  192. package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js.map +0 -7
  193. package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js +0 -4376
  194. package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js.map +0 -7
  195. package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js +0 -688
  196. package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js.map +0 -7
  197. package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js +0 -5099
  198. package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js.map +0 -7
  199. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +0 -23
  200. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +0 -7
  201. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +0 -148
  202. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +0 -7
  203. package/playground/node_modules/.vite/deps/package.json +0 -3
  204. package/playground/node_modules/.vite/deps/smol-toml.js +0 -843
  205. package/playground/node_modules/.vite/deps/smol-toml.js.map +0 -7
  206. package/playground/node_modules/.vite/deps/svelte.js +0 -55
  207. package/playground/node_modules/.vite/deps/svelte.js.map +0 -7
  208. package/playground/node_modules/.vite/deps/svelte___clsx.js +0 -9
  209. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +0 -7
  210. package/playground/node_modules/.vite/deps/svelte_animate.js +0 -57
  211. package/playground/node_modules/.vite/deps/svelte_animate.js.map +0 -7
  212. package/playground/node_modules/.vite/deps/svelte_attachments.js +0 -15
  213. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +0 -7
  214. package/playground/node_modules/.vite/deps/svelte_easing.js +0 -67
  215. package/playground/node_modules/.vite/deps/svelte_easing.js.map +0 -7
  216. package/playground/node_modules/.vite/deps/svelte_events.js +0 -11
  217. package/playground/node_modules/.vite/deps/svelte_events.js.map +0 -7
  218. package/playground/node_modules/.vite/deps/svelte_internal.js +0 -5
  219. package/playground/node_modules/.vite/deps/svelte_internal.js.map +0 -7
  220. package/playground/node_modules/.vite/deps/svelte_internal_client.js +0 -402
  221. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +0 -7
  222. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +0 -10
  223. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +0 -7
  224. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +0 -8
  225. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +0 -7
  226. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +0 -8
  227. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +0 -7
  228. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +0 -8
  229. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +0 -7
  230. package/playground/node_modules/.vite/deps/svelte_legacy.js +0 -35
  231. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +0 -7
  232. package/playground/node_modules/.vite/deps/svelte_motion.js +0 -545
  233. package/playground/node_modules/.vite/deps/svelte_motion.js.map +0 -7
  234. package/playground/node_modules/.vite/deps/svelte_reactivity.js +0 -29
  235. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +0 -7
  236. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +0 -127
  237. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +0 -7
  238. package/playground/node_modules/.vite/deps/svelte_store.js +0 -103
  239. package/playground/node_modules/.vite/deps/svelte_store.js.map +0 -7
  240. package/playground/node_modules/.vite/deps/svelte_transition.js +0 -208
  241. package/playground/node_modules/.vite/deps/svelte_transition.js.map +0 -7
  242. package/playground/package.json +0 -16
  243. package/playground/pnpm-lock.yaml +0 -3167
  244. package/playground/src/content/authors/jane-doe.json +0 -8
  245. package/playground/src/content/config/build.toml +0 -2
  246. package/playground/src/content/courses/web-fundamentals.json +0 -29
  247. package/playground/src/content/docs/advanced.mdx +0 -6
  248. package/playground/src/content/docs/intro.md +0 -6
  249. package/playground/src/content/guides/getting-started.mdx +0 -6
  250. package/playground/src/content/posts/hello-world.md +0 -7
  251. package/playground/src/content/products/t-shirt.json +0 -16
  252. package/playground/src/content/recipes/pancakes.mdoc +0 -8
  253. package/playground/src/content/settings/site.yml +0 -2
  254. package/playground/src/content.config.ts +0 -198
  255. package/playground/src/env.d.ts +0 -1
  256. package/playground/src/pages/index.astro +0 -11
  257. package/playground/src/pages/nebula.astro +0 -14
  258. package/pnpm-workspace.yaml +0 -2
  259. package/scripts/subset-icons.mjs +0 -178
  260. package/src/astro/index.ts +0 -295
  261. package/src/client/js/drafts/merge.svelte.ts +0 -121
  262. package/src/client/js/drafts/ops.svelte.ts +0 -227
  263. package/src/client/js/drafts/storage.ts +0 -108
  264. package/src/client/js/drafts/workers/diff.ts +0 -40
  265. package/src/client/js/editor/editor.svelte.ts +0 -343
  266. package/src/client/js/editor/languages.ts +0 -98
  267. package/src/client/js/editor/markdown-shortcuts.ts +0 -261
  268. package/src/client/js/handlers/admin.ts +0 -246
  269. package/src/client/js/state/dialogs.svelte.ts +0 -35
  270. package/src/client/js/state/router.svelte.ts +0 -156
  271. package/src/client/js/state/state.svelte.ts +0 -334
  272. package/src/client/js/storage/adapter.ts +0 -102
  273. package/src/client/js/storage/client.ts +0 -150
  274. package/src/client/js/storage/db.ts +0 -36
  275. package/src/client/js/storage/fsa.ts +0 -110
  276. package/src/client/js/storage/github.ts +0 -297
  277. package/src/client/js/storage/storage.ts +0 -83
  278. package/src/client/js/storage/workers/frontmatter.ts +0 -320
  279. package/src/client/js/storage/workers/storage.ts +0 -177
  280. package/src/client/js/storage/workers/toml-parser.ts +0 -106
  281. package/src/client/js/storage/workers/yaml-parser.ts +0 -132
  282. package/src/client/js/utils/frontmatter.ts +0 -38
  283. package/src/client/js/utils/schema-utils.ts +0 -295
  284. package/src/client/js/utils/sort.ts +0 -84
  285. package/src/client/js/utils/stable-stringify.ts +0 -27
  286. package/src/types.ts +0 -25
  287. package/svelte.config.js +0 -4
  288. package/tests/astro/build.test.ts +0 -63
  289. package/tests/astro/index.test.ts +0 -689
  290. package/tests/client/components/Admin.test.ts +0 -446
  291. package/tests/client/components/BackendPicker.test.ts +0 -239
  292. package/tests/client/components/DraftChip.test.ts +0 -53
  293. package/tests/client/components/MetadataForm.test.ts +0 -164
  294. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +0 -91
  295. package/tests/client/components/dialogs/FilenameDialog.test.ts +0 -209
  296. package/tests/client/components/dialogs/dialog-stubs.ts +0 -19
  297. package/tests/client/components/editor/EditorPane.test.ts +0 -100
  298. package/tests/client/components/editor/EditorTabs.test.ts +0 -253
  299. package/tests/client/components/editor/EditorToolbar.test.ts +0 -252
  300. package/tests/client/components/editor/fixtures.ts +0 -31
  301. package/tests/client/components/fields/ArrayField.test.ts +0 -197
  302. package/tests/client/components/fields/BooleanField.test.ts +0 -206
  303. package/tests/client/components/fields/DateField.test.ts +0 -210
  304. package/tests/client/components/fields/EnumField.test.ts +0 -246
  305. package/tests/client/components/fields/NumberField.test.ts +0 -240
  306. package/tests/client/components/fields/ObjectField.test.ts +0 -157
  307. package/tests/client/components/fields/SchemaField.test.ts +0 -190
  308. package/tests/client/components/fields/StringField.test.ts +0 -223
  309. package/tests/client/components/sidebar/AdminSidebar.test.ts +0 -285
  310. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +0 -135
  311. package/tests/client/components/sidebar/sort-mock.ts +0 -23
  312. package/tests/client/js/drafts/fixtures.ts +0 -22
  313. package/tests/client/js/drafts/merge.test.ts +0 -282
  314. package/tests/client/js/drafts/ops.test.ts +0 -658
  315. package/tests/client/js/drafts/storage.test.ts +0 -200
  316. package/tests/client/js/drafts/workers/diff.test.ts +0 -165
  317. package/tests/client/js/editor/editor.test.ts +0 -616
  318. package/tests/client/js/editor/link-wrap.test.ts +0 -225
  319. package/tests/client/js/editor/markdown-shortcuts.test.ts +0 -370
  320. package/tests/client/js/handlers/admin.test.ts +0 -467
  321. package/tests/client/js/state/router.test.ts +0 -619
  322. package/tests/client/js/state/schema.test.ts +0 -266
  323. package/tests/client/js/state/state.test.ts +0 -328
  324. package/tests/client/js/storage/adapter.test.ts +0 -115
  325. package/tests/client/js/storage/client.test.ts +0 -250
  326. package/tests/client/js/storage/db.test.ts +0 -59
  327. package/tests/client/js/storage/fsa.test.ts +0 -284
  328. package/tests/client/js/storage/github.test.ts +0 -349
  329. package/tests/client/js/storage/mock-port.ts +0 -95
  330. package/tests/client/js/storage/storage.test.ts +0 -77
  331. package/tests/client/js/storage/workers/frontmatter.test.ts +0 -479
  332. package/tests/client/js/storage/workers/storage.test.ts +0 -299
  333. package/tests/client/js/storage/workers/toml-parser.test.ts +0 -169
  334. package/tests/client/js/storage/workers/yaml-parser.test.ts +0 -168
  335. package/tests/client/js/utils/file-types.test.ts +0 -268
  336. package/tests/client/js/utils/frontmatter.test.ts +0 -87
  337. package/tests/client/js/utils/schema-utils.test.ts +0 -318
  338. package/tests/client/js/utils/slug.test.ts +0 -58
  339. package/tests/client/js/utils/sort.test.ts +0 -276
  340. package/tests/client/js/utils/stable-stringify.test.ts +0 -68
  341. package/tests/client/js/utils/url-utils.test.ts +0 -70
  342. package/tests/e2e/backend-connection.test.ts +0 -301
  343. package/tests/e2e/draft-lifecycle.test.ts +0 -388
  344. package/tests/e2e/editing.test.ts +0 -355
  345. package/tests/e2e/github-adapter.test.ts +0 -330
  346. package/tests/e2e/helpers/mock-adapter.ts +0 -166
  347. package/tests/e2e/helpers/test-app.ts +0 -155
  348. package/tests/e2e/navigation.test.ts +0 -358
  349. package/tests/e2e/publishing.test.ts +0 -345
  350. package/tests/e2e/unsaved-changes.test.ts +0 -317
  351. package/tests/setup.ts +0 -2
  352. package/tests/stubs/codemirror.ts +0 -197
  353. package/tsconfig.json +0 -19
  354. package/vitest.config.ts +0 -178
  355. /package/{src → dist}/client/Admin.svelte +0 -0
  356. /package/{src → dist}/client/components/BackendPicker.svelte +0 -0
  357. /package/{src → dist}/client/components/DraftChip.svelte +0 -0
  358. /package/{src → dist}/client/components/MetadataForm.svelte +0 -0
  359. /package/{src → dist}/client/components/ThemeToggle.svelte +0 -0
  360. /package/{src → dist}/client/components/dialogs/DeleteDraftDialog.svelte +0 -0
  361. /package/{src → dist}/client/components/dialogs/FilenameDialog.svelte +0 -0
  362. /package/{src → dist}/client/components/editor/EditorPane.svelte +0 -0
  363. /package/{src → dist}/client/components/editor/EditorTabs.svelte +0 -0
  364. /package/{src → dist}/client/components/editor/EditorToolbar.svelte +0 -0
  365. /package/{src → dist}/client/components/editor/FormatSelector.svelte +0 -0
  366. /package/{src → dist}/client/components/editor/Toolbar.svelte +0 -0
  367. /package/{src → dist}/client/components/fields/ArrayField.svelte +0 -0
  368. /package/{src → dist}/client/components/fields/ArrayItem.svelte +0 -0
  369. /package/{src → dist}/client/components/fields/BooleanField.svelte +0 -0
  370. /package/{src → dist}/client/components/fields/DateField.svelte +0 -0
  371. /package/{src → dist}/client/components/fields/EnumField.svelte +0 -0
  372. /package/{src → dist}/client/components/fields/FieldWrapper.svelte +0 -0
  373. /package/{src → dist}/client/components/fields/NumberField.svelte +0 -0
  374. /package/{src → dist}/client/components/fields/ObjectField.svelte +0 -0
  375. /package/{src → dist}/client/components/fields/SchemaField.svelte +0 -0
  376. /package/{src → dist}/client/components/fields/StringField.svelte +0 -0
  377. /package/{src → dist}/client/components/sidebar/AdminSidebar.svelte +0 -0
  378. /package/{src → dist}/client/components/sidebar/AdminSidebarSort.svelte +0 -0
  379. /package/{src → dist}/client/css/a11y.css +0 -0
  380. /package/{src → dist}/client/css/btn.css +0 -0
  381. /package/{src → dist}/client/css/dialog.css +0 -0
  382. /package/{src → dist}/client/css/field-input.css +0 -0
  383. /package/{src → dist}/client/css/reset.css +0 -0
  384. /package/{src → dist}/client/css/theme.css +0 -0
  385. /package/{src/client/index.ts → dist/client/index.js} +0 -0
  386. /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
- });