includio-cms 0.1.3 → 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 (313) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/ROADMAP.md +23 -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 -256
  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/paraglide/.prettierignore +3 -0
  272. package/dist/paraglide/messages/_index.d.ts +36 -0
  273. package/dist/paraglide/messages/_index.js +72 -0
  274. package/dist/paraglide/messages/en.d.ts +5 -0
  275. package/dist/paraglide/messages/en.js +14 -0
  276. package/dist/paraglide/messages/pl.d.ts +5 -0
  277. package/dist/paraglide/messages/pl.js +14 -0
  278. package/dist/paraglide/messages.d.ts +2 -0
  279. package/dist/paraglide/messages.js +4 -0
  280. package/dist/paraglide/registry.d.ts +21 -0
  281. package/dist/paraglide/registry.js +31 -0
  282. package/dist/paraglide/runtime.d.ts +583 -0
  283. package/dist/paraglide/runtime.js +1402 -0
  284. package/dist/paraglide/server.d.ts +67 -0
  285. package/dist/paraglide/server.js +175 -0
  286. package/dist/server/auth.d.ts +5 -0
  287. package/dist/server/auth.js +12 -1
  288. package/dist/sveltekit/components/structured-content.svelte +204 -0
  289. package/dist/sveltekit/components/structured-content.svelte.d.ts +21 -0
  290. package/dist/sveltekit/config.d.ts +13 -3
  291. package/dist/sveltekit/index.d.ts +3 -0
  292. package/dist/sveltekit/index.js +3 -0
  293. package/dist/sveltekit/server/handle.js +1 -0
  294. package/dist/types/config.d.ts +3 -0
  295. package/dist/types/fields.d.ts +19 -2
  296. package/dist/types/index.d.ts +2 -0
  297. package/dist/types/index.js +2 -0
  298. package/dist/types/layout.d.ts +54 -0
  299. package/dist/types/layout.js +6 -0
  300. package/dist/types/structured-content.d.ts +63 -0
  301. package/dist/types/structured-content.js +1 -0
  302. package/dist/updates/0.1.4/index.d.ts +2 -0
  303. package/dist/updates/0.1.4/index.js +11 -0
  304. package/dist/updates/0.1.5/index.d.ts +2 -0
  305. package/dist/updates/0.1.5/index.js +18 -0
  306. package/dist/updates/0.2.0/index.d.ts +2 -0
  307. package/dist/updates/0.2.0/index.js +11 -0
  308. package/dist/updates/0.2.2/index.d.ts +2 -0
  309. package/dist/updates/0.2.2/index.js +13 -0
  310. package/dist/updates/0.5.0/index.d.ts +2 -0
  311. package/dist/updates/0.5.0/index.js +14 -0
  312. package/dist/updates/index.js +6 -1
  313. package/package.json +17 -10
@@ -1,16 +1,20 @@
1
1
  <script lang="ts">
2
2
  import { getContentLanguage } from '../../state/content-language.svelte.js';
3
- import type { DbEntryVersion, EntryStatus, RawEntry } from '../../../types/entries.js';
4
- import * as ToggleGroup from '../../../components/ui/toggle-group/index.js';
3
+ import type { DbEntryVersion, RawEntry } from '../../../types/entries.js';
5
4
  import Button from '../../../components/ui/button/button.svelte';
6
5
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
7
6
  import type { InterfaceLanguage } from '../../../types/languages.js';
8
7
  import StatusBadge from './header/status-badge.svelte';
9
8
  import SaveIndicator from './header/save-indicator.svelte';
10
9
  import type { UpdateEntryVersionCommandType } from '../../../core/server/entries/operations/update.js';
10
+ import type { Field } from '../../../types/fields.js';
11
11
  import LayoutSidebar from '@tabler/icons-svelte/icons/layout-sidebar';
