nebula-cms 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/README.md +3 -1
  2. package/dist/astro/index.d.ts +43 -0
  3. package/dist/astro/index.d.ts.map +1 -0
  4. package/dist/astro/index.js +223 -0
  5. package/{src → dist}/client/Admin.svelte +20 -11
  6. package/dist/client/Admin.svelte.d.ts +11 -0
  7. package/dist/client/Admin.svelte.d.ts.map +1 -0
  8. package/dist/client/components/BackendPicker.svelte.d.ts +4 -0
  9. package/dist/client/components/BackendPicker.svelte.d.ts.map +1 -0
  10. package/dist/client/components/DraftChip.svelte.d.ts +10 -0
  11. package/dist/client/components/DraftChip.svelte.d.ts.map +1 -0
  12. package/dist/client/components/MetadataForm.svelte.d.ts +12 -0
  13. package/dist/client/components/MetadataForm.svelte.d.ts.map +1 -0
  14. package/dist/client/components/ThemeToggle.svelte.d.ts +19 -0
  15. package/dist/client/components/ThemeToggle.svelte.d.ts.map +1 -0
  16. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts +11 -0
  17. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts.map +1 -0
  18. package/{src → dist}/client/components/dialogs/FilenameDialog.svelte +1 -2
  19. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts +13 -0
  20. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts.map +1 -0
  21. package/dist/client/components/editor/EditorPane.svelte.d.ts +4 -0
  22. package/dist/client/components/editor/EditorPane.svelte.d.ts.map +1 -0
  23. package/dist/client/components/editor/EditorTabs.svelte.d.ts +8 -0
  24. package/dist/client/components/editor/EditorTabs.svelte.d.ts.map +1 -0
  25. package/dist/client/components/editor/EditorToolbar.svelte.d.ts +4 -0
  26. package/dist/client/components/editor/EditorToolbar.svelte.d.ts.map +1 -0
  27. package/dist/client/components/editor/FormatSelector.svelte.d.ts +4 -0
  28. package/dist/client/components/editor/FormatSelector.svelte.d.ts.map +1 -0
  29. package/dist/client/components/editor/Toolbar.svelte.d.ts +19 -0
  30. package/dist/client/components/editor/Toolbar.svelte.d.ts.map +1 -0
  31. package/dist/client/components/fields/ArrayField.svelte.d.ts +15 -0
  32. package/dist/client/components/fields/ArrayField.svelte.d.ts.map +1 -0
  33. package/dist/client/components/fields/ArrayItem.svelte.d.ts +28 -0
  34. package/dist/client/components/fields/ArrayItem.svelte.d.ts.map +1 -0
  35. package/dist/client/components/fields/BooleanField.svelte.d.ts +16 -0
  36. package/dist/client/components/fields/BooleanField.svelte.d.ts.map +1 -0
  37. package/dist/client/components/fields/DateField.svelte.d.ts +16 -0
  38. package/dist/client/components/fields/DateField.svelte.d.ts.map +1 -0
  39. package/dist/client/components/fields/EnumField.svelte.d.ts +17 -0
  40. package/dist/client/components/fields/EnumField.svelte.d.ts.map +1 -0
  41. package/dist/client/components/fields/FieldWrapper.svelte.d.ts +18 -0
  42. package/dist/client/components/fields/FieldWrapper.svelte.d.ts.map +1 -0
  43. package/dist/client/components/fields/NumberField.svelte.d.ts +16 -0
  44. package/dist/client/components/fields/NumberField.svelte.d.ts.map +1 -0
  45. package/dist/client/components/fields/ObjectField.svelte.d.ts +16 -0
  46. package/dist/client/components/fields/ObjectField.svelte.d.ts.map +1 -0
  47. package/dist/client/components/fields/SchemaField.svelte.d.ts +16 -0
  48. package/dist/client/components/fields/SchemaField.svelte.d.ts.map +1 -0
  49. package/dist/client/components/fields/StringField.svelte.d.ts +16 -0
  50. package/dist/client/components/fields/StringField.svelte.d.ts.map +1 -0
  51. package/{src → dist}/client/components/sidebar/AdminSidebar.svelte +2 -4
  52. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts +19 -0
  53. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts.map +1 -0
  54. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts +12 -0
  55. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts.map +1 -0
  56. package/dist/client/css/icons.css +29 -0
  57. package/dist/client/index.d.ts +2 -0
  58. package/dist/client/index.d.ts.map +1 -0
  59. package/dist/client/js/drafts/merge.svelte.d.ts +24 -0
  60. package/dist/client/js/drafts/merge.svelte.d.ts.map +1 -0
  61. package/dist/client/js/drafts/merge.svelte.js +106 -0
  62. package/dist/client/js/drafts/ops.svelte.d.ts +31 -0
  63. package/dist/client/js/drafts/ops.svelte.d.ts.map +1 -0
  64. package/dist/client/js/drafts/ops.svelte.js +182 -0
  65. package/dist/client/js/drafts/storage.d.ts +45 -0
  66. package/dist/client/js/drafts/storage.d.ts.map +1 -0
  67. package/dist/client/js/drafts/storage.js +76 -0
  68. package/dist/client/js/drafts/workers/diff.d.ts +2 -0
  69. package/dist/client/js/drafts/workers/diff.d.ts.map +1 -0
  70. package/dist/client/js/drafts/workers/diff.js +20 -0
  71. package/dist/client/js/editor/editor.svelte.d.ts +124 -0
  72. package/dist/client/js/editor/editor.svelte.d.ts.map +1 -0
  73. package/dist/client/js/editor/editor.svelte.js +294 -0
  74. package/dist/client/js/editor/languages.d.ts +11 -0
  75. package/dist/client/js/editor/languages.d.ts.map +1 -0
  76. package/dist/client/js/editor/languages.js +93 -0
  77. package/dist/client/js/editor/link-wrap.d.ts +6 -0
  78. package/dist/client/js/editor/link-wrap.d.ts.map +1 -0
  79. package/{src/client/js/editor/link-wrap.ts → dist/client/js/editor/link-wrap.js} +17 -24
  80. package/dist/client/js/editor/markdown-shortcuts.d.ts +4 -0
  81. package/dist/client/js/editor/markdown-shortcuts.d.ts.map +1 -0
  82. package/dist/client/js/editor/markdown-shortcuts.js +219 -0
  83. package/dist/client/js/handlers/admin.d.ts +64 -0
  84. package/dist/client/js/handlers/admin.d.ts.map +1 -0
  85. package/dist/client/js/handlers/admin.js +186 -0
  86. package/dist/client/js/state/dialogs.svelte.d.ts +16 -0
  87. package/dist/client/js/state/dialogs.svelte.d.ts.map +1 -0
  88. package/dist/client/js/state/dialogs.svelte.js +28 -0
  89. package/dist/client/js/state/router.svelte.d.ts +44 -0
  90. package/dist/client/js/state/router.svelte.d.ts.map +1 -0
  91. package/dist/client/js/state/router.svelte.js +141 -0
  92. package/dist/client/js/state/schema.svelte.d.ts +51 -0
  93. package/dist/client/js/state/schema.svelte.d.ts.map +1 -0
  94. package/{src/client/js/state/schema.svelte.ts → dist/client/js/state/schema.svelte.js} +55 -70
  95. package/dist/client/js/state/state.svelte.d.ts +68 -0
  96. package/dist/client/js/state/state.svelte.d.ts.map +1 -0
  97. package/dist/client/js/state/state.svelte.js +300 -0
  98. package/dist/client/js/state/theme.svelte.d.ts +24 -0
  99. package/dist/client/js/state/theme.svelte.d.ts.map +1 -0
  100. package/{src/client/js/state/theme.svelte.ts → dist/client/js/state/theme.svelte.js} +54 -91
  101. package/dist/client/js/storage/adapter.d.ts +130 -0
  102. package/dist/client/js/storage/adapter.d.ts.map +1 -0
  103. package/dist/client/js/storage/adapter.js +5 -0
  104. package/dist/client/js/storage/client.d.ts +72 -0
  105. package/dist/client/js/storage/client.d.ts.map +1 -0
  106. package/dist/client/js/storage/client.js +121 -0
  107. package/dist/client/js/storage/db.d.ts +8 -0
  108. package/dist/client/js/storage/db.d.ts.map +1 -0
  109. package/dist/client/js/storage/db.js +35 -0
  110. package/dist/client/js/storage/fsa.d.ts +51 -0
  111. package/dist/client/js/storage/fsa.d.ts.map +1 -0
  112. package/dist/client/js/storage/fsa.js +91 -0
  113. package/dist/client/js/storage/github.d.ts +62 -0
  114. package/dist/client/js/storage/github.d.ts.map +1 -0
  115. package/dist/client/js/storage/github.js +216 -0
  116. package/dist/client/js/storage/storage.d.ts +32 -0
  117. package/dist/client/js/storage/storage.d.ts.map +1 -0
  118. package/dist/client/js/storage/storage.js +68 -0
  119. package/dist/client/js/storage/workers/frontmatter.d.ts +2 -0
  120. package/dist/client/js/storage/workers/frontmatter.d.ts.map +1 -0
  121. package/dist/client/js/storage/workers/frontmatter.js +253 -0
  122. package/dist/client/js/storage/workers/storage.d.ts +2 -0
  123. package/dist/client/js/storage/workers/storage.d.ts.map +1 -0
  124. package/dist/client/js/storage/workers/storage.js +167 -0
  125. package/dist/client/js/storage/workers/toml-parser.d.ts +2 -0
  126. package/dist/client/js/storage/workers/toml-parser.d.ts.map +1 -0
  127. package/dist/client/js/storage/workers/toml-parser.js +75 -0
  128. package/dist/client/js/storage/workers/yaml-parser.d.ts +2 -0
  129. package/dist/client/js/storage/workers/yaml-parser.d.ts.map +1 -0
  130. package/dist/client/js/storage/workers/yaml-parser.js +100 -0
  131. package/dist/client/js/utils/file-types.d.ts +58 -0
  132. package/dist/client/js/utils/file-types.d.ts.map +1 -0
  133. package/{src/client/js/utils/file-types.ts → dist/client/js/utils/file-types.js} +75 -107
  134. package/dist/client/js/utils/format.d.ts +8 -0
  135. package/dist/client/js/utils/format.d.ts.map +1 -0
  136. package/{src/client/js/utils/format.ts → dist/client/js/utils/format.js} +5 -6
  137. package/dist/client/js/utils/frontmatter.d.ts +12 -0
  138. package/dist/client/js/utils/frontmatter.d.ts.map +1 -0
  139. package/dist/client/js/utils/frontmatter.js +29 -0
  140. package/dist/client/js/utils/schema-utils.d.ts +110 -0
  141. package/dist/client/js/utils/schema-utils.d.ts.map +1 -0
  142. package/dist/client/js/utils/schema-utils.js +242 -0
  143. package/dist/client/js/utils/slug.d.ts +8 -0
  144. package/dist/client/js/utils/slug.d.ts.map +1 -0
  145. package/{src/client/js/utils/slug.ts → dist/client/js/utils/slug.js} +6 -7
  146. package/dist/client/js/utils/sort.d.ts +41 -0
  147. package/dist/client/js/utils/sort.d.ts.map +1 -0
  148. package/dist/client/js/utils/sort.js +65 -0
  149. package/dist/client/js/utils/stable-stringify.d.ts +8 -0
  150. package/dist/client/js/utils/stable-stringify.d.ts.map +1 -0
  151. package/dist/client/js/utils/stable-stringify.js +23 -0
  152. package/dist/client/js/utils/url-utils.d.ts +11 -0
  153. package/dist/client/js/utils/url-utils.d.ts.map +1 -0
  154. package/{src/client/js/utils/url-utils.ts → dist/client/js/utils/url-utils.js} +22 -23
  155. package/dist/client/types/browser-apis.d.ts +39 -0
  156. package/dist/types.d.ts +22 -0
  157. package/dist/types.d.ts.map +1 -0
  158. package/dist/types.js +1 -0
  159. package/package.json +13 -3
  160. package/.github/workflows/ci.yml +0 -27
  161. package/.github/workflows/publish.yml +0 -34
  162. package/.mcp.json +0 -12
  163. package/.prettierignore +0 -5
  164. package/.prettierrc.cjs +0 -22
  165. package/AGENTS.md +0 -183
  166. package/playground/astro.config.mjs +0 -7
  167. package/playground/node_modules/.bin/astro +0 -21
  168. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +0 -85
  169. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +0 -7
  170. package/playground/node_modules/.vite/deps/_metadata.json +0 -184
  171. package/playground/node_modules/.vite/deps/astro___aria-query.js +0 -6776
  172. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
  173. package/playground/node_modules/.vite/deps/astro___axobject-query.js +0 -3754
  174. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
  175. package/playground/node_modules/.vite/deps/astro___html-escaper.js +0 -34
  176. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
  177. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  178. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +0 -7
  179. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +0 -8
  180. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +0 -7
  181. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +0 -21
  182. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +0 -7
  183. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +0 -223
  184. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +0 -7
  185. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js +0 -27
  186. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js.map +0 -7
  187. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js +0 -1005
  188. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js.map +0 -7
  189. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js +0 -204
  190. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js.map +0 -7
  191. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js +0 -688
  192. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js.map +0 -7
  193. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js +0 -5099
  194. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js.map +0 -7
  195. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js +0 -4376
  196. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js.map +0 -7
  197. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +0 -23
  198. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +0 -7
  199. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +0 -148
  200. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +0 -7
  201. package/playground/node_modules/.vite/deps/package.json +0 -3
  202. package/playground/node_modules/.vite/deps/smol-toml.js +0 -843
  203. package/playground/node_modules/.vite/deps/smol-toml.js.map +0 -7
  204. package/playground/node_modules/.vite/deps/svelte.js +0 -55
  205. package/playground/node_modules/.vite/deps/svelte.js.map +0 -7
  206. package/playground/node_modules/.vite/deps/svelte___clsx.js +0 -9
  207. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +0 -7
  208. package/playground/node_modules/.vite/deps/svelte_animate.js +0 -57
  209. package/playground/node_modules/.vite/deps/svelte_animate.js.map +0 -7
  210. package/playground/node_modules/.vite/deps/svelte_attachments.js +0 -15
  211. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +0 -7
  212. package/playground/node_modules/.vite/deps/svelte_easing.js +0 -67
  213. package/playground/node_modules/.vite/deps/svelte_easing.js.map +0 -7
  214. package/playground/node_modules/.vite/deps/svelte_events.js +0 -11
  215. package/playground/node_modules/.vite/deps/svelte_events.js.map +0 -7
  216. package/playground/node_modules/.vite/deps/svelte_internal.js +0 -5
  217. package/playground/node_modules/.vite/deps/svelte_internal.js.map +0 -7
  218. package/playground/node_modules/.vite/deps/svelte_internal_client.js +0 -402
  219. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +0 -7
  220. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +0 -10
  221. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +0 -7
  222. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +0 -8
  223. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +0 -7
  224. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +0 -8
  225. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +0 -7
  226. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +0 -8
  227. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +0 -7
  228. package/playground/node_modules/.vite/deps/svelte_legacy.js +0 -35
  229. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +0 -7
  230. package/playground/node_modules/.vite/deps/svelte_motion.js +0 -545
  231. package/playground/node_modules/.vite/deps/svelte_motion.js.map +0 -7
  232. package/playground/node_modules/.vite/deps/svelte_reactivity.js +0 -29
  233. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +0 -7
  234. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +0 -127
  235. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +0 -7
  236. package/playground/node_modules/.vite/deps/svelte_store.js +0 -103
  237. package/playground/node_modules/.vite/deps/svelte_store.js.map +0 -7
  238. package/playground/node_modules/.vite/deps/svelte_transition.js +0 -208
  239. package/playground/node_modules/.vite/deps/svelte_transition.js.map +0 -7
  240. package/playground/package.json +0 -16
  241. package/playground/pnpm-lock.yaml +0 -3167
  242. package/playground/src/content/authors/jane-doe.json +0 -8
  243. package/playground/src/content/config/build.toml +0 -2
  244. package/playground/src/content/courses/web-fundamentals.json +0 -29
  245. package/playground/src/content/docs/advanced.mdx +0 -6
  246. package/playground/src/content/docs/intro.md +0 -6
  247. package/playground/src/content/guides/getting-started.mdx +0 -6
  248. package/playground/src/content/posts/hello-world.md +0 -7
  249. package/playground/src/content/products/t-shirt.json +0 -16
  250. package/playground/src/content/recipes/pancakes.mdoc +0 -8
  251. package/playground/src/content/settings/site.yml +0 -2
  252. package/playground/src/content.config.ts +0 -198
  253. package/playground/src/env.d.ts +0 -1
  254. package/playground/src/pages/index.astro +0 -11
  255. package/playground/src/pages/nebula.astro +0 -14
  256. package/pnpm-workspace.yaml +0 -2
  257. package/scripts/subset-icons.mjs +0 -178
  258. package/src/astro/index.ts +0 -295
  259. package/src/client/js/drafts/merge.svelte.ts +0 -121
  260. package/src/client/js/drafts/ops.svelte.ts +0 -227
  261. package/src/client/js/drafts/storage.ts +0 -108
  262. package/src/client/js/drafts/workers/diff.ts +0 -40
  263. package/src/client/js/editor/editor.svelte.ts +0 -343
  264. package/src/client/js/editor/languages.ts +0 -98
  265. package/src/client/js/editor/markdown-shortcuts.ts +0 -261
  266. package/src/client/js/handlers/admin.ts +0 -246
  267. package/src/client/js/state/dialogs.svelte.ts +0 -35
  268. package/src/client/js/state/router.svelte.ts +0 -156
  269. package/src/client/js/state/state.svelte.ts +0 -334
  270. package/src/client/js/storage/adapter.ts +0 -102
  271. package/src/client/js/storage/client.ts +0 -150
  272. package/src/client/js/storage/db.ts +0 -36
  273. package/src/client/js/storage/fsa.ts +0 -110
  274. package/src/client/js/storage/github.ts +0 -297
  275. package/src/client/js/storage/storage.ts +0 -83
  276. package/src/client/js/storage/workers/frontmatter.ts +0 -320
  277. package/src/client/js/storage/workers/storage.ts +0 -177
  278. package/src/client/js/storage/workers/toml-parser.ts +0 -106
  279. package/src/client/js/storage/workers/yaml-parser.ts +0 -132
  280. package/src/client/js/utils/frontmatter.ts +0 -38
  281. package/src/client/js/utils/schema-utils.ts +0 -295
  282. package/src/client/js/utils/sort.ts +0 -84
  283. package/src/client/js/utils/stable-stringify.ts +0 -27
  284. package/src/types.ts +0 -25
  285. package/svelte.config.js +0 -4
  286. package/tests/astro/build.test.ts +0 -63
  287. package/tests/astro/index.test.ts +0 -689
  288. package/tests/client/components/Admin.test.ts +0 -446
  289. package/tests/client/components/BackendPicker.test.ts +0 -239
  290. package/tests/client/components/DraftChip.test.ts +0 -53
  291. package/tests/client/components/MetadataForm.test.ts +0 -164
  292. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +0 -91
  293. package/tests/client/components/dialogs/FilenameDialog.test.ts +0 -209
  294. package/tests/client/components/dialogs/dialog-stubs.ts +0 -19
  295. package/tests/client/components/editor/EditorPane.test.ts +0 -100
  296. package/tests/client/components/editor/EditorTabs.test.ts +0 -253
  297. package/tests/client/components/editor/EditorToolbar.test.ts +0 -252
  298. package/tests/client/components/editor/fixtures.ts +0 -31
  299. package/tests/client/components/fields/ArrayField.test.ts +0 -197
  300. package/tests/client/components/fields/BooleanField.test.ts +0 -206
  301. package/tests/client/components/fields/DateField.test.ts +0 -210
  302. package/tests/client/components/fields/EnumField.test.ts +0 -246
  303. package/tests/client/components/fields/NumberField.test.ts +0 -240
  304. package/tests/client/components/fields/ObjectField.test.ts +0 -157
  305. package/tests/client/components/fields/SchemaField.test.ts +0 -190
  306. package/tests/client/components/fields/StringField.test.ts +0 -223
  307. package/tests/client/components/sidebar/AdminSidebar.test.ts +0 -285
  308. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +0 -135
  309. package/tests/client/components/sidebar/sort-mock.ts +0 -23
  310. package/tests/client/js/drafts/fixtures.ts +0 -22
  311. package/tests/client/js/drafts/merge.test.ts +0 -282
  312. package/tests/client/js/drafts/ops.test.ts +0 -658
  313. package/tests/client/js/drafts/storage.test.ts +0 -200
  314. package/tests/client/js/drafts/workers/diff.test.ts +0 -165
  315. package/tests/client/js/editor/editor.test.ts +0 -616
  316. package/tests/client/js/editor/link-wrap.test.ts +0 -225
  317. package/tests/client/js/editor/markdown-shortcuts.test.ts +0 -370
  318. package/tests/client/js/handlers/admin.test.ts +0 -467
  319. package/tests/client/js/state/router.test.ts +0 -619
  320. package/tests/client/js/state/schema.test.ts +0 -266
  321. package/tests/client/js/state/state.test.ts +0 -328
  322. package/tests/client/js/storage/adapter.test.ts +0 -115
  323. package/tests/client/js/storage/client.test.ts +0 -250
  324. package/tests/client/js/storage/db.test.ts +0 -59
  325. package/tests/client/js/storage/fsa.test.ts +0 -284
  326. package/tests/client/js/storage/github.test.ts +0 -349
  327. package/tests/client/js/storage/mock-port.ts +0 -95
  328. package/tests/client/js/storage/storage.test.ts +0 -77
  329. package/tests/client/js/storage/workers/frontmatter.test.ts +0 -479
  330. package/tests/client/js/storage/workers/storage.test.ts +0 -299
  331. package/tests/client/js/storage/workers/toml-parser.test.ts +0 -169
  332. package/tests/client/js/storage/workers/yaml-parser.test.ts +0 -168
  333. package/tests/client/js/utils/file-types.test.ts +0 -268
  334. package/tests/client/js/utils/frontmatter.test.ts +0 -87
  335. package/tests/client/js/utils/schema-utils.test.ts +0 -318
  336. package/tests/client/js/utils/slug.test.ts +0 -58
  337. package/tests/client/js/utils/sort.test.ts +0 -276
  338. package/tests/client/js/utils/stable-stringify.test.ts +0 -68
  339. package/tests/client/js/utils/url-utils.test.ts +0 -70
  340. package/tests/e2e/backend-connection.test.ts +0 -301
  341. package/tests/e2e/draft-lifecycle.test.ts +0 -388
  342. package/tests/e2e/editing.test.ts +0 -355
  343. package/tests/e2e/github-adapter.test.ts +0 -330
  344. package/tests/e2e/helpers/mock-adapter.ts +0 -166
  345. package/tests/e2e/helpers/test-app.ts +0 -155
  346. package/tests/e2e/navigation.test.ts +0 -358
  347. package/tests/e2e/publishing.test.ts +0 -345
  348. package/tests/e2e/unsaved-changes.test.ts +0 -317
  349. package/tests/setup.ts +0 -2
  350. package/tests/stubs/codemirror.ts +0 -197
  351. package/tsconfig.json +0 -19
  352. package/vitest.config.ts +0 -178
  353. /package/{src → dist}/client/components/BackendPicker.svelte +0 -0
  354. /package/{src → dist}/client/components/DraftChip.svelte +0 -0
  355. /package/{src → dist}/client/components/MetadataForm.svelte +0 -0
  356. /package/{src → dist}/client/components/ThemeToggle.svelte +0 -0
  357. /package/{src → dist}/client/components/dialogs/DeleteDraftDialog.svelte +0 -0
  358. /package/{src → dist}/client/components/editor/EditorPane.svelte +0 -0
  359. /package/{src → dist}/client/components/editor/EditorTabs.svelte +0 -0
  360. /package/{src → dist}/client/components/editor/EditorToolbar.svelte +0 -0
  361. /package/{src → dist}/client/components/editor/FormatSelector.svelte +0 -0
  362. /package/{src → dist}/client/components/editor/Toolbar.svelte +0 -0
  363. /package/{src → dist}/client/components/fields/ArrayField.svelte +0 -0
  364. /package/{src → dist}/client/components/fields/ArrayItem.svelte +0 -0
  365. /package/{src → dist}/client/components/fields/BooleanField.svelte +0 -0
  366. /package/{src → dist}/client/components/fields/DateField.svelte +0 -0
  367. /package/{src → dist}/client/components/fields/EnumField.svelte +0 -0
  368. /package/{src → dist}/client/components/fields/FieldWrapper.svelte +0 -0
  369. /package/{src → dist}/client/components/fields/NumberField.svelte +0 -0
  370. /package/{src → dist}/client/components/fields/ObjectField.svelte +0 -0
  371. /package/{src → dist}/client/components/fields/SchemaField.svelte +0 -0
  372. /package/{src → dist}/client/components/fields/StringField.svelte +0 -0
  373. /package/{src → dist}/client/components/sidebar/AdminSidebarSort.svelte +0 -0
  374. /package/{src → dist}/client/css/a11y.css +0 -0
  375. /package/{src → dist}/client/css/btn.css +0 -0
  376. /package/{src → dist}/client/css/dialog.css +0 -0
  377. /package/{src → dist}/client/css/field-input.css +0 -0
  378. /package/{src → dist}/client/css/reset.css +0 -0
  379. /package/{src → dist}/client/css/theme.css +0 -0
  380. /package/{src/client/index.ts → dist/client/index.js} +0 -0
  381. /package/{src → dist}/virtual.d.ts +0 -0
@@ -1,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
- });