includio-cms 0.1.4 → 0.5.1

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 (310) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/ROADMAP.md +26 -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 +35 -67
  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 +100 -0
  22. package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +10 -0
  23. package/dist/admin/client/collection/collection-entries.svelte +277 -258
  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 +69 -0
  38. package/dist/admin/client/collection/row-actions.svelte.d.ts +10 -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 +143 -37
  46. package/dist/admin/client/entry/entry-header.svelte.d.ts +8 -0
  47. package/dist/admin/client/entry/entry.svelte +272 -61
  48. package/dist/admin/client/entry/header/a11y-validator.d.ts +47 -0
  49. package/dist/admin/client/entry/header/a11y-validator.js +352 -0
  50. package/dist/admin/client/entry/header/publish-panel.svelte +531 -129
  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 +322 -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/changelog-dialog.svelte +157 -0
  87. package/dist/admin/components/dashboard/changelog-dialog.svelte.d.ts +6 -0
  88. package/dist/admin/components/dashboard/form-submissions-widget.svelte +71 -113
  89. package/dist/admin/components/dashboard/index.d.ts +4 -2
  90. package/dist/admin/components/dashboard/index.js +4 -2
  91. package/dist/admin/components/dashboard/orphaned-entries-notice.svelte +240 -0
  92. package/dist/admin/components/dashboard/orphaned-entries-notice.svelte.d.ts +13 -0
  93. package/dist/admin/components/dashboard/recent-activity.svelte +53 -75
  94. package/dist/admin/components/dashboard/recent-entries.svelte +94 -0
  95. package/dist/admin/components/dashboard/recent-entries.svelte.d.ts +18 -0
  96. package/dist/admin/components/dashboard/stat-card.svelte +2 -2
  97. package/dist/admin/components/dashboard/tip-of-the-day.svelte +109 -0
  98. package/dist/admin/components/dashboard/tip-of-the-day.svelte.d.ts +3 -0
  99. package/dist/admin/components/dashboard/welcome-header.svelte +45 -0
  100. package/dist/admin/components/dashboard/welcome-header.svelte.d.ts +3 -0
  101. package/dist/admin/components/fields/{array-field.svelte → blocks-field.svelte} +4 -4
  102. package/dist/admin/components/fields/{array-field.svelte.d.ts → blocks-field.svelte.d.ts} +5 -5
  103. package/dist/admin/components/fields/content-field.svelte +27 -0
  104. package/dist/admin/components/fields/content-field.svelte.d.ts +31 -0
  105. package/dist/admin/components/fields/field-renderer.svelte +9 -7
  106. package/dist/admin/components/fields/image-field.svelte +2 -2
  107. package/dist/admin/components/fields/media-field.svelte +2 -2
  108. package/dist/admin/components/fields/seo-field.svelte +205 -25
  109. package/dist/admin/components/fields/simple-array-field.svelte +289 -0
  110. package/dist/admin/components/fields/simple-array-field.svelte.d.ts +30 -0
  111. package/dist/admin/components/fields/slug-field.svelte +3 -2
  112. package/dist/admin/components/fields/standalone-field-renderer.svelte +148 -0
  113. package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +9 -0
  114. package/dist/admin/components/fields/text-field-wrapper.svelte +147 -3
  115. package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
  116. package/dist/admin/components/fields/url-field.svelte +5 -4
  117. package/dist/admin/components/layout/app-sidebar.svelte +27 -24
  118. package/dist/admin/components/layout/lang.d.ts +6 -0
  119. package/dist/admin/components/layout/lang.js +13 -1
  120. package/dist/admin/components/layout/layout-renderer.svelte +352 -0
  121. package/dist/admin/components/layout/layout-renderer.svelte.d.ts +14 -0
  122. package/dist/admin/components/layout/nav-breadcrumbs.svelte +4 -4
  123. package/dist/admin/components/layout/nav-collections.svelte +65 -36
  124. package/dist/admin/components/layout/nav-footer.svelte +38 -0
  125. package/dist/admin/components/layout/nav-footer.svelte.d.ts +3 -0
  126. package/dist/admin/components/layout/nav-forms.svelte +55 -30
  127. package/dist/admin/components/layout/nav-main.svelte +14 -52
  128. package/dist/admin/components/layout/nav-search.svelte +4 -3
  129. package/dist/admin/components/layout/nav-singletons.svelte +59 -17
  130. package/dist/admin/components/layout/nav-singletons.svelte.d.ts +17 -8
  131. package/dist/admin/components/layout/site-header.svelte +74 -13
  132. package/dist/admin/components/media/alt-input.svelte +32 -22
  133. package/dist/admin/components/media/bulk-action-bar.svelte +139 -150
  134. package/dist/admin/components/media/file/file-details.svelte +299 -217
  135. package/dist/admin/components/media/file/file-miniature.svelte +54 -41
  136. package/dist/admin/components/media/file/file-miniature.svelte.d.ts +1 -0
  137. package/dist/admin/components/media/file/file-preview.svelte +1 -1
  138. package/dist/admin/components/media/file-upload.svelte +24 -26
  139. package/dist/admin/components/media/files-list.svelte +112 -40
  140. package/dist/admin/components/media/files-list.svelte.d.ts +2 -0
  141. package/dist/admin/components/media/focal-point-input.svelte +122 -26
  142. package/dist/admin/components/media/media-library.svelte +127 -70
  143. package/dist/admin/components/media/media-search.svelte +6 -6
  144. package/dist/admin/components/media/media-sort.svelte +3 -1
  145. package/dist/admin/components/media/multi-file-summary.svelte +88 -68
  146. package/dist/admin/components/media/tag-combobox.svelte +141 -66
  147. package/dist/admin/components/media/tag-combobox.svelte.d.ts +1 -0
  148. package/dist/admin/components/media/tag-sidebar.svelte +139 -121
  149. package/dist/admin/components/tiptap/FigureNodeView.svelte +144 -15
  150. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +254 -0
  151. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte.d.ts +4 -0
  152. package/dist/admin/components/tiptap/SlashCommandPopup.svelte +212 -0
  153. package/dist/admin/components/tiptap/SlashCommandPopup.svelte.d.ts +8 -0
  154. package/dist/admin/components/tiptap/content-editor.svelte +280 -0
  155. package/dist/admin/components/tiptap/content-editor.svelte.d.ts +9 -0
  156. package/dist/admin/components/tiptap/editor-toolbar.svelte +230 -0
  157. package/dist/admin/components/tiptap/editor-toolbar.svelte.d.ts +16 -0
  158. package/dist/admin/components/tiptap/heading-a11y-plugin.d.ts +2 -0
  159. package/dist/admin/components/tiptap/heading-a11y-plugin.js +67 -0
  160. package/dist/admin/components/tiptap/image-dialog.svelte +172 -11
  161. package/dist/admin/components/tiptap/inline-block-node.d.ts +19 -0
  162. package/dist/admin/components/tiptap/inline-block-node.js +98 -0
  163. package/dist/admin/components/tiptap/link-dialog.svelte +9 -4
  164. package/dist/admin/components/tiptap/slash-command.d.ts +17 -0
  165. package/dist/admin/components/tiptap/slash-command.js +181 -0
  166. package/dist/admin/components/tiptap/structured-content-utils.d.ts +21 -0
  167. package/dist/admin/components/tiptap/structured-content-utils.js +150 -0
  168. package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -190
  169. package/dist/admin/email/invite-template.d.ts +8 -0
  170. package/dist/admin/email/invite-template.js +99 -0
  171. package/dist/admin/email/reset-password-template.d.ts +7 -0
  172. package/dist/admin/email/reset-password-template.js +96 -0
  173. package/dist/admin/remote/ai.remote.d.ts +1 -0
  174. package/dist/admin/remote/ai.remote.js +4 -1
  175. package/dist/admin/remote/entry.remote.d.ts +9 -0
  176. package/dist/admin/remote/entry.remote.js +58 -8
  177. package/dist/admin/remote/preview.remote.js +2 -1
  178. package/dist/admin/shared/password-schema.d.ts +5 -0
  179. package/dist/admin/shared/password-schema.js +10 -0
  180. package/dist/admin/state/content-language.svelte.d.ts +3 -0
  181. package/dist/admin/state/content-language.svelte.js +8 -0
  182. package/dist/admin/styles/admin.css +1530 -151
  183. package/dist/admin/utils/formatDate.d.ts +1 -0
  184. package/dist/admin/utils/formatDate.js +8 -0
  185. package/dist/admin/utils/roleLabel.d.ts +2 -0
  186. package/dist/admin/utils/roleLabel.js +13 -0
  187. package/dist/admin/utils/translationStatus.d.ts +17 -0
  188. package/dist/admin/utils/translationStatus.js +134 -0
  189. package/dist/ai-claude/index.d.ts +2 -0
  190. package/dist/ai-claude/index.js +56 -0
  191. package/dist/cms/runtime/api.d.ts +6 -1
  192. package/dist/cms/runtime/api.js +3 -0
  193. package/dist/cms/runtime/schemas.d.ts +9 -1
  194. package/dist/cms/runtime/schemas.js +8 -0
  195. package/dist/cms/runtime/types.d.ts +82 -10
  196. package/dist/cms/runtime/types.js +4 -0
  197. package/dist/components/ui/accordion/accordion.stories.svelte +39 -0
  198. package/dist/components/ui/accordion/accordion.stories.svelte.d.ts +27 -0
  199. package/dist/components/ui/alert/alert.stories.svelte +53 -0
  200. package/dist/components/ui/alert/alert.stories.svelte.d.ts +27 -0
  201. package/dist/components/ui/alert/alert.svelte +5 -0
  202. package/dist/components/ui/alert/alert.svelte.d.ts +9 -0
  203. package/dist/components/ui/avatar/avatar.stories.svelte +16 -0
  204. package/dist/components/ui/avatar/avatar.stories.svelte.d.ts +27 -0
  205. package/dist/components/ui/badge/badge.stories.svelte +33 -0
  206. package/dist/components/ui/badge/badge.stories.svelte.d.ts +27 -0
  207. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +33 -0
  208. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte.d.ts +27 -0
  209. package/dist/components/ui/button/button.stories.svelte +43 -0
  210. package/dist/components/ui/button/button.stories.svelte.d.ts +27 -0
  211. package/dist/components/ui/button/button.svelte +1 -2
  212. package/dist/components/ui/button/button.svelte.d.ts +0 -3
  213. package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
  214. package/dist/components/ui/card/card.stories.svelte +42 -0
  215. package/dist/components/ui/card/card.stories.svelte.d.ts +27 -0
  216. package/dist/components/ui/command/command.stories.svelte +51 -0
  217. package/dist/components/ui/command/command.stories.svelte.d.ts +27 -0
  218. package/dist/components/ui/dialog/dialog.stories.svelte +29 -0
  219. package/dist/components/ui/dialog/dialog.stories.svelte.d.ts +27 -0
  220. package/dist/components/ui/field/field-label.svelte.d.ts +1 -1
  221. package/dist/components/ui/field/field.stories.svelte +21 -0
  222. package/dist/components/ui/field/field.stories.svelte.d.ts +27 -0
  223. package/dist/components/ui/input/input.stories.svelte +40 -0
  224. package/dist/components/ui/input/input.stories.svelte.d.ts +27 -0
  225. package/dist/components/ui/input/input.svelte +2 -4
  226. package/dist/components/ui/item/item-separator.svelte.d.ts +1 -1
  227. package/dist/components/ui/label/label.stories.svelte +20 -0
  228. package/dist/components/ui/label/label.stories.svelte.d.ts +27 -0
  229. package/dist/components/ui/popover/popover.stories.svelte +29 -0
  230. package/dist/components/ui/popover/popover.stories.svelte.d.ts +27 -0
  231. package/dist/components/ui/select/select-group-heading.svelte.d.ts +1 -1
  232. package/dist/components/ui/select/select.stories.svelte +23 -0
  233. package/dist/components/ui/select/select.stories.svelte.d.ts +27 -0
  234. package/dist/components/ui/separator/separator.stories.svelte +24 -0
  235. package/dist/components/ui/separator/separator.stories.svelte.d.ts +27 -0
  236. package/dist/components/ui/sheet/sheet.stories.svelte +29 -0
  237. package/dist/components/ui/sheet/sheet.stories.svelte.d.ts +27 -0
  238. package/dist/components/ui/sidebar/sidebar-group.svelte +3 -3
  239. package/dist/components/ui/sidebar/sidebar-group.svelte.d.ts +2 -2
  240. package/dist/components/ui/sidebar/sidebar-menu-button.svelte +28 -30
  241. package/dist/components/ui/sidebar/sidebar-menu-button.svelte.d.ts +7 -7
  242. package/dist/components/ui/sidebar/sidebar-separator.svelte.d.ts +1 -1
  243. package/dist/components/ui/sidebar/sidebar-trigger.svelte +4 -4
  244. package/dist/components/ui/sonner/sonner.stories.svelte +22 -0
  245. package/dist/components/ui/sonner/sonner.stories.svelte.d.ts +26 -0
  246. package/dist/components/ui/sonner/sonner.svelte +8 -2
  247. package/dist/components/ui/sonner/toast-demo.svelte +29 -0
  248. package/dist/components/ui/sonner/toast-demo.svelte.d.ts +6 -0
  249. package/dist/components/ui/textarea/textarea.stories.svelte +22 -0
  250. package/dist/components/ui/textarea/textarea.stories.svelte.d.ts +27 -0
  251. package/dist/components/ui/textarea/textarea.svelte +0 -2
  252. package/dist/components/ui/toggle/toggle.stories.svelte +22 -0
  253. package/dist/components/ui/toggle/toggle.stories.svelte.d.ts +27 -0
  254. package/dist/components/ui/toggle-group/toggle-group.stories.svelte +17 -0
  255. package/dist/components/ui/toggle-group/toggle-group.stories.svelte.d.ts +27 -0
  256. package/dist/components/ui/tooltip/tooltip.stories.svelte +26 -0
  257. package/dist/components/ui/tooltip/tooltip.stories.svelte.d.ts +27 -0
  258. package/dist/core/fields/fieldSchemaToTs.d.ts +1 -0
  259. package/dist/core/fields/fieldSchemaToTs.js +133 -1
  260. package/dist/core/fields/layoutUtils.d.ts +17 -0
  261. package/dist/core/fields/layoutUtils.js +149 -0
  262. package/dist/core/fields/structuredToHtml.d.ts +9 -0
  263. package/dist/core/fields/structuredToHtml.js +161 -0
  264. package/dist/core/server/entries/operations/create.js +2 -1
  265. package/dist/core/server/entries/operations/get.js +10 -7
  266. package/dist/core/server/entries/operations/update.d.ts +3 -0
  267. package/dist/core/server/entries/operations/update.js +30 -2
  268. package/dist/core/server/fields/queryStructuredContent.d.ts +15 -0
  269. package/dist/core/server/fields/queryStructuredContent.js +65 -0
  270. package/dist/core/server/fields/resolveImageFields.js +51 -2
  271. package/dist/core/server/fields/resolveRelationFields.js +2 -2
  272. package/dist/core/server/fields/resolveRichtextLinks.js +80 -13
  273. package/dist/core/server/fields/resolveUrlFields.js +57 -6
  274. package/dist/core/server/fields/slugResolver.d.ts +10 -0
  275. package/dist/core/server/fields/slugResolver.js +34 -0
  276. package/dist/core/server/generator/fields.js +15 -4
  277. package/dist/core/server/generator/generator.js +3 -2
  278. package/dist/db-postgres/index.js +10 -6
  279. package/dist/files-local/index.js +126 -64
  280. package/dist/server/auth.d.ts +5 -0
  281. package/dist/server/auth.js +12 -1
  282. package/dist/sveltekit/components/structured-content.svelte +204 -0
  283. package/dist/sveltekit/components/structured-content.svelte.d.ts +21 -0
  284. package/dist/sveltekit/config.d.ts +13 -3
  285. package/dist/sveltekit/index.d.ts +3 -0
  286. package/dist/sveltekit/index.js +3 -0
  287. package/dist/sveltekit/server/handle.js +1 -0
  288. package/dist/types/config.d.ts +3 -0
  289. package/dist/types/entries.d.ts +3 -0
  290. package/dist/types/fields.d.ts +19 -2
  291. package/dist/types/index.d.ts +2 -0
  292. package/dist/types/index.js +2 -0
  293. package/dist/types/layout.d.ts +54 -0
  294. package/dist/types/layout.js +6 -0
  295. package/dist/types/structured-content.d.ts +63 -0
  296. package/dist/types/structured-content.js +1 -0
  297. package/dist/updates/0.1.5/index.d.ts +2 -0
  298. package/dist/updates/0.1.5/index.js +18 -0
  299. package/dist/updates/0.2.0/index.d.ts +2 -0
  300. package/dist/updates/0.2.0/index.js +11 -0
  301. package/dist/updates/0.2.2/index.d.ts +2 -0
  302. package/dist/updates/0.2.2/index.js +13 -0
  303. package/dist/updates/0.5.0/index.d.ts +2 -0
  304. package/dist/updates/0.5.0/index.js +14 -0
  305. package/dist/updates/0.5.1/index.d.ts +2 -0
  306. package/dist/updates/0.5.1/index.js +17 -0
  307. package/dist/updates/index.js +6 -1
  308. package/package.json +16 -9
  309. package/dist/admin/components/dashboard/updates-banner.svelte +0 -170
  310. package/dist/admin/components/dashboard/updates-banner.svelte.d.ts +0 -3
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import { authClient } from '../../auth-client.js';
3
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
4
+ import type { InterfaceLanguage } from '../../../types/languages.js';
5
+
6
+ let session = authClient.useSession();
7
+ const interfaceLanguage = useInterfaceLanguage();
8
+
9
+ const firstName = $derived($session.data?.user?.name?.split(' ')[0] ?? '');
10
+
11
+ const lang: Record<InterfaceLanguage, { greeting: (name: string) => string }> = {
12
+ pl: {
13
+ greeting: (name) => {
14
+ const h = new Date().getHours();
15
+ if (h < 12) return `Dzień dobry, ${name}`;
16
+ if (h < 18) return `Cześć, ${name}`;
17
+ return `Dobry wieczór, ${name}`;
18
+ }
19
+ },
20
+ en: {
21
+ greeting: (name) => {
22
+ const h = new Date().getHours();
23
+ if (h < 12) return `Good morning, ${name}`;
24
+ if (h < 18) return `Good afternoon, ${name}`;
25
+ return `Good evening, ${name}`;
26
+ }
27
+ }
28
+ };
29
+
30
+ const formattedDate = $derived(
31
+ new Date().toLocaleDateString(interfaceLanguage.current === 'pl' ? 'pl-PL' : 'en-US', {
32
+ weekday: 'long',
33
+ year: 'numeric',
34
+ month: 'long',
35
+ day: 'numeric'
36
+ })
37
+ );
38
+ </script>
39
+
40
+ <section class="welcome-header" aria-label={interfaceLanguage.current === 'pl' ? 'Powitanie' : 'Welcome'}>
41
+ <div>
42
+ <h1 class="welcome-title">{lang[interfaceLanguage.current].greeting(firstName)}</h1>
43
+ <p class="welcome-date"><time datetime={new Date().toISOString().split('T')[0]}>{formattedDate}</time></p>
44
+ </div>
45
+ </section>
@@ -0,0 +1,3 @@
1
+ declare const WelcomeHeader: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type WelcomeHeader = ReturnType<typeof WelcomeHeader>;
3
+ export default WelcomeHeader;
@@ -13,7 +13,7 @@
13
13
  } from 'sveltekit-superforms';
