nebula-cms 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/README.md +2 -0
  2. package/dist/astro/index.d.ts +43 -0
  3. package/dist/astro/index.d.ts.map +1 -0
  4. package/dist/astro/index.js +223 -0
  5. package/dist/client/Admin.svelte.d.ts +11 -0
  6. package/dist/client/Admin.svelte.d.ts.map +1 -0
  7. package/dist/client/components/BackendPicker.svelte.d.ts +4 -0
  8. package/dist/client/components/BackendPicker.svelte.d.ts.map +1 -0
  9. package/dist/client/components/DraftChip.svelte.d.ts +10 -0
  10. package/dist/client/components/DraftChip.svelte.d.ts.map +1 -0
  11. package/dist/client/components/MetadataForm.svelte.d.ts +12 -0
  12. package/dist/client/components/MetadataForm.svelte.d.ts.map +1 -0
  13. package/dist/client/components/ThemeToggle.svelte.d.ts +19 -0
  14. package/dist/client/components/ThemeToggle.svelte.d.ts.map +1 -0
  15. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts +11 -0
  16. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts.map +1 -0
  17. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts +13 -0
  18. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts.map +1 -0
  19. package/dist/client/components/editor/EditorPane.svelte.d.ts +4 -0
  20. package/dist/client/components/editor/EditorPane.svelte.d.ts.map +1 -0
  21. package/dist/client/components/editor/EditorTabs.svelte.d.ts +8 -0
  22. package/dist/client/components/editor/EditorTabs.svelte.d.ts.map +1 -0
  23. package/dist/client/components/editor/EditorToolbar.svelte.d.ts +4 -0
  24. package/dist/client/components/editor/EditorToolbar.svelte.d.ts.map +1 -0
  25. package/dist/client/components/editor/FormatSelector.svelte.d.ts +4 -0
  26. package/dist/client/components/editor/FormatSelector.svelte.d.ts.map +1 -0
  27. package/dist/client/components/editor/Toolbar.svelte.d.ts +19 -0
  28. package/dist/client/components/editor/Toolbar.svelte.d.ts.map +1 -0
  29. package/dist/client/components/fields/ArrayField.svelte.d.ts +15 -0
  30. package/dist/client/components/fields/ArrayField.svelte.d.ts.map +1 -0
  31. package/dist/client/components/fields/ArrayItem.svelte.d.ts +28 -0
  32. package/dist/client/components/fields/ArrayItem.svelte.d.ts.map +1 -0
  33. package/dist/client/components/fields/BooleanField.svelte.d.ts +16 -0
  34. package/dist/client/components/fields/BooleanField.svelte.d.ts.map +1 -0
  35. package/dist/client/components/fields/DateField.svelte.d.ts +16 -0
  36. package/dist/client/components/fields/DateField.svelte.d.ts.map +1 -0
  37. package/dist/client/components/fields/EnumField.svelte.d.ts +17 -0
  38. package/dist/client/components/fields/EnumField.svelte.d.ts.map +1 -0
  39. package/dist/client/components/fields/FieldWrapper.svelte.d.ts +18 -0
  40. package/dist/client/components/fields/FieldWrapper.svelte.d.ts.map +1 -0
  41. package/dist/client/components/fields/NumberField.svelte.d.ts +16 -0
  42. package/dist/client/components/fields/NumberField.svelte.d.ts.map +1 -0
  43. package/dist/client/components/fields/ObjectField.svelte.d.ts +16 -0
  44. package/dist/client/components/fields/ObjectField.svelte.d.ts.map +1 -0
  45. package/dist/client/components/fields/SchemaField.svelte.d.ts +16 -0
  46. package/dist/client/components/fields/SchemaField.svelte.d.ts.map +1 -0
  47. package/dist/client/components/fields/StringField.svelte.d.ts +16 -0
  48. package/dist/client/components/fields/StringField.svelte.d.ts.map +1 -0
  49. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts +19 -0
  50. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts.map +1 -0
  51. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts +12 -0
  52. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts.map +1 -0
  53. package/dist/client/css/icons.css +29 -0
  54. package/dist/client/index.d.ts +2 -0
  55. package/dist/client/index.d.ts.map +1 -0
  56. package/dist/client/js/drafts/merge.svelte.d.ts +24 -0
  57. package/dist/client/js/drafts/merge.svelte.d.ts.map +1 -0
  58. package/dist/client/js/drafts/merge.svelte.js +106 -0
  59. package/dist/client/js/drafts/ops.svelte.d.ts +31 -0
  60. package/dist/client/js/drafts/ops.svelte.d.ts.map +1 -0
  61. package/dist/client/js/drafts/ops.svelte.js +182 -0
  62. package/dist/client/js/drafts/storage.d.ts +45 -0
  63. package/dist/client/js/drafts/storage.d.ts.map +1 -0
  64. package/dist/client/js/drafts/storage.js +76 -0
  65. package/dist/client/js/drafts/workers/diff.d.ts +2 -0
  66. package/dist/client/js/drafts/workers/diff.d.ts.map +1 -0
  67. package/dist/client/js/drafts/workers/diff.js +20 -0
  68. package/dist/client/js/editor/editor.svelte.d.ts +124 -0
  69. package/dist/client/js/editor/editor.svelte.d.ts.map +1 -0
  70. package/dist/client/js/editor/editor.svelte.js +294 -0
  71. package/dist/client/js/editor/languages.d.ts +11 -0
  72. package/dist/client/js/editor/languages.d.ts.map +1 -0
  73. package/dist/client/js/editor/languages.js +93 -0
  74. package/dist/client/js/editor/link-wrap.d.ts +6 -0
  75. package/dist/client/js/editor/link-wrap.d.ts.map +1 -0
  76. package/{src/client/js/editor/link-wrap.ts → dist/client/js/editor/link-wrap.js} +17 -24
  77. package/dist/client/js/editor/markdown-shortcuts.d.ts +4 -0
  78. package/dist/client/js/editor/markdown-shortcuts.d.ts.map +1 -0
  79. package/dist/client/js/editor/markdown-shortcuts.js +219 -0
  80. package/dist/client/js/handlers/admin.d.ts +64 -0
  81. package/dist/client/js/handlers/admin.d.ts.map +1 -0
  82. package/dist/client/js/handlers/admin.js +186 -0
  83. package/dist/client/js/state/dialogs.svelte.d.ts +16 -0
  84. package/dist/client/js/state/dialogs.svelte.d.ts.map +1 -0
  85. package/dist/client/js/state/dialogs.svelte.js +28 -0
  86. package/dist/client/js/state/router.svelte.d.ts +44 -0
  87. package/dist/client/js/state/router.svelte.d.ts.map +1 -0
  88. package/dist/client/js/state/router.svelte.js +135 -0
  89. package/dist/client/js/state/schema.svelte.d.ts +51 -0
  90. package/dist/client/js/state/schema.svelte.d.ts.map +1 -0
  91. package/{src/client/js/state/schema.svelte.ts → dist/client/js/state/schema.svelte.js} +55 -70
  92. package/dist/client/js/state/state.svelte.d.ts +68 -0
  93. package/dist/client/js/state/state.svelte.d.ts.map +1 -0
  94. package/dist/client/js/state/state.svelte.js +291 -0
  95. package/dist/client/js/state/theme.svelte.d.ts +24 -0
  96. package/dist/client/js/state/theme.svelte.d.ts.map +1 -0
  97. package/{src/client/js/state/theme.svelte.ts → dist/client/js/state/theme.svelte.js} +54 -91
  98. package/dist/client/js/storage/adapter.d.ts +130 -0
  99. package/dist/client/js/storage/adapter.d.ts.map +1 -0
  100. package/dist/client/js/storage/adapter.js +5 -0
  101. package/dist/client/js/storage/client.d.ts +72 -0
  102. package/dist/client/js/storage/client.d.ts.map +1 -0
  103. package/dist/client/js/storage/client.js +121 -0
  104. package/dist/client/js/storage/db.d.ts +8 -0
  105. package/dist/client/js/storage/db.d.ts.map +1 -0
  106. package/dist/client/js/storage/db.js +35 -0
  107. package/dist/client/js/storage/fsa.d.ts +51 -0
  108. package/dist/client/js/storage/fsa.d.ts.map +1 -0
  109. package/dist/client/js/storage/fsa.js +91 -0
  110. package/dist/client/js/storage/github.d.ts +62 -0
  111. package/dist/client/js/storage/github.d.ts.map +1 -0
  112. package/dist/client/js/storage/github.js +216 -0
  113. package/dist/client/js/storage/storage.d.ts +32 -0
  114. package/dist/client/js/storage/storage.d.ts.map +1 -0
  115. package/dist/client/js/storage/storage.js +68 -0
  116. package/dist/client/js/storage/workers/frontmatter.d.ts +2 -0
  117. package/dist/client/js/storage/workers/frontmatter.d.ts.map +1 -0
  118. package/dist/client/js/storage/workers/frontmatter.js +253 -0
  119. package/dist/client/js/storage/workers/storage.d.ts +2 -0
  120. package/dist/client/js/storage/workers/storage.d.ts.map +1 -0
  121. package/dist/client/js/storage/workers/storage.js +167 -0
  122. package/dist/client/js/storage/workers/toml-parser.d.ts +2 -0
  123. package/dist/client/js/storage/workers/toml-parser.d.ts.map +1 -0
  124. package/dist/client/js/storage/workers/toml-parser.js +75 -0
  125. package/dist/client/js/storage/workers/yaml-parser.d.ts +2 -0
  126. package/dist/client/js/storage/workers/yaml-parser.d.ts.map +1 -0
  127. package/dist/client/js/storage/workers/yaml-parser.js +100 -0
  128. package/dist/client/js/utils/file-types.d.ts +58 -0
  129. package/dist/client/js/utils/file-types.d.ts.map +1 -0
  130. package/{src/client/js/utils/file-types.ts → dist/client/js/utils/file-types.js} +75 -107
  131. package/dist/client/js/utils/format.d.ts +8 -0
  132. package/dist/client/js/utils/format.d.ts.map +1 -0
  133. package/{src/client/js/utils/format.ts → dist/client/js/utils/format.js} +5 -6
  134. package/dist/client/js/utils/frontmatter.d.ts +12 -0
  135. package/dist/client/js/utils/frontmatter.d.ts.map +1 -0
  136. package/dist/client/js/utils/frontmatter.js +29 -0
  137. package/dist/client/js/utils/schema-utils.d.ts +110 -0
  138. package/dist/client/js/utils/schema-utils.d.ts.map +1 -0
  139. package/dist/client/js/utils/schema-utils.js +242 -0
  140. package/dist/client/js/utils/slug.d.ts +8 -0
  141. package/dist/client/js/utils/slug.d.ts.map +1 -0
  142. package/{src/client/js/utils/slug.ts → dist/client/js/utils/slug.js} +6 -7
  143. package/dist/client/js/utils/sort.d.ts +41 -0
  144. package/dist/client/js/utils/sort.d.ts.map +1 -0
  145. package/dist/client/js/utils/sort.js +65 -0
  146. package/dist/client/js/utils/stable-stringify.d.ts +8 -0
  147. package/dist/client/js/utils/stable-stringify.d.ts.map +1 -0
  148. package/dist/client/js/utils/stable-stringify.js +23 -0
  149. package/dist/client/js/utils/url-utils.d.ts +11 -0
  150. package/dist/client/js/utils/url-utils.d.ts.map +1 -0
  151. package/{src/client/js/utils/url-utils.ts → dist/client/js/utils/url-utils.js} +22 -23
  152. package/dist/types.d.ts +22 -0
  153. package/dist/types.d.ts.map +1 -0
  154. package/dist/types.js +1 -0
  155. package/package.json +4 -1
  156. package/.github/workflows/ci.yml +0 -27
  157. package/.github/workflows/publish.yml +0 -34
  158. package/.mcp.json +0 -12
  159. package/.prettierignore +0 -5
  160. package/.prettierrc.cjs +0 -22
  161. package/AGENTS.md +0 -183
  162. package/playground/astro.config.mjs +0 -7
  163. package/playground/node_modules/.bin/astro +0 -21
  164. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +0 -85
  165. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +0 -7
  166. package/playground/node_modules/.vite/deps/_metadata.json +0 -184
  167. package/playground/node_modules/.vite/deps/astro___aria-query.js +0 -6776
  168. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
  169. package/playground/node_modules/.vite/deps/astro___axobject-query.js +0 -3754
  170. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
  171. package/playground/node_modules/.vite/deps/astro___html-escaper.js +0 -34
  172. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
  173. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  174. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +0 -7
  175. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +0 -8
  176. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +0 -7
  177. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +0 -21
  178. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +0 -7
  179. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +0 -223
  180. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +0 -7
  181. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js +0 -27
  182. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js.map +0 -7
  183. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js +0 -1005
  184. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js.map +0 -7
  185. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js +0 -204
  186. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js.map +0 -7
  187. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js +0 -688
  188. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js.map +0 -7
  189. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js +0 -5099
  190. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js.map +0 -7
  191. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js +0 -4376
  192. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js.map +0 -7
  193. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +0 -23
  194. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +0 -7
  195. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +0 -148
  196. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +0 -7
  197. package/playground/node_modules/.vite/deps/package.json +0 -3
  198. package/playground/node_modules/.vite/deps/smol-toml.js +0 -843
  199. package/playground/node_modules/.vite/deps/smol-toml.js.map +0 -7
  200. package/playground/node_modules/.vite/deps/svelte.js +0 -55
  201. package/playground/node_modules/.vite/deps/svelte.js.map +0 -7
  202. package/playground/node_modules/.vite/deps/svelte___clsx.js +0 -9
  203. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +0 -7
  204. package/playground/node_modules/.vite/deps/svelte_animate.js +0 -57
  205. package/playground/node_modules/.vite/deps/svelte_animate.js.map +0 -7
  206. package/playground/node_modules/.vite/deps/svelte_attachments.js +0 -15
  207. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +0 -7
  208. package/playground/node_modules/.vite/deps/svelte_easing.js +0 -67
  209. package/playground/node_modules/.vite/deps/svelte_easing.js.map +0 -7
  210. package/playground/node_modules/.vite/deps/svelte_events.js +0 -11
  211. package/playground/node_modules/.vite/deps/svelte_events.js.map +0 -7
  212. package/playground/node_modules/.vite/deps/svelte_internal.js +0 -5
  213. package/playground/node_modules/.vite/deps/svelte_internal.js.map +0 -7
  214. package/playground/node_modules/.vite/deps/svelte_internal_client.js +0 -402
  215. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +0 -7
  216. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +0 -10
  217. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +0 -7
  218. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +0 -8
  219. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +0 -7
  220. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +0 -8
  221. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +0 -7
  222. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +0 -8
  223. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +0 -7
  224. package/playground/node_modules/.vite/deps/svelte_legacy.js +0 -35
  225. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +0 -7
  226. package/playground/node_modules/.vite/deps/svelte_motion.js +0 -545
  227. package/playground/node_modules/.vite/deps/svelte_motion.js.map +0 -7
  228. package/playground/node_modules/.vite/deps/svelte_reactivity.js +0 -29
  229. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +0 -7
  230. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +0 -127
  231. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +0 -7
  232. package/playground/node_modules/.vite/deps/svelte_store.js +0 -103
  233. package/playground/node_modules/.vite/deps/svelte_store.js.map +0 -7
  234. package/playground/node_modules/.vite/deps/svelte_transition.js +0 -208
  235. package/playground/node_modules/.vite/deps/svelte_transition.js.map +0 -7
  236. package/playground/package.json +0 -16
  237. package/playground/pnpm-lock.yaml +0 -3167
  238. package/playground/src/content/authors/jane-doe.json +0 -8
  239. package/playground/src/content/config/build.toml +0 -2
  240. package/playground/src/content/courses/web-fundamentals.json +0 -29
  241. package/playground/src/content/docs/advanced.mdx +0 -6
  242. package/playground/src/content/docs/intro.md +0 -6
  243. package/playground/src/content/guides/getting-started.mdx +0 -6
  244. package/playground/src/content/posts/hello-world.md +0 -7
  245. package/playground/src/content/products/t-shirt.json +0 -16
  246. package/playground/src/content/recipes/pancakes.mdoc +0 -8
  247. package/playground/src/content/settings/site.yml +0 -2
  248. package/playground/src/content.config.ts +0 -198
  249. package/playground/src/env.d.ts +0 -1
  250. package/playground/src/pages/index.astro +0 -11
  251. package/playground/src/pages/nebula.astro +0 -14
  252. package/pnpm-workspace.yaml +0 -2
  253. package/scripts/subset-icons.mjs +0 -178
  254. package/src/astro/index.ts +0 -295
  255. package/src/client/js/drafts/merge.svelte.ts +0 -121
  256. package/src/client/js/drafts/ops.svelte.ts +0 -227
  257. package/src/client/js/drafts/storage.ts +0 -108
  258. package/src/client/js/drafts/workers/diff.ts +0 -40
  259. package/src/client/js/editor/editor.svelte.ts +0 -343
  260. package/src/client/js/editor/languages.ts +0 -98
  261. package/src/client/js/editor/markdown-shortcuts.ts +0 -261
  262. package/src/client/js/handlers/admin.ts +0 -246
  263. package/src/client/js/state/dialogs.svelte.ts +0 -35
  264. package/src/client/js/state/router.svelte.ts +0 -156
  265. package/src/client/js/state/state.svelte.ts +0 -334
  266. package/src/client/js/storage/adapter.ts +0 -102
  267. package/src/client/js/storage/client.ts +0 -150
  268. package/src/client/js/storage/db.ts +0 -36
  269. package/src/client/js/storage/fsa.ts +0 -110
  270. package/src/client/js/storage/github.ts +0 -297
  271. package/src/client/js/storage/storage.ts +0 -83
  272. package/src/client/js/storage/workers/frontmatter.ts +0 -320
  273. package/src/client/js/storage/workers/storage.ts +0 -177
  274. package/src/client/js/storage/workers/toml-parser.ts +0 -106
  275. package/src/client/js/storage/workers/yaml-parser.ts +0 -132
  276. package/src/client/js/utils/frontmatter.ts +0 -38
  277. package/src/client/js/utils/schema-utils.ts +0 -295
  278. package/src/client/js/utils/sort.ts +0 -84
  279. package/src/client/js/utils/stable-stringify.ts +0 -27
  280. package/src/types.ts +0 -25
  281. package/svelte.config.js +0 -4
  282. package/tests/astro/build.test.ts +0 -63
  283. package/tests/astro/index.test.ts +0 -689
  284. package/tests/client/components/Admin.test.ts +0 -446
  285. package/tests/client/components/BackendPicker.test.ts +0 -239
  286. package/tests/client/components/DraftChip.test.ts +0 -53
  287. package/tests/client/components/MetadataForm.test.ts +0 -164
  288. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +0 -91
  289. package/tests/client/components/dialogs/FilenameDialog.test.ts +0 -209
  290. package/tests/client/components/dialogs/dialog-stubs.ts +0 -19
  291. package/tests/client/components/editor/EditorPane.test.ts +0 -100
  292. package/tests/client/components/editor/EditorTabs.test.ts +0 -253
  293. package/tests/client/components/editor/EditorToolbar.test.ts +0 -252
  294. package/tests/client/components/editor/fixtures.ts +0 -31
  295. package/tests/client/components/fields/ArrayField.test.ts +0 -197
  296. package/tests/client/components/fields/BooleanField.test.ts +0 -206
  297. package/tests/client/components/fields/DateField.test.ts +0 -210
  298. package/tests/client/components/fields/EnumField.test.ts +0 -246
  299. package/tests/client/components/fields/NumberField.test.ts +0 -240
  300. package/tests/client/components/fields/ObjectField.test.ts +0 -157
  301. package/tests/client/components/fields/SchemaField.test.ts +0 -190
  302. package/tests/client/components/fields/StringField.test.ts +0 -223
  303. package/tests/client/components/sidebar/AdminSidebar.test.ts +0 -285
  304. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +0 -135
  305. package/tests/client/components/sidebar/sort-mock.ts +0 -23
  306. package/tests/client/js/drafts/fixtures.ts +0 -22
  307. package/tests/client/js/drafts/merge.test.ts +0 -282
  308. package/tests/client/js/drafts/ops.test.ts +0 -658
  309. package/tests/client/js/drafts/storage.test.ts +0 -200
  310. package/tests/client/js/drafts/workers/diff.test.ts +0 -165
  311. package/tests/client/js/editor/editor.test.ts +0 -616
  312. package/tests/client/js/editor/link-wrap.test.ts +0 -225
  313. package/tests/client/js/editor/markdown-shortcuts.test.ts +0 -370
  314. package/tests/client/js/handlers/admin.test.ts +0 -467
  315. package/tests/client/js/state/router.test.ts +0 -619
  316. package/tests/client/js/state/schema.test.ts +0 -266
  317. package/tests/client/js/state/state.test.ts +0 -328
  318. package/tests/client/js/storage/adapter.test.ts +0 -115
  319. package/tests/client/js/storage/client.test.ts +0 -250
  320. package/tests/client/js/storage/db.test.ts +0 -59
  321. package/tests/client/js/storage/fsa.test.ts +0 -284
  322. package/tests/client/js/storage/github.test.ts +0 -349
  323. package/tests/client/js/storage/mock-port.ts +0 -95
  324. package/tests/client/js/storage/storage.test.ts +0 -77
  325. package/tests/client/js/storage/workers/frontmatter.test.ts +0 -479
  326. package/tests/client/js/storage/workers/storage.test.ts +0 -299
  327. package/tests/client/js/storage/workers/toml-parser.test.ts +0 -169
  328. package/tests/client/js/storage/workers/yaml-parser.test.ts +0 -168
  329. package/tests/client/js/utils/file-types.test.ts +0 -268
  330. package/tests/client/js/utils/frontmatter.test.ts +0 -87
  331. package/tests/client/js/utils/schema-utils.test.ts +0 -318
  332. package/tests/client/js/utils/slug.test.ts +0 -58
  333. package/tests/client/js/utils/sort.test.ts +0 -276
  334. package/tests/client/js/utils/stable-stringify.test.ts +0 -68
  335. package/tests/client/js/utils/url-utils.test.ts +0 -70
  336. package/tests/e2e/backend-connection.test.ts +0 -301
  337. package/tests/e2e/draft-lifecycle.test.ts +0 -388
  338. package/tests/e2e/editing.test.ts +0 -355
  339. package/tests/e2e/github-adapter.test.ts +0 -330
  340. package/tests/e2e/helpers/mock-adapter.ts +0 -166
  341. package/tests/e2e/helpers/test-app.ts +0 -155
  342. package/tests/e2e/navigation.test.ts +0 -358
  343. package/tests/e2e/publishing.test.ts +0 -345
  344. package/tests/e2e/unsaved-changes.test.ts +0 -317
  345. package/tests/setup.ts +0 -2
  346. package/tests/stubs/codemirror.ts +0 -197
  347. package/tsconfig.json +0 -19
  348. package/vitest.config.ts +0 -178
  349. /package/{src → dist}/client/Admin.svelte +0 -0
  350. /package/{src → dist}/client/components/BackendPicker.svelte +0 -0
  351. /package/{src → dist}/client/components/DraftChip.svelte +0 -0
  352. /package/{src → dist}/client/components/MetadataForm.svelte +0 -0
  353. /package/{src → dist}/client/components/ThemeToggle.svelte +0 -0
  354. /package/{src → dist}/client/components/dialogs/DeleteDraftDialog.svelte +0 -0
  355. /package/{src → dist}/client/components/dialogs/FilenameDialog.svelte +0 -0
  356. /package/{src → dist}/client/components/editor/EditorPane.svelte +0 -0
  357. /package/{src → dist}/client/components/editor/EditorTabs.svelte +0 -0
  358. /package/{src → dist}/client/components/editor/EditorToolbar.svelte +0 -0
  359. /package/{src → dist}/client/components/editor/FormatSelector.svelte +0 -0
  360. /package/{src → dist}/client/components/editor/Toolbar.svelte +0 -0
  361. /package/{src → dist}/client/components/fields/ArrayField.svelte +0 -0
  362. /package/{src → dist}/client/components/fields/ArrayItem.svelte +0 -0
  363. /package/{src → dist}/client/components/fields/BooleanField.svelte +0 -0
  364. /package/{src → dist}/client/components/fields/DateField.svelte +0 -0
  365. /package/{src → dist}/client/components/fields/EnumField.svelte +0 -0
  366. /package/{src → dist}/client/components/fields/FieldWrapper.svelte +0 -0
  367. /package/{src → dist}/client/components/fields/NumberField.svelte +0 -0
  368. /package/{src → dist}/client/components/fields/ObjectField.svelte +0 -0
  369. /package/{src → dist}/client/components/fields/SchemaField.svelte +0 -0
  370. /package/{src → dist}/client/components/fields/StringField.svelte +0 -0
  371. /package/{src → dist}/client/components/sidebar/AdminSidebar.svelte +0 -0
  372. /package/{src → dist}/client/components/sidebar/AdminSidebarSort.svelte +0 -0
  373. /package/{src → dist}/client/css/a11y.css +0 -0
  374. /package/{src → dist}/client/css/btn.css +0 -0
  375. /package/{src → dist}/client/css/dialog.css +0 -0
  376. /package/{src → dist}/client/css/field-input.css +0 -0
  377. /package/{src → dist}/client/css/reset.css +0 -0
  378. /package/{src → dist}/client/css/theme.css +0 -0
  379. /package/{src/client/index.ts → dist/client/index.js} +0 -0
  380. /package/{src → dist}/virtual.d.ts +0 -0
