includio-cms 0.1.4 → 0.5.0

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 (296) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/ROADMAP.md +18 -13
  3. package/dist/admin/api/accept-invite.js +1 -5
  4. package/dist/admin/api/invite.js +7 -16
  5. package/dist/admin/client/account/account-page.svelte +20 -50
  6. package/dist/admin/client/account/lang.d.ts +15 -23
  7. package/dist/admin/client/account/lang.js +51 -67
  8. package/dist/admin/client/account/preferences-section.svelte +26 -84
  9. package/dist/admin/client/account/profile-section.svelte +60 -40
  10. package/dist/admin/client/account/schema.d.ts +11 -3
  11. package/dist/admin/client/account/schema.js +25 -16
  12. package/dist/admin/client/account/security-section.svelte +139 -105
  13. package/dist/admin/client/account/sessions-section.svelte +35 -34
  14. package/dist/admin/client/admin/admin-after-login-layout-content.svelte +3 -5
  15. package/dist/admin/client/admin/admin-layout.svelte +3 -2
  16. package/dist/admin/client/admin/admin-preloader.svelte +36 -0
  17. package/dist/admin/client/admin/admin-preloader.svelte.d.ts +18 -0
  18. package/dist/admin/client/admin/dashboard-page.svelte +55 -41
  19. package/dist/admin/client/collection/a11y-score-cell.svelte +45 -0
  20. package/dist/admin/client/collection/a11y-score-cell.svelte.d.ts +6 -0
  21. package/dist/admin/client/collection/bulk-actions-bar.svelte +83 -0
  22. package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +9 -0
  23. package/dist/admin/client/collection/collection-entries.svelte +255 -260
  24. package/dist/admin/client/collection/collection-view.svelte.d.ts +4 -3
  25. package/dist/admin/client/collection/collection-view.svelte.js +9 -5
  26. package/dist/admin/client/collection/collection.svelte +22 -12
  27. package/dist/admin/client/collection/data-table.svelte +50 -39
  28. package/dist/admin/client/collection/data-table.svelte.d.ts +1 -0
  29. package/dist/admin/client/collection/date-cell.svelte +7 -5
  30. package/dist/admin/client/collection/date-cell.svelte.d.ts +1 -1
  31. package/dist/admin/client/collection/empty-state.svelte +28 -0
  32. package/dist/admin/client/collection/empty-state.svelte.d.ts +9 -0
  33. package/dist/admin/client/collection/entry-link.svelte +10 -4
  34. package/dist/admin/client/collection/entry-link.svelte.d.ts +1 -0
  35. package/dist/admin/client/collection/grid-view.svelte +21 -23
  36. package/dist/admin/client/collection/grid-view.svelte.d.ts +1 -2
  37. package/dist/admin/client/collection/row-actions.svelte +60 -0
  38. package/dist/admin/client/collection/row-actions.svelte.d.ts +9 -0
  39. package/dist/admin/client/collection/status-badge.svelte +7 -8
  40. package/dist/admin/client/collection/table-pagination.svelte +122 -79
  41. package/dist/admin/client/collection/table-pagination.svelte.d.ts +1 -0
  42. package/dist/admin/client/collection/table-toolbar.svelte +108 -88
  43. package/dist/admin/client/collection/table-toolbar.svelte.d.ts +8 -9
  44. package/dist/admin/client/entry/entry-form.svelte +109 -1
  45. package/dist/admin/client/entry/entry-header.svelte +96 -37
  46. package/dist/admin/client/entry/entry-header.svelte.d.ts +5 -0
  47. package/dist/admin/client/entry/entry.svelte +171 -60
  48. package/dist/admin/client/entry/header/a11y-validator.d.ts +46 -0
  49. package/dist/admin/client/entry/header/a11y-validator.js +311 -0
  50. package/dist/admin/client/entry/header/publish-panel.svelte +373 -131
  51. package/dist/admin/client/entry/header/publish-panel.svelte.d.ts +4 -0
  52. package/dist/admin/client/entry/header/save-indicator.svelte +33 -23
  53. package/dist/admin/client/entry/header/schedule-popover.svelte +1 -1
  54. package/dist/admin/client/entry/header/status-badge.svelte +25 -118
  55. package/dist/admin/client/entry/header/version-history-sheet.svelte +314 -98
  56. package/dist/admin/client/form/form-submission/form-submission.svelte +271 -83
  57. package/dist/admin/client/form/form-submission/submission-field.svelte +12 -12
  58. package/dist/admin/client/form/form-submissions.svelte +421 -139
  59. package/dist/admin/client/form/submission-link.svelte +8 -2
  60. package/dist/admin/client/form/submission-link.svelte.d.ts +1 -0
  61. package/dist/admin/client/form/submission-status-badge.svelte +18 -4
  62. package/dist/admin/client/form/submission-status-badge.svelte.d.ts +1 -0
  63. package/dist/admin/client/login/lang.d.ts +32 -0
  64. package/dist/admin/client/login/lang.js +66 -2
  65. package/dist/admin/client/login/login-form.svelte +237 -95
  66. package/dist/admin/client/login/login-form.svelte.d.ts +2 -17
  67. package/dist/admin/client/login/login-page.svelte +34 -98
  68. package/dist/admin/client/login/reset-password-page.svelte +235 -0
  69. package/dist/admin/client/login/reset-password-page.svelte.d.ts +4 -0
  70. package/dist/admin/client/login/schema.d.ts +15 -0
  71. package/dist/admin/client/login/schema.js +21 -0
  72. package/dist/admin/client/users/accept-invite-page.svelte +166 -37
  73. package/dist/admin/client/users/create-user-dialog.svelte +15 -7
  74. package/dist/admin/client/users/delete-user-dialog.svelte +81 -16
  75. package/dist/admin/client/users/delete-user-dialog.svelte.d.ts +4 -1
  76. package/dist/admin/client/users/edit-user-dialog.svelte +3 -0
  77. package/dist/admin/client/users/invite-user-dialog.svelte +16 -3
  78. package/dist/admin/client/users/lang.d.ts +27 -0
  79. package/dist/admin/client/users/lang.js +64 -10
  80. package/dist/admin/client/users/pending-invitations.svelte +59 -23
  81. package/dist/admin/client/users/users-page.svelte +471 -72
  82. package/dist/admin/components/accessibility/accessibility-overview.svelte +2 -7
  83. package/dist/admin/components/dashboard/a11y-gauge.svelte +90 -0
  84. package/dist/admin/components/dashboard/a11y-gauge.svelte.d.ts +18 -0
  85. package/dist/admin/components/dashboard/accessibility-hub.svelte +13 -12
  86. package/dist/admin/components/dashboard/form-submissions-widget.svelte +71 -113
  87. package/dist/admin/components/dashboard/index.d.ts +4 -2
  88. package/dist/admin/components/dashboard/index.js +4 -2
  89. package/dist/admin/components/dashboard/recent-activity.svelte +53 -75
  90. package/dist/admin/components/dashboard/recent-entries.svelte +94 -0
  91. package/dist/admin/components/dashboard/recent-entries.svelte.d.ts +18 -0
  92. package/dist/admin/components/dashboard/stat-card.svelte +2 -2
  93. package/dist/admin/components/dashboard/tip-of-the-day.svelte +109 -0
  94. package/dist/admin/components/dashboard/tip-of-the-day.svelte.d.ts +3 -0
  95. package/dist/admin/components/dashboard/welcome-header.svelte +45 -0
  96. package/dist/admin/components/dashboard/welcome-header.svelte.d.ts +3 -0
  97. package/dist/admin/components/fields/{array-field.svelte → blocks-field.svelte} +4 -4
  98. package/dist/admin/components/fields/{array-field.svelte.d.ts → blocks-field.svelte.d.ts} +5 -5
  99. package/dist/admin/components/fields/content-field.svelte +27 -0
  100. package/dist/admin/components/fields/content-field.svelte.d.ts +31 -0
  101. package/dist/admin/components/fields/field-renderer.svelte +9 -7
  102. package/dist/admin/components/fields/image-field.svelte +2 -2
  103. package/dist/admin/components/fields/media-field.svelte +2 -2
  104. package/dist/admin/components/fields/seo-field.svelte +205 -25
  105. package/dist/admin/components/fields/simple-array-field.svelte +289 -0
  106. package/dist/admin/components/fields/simple-array-field.svelte.d.ts +30 -0
  107. package/dist/admin/components/fields/slug-field.svelte +3 -2
  108. package/dist/admin/components/fields/standalone-field-renderer.svelte +148 -0
  109. package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +9 -0
  110. package/dist/admin/components/fields/text-field-wrapper.svelte +13 -1
  111. package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
  112. package/dist/admin/components/fields/url-field.svelte +5 -4
  113. package/dist/admin/components/layout/app-sidebar.svelte +27 -24
  114. package/dist/admin/components/layout/lang.d.ts +6 -0
  115. package/dist/admin/components/layout/lang.js +13 -1
  116. package/dist/admin/components/layout/layout-renderer.svelte +352 -0
  117. package/dist/admin/components/layout/layout-renderer.svelte.d.ts +14 -0
  118. package/dist/admin/components/layout/nav-breadcrumbs.svelte +4 -4
  119. package/dist/admin/components/layout/nav-collections.svelte +65 -36
  120. package/dist/admin/components/layout/nav-footer.svelte +31 -0
  121. package/dist/admin/components/layout/nav-footer.svelte.d.ts +18 -0
  122. package/dist/admin/components/layout/nav-forms.svelte +55 -30
  123. package/dist/admin/components/layout/nav-main.svelte +14 -52
  124. package/dist/admin/components/layout/nav-search.svelte +4 -3
  125. package/dist/admin/components/layout/nav-singletons.svelte +59 -17
  126. package/dist/admin/components/layout/nav-singletons.svelte.d.ts +17 -8
  127. package/dist/admin/components/layout/site-header.svelte +74 -13
  128. package/dist/admin/components/media/alt-input.svelte +32 -22
  129. package/dist/admin/components/media/bulk-action-bar.svelte +139 -150
  130. package/dist/admin/components/media/file/file-details.svelte +299 -217
  131. package/dist/admin/components/media/file/file-miniature.svelte +54 -41
  132. package/dist/admin/components/media/file/file-miniature.svelte.d.ts +1 -0
  133. package/dist/admin/components/media/file/file-preview.svelte +1 -1
  134. package/dist/admin/components/media/file-upload.svelte +24 -26
  135. package/dist/admin/components/media/files-list.svelte +112 -40
  136. package/dist/admin/components/media/files-list.svelte.d.ts +2 -0
  137. package/dist/admin/components/media/focal-point-input.svelte +122 -26
  138. package/dist/admin/components/media/media-library.svelte +127 -70
  139. package/dist/admin/components/media/media-search.svelte +6 -6
  140. package/dist/admin/components/media/media-sort.svelte +3 -1
  141. package/dist/admin/components/media/multi-file-summary.svelte +88 -68
  142. package/dist/admin/components/media/tag-combobox.svelte +141 -66
  143. package/dist/admin/components/media/tag-combobox.svelte.d.ts +1 -0
  144. package/dist/admin/components/media/tag-sidebar.svelte +139 -121
  145. package/dist/admin/components/tiptap/FigureNodeView.svelte +144 -15
  146. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +254 -0
  147. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte.d.ts +4 -0
  148. package/dist/admin/components/tiptap/SlashCommandPopup.svelte +212 -0
  149. package/dist/admin/components/tiptap/SlashCommandPopup.svelte.d.ts +8 -0
  150. package/dist/admin/components/tiptap/content-editor.svelte +280 -0
  151. package/dist/admin/components/tiptap/content-editor.svelte.d.ts +9 -0
  152. package/dist/admin/components/tiptap/editor-toolbar.svelte +230 -0
  153. package/dist/admin/components/tiptap/editor-toolbar.svelte.d.ts +16 -0
  154. package/dist/admin/components/tiptap/heading-a11y-plugin.d.ts +2 -0
  155. package/dist/admin/components/tiptap/heading-a11y-plugin.js +67 -0
  156. package/dist/admin/components/tiptap/image-dialog.svelte +172 -11
  157. package/dist/admin/components/tiptap/inline-block-node.d.ts +19 -0
  158. package/dist/admin/components/tiptap/inline-block-node.js +98 -0
  159. package/dist/admin/components/tiptap/link-dialog.svelte +9 -4
  160. package/dist/admin/components/tiptap/slash-command.d.ts +17 -0
  161. package/dist/admin/components/tiptap/slash-command.js +181 -0
  162. package/dist/admin/components/tiptap/structured-content-utils.d.ts +21 -0
  163. package/dist/admin/components/tiptap/structured-content-utils.js +150 -0
  164. package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -190
  165. package/dist/admin/email/invite-template.d.ts +8 -0
  166. package/dist/admin/email/invite-template.js +99 -0
  167. package/dist/admin/email/reset-password-template.d.ts +7 -0
  168. package/dist/admin/email/reset-password-template.js +96 -0
  169. package/dist/admin/remote/ai.remote.d.ts +1 -0
  170. package/dist/admin/remote/ai.remote.js +4 -1
  171. package/dist/admin/remote/entry.remote.d.ts +8 -0
  172. package/dist/admin/remote/entry.remote.js +53 -4
  173. package/dist/admin/remote/preview.remote.js +2 -1
  174. package/dist/admin/shared/password-schema.d.ts +5 -0
  175. package/dist/admin/shared/password-schema.js +10 -0
  176. package/dist/admin/styles/admin.css +1530 -151
  177. package/dist/admin/utils/formatDate.d.ts +1 -0
  178. package/dist/admin/utils/formatDate.js +8 -0
  179. package/dist/admin/utils/roleLabel.d.ts +2 -0
  180. package/dist/admin/utils/roleLabel.js +13 -0
  181. package/dist/ai-claude/index.d.ts +2 -0
  182. package/dist/ai-claude/index.js +56 -0
  183. package/dist/cms/runtime/api.d.ts +6 -1
  184. package/dist/cms/runtime/api.js +3 -0
  185. package/dist/cms/runtime/schemas.d.ts +9 -1
  186. package/dist/cms/runtime/schemas.js +8 -0
  187. package/dist/cms/runtime/types.d.ts +82 -10
  188. package/dist/cms/runtime/types.js +4 -0
  189. package/dist/components/ui/accordion/accordion.stories.svelte +39 -0
  190. package/dist/components/ui/accordion/accordion.stories.svelte.d.ts +27 -0
  191. package/dist/components/ui/alert/alert.stories.svelte +53 -0
  192. package/dist/components/ui/alert/alert.stories.svelte.d.ts +27 -0
  193. package/dist/components/ui/alert/alert.svelte +5 -0
  194. package/dist/components/ui/alert/alert.svelte.d.ts +9 -0
  195. package/dist/components/ui/avatar/avatar.stories.svelte +16 -0
  196. package/dist/components/ui/avatar/avatar.stories.svelte.d.ts +27 -0
  197. package/dist/components/ui/badge/badge.stories.svelte +33 -0
  198. package/dist/components/ui/badge/badge.stories.svelte.d.ts +27 -0
  199. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +33 -0
  200. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte.d.ts +27 -0
  201. package/dist/components/ui/button/button.stories.svelte +43 -0
  202. package/dist/components/ui/button/button.stories.svelte.d.ts +27 -0
  203. package/dist/components/ui/button/button.svelte +1 -2
  204. package/dist/components/ui/button/button.svelte.d.ts +0 -3
  205. package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
  206. package/dist/components/ui/card/card.stories.svelte +42 -0
  207. package/dist/components/ui/card/card.stories.svelte.d.ts +27 -0
  208. package/dist/components/ui/command/command.stories.svelte +51 -0
  209. package/dist/components/ui/command/command.stories.svelte.d.ts +27 -0
  210. package/dist/components/ui/dialog/dialog.stories.svelte +29 -0
  211. package/dist/components/ui/dialog/dialog.stories.svelte.d.ts +27 -0
  212. package/dist/components/ui/field/field-label.svelte.d.ts +1 -1
  213. package/dist/components/ui/field/field.stories.svelte +21 -0
  214. package/dist/components/ui/field/field.stories.svelte.d.ts +27 -0
  215. package/dist/components/ui/input/input.stories.svelte +40 -0
  216. package/dist/components/ui/input/input.stories.svelte.d.ts +27 -0
  217. package/dist/components/ui/input/input.svelte +2 -4
  218. package/dist/components/ui/item/item-separator.svelte.d.ts +1 -1
  219. package/dist/components/ui/label/label.stories.svelte +20 -0
  220. package/dist/components/ui/label/label.stories.svelte.d.ts +27 -0
  221. package/dist/components/ui/popover/popover.stories.svelte +29 -0
  222. package/dist/components/ui/popover/popover.stories.svelte.d.ts +27 -0
  223. package/dist/components/ui/select/select-group-heading.svelte.d.ts +1 -1
  224. package/dist/components/ui/select/select.stories.svelte +23 -0
  225. package/dist/components/ui/select/select.stories.svelte.d.ts +27 -0
  226. package/dist/components/ui/separator/separator.stories.svelte +24 -0
  227. package/dist/components/ui/separator/separator.stories.svelte.d.ts +27 -0
  228. package/dist/components/ui/sheet/sheet.stories.svelte +29 -0
  229. package/dist/components/ui/sheet/sheet.stories.svelte.d.ts +27 -0
  230. package/dist/components/ui/sidebar/sidebar-group.svelte +3 -3
  231. package/dist/components/ui/sidebar/sidebar-group.svelte.d.ts +2 -2
  232. package/dist/components/ui/sidebar/sidebar-menu-button.svelte +28 -30
  233. package/dist/components/ui/sidebar/sidebar-menu-button.svelte.d.ts +7 -7
  234. package/dist/components/ui/sidebar/sidebar-separator.svelte.d.ts +1 -1
  235. package/dist/components/ui/sidebar/sidebar-trigger.svelte +4 -4
  236. package/dist/components/ui/sonner/sonner.stories.svelte +22 -0
  237. package/dist/components/ui/sonner/sonner.stories.svelte.d.ts +26 -0
  238. package/dist/components/ui/sonner/sonner.svelte +8 -2
  239. package/dist/components/ui/sonner/toast-demo.svelte +29 -0
  240. package/dist/components/ui/sonner/toast-demo.svelte.d.ts +6 -0
  241. package/dist/components/ui/textarea/textarea.stories.svelte +22 -0
  242. package/dist/components/ui/textarea/textarea.stories.svelte.d.ts +27 -0
  243. package/dist/components/ui/textarea/textarea.svelte +0 -2
  244. package/dist/components/ui/toggle/toggle.stories.svelte +22 -0
  245. package/dist/components/ui/toggle/toggle.stories.svelte.d.ts +27 -0
  246. package/dist/components/ui/toggle-group/toggle-group.stories.svelte +17 -0
  247. package/dist/components/ui/toggle-group/toggle-group.stories.svelte.d.ts +27 -0
  248. package/dist/components/ui/tooltip/tooltip.stories.svelte +26 -0
  249. package/dist/components/ui/tooltip/tooltip.stories.svelte.d.ts +27 -0
  250. package/dist/core/fields/fieldSchemaToTs.d.ts +1 -0
  251. package/dist/core/fields/fieldSchemaToTs.js +133 -1
  252. package/dist/core/fields/layoutUtils.d.ts +17 -0
  253. package/dist/core/fields/layoutUtils.js +149 -0
  254. package/dist/core/fields/structuredToHtml.d.ts +9 -0
  255. package/dist/core/fields/structuredToHtml.js +161 -0
  256. package/dist/core/server/entries/operations/create.js +2 -1
  257. package/dist/core/server/entries/operations/get.js +8 -6
  258. package/dist/core/server/entries/operations/update.d.ts +3 -0
  259. package/dist/core/server/entries/operations/update.js +30 -2
  260. package/dist/core/server/fields/queryStructuredContent.d.ts +15 -0
  261. package/dist/core/server/fields/queryStructuredContent.js +65 -0
  262. package/dist/core/server/fields/resolveImageFields.js +51 -2
  263. package/dist/core/server/fields/resolveRelationFields.js +2 -2
  264. package/dist/core/server/fields/resolveRichtextLinks.js +80 -13
  265. package/dist/core/server/fields/resolveUrlFields.js +57 -6
  266. package/dist/core/server/fields/slugResolver.d.ts +10 -0
  267. package/dist/core/server/fields/slugResolver.js +34 -0
  268. package/dist/core/server/generator/fields.js +15 -4
  269. package/dist/core/server/generator/generator.js +3 -2
  270. package/dist/files-local/index.js +126 -64
  271. package/dist/server/auth.d.ts +5 -0
  272. package/dist/server/auth.js +12 -1
  273. package/dist/sveltekit/components/structured-content.svelte +204 -0
  274. package/dist/sveltekit/components/structured-content.svelte.d.ts +21 -0
  275. package/dist/sveltekit/config.d.ts +13 -3
  276. package/dist/sveltekit/index.d.ts +3 -0
  277. package/dist/sveltekit/index.js +3 -0
  278. package/dist/sveltekit/server/handle.js +1 -0
  279. package/dist/types/config.d.ts +3 -0
  280. package/dist/types/fields.d.ts +19 -2
  281. package/dist/types/index.d.ts +2 -0
  282. package/dist/types/index.js +2 -0
  283. package/dist/types/layout.d.ts +54 -0
  284. package/dist/types/layout.js +6 -0
  285. package/dist/types/structured-content.d.ts +63 -0
  286. package/dist/types/structured-content.js +1 -0
  287. package/dist/updates/0.1.5/index.d.ts +2 -0
  288. package/dist/updates/0.1.5/index.js +18 -0
  289. package/dist/updates/0.2.0/index.d.ts +2 -0
  290. package/dist/updates/0.2.0/index.js +11 -0
  291. package/dist/updates/0.2.2/index.d.ts +2 -0
  292. package/dist/updates/0.2.2/index.js +13 -0
  293. package/dist/updates/0.5.0/index.d.ts +2 -0
  294. package/dist/updates/0.5.0/index.js +14 -0
  295. package/dist/updates/index.js +5 -1
  296. package/package.json +16 -9