12
+ import SendIcon from '@tabler/icons-svelte/icons/send';
13
+ import ChevronDownIcon from '@tabler/icons-svelte/icons/chevron-down';
14
+ import * as DropdownMenu from '../../../components/ui/dropdown-menu/index.js';
12
15
  import { hasHybridContext, getHybridContext } from './hybrid/hybrid-context.svelte.js';
13
16
  import { getEntryStatus } from './utils.js';
17
+ import { onMount } from 'svelte';
14
18
 
15
19
  const contentLanguage = getContentLanguage();
16
20
  const interfaceLanguage = useInterfaceLanguage();
@@ -26,7 +30,7 @@
26
30
  en: {
27
31
  publish: 'Publish',
28
32
  update: 'Update',
29
- saveDraft: 'Save Draft'
33
+ saveDraft: 'Save draft'
30
34
  },
31
35
  pl: {
32
36
  publish: 'Publikuj',
@@ -40,12 +44,26 @@
40
44
  type Props = {
41
45
  entry: RawEntry;
42
46
  version: DbEntryVersion;
47
+ fields?: Field[];
48
+ getFormData?: () => Record<string, unknown>;
43
49
  onSave: (type: UpdateEntryVersionCommandType, scheduledAt?: Date) => void;
50
+ onSaveDraft: () => void;
44
51
  onArchive: () => void;
45
52
  saveStatus?: SaveStatus;
53
+ onScrollToIssue?: (fieldSlug: string, nodePos: number) => void;
46
54
  };
47
55
 
48
- let { entry, onSave, onArchive, version, saveStatus = 'idle' }: Props = $props();
56
+ let {
57
+ entry,
58
+ onSave,
59
+ onSaveDraft,
60
+ onArchive,
61
+ version,
62
+ fields = [],
63
+ getFormData,
64
+ saveStatus = 'idle',
65
+ onScrollToIssue
66
+ }: Props = $props();
49
67
  let { collection } = entry;
50
68
 
51
69
  const hybridContext = hasHybridContext() ? getHybridContext() : null;
@@ -62,52 +80,93 @@
62
80
  return t.publish;
63
81
  }
64
82
  });
83
+
84
+ let isMac = $state(false);
85
+ onMount(() => {
86
+ isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform);
87
+ });
88
+ const shortcutLabel = $derived(isMac ? '⌘S' : 'Ctrl+S');
65
89
  </script>
66
90
 
67
91
  <div
68
- class="sticky top-0 z-50 flex items-center justify-between gap-4 border-b border-slate-200/30 bg-white/60 px-4 py-3 backdrop-blur-xl dark:border-white/10 dark:bg-slate-900/60 md:px-6"
92
+ class="bg-background/60 sticky top-0 z-50 flex h-12 shrink-0 items-center justify-between gap-4 border-b px-6 backdrop-blur-xl"
69
93
  >
70
- <div class="flex items-center gap-4">
94
+ <!-- Left: Status + Save indicator -->
95
+ <div class="flex items-center gap-2.5">
71
96
  <StatusBadge {entry} {version} />
72
97
  <SaveIndicator status={saveStatus} />
73
98
  </div>
74
99
 