14
14
  import { joinPath, normalizePath } from '../../utils/objectPath.js';
15
15
  import type {
16
- ArrayField,
16
+ BlocksField,
17
17
  ObjectField as ObjectFieldType,
18
18
  ObjectFieldData
19
19
  } from '../../../types/fields.js';
@@ -46,7 +46,7 @@
46
46
  }
47
47
 
48
48
  type Props = {
49
- field: ArrayField;
49
+ field: BlocksField;
50
50
  form: SuperForm<T>;
51
51
  path: FormPathLeaves<T, ObjectFieldData[] | undefined>;
52
52
  focusedPath?: string | null;
@@ -295,7 +295,7 @@
295
295
  {#if objectField}
296
296
  <Accordion.Item value={index.toString()} class="border-0" data-depth={depth + 1}>
297
297
  <Accordion.Trigger
298
- class="items-center border px-4 text-base font-normal data-[state=open]:rounded-b-none dark:bg-slate-800/30 dark:hover:bg-slate-700/40 dark:border-white/[0.08]"
298
+ class="items-center border px-4 text-base font-normal data-[state=open]:rounded-b-none"
299
299
  >
300
300
  <div class="flex grow items-center justify-between gap-4">
301
301
  <div class="flex items-center gap-4">
@@ -353,7 +353,7 @@
353
353
  </div>
354
354
  </Accordion.Trigger>
355
355
  <Accordion.Content
356
- class="space-y-4 rounded-b-md border border-t-0 dark:bg-slate-900/30 dark:shadow-[inset_0_2px_4px_rgb(0_0_0/0.1)] dark:border-white/[0.08]"
356
+ class="space-y-4 rounded-b-md border border-t-0"
357
357
  style="padding: {Math.max(4, 16 - depth * 3)}px;"
358
358
  >
359
359
  {@const itemPath = joinPath(path, index)}
@@ -1,8 +1,8 @@
1
1
  import { type FormPathLeaves, type SuperForm } from 'sveltekit-superforms';
2
- import type { ArrayField, ObjectFieldData } from '../../../types/fields.js';
2
+ import type { BlocksField, ObjectFieldData } from '../../../types/fields.js';
3
3
  declare function $$render<T extends Record<string, unknown>>(): {
4
4
  props: {
5
- field: ArrayField;
5
+ field: BlocksField;
6
6
  form: SuperForm<T>;
7
7
  path: FormPathLeaves<T, ObjectFieldData[] | undefined>;
8
8
  focusedPath?: string | null;
@@ -28,6 +28,6 @@ interface $$IsomorphicComponent {
28
28
  <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
29
29
  z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
30
30
  }
31
- declare const ArrayField: $$IsomorphicComponent;
32
- type ArrayField<T extends Record<string, unknown>> = InstanceType<typeof ArrayField<T>>;
33
- export default ArrayField;
31
+ declare const BlocksField: $$IsomorphicComponent;
32
+ type BlocksField<T extends Record<string, unknown>> = InstanceType<typeof BlocksField<T>>;
33
+ export default BlocksField;
@@ -0,0 +1,27 @@
1
+ <script lang="ts" module>
2
+ type T = Record<string, unknown>;
3
+ </script>
4
+
5
+ <script lang="ts" generics="T extends Record<string, unknown>">
6
+ import {
7
+ formFieldProxy,
8
+ type FormFieldProxy,
9
+ type FormPathLeaves,
10
+ type SuperForm
11
+ } from 'sveltekit-superforms';
12
+ import ContentEditor from '../tiptap/content-editor.svelte';
13
+ import type { ContentField } from '../../../types/fields.js';
14
+ import type { StructuredContentDoc } from '../../../types/structured-content.js';
15
+
16
+ type Props = {
17
+ field: ContentField;
18
+ form: SuperForm<T>;
19
+ path: FormPathLeaves<T, StructuredContentDoc | undefined>;
20
+ };
21
+
22
+ let { field, form, path, ...props }: Props = $props();
23
+
24
+ const { value } = formFieldProxy(form, path) satisfies FormFieldProxy<StructuredContentDoc | undefined>;
25
+ </script>
26
+
27
+ <ContentEditor bind:value={$value} inlineBlocks={field.inlineBlocks} />
@@ -0,0 +1,31 @@
1
+ import { type FormPathLeaves, type SuperForm } from 'sveltekit-superforms';
2
+ import type { ContentField } from '../../../types/fields.js';
3
+ import type { StructuredContentDoc } from '../../../types/structured-content.js';
4
+ declare function $$render<T extends Record<string, unknown>>(): {
5
+ props: {
6
+ field: ContentField;
7
+ form: SuperForm<T>;
8
+ path: FormPathLeaves<T, StructuredContentDoc | undefined>;
9
+ };
10
+ exports: {};
11
+ bindings: "";
12
+ slots: {};
13
+ events: {};
14
+ };
15
+ declare class __sveltets_Render<T extends Record<string, unknown>> {
16
+ props(): ReturnType<typeof $$render<T>>['props'];
17
+ events(): ReturnType<typeof $$render<T>>['events'];
18
+ slots(): ReturnType<typeof $$render<T>>['slots'];
19
+ bindings(): "";
20
+ exports(): {};
21
+ }
22
+ interface $$IsomorphicComponent {
23
+ new <T extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
24
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
25
+ } & ReturnType<__sveltets_Render<T>['exports']>;
26
+ <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
27
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
28
+ }
29
+ declare const ContentField: $$IsomorphicComponent;
30
+ type ContentField<T extends Record<string, unknown>> = InstanceType<typeof ContentField<T>>;
31
+ export default ContentField;
@@ -32,13 +32,13 @@
32
32
 
33
33
  const interfaceLanguage = useInterfaceLanguage();
34
34
 
35
- const fieldsWithNoDescription: FieldType[] = ['boolean', 'object', 'array', 'seo'];
36
- const fieldsWithNoLabel: FieldType[] = ['boolean', 'object', 'array', 'seo'];
35
+ const fieldsWithNoDescription: FieldType[] = ['boolean', 'object', 'blocks', 'seo'];
36
+ const fieldsWithNoLabel: FieldType[] = ['boolean', 'object', 'blocks', 'seo'];
37
37
 
38
- const fieldsWithAlternativeDescription: FieldType[] = ['image', 'media', 'object', 'array'];
38
+ const fieldsWithAlternativeDescription: FieldType[] = ['image', 'media', 'object', 'blocks'];
39
39
 
40
- function isTextField(field: Field): field is Extract<Field, { type: 'text' | 'richtext' }> {
41
- return ['text', 'richtext'].includes(field.type);
40
+ function isTextField(field: Field): field is Extract<Field, { type: 'text' | 'richtext' | 'content' }> {
41
+ return ['text', 'richtext', 'content'].includes(field.type);
42
42
  }
43
43
 
44
44
  function isRadioField(field: Field): field is Extract<Field, { type: 'radio' }> {
@@ -106,7 +106,7 @@
106
106
  {#snippet children({ props })}
107
107
  <div class="space-y-2">
108
108
  {#if field.label && !fieldsWithNoLabel.includes(field.type)}
109
- <RequiredLabel required={field.required} class={field.type === 'array' ? 'text-lg font-medium' : ''}>
109
+ <RequiredLabel required={field.required} class={field.type === 'blocks' ? 'text-lg font-medium' : ''}>
110
110
  {getLocalizedLabel(field.label, interfaceLanguage.current)}
111
111
  </RequiredLabel>
112
112
  {/if}
@@ -121,8 +121,10 @@
121
121
  <LazyField loader={() => import('./media-field.svelte')} props={{ field, form, path, ...props }} skeletonClass="h-24" />
122
122
  {:else if field.type === 'file'}
123
123
  <LazyField loader={() => import('./file-field.svelte')} props={{ field, form, path, ...props }} skeletonClass="h-12" />
124
+ {:else if field.type === 'blocks'}
125
+ <LazyField loader={() => import('./blocks-field.svelte')} props={{ field, form, path, focusedPath, flashingPath, depth, ...props }} skeletonClass="h-20" />
124
126
  {:else if field.type === 'array'}
125
- <LazyField loader={() => import('./array-field.svelte')} props={{ field, form, path, focusedPath, flashingPath, depth, ...props }} skeletonClass="h-20" />
127
+ <LazyField loader={() => import('./simple-array-field.svelte')} props={{ field, form, path, ...props }} skeletonClass="h-12" />
126
128
  {:else if field.type === 'object'}
127
129
  <LazyField loader={() => import('./object-field.svelte')} props={{ field, form, path, objectFieldType, focusedPath, flashingPath, depth, ...props }} skeletonClass="h-20" />
128
130
  {:else if field.type === 'slug'}
@@ -108,7 +108,7 @@
108
108
  <div class="absolute inset-x-0 top-1/2 flex -translate-y-1/2 justify-between px-1 pointer-events-none">
109
109
  <button
110
110
  type="button"
111
- class="pointer-events-auto flex h-8 w-8 items-center justify-center rounded-full bg-white/80 backdrop-blur shadow-md transition hover:bg-white hover:scale-105 dark:bg-slate-900/80 dark:hover:bg-slate-900"
111
+ class="pointer-events-auto flex h-8 w-8 items-center justify-center rounded-full bg-white/80 backdrop-blur shadow-md transition hover:bg-white hover:scale-105 dark:bg-background/80 dark:hover:bg-background"
112
112
  onclick={(e) => {
113
113
  e.stopPropagation();
114
114
  currentIndex = currentIndex > 0 ? currentIndex - 1 : valueArr.length - 1;
@@ -118,7 +118,7 @@
118
118
  </button>
119
119
  <button
120
120
  type="button"
121
- class="pointer-events-auto flex h-8 w-8 items-center justify-center rounded-full bg-white/80 backdrop-blur shadow-md transition hover:bg-white hover:scale-105 dark:bg-slate-900/80 dark:hover:bg-slate-900"
121
+ class="pointer-events-auto flex h-8 w-8 items-center justify-center rounded-full bg-white/80 backdrop-blur shadow-md transition hover:bg-white hover:scale-105 dark:bg-background/80 dark:hover:bg-background"
122
122
  onclick={(e) => {
123
123
  e.stopPropagation();
124
124
  currentIndex = currentIndex < valueArr.length - 1 ? currentIndex + 1 : 0;
@@ -280,14 +280,14 @@
280
280
  <div class="absolute inset-x-0 top-1/2 flex -translate-y-1/2 justify-between px-1 pointer-events-none">
281
281
  <button
282
282
  type="button"
283
- class="pointer-events-auto flex h-8 w-8 items-center justify-center rounded-full bg-white/80 backdrop-blur shadow-md transition hover:bg-white hover:scale-105 dark:bg-slate-900/80 dark:hover:bg-slate-900"
283
+ class="pointer-events-auto flex h-8 w-8 items-center justify-center rounded-full bg-white/80 backdrop-blur shadow-md transition hover:bg-white hover:scale-105 dark:bg-background/80 dark:hover:bg-background"
284
284
  onclick={(e) => { e.stopPropagation(); currentIndex = currentIndex > 0 ? currentIndex - 1 : valueArr.length - 1; }}
285
285
  >
286
286
  <ChevronLeft class="h-5 w-5" />
287
287
  </button>
288
288
  <button
289
289
  type="button"
290
- class="pointer-events-auto flex h-8 w-8 items-center justify-center rounded-full bg-white/80 backdrop-blur shadow-md transition hover:bg-white hover:scale-105 dark:bg-slate-900/80 dark:hover:bg-slate-900"
290
+ class="pointer-events-auto flex h-8 w-8 items-center justify-center rounded-full bg-white/80 backdrop-blur shadow-md transition hover:bg-white hover:scale-105 dark:bg-background/80 dark:hover:bg-background"
291
291
  onclick={(e) => { e.stopPropagation(); currentIndex = currentIndex < valueArr.length - 1 ? currentIndex + 1 : 0; }}
292
292
  >
293
293
  <ChevronRight class="h-5 w-5" />
@@ -4,23 +4,27 @@
4
4
 
5
5
  <script lang="ts" generics="T extends Record<string, unknown>">
6
6
  import {
7
- formFieldProxy,
8
- type FormFieldProxy,
9
7
  type FormPathLeaves,
10
8
  type SuperForm
11
9
  } from 'sveltekit-superforms';
12
10
  import FieldRenderer from './field-renderer.svelte';
13
11
  import { joinPath } from '../../utils/objectPath.js';
12
+ import { getAtPath, setAtPath } from '../../utils/objectPath.js';
14
13
  import type {
15
14
  Field,
16
15
  ImageField,
17
- ObjectFieldData,
18
16
  SeoField,
19
17
  SeoFieldData,
20
18
  TextField
21
19
  } from '../../../types/fields.js';
22
- // import { onMount } from 'svelte';
23
- import * as Item from '../../../components/ui/item/index.js';
20
+ import { untrack } from 'svelte';
21
+ import slugify from '../../imports/slugify.js';
22
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
23
+ import { getContentLanguage } from '../../state/content-language.svelte.js';
24
+ import { getLocalizedLabel } from '../../utils/collectionLabel.js';
25
+
26
+ const interfaceLanguage = useInterfaceLanguage();
27
+ const contentLanguage = getContentLanguage();
24
28
 
25
29
  type Props = {
26
30
  field: SeoField;
@@ -30,31 +34,89 @@
30
34
 
31
35
  let { field, form, path, ...props }: Props = $props();
32
36
 
37
+ const { form: formData } = form;
38
+
39
+ const labels = {
40
+ slug: {
41
+ label: { en: 'URL Slug', pl: 'Slug URL' },
42
+ description: {
43
+ en: 'Page address shown in the browser bar',
44
+ pl: 'Adres strony widoczny w pasku przeglądarki'
45
+ }
46
+ },
47
+ canonicalUrl: {
48
+ label: { en: 'Canonical URL', pl: 'Adres kanoniczny' },
49
+ description: {
50
+ en: 'If this content appears at another address, enter the original URL',
51
+ pl: 'Jeśli ta treść pojawia się pod innym adresem, podaj oryginalny URL'
52
+ }
53
+ },
54
+ title: {
55
+ label: { en: 'SEO Title', pl: 'Tytuł SEO' },
56
+ description: {
57
+ en: 'Appears in search results and browser tabs (50–60 characters)',
58
+ pl: 'Widoczny w wynikach wyszukiwania i zakładce przeglądarki (50–60 znaków)'
59
+ }
60
+ },
61
+ description: {
62
+ label: { en: 'Description', pl: 'Opis' },
63
+ description: {
64
+ en: 'Short summary for search engines (120–160 characters)',
65
+ pl: 'Krótkie podsumowanie dla wyszukiwarek (120–160 znaków)'
66
+ }
67
+ },
68
+ keywords: {
69
+ label: { en: 'Keywords', pl: 'Słowa kluczowe' },
70
+ description: {
71
+ en: 'Comma-separated list of keywords',
72
+ pl: 'Lista słów kluczowych oddzielonych przecinkami'
73
+ }
74
+ },
75
+ ogImage: {
76
+ label: { en: 'Social media image', pl: 'Obraz w mediach społecznościowych' },
77
+ description: {
78
+ en: 'Image displayed when sharing this page on social media',
79
+ pl: 'Obraz wyświetlany przy udostępnianiu strony w mediach społecznościowych'
80
+ }
81
+ },
82
+ customCode: {
83
+ label: { en: 'Custom code', pl: 'Własny kod' },
84
+ description: {
85
+ en: 'Additional HTML inserted into the page <head>',
86
+ pl: 'Dodatkowy HTML wstawiany do sekcji <head> strony'
87
+ }
88
+ }
89
+ };
90
+
33
91
  const slugField: TextField = {
34
92
  type: 'text',
35
93
  slug: 'slug',
36
- label: 'Slug',
94
+ label: labels.slug.label,
95
+ description: labels.slug.description,
37
96
  required: true
38
97
  };
39
98
 
40
99
  const canonicalUrlField: TextField = {
41
100
  type: 'text',
42
101
  slug: 'canonicalUrl',
43
- label: 'Canonical Url',
102
+ label: labels.canonicalUrl.label,
103
+ description: labels.canonicalUrl.description,
44
104
  required: false
45
105
  };
46
106
 
47
107
  const titleField: TextField = {
48
108
  type: 'text',
49
109
  slug: 'title',
50
- label: 'Title',
110
+ label: labels.title.label,
111
+ description: labels.title.description,
51
112
  required: true
52
113
  };
53
114
 
54
115
  const descriptionField: TextField = {
55
116
  type: 'text',
56
117
  slug: 'description',
57
- label: 'Description',
118
+ label: labels.description.label,
119
+ description: labels.description.description,
58
120
  required: false,
59
121
  multiline: true
60
122
  };
@@ -62,7 +124,8 @@
62
124
  const keyWordsField: TextField = {
63
125
  type: 'text',
64
126
  slug: 'keywords',
65
- label: 'Keywords',
127
+ label: labels.keywords.label,
128
+ description: labels.keywords.description,
66
129
  required: false,
67
130
  multiline: true
68
131
  };
@@ -70,14 +133,16 @@
70
133
  const ogImageField: ImageField = {
71
134
  type: 'image',
72
135
  slug: 'ogImage',
73
- label: 'Open Graph Image',
136
+ label: labels.ogImage.label,
137
+ description: labels.ogImage.description,
74
138
  required: false
75
139
  };
76
140
 
77
141
  const customCodeField: TextField = {
78
142
  type: 'text',
79
143
  slug: 'customCode',
80
- label: 'Custom Code',
144
+ label: labels.customCode.label,
145
+ description: labels.customCode.description,
81
146
  required: false,
82
147
  multiline: true
83
148
  };
@@ -91,19 +156,134 @@
91
156
  ogImageField,
92
157
  customCodeField
93
158
  ];
159
+
160
+ // Helper: read a value from formData, handling both localized (object) and plain (string) values
161
+ function readSourceValue(sourcePath: string): string | undefined {
162
+ const raw = ($formData as Record<string, unknown>)[sourcePath];
163
+ if (typeof raw === 'string') return raw;
164
+ if (raw && typeof raw === 'object') {
165
+ return (raw as Record<string, string>)[contentLanguage.current];
166
+ }
167
+ return undefined;
168
+ }
169
+
170
+ // Helper: read value at a dot-path from formData
171
+ function readPath(dotPath: string): string | undefined {
172
+ const val = getAtPath($formData as Record<string, unknown>, dotPath);
173
+ return typeof val === 'string' ? val : undefined;
174
+ }
175
+
176
+ // Character count for current language
177
+ let titleLength = $derived.by(() => {
178
+ const val = readPath(joinPath(String(path), 'title', contentLanguage.current));
179
+ return val?.length ?? 0;
180
+ });
181
+ let descLength = $derived.by(() => {
182
+ const val = readPath(joinPath(String(path), 'description', contentLanguage.current));
183
+ return val?.length ?? 0;
184
+ });
185
+
186
+ function charHintClass(len: number, min: number, max: number): string {
187
+ if (len === 0) return 'text-muted-foreground';
188
+ if (len >= min && len <= max) return 'text-success';
189
+ if (len < min) return 'text-warning';
190
+ return 'text-destructive';
191
+ }
192
+
193
+ // Auto-gen: track last auto-generated values per language
194
+ let lastAutoSlugs: Record<string, string> = {};
195
+ let lastAutoTitles: Record<string, string> = {};
196
+
197
+ // slugSource → auto-gen seo.slug per language
198
+ $effect(() => {
199
+ if (!field.slugSource) return;
200
+ const sourceRaw = ($formData as Record<string, unknown>)[field.slugSource];
201
+ if (!sourceRaw) return;
202
+
203
+ untrack(() => {
204
+ const pairs: [string, string][] =
205
+ typeof sourceRaw === 'string'
206
+ ? [[contentLanguage.current, sourceRaw]]
207
+ : typeof sourceRaw === 'object'
208
+ ? Object.entries(sourceRaw as Record<string, string>).filter(([, v]) => typeof v === 'string' && v)
209
+ : [];
210
+
211
+ let changed = false;
212
+ for (const [lang, text] of pairs) {
213
+ const targetPath = joinPath(String(path), 'slug', lang);
214
+ const current = getAtPath($formData as Record<string, unknown>, targetPath) as string | undefined;
215
+ if (!current || current === lastAutoSlugs[lang]) {
216
+ const newSlug = slugify(String(text), { lower: true, strict: true, trim: true });
217
+ if (newSlug !== current) {
218
+ setAtPath($formData as Record<string, unknown>, targetPath, newSlug);
219
+ changed = true;
220
+ }
221
+ lastAutoSlugs[lang] = newSlug;
222
+ }
223
+ }
224
+ if (changed) $formData = $formData;
225
+ });
226
+ });
227
+
228
+ // titleSource → auto-fill seo.title per language
229
+ $effect(() => {
230
+ if (!field.titleSource) return;
231
+ const sourceRaw = ($formData as Record<string, unknown>)[field.titleSource];
232
+ if (!sourceRaw) return;
233
+
234
+ untrack(() => {
235
+ const pairs: [string, string][] =
236
+ typeof sourceRaw === 'string'
237
+ ? [[contentLanguage.current, sourceRaw]]
238
+ : typeof sourceRaw === 'object'
239
+ ? Object.entries(sourceRaw as Record<string, string>).filter(([, v]) => typeof v === 'string' && v)
240
+ : [];
241
+
242
+ let changed = false;
243
+ for (const [lang, text] of pairs) {
244
+ const targetPath = joinPath(String(path), 'title', lang);
245
+ const current = getAtPath($formData as Record<string, unknown>, targetPath) as string | undefined;
246
+ if (!current || current === lastAutoTitles[lang]) {
247
+ if (text !== current) {
248
+ setAtPath($formData as Record<string, unknown>, targetPath, text);
249
+ changed = true;
250
+ }
251
+ lastAutoTitles[lang] = text;
252
+ }
253
+ }
254
+ if (changed) $formData = $formData;
255
+ });
256
+ });
94
257
  </script>
95
258
 
96
- <Item.Root variant="outline">
97
- <Item.Content>
98
- <Item.Title class="mb-4 text-lg">SEO</Item.Title>
99
- <div class="space-y-4">
100
- {#each fields as f}
101
- <FieldRenderer
102
- field={f}
103
- form={form as SuperForm<Record<string, unknown>>}
104
- path={joinPath(path, f.slug)}
105
- />
106
- {/each}
259
+ <div class="space-y-4">
260
+ {#each fields as f}
261
+ <div>
262
+ <FieldRenderer
263
+ field={f}
264
+ form={form as SuperForm<Record<string, unknown>>}
265
+ path={joinPath(path, f.slug)}
266
+ />
267
+ {#if f.slug === 'title'}
268
+ <p class="mt-1 text-xs {charHintClass(titleLength, 50, 60)}">
269
+ {titleLength}/60
270
+ {#if titleLength > 0 && titleLength < 50}
271
+ — {getLocalizedLabel({ en: 'a bit short', pl: 'trochę za krótko' }, interfaceLanguage.current)}
272
+ {:else if titleLength > 60}
273
+ — {getLocalizedLabel({ en: 'too long', pl: 'za długo' }, interfaceLanguage.current)}
274
+ {/if}
275
+ </p>
276
+ {/if}
277
+ {#if f.slug === 'description'}
278
+ <p class="mt-1 text-xs {charHintClass(descLength, 120, 160)}">
279
+ {descLength}/160
280
+ {#if descLength > 0 && descLength < 120}
281
+ — {getLocalizedLabel({ en: 'a bit short', pl: 'trochę za krótko' }, interfaceLanguage.current)}
282
+ {:else if descLength > 160}
283
+ — {getLocalizedLabel({ en: 'too long', pl: 'za długo' }, interfaceLanguage.current)}
284
+ {/if}
285
+ </p>
286
+ {/if}
107
287
  </div>
108
- </Item.Content>
109
- </Item.Root>
288
+ {/each}
289
+ </div>