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
@@ -4,6 +4,7 @@
4
4
  import AlignLeft from '@tabler/icons-svelte/icons/align-left';
5
5
  import AlignCenter from '@tabler/icons-svelte/icons/align-center';
6
6
  import AlignRight from '@tabler/icons-svelte/icons/align-right';
7
+ import AlertTriangle from '@tabler/icons-svelte/icons/alert-triangle';
7
8
 
8
9
  let { node, updateAttributes, selected }: NodeViewProps = $props();
9
10
 
@@ -11,12 +12,34 @@
11
12
  let captionInput = $state(node.attrs.caption || '');
12
13
  let captionInputEl = $state<HTMLInputElement | null>(null);
13
14
 
15
+ let editingAlt = $state(false);
16
+ let altInput = $state(node.attrs.alt || '');
17
+ let altInputEl = $state<HTMLInputElement | null>(null);
18
+ let figureEl = $state<HTMLDivElement | null>(null);
19
+ let toolbarFlipped = $state(false);
20
+
21
+ const hasAlt = $derived(!!(node.attrs.alt && (node.attrs.alt as string).trim()));
22
+
14
23
  $effect(() => {
15
24
  if (editing && captionInputEl) {
16
25
  captionInputEl.focus();
17
26
  }
18
27
  });
19
28
 
29
+ $effect(() => {
30
+ if (editingAlt && altInputEl) {
31
+ altInputEl.focus();
32
+ }
33
+ });
34
+
35
+ // Flip toolbar to bottom when too close to viewport top
36
+ $effect(() => {
37
+ if (selected && figureEl) {
38
+ const rect = figureEl.getBoundingClientRect();
39
+ toolbarFlipped = rect.top < 56;
40
+ }
41
+ });
42
+
20
43
  function setAlign(align: 'left' | 'center' | 'right') {
21
44
  updateAttributes({ align });
22
45
  }
@@ -26,6 +49,11 @@
26
49
  editing = false;
27
50
  }
28
51
 
52
+ function saveAlt() {
53
+ updateAttributes({ alt: altInput.trim() });
54
+ editingAlt = false;
55
+ }
56
+
29
57
  function handleKeydown(e: KeyboardEvent) {
30
58
  if (e.key === 'Enter') {
31
59
  e.preventDefault();
@@ -36,17 +64,34 @@
36
64
  editing = false;
37
65
  }
38
66
  }
67
+
68
+ function handleAltKeydown(e: KeyboardEvent) {
69
+ if (e.key === 'Enter') {
70
+ e.preventDefault();
71
+ saveAlt();
72
+ }
73
+ if (e.key === 'Escape') {
74
+ altInput = node.attrs.alt || '';
75
+ editingAlt = false;
76
+ }
77
+ }
78
+
79
+ function startEditAlt() {
80
+ altInput = node.attrs.alt || '';
81
+ editingAlt = true;
82
+ }
39
83
  </script>
40
84
 
41
85
  <NodeViewWrapper class="tiptap-figure align-{node.attrs.align || 'center'}" data-drag-handle>