75
- <div class="flex items-center gap-2">
100
+ <!-- Right: grouped actions -->
101
+ <div class="flex items-center gap-1.5">
102
+ <!-- Language switcher -->
76
103
  {#if contentLanguage.all.length > 1}
77
- <ToggleGroup.Root
78
- size="sm"
79
- type="single"
80
- variant="outline"
81
- onValueChange={(val) => {
82
- if (val) {
83
- contentLanguage.current = val;
84
- }
85
- }}
86
- value={contentLanguage.current}
87
- >
104
+ <div class="border-border bg-muted inline-flex overflow-hidden rounded-lg border">
88
105
  {#each contentLanguage.all as lang}
89
- <ToggleGroup.Item value={lang}>
106
+ <button
107
+ type="button"
108
+ role="tab"
109
+ aria-selected={lang === contentLanguage.current}
110
+ class="px-2.5 py-1 text-xs font-semibold transition-colors {lang ===
111
+ contentLanguage.current
112
+ ? 'text-primary bg-white shadow-sm'
113
+ : 'text-muted-foreground hover:text-foreground bg-transparent'}"
114
+ onclick={() => (contentLanguage.current = lang)}
115
+ >
90
116
  {lang.toUpperCase()}
91
- </ToggleGroup.Item>
117
+ </button>
92
118
  {/each}
93
- </ToggleGroup.Root>
94
- {/if}
119
+ </div>
95
120
 
96
- <Button size="sm" type="button" variant="ghost" onclick={() => onSave('draft')}
97
- >{lang[interfaceLanguage.current].saveDraft}</Button
98
- >
99
-
100
- <Button
101
- size="sm"
102
- type="button"
103
- variant="gradient"
104
- onclick={() => onSave('published-now')}
105
- >
106
- {primaryButtonLabel}
107
- </Button>
121
+ <div class="bg-border mx-1 h-5 w-px shrink-0"></div>
122
+ {/if}
108
123
 
124
+ <!-- Split button: Publish + Save Draft -->
125
+ <div class="inline-flex items-stretch rounded-lg shadow-[0_1px_3px_rgba(91,74,158,0.3)]">
126
+ <button
127
+ type="button"
128
+ class="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-l-lg px-4 py-1.5 text-[13px] font-semibold transition-colors"
129
+ onclick={() => onSave('published-now')}
130
+ >
131
+ <SendIcon class="size-3.5" />
132
+ {primaryButtonLabel}
133
+ </button>
134
+
135
+ <DropdownMenu.Root>
136
+ <DropdownMenu.Trigger>
137
+ {#snippet child({ props })}
138
+ <button
139
+ {...props}
140
+ type="button"
141
+ class="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center rounded-r-lg border-l border-white/20 px-1.5 py-1.5 transition-colors"
142
+ aria-label={lang[interfaceLanguage.current].saveDraft}
143
+ >
144
+ <ChevronDownIcon class="size-3.5" />
145
+ </button>
146
+ {/snippet}
147
+ </DropdownMenu.Trigger>
148
+ <DropdownMenu.Content align="end" class="w-48">
149
+ <DropdownMenu.Item onclick={onSaveDraft}>
150
+ {lang[interfaceLanguage.current].saveDraft}
151
+ <DropdownMenu.Shortcut>{shortcutLabel}</DropdownMenu.Shortcut>
152
+ </DropdownMenu.Item>
153
+ </DropdownMenu.Content>
154
+ </DropdownMenu.Root>
155
+ </div>
156
+
157
+ <div class="bg-border mx-1 h-5 w-px shrink-0"></div>
158
+
159
+ <!-- Panel triggers + Hybrid toggle -->
109
160
  {#await import('./header/publish-panel.svelte') then { default: PublishPanel }}
110
- <PublishPanel {entry} {version} {onSave} {onArchive} />
161
+ <PublishPanel
162
+ {entry}
163
+ {version}
164
+ {fields}
165
+ {getFormData}
166
+ {onSave}
167
+ {onArchive}
168
+ {onScrollToIssue}
169
+ />
111
170
  {/await}
112
171
 
113
172
  {#await import('./header/version-history-sheet.svelte') then { default: VersionHistorySheet }}
@@ -116,14 +175,14 @@
116
175
 
117
176
  {#if hybridContext?.enabled}
118
177
  <Button
119
- variant={hybridContext.mode === 'hybrid' ? 'secondary' : 'outline'}
178
+ variant="ghost"
120
179
  size="icon"
180
+ class={hybridContext.mode === 'hybrid' ? 'text-primary bg-[var(--lavender-lighter)]' : ''}
121
181
  onclick={() => hybridContext.toggle()}
122
182
  title="Hybrid Editor"
123
183
  >
124
184
  <LayoutSidebar class="size-4" />
125
185
  </Button>
126
186
  {/if}
127
-
128
187
  </div>
129
188
  </div>
@@ -1,12 +1,17 @@
1
1
  import type { DbEntryVersion, RawEntry } from '../../../types/entries.js';
2
2
  import type { UpdateEntryVersionCommandType } from '../../../core/server/entries/operations/update.js';
3
+ import type { Field } from '../../../types/fields.js';
3
4
  type SaveStatus = 'idle' | 'saving' | 'saved' | 'unsaved' | 'error';
4
5
  type Props = {
5
6
  entry: RawEntry;
6
7
  version: DbEntryVersion;
8
+ fields?: Field[];
9
+ getFormData?: () => Record<string, unknown>;
7
10
  onSave: (type: UpdateEntryVersionCommandType, scheduledAt?: Date) => void;
11
+ onSaveDraft: () => void;
8
12
  onArchive: () => void;
9
13
  saveStatus?: SaveStatus;
14
+ onScrollToIssue?: (fieldSlug: string, nodePos: number) => void;
10
15
  };
11
16
  declare const EntryHeader: import("svelte").Component<Props, {}, "">;
12
17
  type EntryHeader = ReturnType<typeof EntryHeader>;
@@ -18,6 +18,7 @@
18
18
  import type { UpdateEntryVersionCommandType } from '../../../core/server/entries/operations/update.js';
19
19
  import { getRawCollectionEntryLabel } from '../../utils/entryLabel.js';
20
20
  import type { Field } from '../../../types/fields.js';
21
+ import { getFieldsFromConfig, hasLayout } from '../../../core/fields/layoutUtils.js';
21
22
  import type { ValidationErrors } from 'sveltekit-superforms';
22
23
  import { createHybridContext } from './hybrid/hybrid-context.svelte.js';
23
24
  import { onMount } from 'svelte';
@@ -56,7 +57,11 @@
56
57
  } else if (typeof value === 'object' && value !== null) {
57
58
  // Nested errors - recurse
58
59
  const nestedFields = field && 'fields' in field ? (field.fields as Field[]) : [];
59
- const nested = flattenErrors(value as ValidationErrors<Record<string, unknown>>, nestedFields, path);
60
+ const nested = flattenErrors(
61
+ value as ValidationErrors<Record<string, unknown>>,
62
+ nestedFields,
63
+ path
64
+ );
60
65
  if (nested.length > 0) {
61
66
  result.push(...nested.map((e) => `${label} > ${e}`));
62
67
  }
@@ -71,30 +76,42 @@
71
76
  {
72
77
  entryArchived: string;
73
78
  saveToast: string;
74
- saveDraftToast: string;
75
79
  publishToast: string;
76
80
  scheduledToast: string;
81
+ unpublishToast: string;
77
82
  saveFailed: string;
78
83
  cannotPublish: string;
84
+ newerDraft: string;
85
+ switchToDraft: string;
86
+ editingDraft: string;
87
+ switchToPublished: string;
79
88
  }
80
89
  > = {
81
90
  en: {
82
91
  entryArchived: 'Entry archived successfully',
83
92
  saveToast: 'Entry saved successfully',
84
- saveDraftToast: 'Draft saved successfully',
85
93
  publishToast: 'Entry published successfully',
86
94
  scheduledToast: 'Entry scheduled successfully',
95
+ unpublishToast: 'Publication withdrawn',
87
96
  saveFailed: 'Save failed',
88
- cannotPublish: 'Cannot publish'
97
+ cannotPublish: 'Cannot publish',
98
+ newerDraft: 'A newer unpublished draft exists',
99
+ switchToDraft: 'Switch to draft',
100
+ editingDraft: 'You are editing an unpublished draft',
101
+ switchToPublished: 'View published'
89
102
  },
90
103
  pl: {
91
104
  entryArchived: 'Wpis został zarchiwizowany pomyślnie',
92
105
  saveToast: 'Wpis został pomyślnie zapisany',
93
- saveDraftToast: 'Wersja robocza została pomyślnie zapisana',
94
106
  publishToast: 'Wpis został pomyślnie opublikowany',
95
107
  scheduledToast: 'Wpis został zaplanowany pomyślnie',
108
+ unpublishToast: 'Wycofano publikację',
96
109
  saveFailed: 'Błąd zapisu',
97
- cannotPublish: 'Nie można opublikować'
110
+ cannotPublish: 'Nie można opublikować',
111
+ newerDraft: 'Istnieje nowszy nieopublikowany szkic',
112
+ switchToDraft: 'Przejdź do szkicu',
113
+ editingDraft: 'Edytujesz nieopublikowany szkic',
114
+ switchToPublished: 'Zobacz opublikowaną'
98
115
  }
99
116
  };
100
117
 
@@ -107,7 +124,10 @@
107
124
  let { collection } = entry;
108
125
 
109
126
  // Create form once at component level
110
- const collectionSchema = generateZodSchemaFromFields(collection.fields, contentLanguage.all);
127
+ const collectionSchema = generateZodSchemaFromFields(
128
+ getFieldsFromConfig(collection),
129
+ contentLanguage.all
130
+ );
111
131
  const form = superForm(defaults(editingEntry.data, zod4(collectionSchema)), {
112
132
  validators: zod4Client(collectionSchema),
113
133
  SPA: true,
@@ -120,6 +140,8 @@
120
140
  let saveStatus = $state<SaveStatus>('idle');
121
141
  let lastSavedData = $state<string>(JSON.stringify(editingEntry.data));
122
142
  let autosaveTimer: ReturnType<typeof setTimeout> | null = null;
143
+ // Tracks draft version created via autosave while editing a published version
144
+ let savedDraftVersionId = $state<string | null>(null);
123
145
 
124
146
  const AUTOSAVE_DELAY = 30000; // 30 seconds
125
147
 
@@ -136,19 +158,22 @@
136
158
  const currentFormData = get(form.form);
137
159
  const currentData = JSON.stringify(currentFormData);
138
160
  if (currentData === lastSavedData) {
139
- saveStatus = 'saved';
140
161
  return;
141
162
  }
142
163
 
143
164
  saveStatus = 'saving';
144
165
  try {
145
- await remotes.updateEntryVersionCommand({
166
+ const result = await remotes.updateEntryVersionCommand({
146
167
  entryId: entry.id,
147
168
  data: currentFormData,
148
169
  type: 'draft'
149
170
  });
150
171
  lastSavedData = currentData;
151
172
  saveStatus = 'saved';
173
+ // If we're editing the published version and saved a draft, track it for the banner
174
+ if (entry.publishedVersion && editingEntry.id === entry.publishedVersion.id && result?.id) {
175
+ savedDraftVersionId = result.id;
176
+ }
152
177
  // Reset to idle after 3s
153
178
  setTimeout(() => {
154
179
  if (saveStatus === 'saved') saveStatus = 'idle';
@@ -158,7 +183,7 @@
158
183
  }
159
184
  }
160
185
 
161
- // Track form changes for autosave
186
+ // Track form changes for autosave + Ctrl/Cmd+S shortcut
162
187
  onMount(() => {
163
188
  const unsub = form.form.subscribe((data) => {
164
189
  const currentData = JSON.stringify(data);
@@ -167,8 +192,22 @@
167
192
  scheduleAutosave();
168
193
  }
169
194
  });
195
+
196
+ function handleKeydown(e: KeyboardEvent) {
197
+ if ((e.ctrlKey || e.metaKey) && e.key === 's') {
198
+ e.preventDefault();
199
+ if (autosaveTimer) {
200
+ clearTimeout(autosaveTimer);
201
+ autosaveTimer = null;
202
+ }
203
+ performAutosave();
204
+ }
205
+ }
206
+ window.addEventListener('keydown', handleKeydown);
207
+
170
208
  return () => {
171
209
  unsub();
210
+ window.removeEventListener('keydown', handleKeydown);
172
211
  if (autosaveTimer) clearTimeout(autosaveTimer);
173
212
  };
174
213
  });
@@ -186,24 +225,23 @@
186
225
  autosaveTimer = null;
187
226
  }
188
227
 
189
- if (type === 'draft') {
190
- // Drafts save without validation
191
- const currentFormData = get(form.form);
228
+ // Unpublish skip validation, call remote directly
229
+ if (type === 'cancel-published') {
192
230
  saveStatus = 'saving';
193
231
  try {
194
232
  await remotes.updateEntryVersionCommand({
195
233
  entryId: entry.id,
196
- data: currentFormData,
197
- type: 'draft'
234
+ data: get(form.form),
235
+ type
198
236
  });
199
- lastSavedData = JSON.stringify(currentFormData);
200
237
  saveStatus = 'saved';
201
- toast.success(lang[interfaceLanguage.current].saveDraftToast);
238
+ toast.success(lang[interfaceLanguage.current].unpublishToast);
239
+
202
240
  setTimeout(() => {
203
241
  if (saveStatus === 'saved') saveStatus = 'idle';
204
242
  }, 3000);
205
243
  } catch (e) {
206
- console.error('Save error:', e);
244
+ console.error('Unpublish error:', e);
207
245
  saveStatus = 'error';
208
246
  toast.error(lang[interfaceLanguage.current].saveFailed, {
209
247
  description: e instanceof Error ? e.message : undefined
@@ -245,14 +283,11 @@
245
283
  });
246
284
  }
247
285
  } else {
248
- const errors = flattenErrors(validatedForm.errors, collection.fields);
249
- toast.error(
250
- lang[interfaceLanguage.current].cannotPublish,
251
- {
252
- description: errors.slice(0, 3).join('\n') + (errors.length > 3 ? '\n...' : ''),
253
- duration: 5000
254
- }
255
- );
286
+ const errors = flattenErrors(validatedForm.errors, getFieldsFromConfig(collection));
287
+ toast.error(lang[interfaceLanguage.current].cannotPublish, {
288
+ description: errors.slice(0, 3).join('\n') + (errors.length > 3 ? '\n...' : ''),
289
+ duration: 5000
290
+ });
256
291
  }
257
292
  }
258
293
 
@@ -292,7 +327,7 @@
292
327
 
293
328
  type SizePreset = keyof typeof sizePresets;
294
329
 
295
- let sizePreset: SizePreset = $state('responsive');
330
+ let sizePreset: SizePreset = $state('desktop');
296
331
 
297
332
  function getOriginFromUrl(url: string): string {
298
333
  try {
@@ -302,7 +337,9 @@
302
337
  }
303
338
  }
304
339
 
305
- const previewOrigin = collection.previewUrl ? getOriginFromUrl(collection.previewUrl) : window.location.origin;
340
+ const previewOrigin = collection.previewUrl
341
+ ? getOriginFromUrl(collection.previewUrl)
342
+ : window.location.origin;
306
343
 
307
344
  const updatePreview = useDebounce(
308
345
  async (window: Window, form: SuperForm<Record<string, unknown>>) => {
@@ -420,44 +457,118 @@
420
457
  }
421
458
  }
422
459
  });
460
+
461
+ // Banner: viewing published version + a separate draft exists
462
+ const showDraftBanner = $derived(
463
+ entry.publishedVersion != null &&
464
+ editingEntry.id === entry.publishedVersion.id &&
465
+ entry.draftVersion != null &&
466
+ entry.draftVersion.id !== entry.publishedVersion.id &&
467
+ entry.draftVersion.createdAt > entry.publishedVersion.createdAt
468
+ );
469
+
470
+ // Banner: viewing draft + a published version exists
471
+ const showPublishedBanner = $derived(
472
+ entry.publishedVersion != null && editingEntry.id !== entry.publishedVersion.id
473
+ );
474
+
475
+ const draftVersionId = $derived(entry.draftVersion?.id ?? savedDraftVersionId);
476
+
477
+ const scrollToIssue = (fieldSlug: string) => {
478
+ const fieldEl = document.querySelector(`[data-field-path="${fieldSlug}"]`);
479
+ if (fieldEl) {
480
+ fieldEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
481
+ const proseMirror = fieldEl.querySelector('.ProseMirror') as HTMLElement | null;
482
+ if (proseMirror) {
483
+ proseMirror.focus();
484
+ }
485
+ }
486
+ };
423
487
  </script>
424
488
 
425
- <EntryHeader {entry} version={editingEntry} {onSave} {onArchive} {saveStatus} />
489
+ <EntryHeader
490
+ {entry}
491
+ version={editingEntry}
492
+ {onSave}
493
+ onSaveDraft={performAutosave}
494
+ {onArchive}
495
+ {saveStatus}
496
+ fields={getFieldsFromConfig(collection)}
497
+ getFormData={() => get(form.form)}
498
+ onScrollToIssue={scrollToIssue}
499
+ />
500
+
501
+ {#if showDraftBanner}
502
+ <div
503
+ class="flex items-center justify-between border-b bg-[var(--lavender-lighter)] px-6 py-2 text-sm"
504
+ >
505
+ <span class="text-[var(--text-secondary)]">{lang[interfaceLanguage.current].newerDraft}</span>
506
+ <button
507
+ type="button"
508
+ class="font-semibold text-[var(--primary)] hover:underline"
509
+ onclick={() => goto(`?version=${draftVersionId}`)}
510
+ >
511
+ {lang[interfaceLanguage.current].switchToDraft}
512
+ </button>
513
+ </div>
514
+ {:else if showPublishedBanner}
515
+ <div
516
+ class="flex items-center justify-between border-b bg-[var(--lavender-lighter)] px-6 py-2 text-sm"
517
+ >
518
+ <span class="text-[var(--text-secondary)]">{lang[interfaceLanguage.current].editingDraft}</span>
519
+ <button
520
+ type="button"
521
+ class="font-semibold text-[var(--primary)] hover:underline"
522
+ onclick={() => goto(`?version=${entry.publishedVersion!.id}`)}
523
+ >
524
+ {lang[interfaceLanguage.current].switchToPublished}
525
+ </button>
526
+ </div>
527
+ {/if}
426
528
 
427
529
  {#if hybridContext.mode === 'hybrid' && collection.previewUrl}
428
- <div class="flex min-h-0 flex-1 overflow-hidden">
429
- {#await import('./hybrid/hybrid-layout.svelte')}
430
- <div class="h-full animate-pulse rounded-md bg-accent"></div>
431
- {:then { default: HybridLayout }}
432
- <HybridLayout>
433
- {#snippet preview()}
434
- {#await import('./hybrid/hybrid-preview.svelte')}
435
- <div class="h-full animate-pulse rounded-md bg-accent"></div>
436
- {:then { default: HybridPreview }}
437
- <HybridPreview {collection} {editingEntry} bind:previewIframe bind:sizePreset {size} bind:el />
438
- {:catch}
439
- <p class="p-4 text-sm text-destructive">Failed to load preview</p>
440
- {/await}
441
- {/snippet}
442
- {#snippet formPanel()}
443
- <EntryForm
444
- {form}
445
- {entry}
446
- focusedPath={hybridContext.focusedPath}
447
- onPathSelect={(path) => hybridContext.focusedPath = path}
448
- />
449
- {/snippet}
450
- </HybridLayout>
451
- {:catch}
452
- <p class="p-4 text-sm text-destructive">Failed to load layout</p>
453
- {/await}
454
- </div>
530
+ <div class="flex min-h-0 flex-1 overflow-hidden">
531
+ {#await import('./hybrid/hybrid-layout.svelte')}
532
+ <div class="bg-accent h-full animate-pulse rounded-md"></div>
533
+ {:then { default: HybridLayout }}
534
+ <HybridLayout>
535
+ {#snippet preview()}
536
+ {#await import('./hybrid/hybrid-preview.svelte')}
537
+ <div class="bg-accent h-full animate-pulse rounded-md"></div>
538
+ {:then { default: HybridPreview }}
539
+ <HybridPreview
540
+ {collection}
541
+ {editingEntry}
542
+ bind:previewIframe
543
+ bind:sizePreset
544
+ {size}
545
+ bind:el
546
+ />
547
+ {:catch}
548
+ <p class="text-destructive p-4 text-sm">Failed to load preview</p>
549
+ {/await}
550
+ {/snippet}
551
+ {#snippet formPanel()}
552
+ <EntryForm
553
+ {form}
554
+ {entry}
555
+ focusedPath={hybridContext.focusedPath}
556
+ onPathSelect={(path) => (hybridContext.focusedPath = path)}
557
+ />
558
+ {/snippet}
559
+ </HybridLayout>
560
+ {:catch}
561
+ <p class="text-destructive p-4 text-sm">Failed to load layout</p>
562
+ {/await}
563
+ </div>
564
+ {:else if hasLayout(collection)}
565
+ <div class="overflow-y-auto" style="scroll-padding-top: 48px;">
566
+ <EntryForm {form} {entry} />
567
+ </div>
455
568
  {:else}
456
- <div class="flex items-stretch justify-center">
569
+ <div class="flex items-stretch justify-center" style="scroll-padding-top: 48px;">
457
570
  <div class="max-w-2xl grow p-4 lg:p-6">
458
- <div
459
- class="glass-panel rounded-2xl border-slate-200/50 bg-white/80 p-4 backdrop-blur-xl dark:border-white/10 dark:bg-slate-900/60 lg:p-6"
460
- >
571
+ <div class="bg-card rounded-2xl border p-4 shadow-sm lg:p-6">
461
572
  <EntryForm {form} {entry} />
462
573
  </div>
463
574
  </div>
@@ -0,0 +1,46 @@
1
+ import type { Field } from '../../../../types/fields.js';
2
+ export type A11yIssueType = 'warning' | 'success';
3
+ export interface A11yIssue {
4
+ type: A11yIssueType;
5
+ message: string;
6
+ fieldSlug?: string;
7
+ firstNodePos?: number;
8
+ }
9
+ /** Get heading issue positions for inline decoration in the editor.
10
+ * Works with TipTap JSON doc format (with `type`, `attrs.level`, `content`).
11
+ * Returns issues for: first heading not H2, H3 appearing before any H2. */
12
+ export interface HeadingIssue {
13
+ pos: number;
14
+ message: string;
15
+ }
16
+ export declare function getHeadingIssuePositions(doc: {
17
+ type: string;
18
+ content?: any[];
19
+ }): HeadingIssue[];
20
+ /** Find position of first image without alt in TipTap JSON doc. */
21
+ export declare function findFirstImageWithoutAlt(doc: {
22
+ type: string;
23
+ content?: any[];
24
+ }): number | null;
25
+ /** Find position of first heading hierarchy issue in TipTap JSON doc. */
26
+ export declare function findFirstHeadingIssue(doc: {
27
+ type: string;
28
+ content?: any[];
29
+ }): number | null;
30
+ /** Find position of first generic link in TipTap JSON doc. */
31
+ export declare function findFirstGenericLink(doc: {
32
+ type: string;
33
+ content?: any[];
34
+ }): number | null;
35
+ export interface A11yLang {
36
+ imagesWithoutAlt: (count: number) => string;
37
+ headingsOk: string;
38
+ headingsSkipped: string;
39
+ genericLinks: (count: number) => string;
40
+ allImagesHaveAlt: string;
41
+ noGenericLinks: string;
42
+ }
43
+ export declare const a11yLangPl: A11yLang;
44
+ export declare const a11yLangEn: A11yLang;
45
+ /** Run a11y validation on entry data and return issues list. */
46
+ export declare function validateA11y(data: Record<string, unknown>, fields: Field[], lang?: A11yLang): A11yIssue[];