@@ -0,0 +1,212 @@
1
+ <script lang="ts">
2
+ import type { SlashCommandItem } from './slash-command.js';
3
+ import { onMount } from 'svelte';
4
+
5
+ type Props = {
6
+ items: SlashCommandItem[];
7
+ command: (item: SlashCommandItem) => void;
8
+ };
9
+
10
+ let { items, command }: Props = $props();
11
+
12
+ let selectedIndex = $state(0);
13
+ let listEl = $state<HTMLDivElement | null>(null);
14
+ let showTier2 = $state(false);
15
+
16
+ // When user is searching (items filtered), show all tiers
17
+ const isFiltered = $derived(items.length > 0 && items.some((i) => i.tier === 2) && items.some((i) => i.tier === 1)
18
+ ? false
19
+ : true
20
+ );
21
+
22
+ const visibleItems = $derived.by(() => {
23
+ if (isFiltered || showTier2) return items;
24
+ return items.filter((i) => !i.tier || i.tier === 1);
25
+ });
26
+
27
+ const hasTier2Hidden = $derived(!isFiltered && !showTier2 && items.some((i) => i.tier === 2));
28
+
29
+ // Group items by group name
30
+ const grouped = $derived(() => {
31
+ const groups = new Map<string, SlashCommandItem[]>();
32
+ for (const item of visibleItems) {
33
+ const existing = groups.get(item.group) ?? [];
34
+ existing.push(item);
35
+ groups.set(item.group, existing);
36
+ }
37
+ return groups;
38
+ });
39
+
40
+ function selectItem(index: number) {
41
+ const item = visibleItems[index];
42
+ if (item) command(item);
43
+ }
44
+
45
+ function handleKeydown(e: CustomEvent<{ key: string }>) {
46
+ const { key } = e.detail;
47
+ if (key === 'ArrowDown') {
48
+ e.preventDefault();
49
+ selectedIndex = (selectedIndex + 1) % visibleItems.length;
50
+ scrollToSelected();
51
+ } else if (key === 'ArrowUp') {
52
+ e.preventDefault();
53
+ selectedIndex = (selectedIndex - 1 + visibleItems.length) % visibleItems.length;
54
+ scrollToSelected();
55
+ } else if (key === 'Enter') {
56
+ e.preventDefault();
57
+ selectItem(selectedIndex);
58
+ }
59
+ }
60
+
61
+ function scrollToSelected() {
62
+ const el = listEl?.querySelector(`[data-index="${selectedIndex}"]`);
63
+ el?.scrollIntoView({ block: 'nearest' });
64
+ }
65
+
66
+ onMount(() => {
67
+ const el = listEl;
68
+ if (!el) return;
69
+
70
+ const handler = (e: Event) => handleKeydown(e as CustomEvent<{ key: string }>);
71
+ el.addEventListener('slash-keydown', handler);
72
+ return () => el.removeEventListener('slash-keydown', handler);
73
+ });
74
+
75
+ // Reset selection when items change
76
+ $effect(() => {
77
+ items; // dependency
78
+ selectedIndex = 0;
79
+ });
80
+ </script>
81
+
82
+ <div class="slash-popup" bind:this={listEl} data-slash-popup role="listbox" aria-label="Polecenia">
83
+ {#if items.length === 0}
84
+ <div class="slash-empty">Brak wyników</div>
85
+ {:else}
86
+ {@const groups = grouped()}
87
+ {#each [...groups.entries()] as [group, groupItems]}
88
+ <div class="slash-group-label" role="presentation">{group}</div>
89
+ {#each groupItems as item}
90
+ {@const idx = visibleItems.indexOf(item)}
91
+ <button
92
+ type="button"
93
+ class="slash-item"
94
+ class:selected={idx === selectedIndex}
95
+ data-index={idx}
96
+ role="option"
97
+ aria-selected={idx === selectedIndex}
98
+ onclick={() => selectItem(idx)}
99
+ onmouseenter={() => (selectedIndex = idx)}
100
+ >
101
+ <div class="slash-item-content">
102
+ <span class="slash-item-label">{item.label}</span>
103
+ {#if item.description}
104
+ <span class="slash-item-desc">{item.description}</span>
105
+ {/if}
106
+ </div>
107
+ </button>
108
+ {/each}
109
+ {/each}
110
+
111
+ {#if hasTier2Hidden}
112
+ <button
113
+ type="button"
114
+ class="slash-more-btn"
115
+ onclick={() => (showTier2 = true)}
116
+ >
117
+ Więcej...
118
+ </button>
119
+ {/if}
120
+ {/if}
121
+ </div>
122
+
123
+ <style>
124
+ .slash-popup {
125
+ background: var(--popover);
126
+ color: var(--popover-foreground);
127
+ border: 1px solid var(--border);
128
+ border-radius: 8px;
129
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
130
+ overflow-y: auto;
131
+ max-height: 320px;
132
+ min-width: 220px;
133
+ padding: 0.25rem;
134
+ }
135
+
136
+ .slash-group-label {
137
+ padding: 0.375rem 0.5rem 0.25rem;
138
+ font-size: 0.6875rem;
139
+ font-weight: 600;
140
+ text-transform: uppercase;
141
+ letter-spacing: 0.05em;
142
+ color: var(--muted-foreground);
143
+ }
144
+
145
+ .slash-item {
146
+ display: flex;
147
+ align-items: center;
148
+ width: 100%;
149
+ padding: 0.375rem 0.5rem;
150
+ border: none;
151
+ background: transparent;
152
+ border-radius: 6px;
153
+ cursor: pointer;
154
+ font-size: 0.8125rem;
155
+ color: var(--foreground);
156
+ text-align: left;
157
+ min-height: 36px;
158
+ transition: background-color 0.1s;
159
+ }
160
+
161
+ .slash-item:hover,
162
+ .slash-item.selected {
163
+ background: var(--accent);
164
+ }
165
+
166
+ .slash-item:focus-visible {
167
+ outline: 2px solid var(--ring);
168
+ outline-offset: -2px;
169
+ }
170
+
171
+ .slash-item-content {
172
+ display: flex;
173
+ flex-direction: column;
174
+ gap: 1px;
175
+ }
176
+
177
+ .slash-item-label {
178
+ font-weight: 500;
179
+ }
180
+
181
+ .slash-item-desc {
182
+ font-size: 0.6875rem;
183
+ color: var(--muted-foreground);
184
+ line-height: 1.3;
185
+ }
186
+
187
+ .slash-more-btn {
188
+ display: block;
189
+ width: 100%;
190
+ padding: 0.375rem 0.5rem;
191
+ border: none;
192
+ background: transparent;
193
+ border-radius: 6px;
194
+ cursor: pointer;
195
+ font-size: 0.75rem;
196
+ font-weight: 500;
197
+ color: var(--primary);
198
+ text-align: left;
199
+ transition: background-color 0.1s;
200
+ }
201
+
202
+ .slash-more-btn:hover {
203
+ background: var(--accent);
204
+ }
205
+
206
+ .slash-empty {
207
+ padding: 1rem;
208
+ text-align: center;
209
+ color: var(--muted-foreground);
210
+ font-size: 0.8125rem;
211
+ }
212
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { SlashCommandItem } from './slash-command.js';
2
+ type Props = {
3
+ items: SlashCommandItem[];
4
+ command: (item: SlashCommandItem) => void;
5
+ };
6
+ declare const SlashCommandPopup: import("svelte").Component<Props, {}, "">;
7
+ type SlashCommandPopup = ReturnType<typeof SlashCommandPopup>;
8
+ export default SlashCommandPopup;
@@ -0,0 +1,280 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { Editor, type Editor as EditorType } from '@tiptap/core';
4
+ import BubbleMenu from '@tiptap/extension-bubble-menu';
5
+ import { extensions } from './extensions.js';
6
+ import ToolbarButton from './toolbar-button.svelte';
7
+ import EditorToolbar from './editor-toolbar.svelte';
8
+ import LinkDialog from './link-dialog.svelte';
9
+ import ImageDialog from './image-dialog.svelte';
10
+ import VideoDialog from './video-dialog.svelte';
11
+ import TableDialog from './table-dialog.svelte';
12
+ import * as Tooltip from '../../../components/ui/tooltip/index.js';
13
+ import Separator from '../../../components/ui/separator/separator.svelte';
14
+ import type { StructuredContentDoc } from '../../../types/structured-content.js';
15
+ import type { ObjectField } from '../../../types/fields.js';
16
+ import { tiptapDocToStructured, structuredToTiptapDoc } from './structured-content-utils.js';
17
+ import { InlineBlockNode } from './inline-block-node.js';
18
+ import { SlashCommand } from './slash-command.js';
19
+ import { HeadingA11yPlugin } from './heading-a11y-plugin.js';
20
+
21
+ // Icons (bubble menu only)
22
+ import Bold from '@tabler/icons-svelte/icons/bold';
23
+ import Italic from '@tabler/icons-svelte/icons/italic';
24
+ import Underline from '@tabler/icons-svelte/icons/underline';
25
+ import Strikethrough from '@tabler/icons-svelte/icons/strikethrough';
26
+ import Highlight from '@tabler/icons-svelte/icons/highlight';
27
+ import LinkIcon from '@tabler/icons-svelte/icons/link';
28
+ import RowInsertBottom from '@tabler/icons-svelte/icons/row-insert-bottom';
29
+ import RowInsertTop from '@tabler/icons-svelte/icons/row-insert-top';
30
+ import ColumnInsertLeft from '@tabler/icons-svelte/icons/column-insert-left';
31
+ import ColumnInsertRight from '@tabler/icons-svelte/icons/column-insert-right';
32
+ import RowRemove from '@tabler/icons-svelte/icons/row-remove';
33
+ import ColumnRemove from '@tabler/icons-svelte/icons/column-remove';
34
+ import TableOff from '@tabler/icons-svelte/icons/table-off';
35
+ import Unlink from '@tabler/icons-svelte/icons/unlink';
36
+ import Pencil from '@tabler/icons-svelte/icons/pencil';
37
+ import ExternalLink from '@tabler/icons-svelte/icons/external-link';
38
+
39
+ type Props = {
40
+ value?: StructuredContentDoc;
41
+ inlineBlocks?: ObjectField[];
42
+ };
43
+
44
+ let { value = $bindable(undefined), inlineBlocks = [] }: Props = $props();
45
+
46
+ const hasInlineBlocks = $derived(inlineBlocks.length > 0);
47
+
48
+ let element = $state<HTMLDivElement | null>(null);
49
+ let editorState = $state<{ editor: EditorType | null }>({ editor: null });
50
+ let bubbleMenu = $state<HTMLDivElement | null>(null);
51
+
52
+ // Dialog states
53
+ let linkDialogOpen = $state(false);
54
+ let imageDialogOpen = $state(false);
55
+ let videoDialogOpen = $state(false);
56
+ let tableDialogOpen = $state(false);
57
+
58
+ // Track last structured JSON to avoid circular updates.
59
+ // Always compare in structured format (blockData as object) to stay consistent.
60
+ let lastStructuredJson = $state('');
61
+
62
+ onMount(() => {
63
+ const initialContent = value ? structuredToTiptapDoc(value) : '';
64
+
65
+ const extraExtensions = hasInlineBlocks
66
+ ? [
67
+ InlineBlockNode.configure({ inlineBlocks }),
68
+ SlashCommand.configure({ inlineBlocks })
69
+ ]
70
+ : [];
71
+
72
+ const editor = new Editor({
73
+ element: element!,
74
+ extensions: [
75
+ ...extensions,
76
+ ...extraExtensions,
77
+ HeadingA11yPlugin,
78
+ BubbleMenu.configure({
79
+ element: bubbleMenu!
80
+ })
81
+ ],
82
+ content: initialContent,
83
+ onTransaction: ({ editor }) => {
84
+ const json = editor.getJSON();
85
+ try {
86
+ const structured = tiptapDocToStructured(json);
87
+ const structuredStr = JSON.stringify(structured);
88
+ if (structuredStr !== lastStructuredJson) {
89
+ lastStructuredJson = structuredStr;
90
+ value = structured;
91
+ }
92
+ } catch {
93
+ // Invalid doc shape during editing — skip update
94
+ }
95
+ editorState = { editor };
96
+ }
97
+ });
98
+
99
+ editorState = { editor };
100
+
101
+ return () => {
102
+ editor.destroy();
103
+ };
104
+ });
105
+
106
+ // Sync incoming value changes (e.g. from form reset)
107
+ $effect(() => {
108
+ if (!editorState.editor || !value) return;
109
+ const incomingJson = JSON.stringify(value);
110
+ if (incomingJson !== lastStructuredJson) {
111
+ lastStructuredJson = incomingJson;
112
+ editorState.editor.commands.setContent(structuredToTiptapDoc(value));
113
+ }
114
+ });
115
+
116
+ const ed = $derived(editorState.editor);
117
+ </script>
118
+
119
+ <Tooltip.Provider>
120
+ <div class="border-input bg-background rounded-lg border">
121
+ <!-- Toolbar — no code view for content editor -->
122
+ {#if ed}
123
+ <EditorToolbar
124
+ editor={ed}
125
+ onLinkDialog={() => (linkDialogOpen = true)}
126
+ onImageDialog={() => (imageDialogOpen = true)}
127
+ onVideoDialog={() => (videoDialogOpen = true)}
128
+ onTableDialog={() => (tableDialogOpen = true)}
129
+ showCodeView={false}
130
+ showInsertBlock={hasInlineBlocks}
131
+ onInsertBlock={() => {
132
+ // Insert "/" at cursor to trigger slash command
133
+ ed?.chain().focus().insertContent('/').run();
134
+ }}
135
+ />
136
+ {/if}
137
+
138
+ <!-- Bubble Menu -->
139
+ <div bind:this={bubbleMenu} class="bubble-menu">
140
+ {#if ed}
141
+ <div class="bg-popover text-popover-foreground flex items-center gap-0.5 rounded-lg border p-1 shadow-md">
142
+ {#if ed.isActive('link')}
143
+ {#if ed.getAttributes('link').target === '_blank'}
144
+ <span class="text-muted-foreground flex items-center px-1" title="Otwiera w nowej karcie">
145
+ <ExternalLink class="h-4 w-4" />
146
+ </span>
147
+ {/if}
148
+ <ToolbarButton label="Edytuj link" active={false} onclick={() => (linkDialogOpen = true)}>
149
+ <Pencil class="h-4 w-4" />
150
+ </ToolbarButton>
151
+ <ToolbarButton
152
+ label="Usuń link"
153
+ active={false}
154
+ onclick={() => ed.chain().focus().unsetLink().run()}
155
+ >
156
+ <Unlink class="h-4 w-4" />
157
+ </ToolbarButton>
158
+ {:else if ed.isActive('table')}
159
+ <ToolbarButton
160
+ label="Wiersz powyżej"
161
+ active={false}
162
+ onclick={() => ed.chain().focus().addRowBefore().run()}
163
+ >
164
+ <RowInsertTop class="h-4 w-4" />
165
+ </ToolbarButton>
166
+ <ToolbarButton
167
+ label="Wiersz poniżej"
168
+ active={false}
169
+ onclick={() => ed.chain().focus().addRowAfter().run()}
170
+ >
171
+ <RowInsertBottom class="h-4 w-4" />
172
+ </ToolbarButton>
173
+ <ToolbarButton
174
+ label="Kolumna z lewej"
175
+ active={false}
176
+ onclick={() => ed.chain().focus().addColumnBefore().run()}
177
+ >
178
+ <ColumnInsertLeft class="h-4 w-4" />
179
+ </ToolbarButton>
180
+ <ToolbarButton
181
+ label="Kolumna z prawej"
182
+ active={false}
183
+ onclick={() => ed.chain().focus().addColumnAfter().run()}
184
+ >
185
+ <ColumnInsertRight class="h-4 w-4" />
186
+ </ToolbarButton>
187
+ <Separator orientation="vertical" class="mx-1 h-6" />
188
+ <ToolbarButton
189
+ label="Usuń wiersz"
190
+ active={false}
191
+ onclick={() => ed.chain().focus().deleteRow().run()}
192
+ >
193
+ <RowRemove class="h-4 w-4" />
194
+ </ToolbarButton>
195
+ <ToolbarButton
196
+ label="Usuń kolumnę"
197
+ active={false}
198
+ onclick={() => ed.chain().focus().deleteColumn().run()}
199
+ >
200
+ <ColumnRemove class="h-4 w-4" />
201
+ </ToolbarButton>
202
+ <ToolbarButton
203
+ label="Usuń tabelę"
204
+ active={false}
205
+ onclick={() => ed.chain().focus().deleteTable().run()}
206
+ >
207
+ <TableOff class="h-4 w-4" />
208
+ </ToolbarButton>
209
+ {:else}
210
+ <ToolbarButton
211
+ label="Pogrubienie"
212
+ active={ed.isActive('bold')}
213
+ onclick={() => ed.chain().focus().toggleBold().run()}
214
+ >
215
+ <Bold class="h-4 w-4" />
216
+ </ToolbarButton>
217
+ <ToolbarButton
218
+ label="Kursywa"
219
+ active={ed.isActive('italic')}
220
+ onclick={() => ed.chain().focus().toggleItalic().run()}
221
+ >
222
+ <Italic class="h-4 w-4" />
223
+ </ToolbarButton>
224
+ <ToolbarButton
225
+ label="Podkreślenie"
226
+ active={ed.isActive('underline')}
227
+ onclick={() => ed.chain().focus().toggleUnderline().run()}
228
+ >
229
+ <Underline class="h-4 w-4" />
230
+ </ToolbarButton>
231
+ <ToolbarButton
232
+ label="Przekreślenie"
233
+ active={ed.isActive('strike')}
234
+ onclick={() => ed.chain().focus().toggleStrike().run()}
235
+ >
236
+ <Strikethrough class="h-4 w-4" />
237
+ </ToolbarButton>
238
+ <ToolbarButton
239
+ label="Wyróżnienie"
240
+ active={ed.isActive('highlight')}
241
+ onclick={() => ed.chain().focus().toggleHighlight().run()}
242
+ >
243
+ <Highlight class="h-4 w-4" />
244
+ </ToolbarButton>
245
+ <Separator orientation="vertical" class="mx-1 h-6" />
246
+ <ToolbarButton
247
+ label="Link"
248
+ active={ed.isActive('link')}
249
+ onclick={() => (linkDialogOpen = true)}
250
+ >
251
+ <LinkIcon class="h-4 w-4" />
252
+ </ToolbarButton>
253
+ {/if}
254
+ </div>
255
+ {/if}
256
+ </div>
257
+
258
+ <!-- Editor content -->
259
+ <div
260
+ bind:this={element}
261
+ class="prose prose-sm dark:prose-invert max-w-none p-4 focus:outline-none [&_.ProseMirror]:min-h-[200px] [&_.ProseMirror]:outline-none [&_.ProseMirror]:scroll-padding-top-12"
262
+ ></div>
263
+ </div>
264
+ </Tooltip.Provider>
265
+
266
+ <!-- Dialogs -->
267
+ <LinkDialog bind:open={linkDialogOpen} editor={ed} />
268
+ <ImageDialog bind:open={imageDialogOpen} editor={ed} />
269
+ <VideoDialog bind:open={videoDialogOpen} editor={ed} />
270
+ <TableDialog bind:open={tableDialogOpen} editor={ed} />
271
+
272
+ <style>
273
+ .bubble-menu {
274
+ visibility: hidden;
275
+ opacity: 0;
276
+ transition:
277
+ visibility 0.1s ease,
278
+ opacity 0.1s ease;
279
+ }
280
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { StructuredContentDoc } from '../../../types/structured-content.js';
2
+ import type { ObjectField } from '../../../types/fields.js';
3
+ type Props = {
4
+ value?: StructuredContentDoc;
5
+ inlineBlocks?: ObjectField[];
6
+ };
7
+ declare const ContentEditor: import("svelte").Component<Props, {}, "value">;
8
+ type ContentEditor = ReturnType<typeof ContentEditor>;
9
+ export default ContentEditor;