42
- <div class="figure-wrapper" class:selected>
86
+ <div class="figure-wrapper" class:selected bind:this={figureEl}>
43
87
  {#if selected}
44
- <div class="figure-toolbar">
88
+ <div class="figure-toolbar" class:figure-toolbar-bottom={toolbarFlipped}>
45
89
  <button
46
90
  type="button"
47
91
  class="toolbar-btn"
48
92
  class:active={node.attrs.align === 'left'}
49
93
  onclick={() => setAlign('left')}
94
+ aria-label="Wyrównaj do lewej"
50
95
  title="Wyrównaj do lewej"
51
96
  >
52
97
  <AlignLeft size={16} />
@@ -56,6 +101,7 @@
56
101
  class="toolbar-btn"
57
102
  class:active={node.attrs.align === 'center'}
58
103
  onclick={() => setAlign('center')}
104
+ aria-label="Wyśrodkuj"
59
105
  title="Wyśrodkuj"
60
106
  >
61
107
  <AlignCenter size={16} />
@@ -65,6 +111,7 @@
65
111
  class="toolbar-btn"
66
112
  class:active={node.attrs.align === 'right'}
67
113
  onclick={() => setAlign('right')}
114
+ aria-label="Wyrównaj do prawej"
68
115
  title="Wyrównaj do prawej"
69
116
  >
70
117
  <AlignRight size={16} />
@@ -74,6 +121,31 @@
74
121
 
75
122
  <img src={node.attrs.src} alt={node.attrs.alt || ''} />
76
123
 
124
+ <!-- Alt text field — always visible -->
125
+ <div class="figure-alt-row" class:figure-alt-warning={!hasAlt}>
126
+ {#if editingAlt}
127
+ <input
128
+ bind:this={altInputEl}
129
+ type="text"
130
+ class="figure-alt-input"
131
+ bind:value={altInput}
132
+ onblur={saveAlt}
133
+ onkeydown={handleAltKeydown}
134
+ placeholder="Opis zdjęcia..."
135
+ />
136
+ {:else if hasAlt}
137
+ <button type="button" class="figure-alt-display" onclick={startEditAlt}>
138
+ Alt: {node.attrs.alt}
139
+ </button>
140
+ {:else}
141
+ <button type="button" class="figure-alt-display figure-alt-missing" onclick={startEditAlt}>
142
+ <AlertTriangle size={14} />
143
+ <span>Dodaj opis zdjęcia, żeby każdy mógł je zrozumieć</span>
144
+ </button>
145
+ {/if}
146
+ </div>
147
+
148
+ <!-- Caption -->
77
149
  {#if editing}
78
150
  <input
79
151
  bind:this={captionInputEl}
@@ -101,7 +173,7 @@
101
173
  }
102
174
 
103
175
  .figure-wrapper.selected {
104
- outline: 2px solid hsl(var(--primary));
176
+ outline: 2px solid var(--primary);
105
177
  outline-offset: 4px;
106
178
  border-radius: 0.5rem;
107
179
  }
@@ -120,14 +192,19 @@
120
192
  transform: translateX(-50%);
121
193
  display: flex;
122
194
  gap: 4px;
123
- background: hsl(var(--popover));
124
- border: 1px solid hsl(var(--border));
195
+ background: var(--popover);
196
+ border: 1px solid var(--border);
125
197
  border-radius: 0.5rem;
126
198
  padding: 4px;
127
199
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
128
200
  z-index: 10;
129
201
  }
130
202
 
203
+ .figure-toolbar-bottom {
204
+ top: auto;
205
+ bottom: -40px;
206
+ }
207
+
131
208
  .toolbar-btn {
132
209
  display: flex;
133
210
  align-items: center;
@@ -138,17 +215,17 @@
138
215
  background: transparent;
139
216
  border-radius: 0.25rem;
140
217
  cursor: pointer;
141
- color: hsl(var(--foreground));
218
+ color: var(--foreground);
142
219
  transition: background-color 0.15s;
143
220
  }
144
221
 
145
222
  .toolbar-btn:hover {
146
- background: hsl(var(--accent));
223
+ background: var(--accent);
147
224
  }
148
225
 
149
226
  .toolbar-btn.active {
150
- background: hsl(var(--primary));
151
- color: hsl(var(--primary-foreground));
227
+ background: var(--primary);
228
+ color: var(--primary-foreground);
152
229
  }
153
230
 
154
231
  .caption-display {
@@ -156,7 +233,7 @@
156
233
  width: 100%;
157
234
  margin-top: 0.5rem;
158
235
  font-size: 0.875rem;
159
- color: hsl(var(--muted-foreground));
236
+ color: var(--muted-foreground);
160
237
  text-align: center;
161
238
  background: none;
162
239
  border: none;
@@ -166,7 +243,7 @@
166
243
  }
167
244
 
168
245
  .caption-display:hover {
169
- background: hsl(var(--accent) / 0.5);
246
+ background: color-mix(in oklch, var(--accent) 50%, transparent);
170
247
  }
171
248
 
172
249
  .caption-input {
@@ -176,14 +253,66 @@
176
253
  font-size: 0.875rem;
177
254
  text-align: center;
178
255
  padding: 0.25rem 0.5rem;
179
- border: 1px solid hsl(var(--border));
256
+ border: 1px solid var(--border);
180
257
  border-radius: 0.25rem;
181
- background: hsl(var(--background));
182
- color: hsl(var(--foreground));
258
+ background: var(--background);
259
+ color: var(--foreground);
183
260
  }
184
261
 
185
262
  .caption-input:focus {
186
263
  outline: none;
187
- border-color: hsl(var(--primary));
264
+ border-color: var(--primary);
265
+ }
266
+
267
+ /* Alt text field */
268
+ .figure-alt-row {
269
+ margin-top: 0.375rem;
270
+ border-radius: 0.375rem;
271
+ transition: background-color 0.15s;
272
+ }
273
+
274
+ .figure-alt-warning {
275
+ background: var(--warning-bg);
276
+ }
277
+
278
+ .figure-alt-display {
279
+ display: flex;
280
+ align-items: center;
281
+ gap: 6px;
282
+ width: 100%;
283
+ padding: 0.25rem 0.5rem;
284
+ font-size: 0.75rem;
285
+ color: var(--muted-foreground);
286
+ text-align: left;
287
+ background: none;
288
+ border: none;
289
+ cursor: pointer;
290
+ border-radius: 0.375rem;
291
+ transition: background-color 0.15s;
292
+ }
293
+
294
+ .figure-alt-display:hover {
295
+ background: color-mix(in oklch, var(--accent) 50%, transparent);
296
+ }
297
+
298
+ .figure-alt-missing {
299
+ color: #7a5520;
300
+ font-weight: 500;
301
+ }
302
+
303
+ .figure-alt-input {
304
+ display: block;
305
+ width: 100%;
306
+ padding: 0.25rem 0.5rem;
307
+ font-size: 0.75rem;
308
+ border: 1px solid var(--border);
309
+ border-radius: 0.375rem;
310
+ background: var(--background);
311
+ color: var(--foreground);
312
+ }
313
+
314
+ .figure-alt-input:focus {
315
+ outline: none;
316
+ border-color: var(--primary);
188
317
  }
189
318
  </style>
@@ -0,0 +1,254 @@
1
+ <script lang="ts">
2
+ import { NodeViewWrapper } from 'svelte-tiptap';
3
+ import type { NodeViewProps } from '@tiptap/core';
4
+ import StandaloneFieldRenderer from '../fields/standalone-field-renderer.svelte';
5
+ import type { ObjectField } from '../../../types/fields.js';
6
+ import GripVertical from '@tabler/icons-svelte/icons/grip-vertical';
7
+ import ArrowUp from '@tabler/icons-svelte/icons/arrow-up';
8
+ import ArrowDown from '@tabler/icons-svelte/icons/arrow-down';
9
+ import Trash from '@tabler/icons-svelte/icons/trash';
10
+
11
+ let { node, updateAttributes, editor, getPos, deleteNode, selected }: NodeViewProps = $props();
12
+
13
+ const inlineBlocks: ObjectField[] = $derived(
14
+ (editor.extensionManager.extensions.find((e) => e.name === 'inlineBlock')?.options as { inlineBlocks: ObjectField[] })?.inlineBlocks ?? []
15
+ );
16
+
17
+ const blockDef = $derived(inlineBlocks.find((b) => b.slug === node.attrs.blockType));
18
+ const blockLabel = $derived(blockDef?.label ? (Object.values(blockDef.label)[0] ?? blockDef.slug) : node.attrs.blockType);
19
+
20
+ let blockData = $state<Record<string, unknown>>(parseBlockData(node.attrs.blockData));
21
+ // Track last JSON we wrote to PM to avoid re-parsing our own writes
22
+ let lastWrittenJson = '';
23
+
24
+ function parseBlockData(raw: unknown): Record<string, unknown> {
25
+ if (typeof raw === 'string') {
26
+ try { return JSON.parse(raw); } catch { return {}; }
27
+ }
28
+ if (typeof raw === 'object' && raw !== null) return raw as Record<string, unknown>;
29
+ return {};
30
+ }
31
+
32
+ // Sync external changes (e.g. undo/redo) — skip if it's our own write
33
+ $effect(() => {
34
+ const raw = node.attrs.blockData;
35
+ const rawJson = typeof raw === 'string' ? raw : JSON.stringify(raw);
36
+ if (rawJson !== lastWrittenJson) {
37
+ blockData = parseBlockData(raw);
38
+ }
39
+ });
40
+
41
+ let debounceTimer: ReturnType<typeof setTimeout> | null = null;
42
+
43
+ function handleFieldChange(slug: string, value: unknown) {
44
+ blockData = { ...blockData, [slug]: value };
45
+ if (debounceTimer) clearTimeout(debounceTimer);
46
+ debounceTimer = setTimeout(() => {
47
+ lastWrittenJson = JSON.stringify(blockData);
48
+ updateAttributes({ blockData: lastWrittenJson });
49
+ }, 300);
50
+ }
51
+
52
+ function moveUp() {
53
+ const pos = getPos();
54
+ if (pos === undefined || pos <= 0) return;
55
+ const resolvedPos = editor.state.doc.resolve(pos);
56
+ const index = resolvedPos.index(resolvedPos.depth);
57
+ if (index <= 0) return;
58
+
59
+ const thisNode = editor.state.doc.nodeAt(pos);
60
+ if (!thisNode) return;
61
+
62
+ const prevNode = resolvedPos.parent.child(index - 1);
63
+ const prevPos = pos - prevNode.nodeSize;
64
+
65
+ editor
66
+ .chain()
67
+ .focus()
68
+ .command(({ tr }) => {
69
+ tr.delete(pos, pos + thisNode.nodeSize);
70
+ tr.insert(prevPos, thisNode);
71
+ return true;
72
+ })
73
+ .run();
74
+ }
75
+
76
+ function moveDown() {
77
+ const pos = getPos();
78
+ if (pos === undefined) return;
79
+ const resolvedPos = editor.state.doc.resolve(pos);
80
+ const index = resolvedPos.index(resolvedPos.depth);
81
+ const parent = resolvedPos.parent;
82
+ if (index >= parent.childCount - 1) return;
83
+
84
+ const thisNode = editor.state.doc.nodeAt(pos);
85
+ if (!thisNode) return;
86
+
87
+ const nextNode = parent.child(index + 1);
88
+ const insertPos = pos + thisNode.nodeSize + nextNode.nodeSize;
89
+
90
+ editor
91
+ .chain()
92
+ .focus()
93
+ .command(({ tr }) => {
94
+ tr.insert(insertPos, thisNode);
95
+ tr.delete(pos, pos + thisNode.nodeSize);
96
+ return true;
97
+ })
98
+ .run();
99
+ }
100
+
101
+ function handleDelete() {
102
+ const pos = getPos();
103
+ deleteNode();
104
+ // Focus previous node
105
+ if (pos !== undefined && pos > 0) {
106
+ editor.chain().focus(pos - 1).run();
107
+ }
108
+ }
109
+ </script>
110
+
111
+ <NodeViewWrapper class="inline-block-node {selected ? 'selected' : ''}" data-drag-handle>
112
+ <div class="inline-block-wrapper">
113
+ <!-- Header -->
114
+ <div class="inline-block-header">
115
+ <div class="inline-block-header-left">
116
+ <button type="button" class="grip-handle" aria-label="Przeciągnij" data-drag-handle>
117
+ <GripVertical size={16} />
118
+ </button>
119
+ <span class="inline-block-label">{blockLabel}</span>
120
+ </div>
121
+ <div class="inline-block-header-actions">
122
+ <button type="button" class="action-btn" onclick={moveUp} aria-label="Przenieś wyżej" title="Przenieś wyżej">
123
+ <ArrowUp size={16} />
124
+ </button>
125
+ <button type="button" class="action-btn" onclick={moveDown} aria-label="Przenieś niżej" title="Przenieś niżej">
126
+ <ArrowDown size={16} />
127
+ </button>
128
+ <button type="button" class="action-btn action-btn-danger" onclick={handleDelete} aria-label="Usuń blok" title="Usuń blok">
129
+ <Trash size={16} />
130
+ </button>
131
+ </div>
132
+ </div>
133
+
134
+ <!-- Body: field renderer -->
135
+ {#if blockDef}
136
+ <div class="inline-block-body">
137
+ <StandaloneFieldRenderer
138
+ fields={blockDef.fields}
139
+ data={blockData}
140
+ onchange={handleFieldChange}
141
+ />
142
+ </div>
143
+ {:else}
144
+ <div class="inline-block-body inline-block-unknown">
145
+ Nieznany typ bloku: {node.attrs.blockType}
146
+ </div>
147
+ {/if}
148
+ </div>
149
+ </NodeViewWrapper>
150
+
151
+ <style>
152
+ .inline-block-wrapper {
153
+ border: 1px solid var(--border);
154
+ border-radius: 12px;
155
+ overflow: hidden;
156
+ margin: 0.5rem 0;
157
+ background: var(--surface, var(--background));
158
+ }
159
+
160
+ .inline-block-wrapper:hover {
161
+ border-color: color-mix(in oklch, var(--primary) 50%, transparent);
162
+ }
163
+
164
+ :global(.selected) .inline-block-wrapper {
165
+ border-color: color-mix(in oklch, var(--primary) 50%, transparent);
166
+ }
167
+
168
+ .inline-block-header {
169
+ display: flex;
170
+ align-items: center;
171
+ justify-content: space-between;
172
+ padding: 0.375rem 0.5rem;
173
+ background: var(--muted);
174
+ border-bottom: 1px solid var(--border);
175
+ min-height: 36px;
176
+ }
177
+
178
+ .inline-block-header-left {
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 0.25rem;
182
+ }
183
+
184
+ .inline-block-label {
185
+ font-size: 0.8125rem;
186
+ font-weight: 600;
187
+ color: var(--foreground);
188
+ }
189
+
190
+ .inline-block-header-actions {
191
+ display: flex;
192
+ align-items: center;
193
+ gap: 2px;
194
+ }
195
+
196
+ .grip-handle {
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: center;
200
+ width: 24px;
201
+ height: 24px;
202
+ border: none;
203
+ background: transparent;
204
+ cursor: grab;
205
+ color: var(--muted-foreground);
206
+ border-radius: 4px;
207
+ padding: 0;
208
+ }
209
+
210
+ .grip-handle:hover {
211
+ background: var(--accent);
212
+ }
213
+
214
+ .action-btn {
215
+ display: flex;
216
+ align-items: center;
217
+ justify-content: center;
218
+ min-width: 24px;
219
+ min-height: 24px;
220
+ border: none;
221
+ background: transparent;
222
+ cursor: pointer;
223
+ color: var(--muted-foreground);
224
+ border-radius: 4px;
225
+ padding: 2px;
226
+ transition: background-color 0.15s, color 0.15s;
227
+ }
228
+
229
+ .action-btn:hover {
230
+ background: var(--accent);
231
+ color: var(--foreground);
232
+ }
233
+
234
+ .action-btn:focus-visible {
235
+ outline: 2px solid var(--ring);
236
+ outline-offset: 1px;
237
+ }
238
+
239
+ .action-btn-danger:hover {
240
+ background: color-mix(in oklch, var(--destructive) 10%, transparent);
241
+ color: var(--destructive);
242
+ }
243
+
244
+ .inline-block-body {
245
+ padding: 0.75rem;
246
+ }
247
+
248
+ .inline-block-unknown {
249
+ text-align: center;
250
+ color: var(--muted-foreground);
251
+ font-size: 0.8125rem;
252
+ font-style: italic;
253
+ }
254
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { NodeViewProps } from '@tiptap/core';
2
+ declare const InlineBlockNodeView: import("svelte").Component<NodeViewProps, {}, "">;
3
+ type InlineBlockNodeView = ReturnType<typeof InlineBlockNodeView>;
4
+ export default InlineBlockNodeView;