@@ -1,225 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { syntaxTree } from '@codemirror/language';
3
-
4
- //////////////////////////////
5
- // CodeMirror API mocks
6
- //
7
- // link-wrap.ts uses ViewPlugin, Decoration, RangeSetBuilder from
8
- // @codemirror/view and @codemirror/state, and syntaxTree from
9
- // @codemirror/language. The mocks return the minimal shapes needed
10
- // for the module to load and for the ViewPlugin.define() call to
11
- // complete without throwing. Testing the full decoration pass
12
- // requires a real CodeMirror document tree, which is out of scope
13
- // for a unit test — the structural tests below verify that the
14
- // plugin is correctly defined and wired.
15
- //////////////////////////////
16
-
17
- vi.mock('@codemirror/language', () => ({
18
- syntaxTree: vi.fn(() => ({
19
- /**
20
- * Minimal syntax tree stub — calls enter once with a non-Link node
21
- * so buildDecorations completes without adding any decorations.
22
- * @param {{ enter: (node: { name: string, from: number, to: number }) => void }} opts - Iteration options
23
- * @return {void}
24
- */
25
- iterate(opts: {
26
- enter: (node: { name: string; from: number; to: number }) => void;
27
- }) {
28
- // Deliberately visit a non-Link node to exercise the filtering branch
29
- opts.enter({ name: 'Document', from: 0, to: 10 });
30
- },
31
- })),
32
- }));
33
-
34
- vi.mock('@codemirror/state', () => {
35
- /**
36
- * Minimal RangeSetBuilder stub that satisfies the builder.finish() call.
37
- * No decorations are added in unit tests (the syntax tree is empty),
38
- * so finish() just needs to return a stable value.
39
- */
40
- class RangeSetBuilder {
41
- /**
42
- * Adds a range — no-op in the stub.
43
- * @param {number} _from - Range start
44
- * @param {number} _to - Range end
45
- * @param {unknown} _value - Decoration value
46
- * @return {void}
47
- */
48
- add(_from: number, _to: number, _value: unknown): void {}
49
-
50
- /**
51
- * Finishes the builder and returns the collected decoration set.
52
- * @return {unknown[]} Empty decoration set stub
53
- */
54
- finish(): unknown[] {
55
- return [];
56
- }
57
- }
58
- return { RangeSetBuilder };
59
- });
60
-
61
- vi.mock('@codemirror/view', () => {
62
- /** Decoration stub that provides a mark() factory. */
63
- const Decoration = {
64
- /**
65
- * Creates a mark decoration with the given options.
66
- * @param {{ class: string }} opts - Decoration options
67
- * @return {{ class: string }} A minimal mark decoration stub
68
- */
69
- mark(opts: { class: string }) {
70
- return opts;
71
- },
72
- };
73
-
74
- /**
75
- * ViewPlugin stub that records the define() call parameters
76
- * and returns a stable fake extension object.
77
- */
78
- const ViewPlugin = {
79
- _lastFactory: null as unknown,
80
- _lastConfig: null as unknown,
81
-
82
- /**
83
- * Captures the factory and config and returns a fake plugin extension.
84
- * @param {(view: unknown) => unknown} factory - The plugin instance factory
85
- * @param {{ decorations: (v: unknown) => unknown }} config - Plugin configuration
86
- * @return {{ isViewPlugin: true, factory: Function, config: object }} Fake extension
87
- */
88
- define(
89
- factory: (view: unknown) => unknown,
90
- config: { decorations: (v: unknown) => unknown },
91
- ) {
92
- ViewPlugin._lastFactory = factory;
93
- ViewPlugin._lastConfig = config;
94
- return { isViewPlugin: true, factory, config };
95
- },
96
- };
97
-
98
- return { ViewPlugin, Decoration };
99
- });
100
-
101
- import { linkWrapPlugin } from '../../../../src/client/js/editor/link-wrap';
102
-
103
- //////////////////////////////
104
- // linkWrapPlugin structural tests
105
- //////////////////////////////
106
-
107
- describe('linkWrapPlugin', () => {
108
- it('is exported and truthy', () => {
109
- expect(linkWrapPlugin).toBeTruthy();
110
- });
111
-
112
- it('is the result of ViewPlugin.define()', () => {
113
- // The mock returns an object with isViewPlugin: true for anything
114
- // produced by ViewPlugin.define()
115
- expect((linkWrapPlugin as any).isViewPlugin).toBe(true);
116
- });
117
-
118
- it('exposes a decorations accessor via the config', () => {
119
- // ViewPlugin.define() must receive a config with a decorations getter
120
- const config = (linkWrapPlugin as any).config as {
121
- decorations: (v: unknown) => unknown;
122
- };
123
- expect(typeof config.decorations).toBe('function');
124
- });
125
-
126
- it('decorations accessor reads the decorations property from the plugin instance', () => {
127
- const config = (linkWrapPlugin as any).config as {
128
- decorations: (v: unknown) => unknown;
129
- };
130
- const fakeInstance = { decorations: ['decoration-sentinel'] };
131
- expect(config.decorations(fakeInstance)).toBe(fakeInstance.decorations);
132
- });
133
-
134
- it('plugin factory produces an object with a decorations property', () => {
135
- const factory = (linkWrapPlugin as any).factory as (view: unknown) => {
136
- decorations: unknown;
137
- update: (update: {
138
- docChanged: boolean;
139
- viewportChanged: boolean;
140
- state: unknown;
141
- }) => void;
142
- };
143
- // Provide a minimal EditorView stub with a state that has a doc
144
- const fakeView = {
145
- state: {
146
- doc: { length: 0 },
147
- },
148
- };
149
- const instance = factory(fakeView);
150
- expect(instance).toHaveProperty('decorations');
151
- });
152
-
153
- it('plugin update method re-builds decorations when doc changes', () => {
154
- const factory = (linkWrapPlugin as any).factory as (view: unknown) => {
155
- decorations: unknown;
156
- update: (update: {
157
- docChanged: boolean;
158
- viewportChanged: boolean;
159
- state: unknown;
160
- }) => void;
161
- };
162
- const fakeView = { state: { doc: { length: 0 } } };
163
- const instance = factory(fakeView);
164
- const before = instance.decorations;
165
-
166
- instance.update({
167
- docChanged: true,
168
- viewportChanged: false,
169
- state: fakeView.state,
170
- });
171
-
172
- // The decorations property should have been reassigned
173
- expect(instance).toHaveProperty('decorations');
174
- // The value comes from buildDecorations (which calls builder.finish() → [])
175
- // — just verify it is defined after update
176
- expect(instance.decorations).toBeDefined();
177
- // Silence the "unused variable" warning for before
178
- void before;
179
- });
180
-
181
- it('plugin update method re-builds decorations when viewport changes', () => {
182
- const factory = (linkWrapPlugin as any).factory as (view: unknown) => {
183
- decorations: unknown;
184
- update: (update: {
185
- docChanged: boolean;
186
- viewportChanged: boolean;
187
- state: unknown;
188
- }) => void;
189
- };
190
- const fakeView = { state: { doc: { length: 0 } } };
191
- const instance = factory(fakeView);
192
-
193
- instance.update({
194
- docChanged: false,
195
- viewportChanged: true,
196
- state: fakeView.state,
197
- });
198
- expect(instance.decorations).toBeDefined();
199
- });
200
-
201
- it('plugin update method does not rebuild when nothing changed', () => {
202
- const factory = (linkWrapPlugin as any).factory as (view: unknown) => {
203
- decorations: unknown;
204
- update: (update: {
205
- docChanged: boolean;
206
- viewportChanged: boolean;
207
- state: unknown;
208
- }) => void;
209
- };
210
-
211
- const fakeView = { state: { doc: { length: 0 } } };
212
- const instance = factory(fakeView);
213
- // Record call count after construction (buildDecorations is called once in the factory)
214
- const callsBefore = vi.mocked(syntaxTree).mock.calls.length;
215
-
216
- instance.update({
217
- docChanged: false,
218
- viewportChanged: false,
219
- state: fakeView.state,
220
- });
221
-
222
- // syntaxTree should not have been called again for the no-change update
223
- expect(vi.mocked(syntaxTree).mock.calls.length).toBe(callsBefore);
224
- });
225
- });
@@ -1,370 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
-
3
- //////////////////////////////
4
- // CodeMirror API mocks
5
- //
6
- // markdown-shortcuts.ts depends on @codemirror/view, @codemirror/state,
7
- // and @codemirror/language. The mocks provide minimal implementations
8
- // that allow the module to load and for the key handler functions to
9
- // be exercised with controlled state. The EditorView.dispatch mock
10
- // captures dispatched transactions so assertions can inspect them.
11
- //////////////////////////////
12
-
13
- vi.mock('@codemirror/language', () => ({
14
- syntaxTree: vi.fn(() => ({
15
- /**
16
- * Stub syntax tree that supports targeted iteration and returns
17
- * a Link or StrongEmphasis/Emphasis node based on a global flag.
18
- * @param {{ from: number, to: number, enter: (node: { name: string, from: number, to: number }) => void }} opts - Iteration options
19
- * @return {void}
20
- */
21
- iterate(opts: {
22
- from?: number;
23
- to?: number;
24
- enter: (node: { name: string; from: number; to: number }) => void;
25
- }) {
26
- // Do not visit any nodes by default — tests override syntaxTree as needed
27
- },
28
- })),
29
- }));
30
-
31
- vi.mock('@codemirror/state', () => {
32
- /**
33
- * Minimal EditorSelection implementation supporting cursor and range creation.
34
- * Matches the shape used by the command handlers.
35
- */
36
- const EditorSelection = {
37
- /**
38
- * Creates a cursor (collapsed) selection at the given position.
39
- * @param {number} pos - The cursor position
40
- * @return {{ anchor: number, head: number, empty: boolean }} Cursor selection
41
- */
42
- cursor(pos: number) {
43
- return { anchor: pos, head: pos, empty: true };
44
- },
45
-
46
- /**
47
- * Creates a ranged selection between from and to.
48
- * @param {number} from - Selection start
49
- * @param {number} to - Selection end
50
- * @return {{ anchor: number, head: number, empty: boolean }} Range selection
51
- */
52
- range(from: number, to: number) {
53
- return { anchor: from, head: to, empty: from === to };
54
- },
55
- };
56
-
57
- return { EditorSelection };
58
- });
59
-
60
- vi.mock('@codemirror/view', () => {
61
- /**
62
- * Stub EditorView class with a controllable dispatch spy.
63
- * Each test constructs a fresh instance so dispatch calls are isolated.
64
- */
65
- class EditorView {
66
- state: {
67
- selection: { main: { from: number; to: number; empty: boolean } };
68
- doc: { length: number };
69
- sliceDoc: (from: number, to: number) => string;
70
- changeByRange: (
71
- fn: (range: { from: number; to: number; empty: boolean }) => {
72
- range: unknown;
73
- changes: unknown;
74
- },
75
- ) => { changes: unknown; selection: unknown };
76
- };
77
-
78
- dispatch = vi.fn();
79
-
80
- /**
81
- * Creates a minimal EditorView stub with controllable selection and doc content.
82
- * @param {{ from: number, to: number, empty: boolean }} selection - The main selection
83
- * @param {string} docContent - The full document content string
84
- */
85
- constructor(
86
- selection: { from: number; to: number; empty: boolean } = {
87
- from: 0,
88
- to: 0,
89
- empty: true,
90
- },
91
- docContent = '',
92
- ) {
93
- this.state = {
94
- selection: { main: selection },
95
- doc: { length: docContent.length },
96
- sliceDoc: (from: number, to: number) => docContent.slice(from, to),
97
- changeByRange: (fn) => {
98
- const result = fn(selection);
99
- return { changes: result.changes, selection: result.range };
100
- },
101
- };
102
- }
103
- }
104
-
105
- /** Minimal domEventHandlers stub — captures the handler object and returns a fake extension. */
106
- const domEventHandlersResults: unknown[] = [];
107
- EditorView.domEventHandlers = vi.fn((handlers: unknown) => {
108
- domEventHandlersResults.push(handlers);
109
- return { isDomEventHandler: true, handlers };
110
- });
111
-
112
- /** Minimal inputHandler.of stub — returns a fake extension. */
113
- EditorView.inputHandler = {
114
- of: vi.fn((fn: unknown) => ({ isInputHandler: true, fn })),
115
- };
116
-
117
- return { EditorView };
118
- });
119
-
120
- vi.mock('../utils/url-utils', async () => {
121
- const actual = await import('../../../../src/client/js/utils/url-utils');
122
- return { isURL: actual.isURL };
123
- });
124
-
125
- import { syntaxTree } from '@codemirror/language';
126
- import { EditorSelection } from '@codemirror/state';
127
- import { EditorView } from '@codemirror/view';
128
- import {
129
- markdownShortcutsKeymap,
130
- markdownShortcutsExtensions,
131
- } from '../../../../src/client/js/editor/markdown-shortcuts';
132
-
133
- //////////////////////////////
134
- // markdownShortcutsKeymap — structure
135
- //////////////////////////////
136
-
137
- describe('markdownShortcutsKeymap', () => {
138
- it('is an array', () => {
139
- expect(Array.isArray(markdownShortcutsKeymap)).toBe(true);
140
- });
141
-
142
- it('contains exactly 3 key bindings', () => {
143
- expect(markdownShortcutsKeymap).toHaveLength(3);
144
- });
145
-
146
- it('contains a Mod-b binding for bold', () => {
147
- const binding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-b');
148
- expect(binding).toBeDefined();
149
- expect(typeof binding?.run).toBe('function');
150
- });
151
-
152
- it('contains a Mod-i binding for italic', () => {
153
- const binding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-i');
154
- expect(binding).toBeDefined();
155
- expect(typeof binding?.run).toBe('function');
156
- });
157
-
158
- it('contains a Mod-k binding for link insertion', () => {
159
- const binding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-k');
160
- expect(binding).toBeDefined();
161
- expect(typeof binding?.run).toBe('function');
162
- });
163
- });
164
-
165
- //////////////////////////////
166
- // markdownShortcutsExtensions — structure
167
- //////////////////////////////
168
-
169
- describe('markdownShortcutsExtensions', () => {
170
- it('is an array', () => {
171
- expect(Array.isArray(markdownShortcutsExtensions)).toBe(true);
172
- });
173
-
174
- it('contains exactly 2 extensions (smart paste + bracket wrap)', () => {
175
- expect(markdownShortcutsExtensions).toHaveLength(2);
176
- });
177
-
178
- it('all items are truthy (valid extension objects)', () => {
179
- for (const ext of markdownShortcutsExtensions) {
180
- expect(ext).toBeTruthy();
181
- }
182
- });
183
- });
184
-
185
- //////////////////////////////
186
- // Mod-b (bold) handler — toggleMarker('**', 'StrongEmphasis')
187
- //////////////////////////////
188
-
189
- describe('Mod-b handler — wrapping', () => {
190
- it('wraps a selection in ** markers', () => {
191
- // No wrapping node found — falls through to wrap-selection branch
192
- vi.mocked(syntaxTree).mockReturnValue({
193
- iterate: vi.fn(),
194
- } as any);
195
-
196
- const boldBinding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-b')!;
197
- // Selection covering "World" in "Hello World" (from=6, to=11)
198
- const view = new (EditorView as any)(
199
- { from: 6, to: 11, empty: false },
200
- 'Hello World',
201
- );
202
- const result = boldBinding.run(view as any);
203
- expect(result).toBe(true);
204
- expect(view.dispatch).toHaveBeenCalled();
205
- const transaction = view.dispatch.mock.calls[0][0];
206
- // changes should include inserting ** at both ends
207
- expect(transaction.changes).toBeDefined();
208
- });
209
-
210
- it('inserts empty ** markers when selection is empty', () => {
211
- vi.mocked(syntaxTree).mockReturnValue({
212
- iterate: vi.fn(),
213
- } as any);
214
-
215
- const boldBinding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-b')!;
216
- const view = new (EditorView as any)(
217
- { from: 5, to: 5, empty: true },
218
- 'Hello World',
219
- );
220
- const result = boldBinding.run(view as any);
221
- expect(result).toBe(true);
222
- expect(view.dispatch).toHaveBeenCalled();
223
- });
224
-
225
- it('unwraps when cursor is inside a StrongEmphasis node', () => {
226
- // Simulate the syntax tree finding a StrongEmphasis node at the cursor position
227
- vi.mocked(syntaxTree).mockReturnValue({
228
- iterate: vi.fn(
229
- (opts: {
230
- enter: (n: { name: string; from: number; to: number }) => void;
231
- }) => {
232
- opts.enter({ name: 'StrongEmphasis', from: 0, to: 13 });
233
- },
234
- ),
235
- } as any);
236
-
237
- const boldBinding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-b')!;
238
- const view = new (EditorView as any)(
239
- { from: 5, to: 5, empty: true },
240
- '**Hello World**',
241
- );
242
- const result = boldBinding.run(view as any);
243
- expect(result).toBe(true);
244
- expect(view.dispatch).toHaveBeenCalled();
245
- const transaction = view.dispatch.mock.calls[0][0];
246
- // Unwrap produces changes that remove the markers
247
- expect(transaction.changes).toBeDefined();
248
- });
249
- });
250
-
251
- //////////////////////////////
252
- // Mod-i (italic) handler — toggleMarker('_', 'Emphasis')
253
- //////////////////////////////
254
-
255
- describe('Mod-i handler — wrapping', () => {
256
- it('wraps a selection in _ markers', () => {
257
- vi.mocked(syntaxTree).mockReturnValue({ iterate: vi.fn() } as any);
258
-
259
- const italicBinding = markdownShortcutsKeymap.find(
260
- (b) => b.key === 'Mod-i',
261
- )!;
262
- const view = new (EditorView as any)(
263
- { from: 0, to: 5, empty: false },
264
- 'Hello World',
265
- );
266
- const result = italicBinding.run(view as any);
267
- expect(result).toBe(true);
268
- expect(view.dispatch).toHaveBeenCalled();
269
- });
270
-
271
- it('inserts empty _ markers when no selection', () => {
272
- vi.mocked(syntaxTree).mockReturnValue({ iterate: vi.fn() } as any);
273
-
274
- const italicBinding = markdownShortcutsKeymap.find(
275
- (b) => b.key === 'Mod-i',
276
- )!;
277
- const view = new (EditorView as any)(
278
- { from: 3, to: 3, empty: true },
279
- 'Hello',
280
- );
281
- const result = italicBinding.run(view as any);
282
- expect(result).toBe(true);
283
- expect(view.dispatch).toHaveBeenCalled();
284
- });
285
- });
286
-
287
- //////////////////////////////
288
- // Mod-k (link) handler — insertLink
289
- //////////////////////////////
290
-
291
- describe('Mod-k handler — link insertion', () => {
292
- it('inserts []() with no selection and places cursor inside []', () => {
293
- // No Link node at cursor
294
- vi.mocked(syntaxTree).mockReturnValue({ iterate: vi.fn() } as any);
295
-
296
- const linkBinding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-k')!;
297
- const view = new (EditorView as any)({ from: 0, to: 0, empty: true }, '');
298
- const result = linkBinding.run(view as any);
299
- expect(result).toBe(true);
300
- expect(view.dispatch).toHaveBeenCalled();
301
- const tx = view.dispatch.mock.calls[0][0];
302
- // The change should insert []()
303
- const changes = Array.isArray(tx.changes) ? tx.changes : [tx.changes];
304
- const insertCall = changes.find(
305
- (c: { insert?: string }) => c.insert === '[]()',
306
- );
307
- expect(insertCall).toBeDefined();
308
- });
309
-
310
- it('wraps selected text as [text]() with cursor in ()', () => {
311
- vi.mocked(syntaxTree).mockReturnValue({ iterate: vi.fn() } as any);
312
-
313
- const linkBinding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-k')!;
314
- const view = new (EditorView as any)(
315
- { from: 0, to: 5, empty: false },
316
- 'Hello World',
317
- );
318
- const result = linkBinding.run(view as any);
319
- expect(result).toBe(true);
320
- expect(view.dispatch).toHaveBeenCalled();
321
- const tx = view.dispatch.mock.calls[0][0];
322
- const changes = Array.isArray(tx.changes) ? tx.changes : [tx.changes];
323
- // The replacement should be [Hello]()
324
- const replaceCall = changes.find(
325
- (c: { insert?: string }) => c.insert === '[Hello]()',
326
- );
327
- expect(replaceCall).toBeDefined();
328
- });
329
-
330
- it('returns false when cursor is already inside a Link node', () => {
331
- // Simulate a Link node containing the cursor
332
- vi.mocked(syntaxTree).mockReturnValue({
333
- iterate: vi.fn(
334
- (opts: {
335
- enter: (n: { name: string; from: number; to: number }) => void;
336
- }) => {
337
- opts.enter({ name: 'Link', from: 0, to: 20 });
338
- },
339
- ),
340
- } as any);
341
-
342
- const linkBinding = markdownShortcutsKeymap.find((b) => b.key === 'Mod-k')!;
343
- const view = new (EditorView as any)(
344
- { from: 5, to: 5, empty: true },
345
- '[existing](http://example.com)',
346
- );
347
- const result = linkBinding.run(view as any);
348
- expect(result).toBe(false);
349
- expect(view.dispatch).not.toHaveBeenCalled();
350
- });
351
- });
352
-
353
- //////////////////////////////
354
- // EditorSelection usage in handlers
355
- //////////////////////////////
356
-
357
- describe('EditorSelection integration', () => {
358
- it('cursor() returns an empty selection at the given position', () => {
359
- const sel = EditorSelection.cursor(7);
360
- expect(sel.anchor).toBe(7);
361
- expect(sel.empty).toBe(true);
362
- });
363
-
364
- it('range() returns a ranged selection', () => {
365
- const sel = EditorSelection.range(3, 10);
366
- expect(sel.anchor).toBe(3);
367
- expect(sel.head).toBe(10);
368
- expect(sel.empty).toBe(false);
369
- });
370
- });