@michaelthielemann/kestrel 3.0.0 → 4.0.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 (353) hide show
  1. package/README.md +35 -110
  2. package/kestrel.config.ts +1 -1
  3. package/layers/access/server/middleware/00.ip-allowlist.ts +3 -18
  4. package/layers/access/server/middleware/access-guard.ts +25 -23
  5. package/layers/access/server/types.d.ts +1 -2
  6. package/layers/admin/app/components/AdminAccount.vue +1 -1
  7. package/layers/admin/app/components/BlockFields.vue +1 -2
  8. package/layers/admin/app/components/BlockPreview.vue +17 -5
  9. package/layers/admin/app/components/BlocksBody.vue +1 -1
  10. package/layers/admin/app/components/CollectionEditor.vue +35 -16
  11. package/layers/admin/app/components/CollectionList.vue +34 -3
  12. package/layers/admin/app/components/CollectionListBulkBar.vue +17 -3
  13. package/layers/admin/app/components/CollectionListFilterPanel.vue +1 -1
  14. package/layers/admin/app/components/CollectionListPager.vue +1 -1
  15. package/layers/admin/app/components/CollectionListTable.vue +41 -2
  16. package/layers/admin/app/components/EditorStatus.vue +8 -7
  17. package/layers/admin/app/components/LocaleBar.vue +3 -1
  18. package/layers/admin/app/components/PageFields.vue +2 -4
  19. package/layers/admin/app/components/SeoFields.vue +2 -4
  20. package/layers/admin/app/components/SingletonEditor.vue +3 -2
  21. package/layers/admin/app/composables/useAuth.ts +3 -3
  22. package/layers/admin/app/composables/useBlockTree.ts +1 -1
  23. package/layers/admin/app/composables/useBlocks.ts +1 -1
  24. package/layers/admin/app/composables/useCollectionOps.ts +45 -8
  25. package/layers/admin/app/composables/useCollections.ts +1 -2
  26. package/layers/admin/app/composables/useEditForm.ts +35 -17
  27. package/layers/admin/app/composables/useLinkResolver.ts +1 -1
  28. package/layers/admin/app/composables/useListBatchActions.ts +20 -3
  29. package/layers/admin/app/composables/useListColumns.ts +1 -1
  30. package/layers/admin/app/composables/useListFilterDraft.ts +3 -2
  31. package/layers/admin/app/composables/useListRows.ts +7 -2
  32. package/layers/admin/app/composables/useListUrlState.ts +1 -1
  33. package/layers/admin/app/composables/usePublishStatus.ts +2 -2
  34. package/layers/admin/app/layouts/admin.vue +0 -1
  35. package/layers/admin/app/pages/admin/[collection]/[id].vue +5 -4
  36. package/layers/admin/app/pages/admin/index.vue +1 -1
  37. package/layers/admin/app/pages/admin/references.vue +2 -2
  38. package/layers/admin/app/utils/block-tree.ts +1 -1
  39. package/layers/admin/app/utils/collection-ops.ts +28 -3
  40. package/layers/admin/app/utils/edit-form.ts +34 -2
  41. package/layers/admin/app/utils/editor-expose.ts +2 -0
  42. package/layers/admin/app/utils/editor-form-context.ts +1 -2
  43. package/layers/admin/app/utils/field-keys.ts +1 -1
  44. package/layers/admin/app/utils/list-columns.ts +3 -2
  45. package/layers/admin/app/utils/list-query.ts +2 -2
  46. package/layers/admin/app/utils/populate-blocks.ts +2 -2
  47. package/layers/admin/app/utils/reauth.ts +5 -6
  48. package/layers/auth/server/plugins/01.register-auth-pipelines.ts +7 -0
  49. package/layers/collections/server/plugins/01.register.ts +9 -12
  50. package/layers/collections/server/plugins/02.register-relation-populate.ts +5 -7
  51. package/layers/{fields → core}/app/utils/field-factories.ts +1 -1
  52. package/layers/core/modules/auto-discovery/extract-block.ts +3 -3
  53. package/layers/core/modules/auto-discovery/index.ts +68 -11
  54. package/layers/core/modules/auto-discovery/package-registry.ts +19 -0
  55. package/layers/core/modules/auto-discovery/scan.ts +77 -0
  56. package/layers/core/modules/auto-discovery/virtual.d.ts +5 -0
  57. package/layers/core/modules/component-namespace/index.ts +24 -0
  58. package/layers/core/modules/kestrel/index.ts +6 -1
  59. package/layers/core/modules/plugin-order/index.ts +69 -0
  60. package/layers/core/modules/plugin-order/plugin-order.ts +273 -0
  61. package/layers/core/nuxt.config.ts +1 -0
  62. package/layers/core/server/api/[...path].ts +98 -0
  63. package/layers/core/server/plugins/00.config.ts +12 -0
  64. package/layers/core/server/plugins/00.migrate.ts +1 -0
  65. package/layers/core/server/plugins/01.register-introspection-pipeline.ts +5 -0
  66. package/layers/core/server/plugins/01.register-openapi-pipeline.ts +5 -0
  67. package/layers/core/server/plugins/01.register-tooling-pipelines.ts +5 -0
  68. package/layers/core/server/plugins/02.schema-sync.ts +2 -7
  69. package/layers/core/server/plugins/04.outbox-worker.ts +31 -0
  70. package/layers/core/server/plugins/05.reindex-refs.ts +5 -0
  71. package/layers/core/server/routes/__kestrel/dashboard.get.ts +29 -0
  72. package/layers/core/server/tasks/db/migrate-module.ts +83 -0
  73. package/layers/core/server/tasks/db/migrate-revisions.ts +20 -0
  74. package/layers/core/server/tasks/db/migrate.ts +2 -6
  75. package/layers/core/server/utils/server-config.ts +66 -3
  76. package/layers/fields/server/plugins/01.register-blocks-pipeline.ts +7 -0
  77. package/layers/fields/server/plugins/01.register-field-populate.ts +2 -2
  78. package/layers/media/app/components/KestrelImg.vue +3 -3
  79. package/layers/media/app/components/MediaLibrary.vue +2 -2
  80. package/layers/media/app/components/field/Media.vue +1 -1
  81. package/layers/media/app/composables/useMediaResolver.ts +1 -1
  82. package/layers/media/app/composables/useMediaUpload.ts +1 -1
  83. package/layers/media/app/composables/useMediaVariant.ts +2 -2
  84. package/layers/media/app/utils/ai-disclosure.ts +1 -1
  85. package/layers/media/app/utils/library.ts +1 -1
  86. package/layers/media/app/utils/ops.ts +1 -3
  87. package/layers/media/app/utils/picture-model.ts +2 -2
  88. package/layers/media/app/utils/variant-name.ts +1 -1
  89. package/layers/media/server/middleware/ondemand-variants.ts +6 -5
  90. package/layers/media/server/plugins/01.register-media-pipelines.ts +7 -0
  91. package/layers/media/server/plugins/02.register-media.ts +10 -7
  92. package/layers/media/server/plugins/04.variant-capture.ts +12 -8
  93. package/layers/media/server/plugins/05.media-cleanup.ts +5 -0
  94. package/layers/media/server/tasks/media/backfill.ts +3 -4
  95. package/layers/public/app/components/KestrelLink.vue +1 -1
  96. package/layers/public/app/pages/[...slug].vue +1 -1
  97. package/layers/public/app/pages/__kestrel/preview.vue +2 -2
  98. package/layers/public/app/utils/json-ld.ts +1 -1
  99. package/layers/public/app/utils/link-href.ts +1 -1
  100. package/layers/public/app/utils/page-head.ts +1 -1
  101. package/layers/public/modules/deploy-output/index.ts +4 -6
  102. package/layers/public/modules/prerender-routes/index.ts +4 -4
  103. package/layers/public/modules/prerender-routes/routes.ts +1 -1
  104. package/layers/public/modules/prune-media/index.ts +2 -2
  105. package/layers/public/server/delivery-live/serve.ts +111 -0
  106. package/layers/public/server/middleware/delivery-live-catchall.ts +7 -0
  107. package/layers/public/server/pipelines/index.ts +23 -0
  108. package/layers/public/server/plugins/00.ensure-snapshot-triggers.ts +14 -0
  109. package/layers/public/server/plugins/01.register-public-pipelines.ts +7 -0
  110. package/layers/public/server/plugins/02.register-links.ts +2 -3
  111. package/layers/public/server/plugins/03.redirects.ts +41 -23
  112. package/layers/public/server/plugins/05.plan-publish.ts +5 -0
  113. package/layers/public/server/plugins/zz.publish.ts +79 -24
  114. package/layers/public/server/routes/llms-full.txt.get.ts +5 -5
  115. package/layers/public/server/routes/llms.txt.get.ts +4 -2
  116. package/layers/public/server/routes/redirects.json.get.ts +2 -2
  117. package/layers/public/server/routes/robots.txt.get.ts +2 -0
  118. package/layers/public/server/routes/sitemap.xml.get.ts +16 -6
  119. package/layers/public/server/tasks/publish/run.ts +9 -4
  120. package/layers/public/server/utils/publish/render-live.ts +26 -0
  121. package/layers/ui/app/components/field/Choice.vue +1 -1
  122. package/layers/ui/app/components/field/Datetime.vue +1 -1
  123. package/layers/ui/app/components/field/Json.vue +1 -2
  124. package/layers/ui/app/components/field/Layout.vue +2 -4
  125. package/layers/ui/app/components/field/Link.vue +1 -1
  126. package/layers/ui/app/components/field/LinkInternalPicker.vue +3 -3
  127. package/layers/ui/app/components/field/Number.vue +2 -2
  128. package/layers/ui/app/components/field/Relation.vue +1 -1
  129. package/layers/ui/app/components/field/Repeater.vue +6 -8
  130. package/layers/ui/app/components/field/Slug.vue +2 -3
  131. package/layers/ui/app/components/field/Text.vue +1 -1
  132. package/layers/ui/app/components/ui/Button.vue +2 -2
  133. package/layers/ui/app/components/ui/ButtonGroup.vue +2 -2
  134. package/layers/ui/app/components/ui/Checkbox.vue +2 -2
  135. package/layers/ui/app/components/ui/Combobox.vue +5 -5
  136. package/layers/ui/app/components/ui/DatePicker.vue +2 -2
  137. package/layers/ui/app/components/ui/DateRangePicker.vue +2 -2
  138. package/layers/ui/app/components/ui/NumberInput.vue +4 -4
  139. package/layers/ui/app/components/ui/Richtext.vue +1 -1
  140. package/layers/ui/app/components/ui/RichtextToolbar.vue +1 -3
  141. package/layers/ui/app/components/ui/Select.vue +2 -2
  142. package/layers/ui/app/components/ui/TextInput.vue +4 -4
  143. package/layers/ui/app/components/ui/Textarea.vue +2 -2
  144. package/layers/ui/app/components/ui/TimeInput.vue +2 -2
  145. package/layers/ui/app/components/ui/Toasts.vue +3 -3
  146. package/layers/ui/app/composables/useLinkField.ts +1 -1
  147. package/layers/ui/app/composables/useRepeater.ts +1 -1
  148. package/layers/ui/app/i18n/de.ts +7 -7
  149. package/layers/ui/app/i18n/en.ts +7 -39
  150. package/layers/ui/app/utils/field-component.ts +1 -1
  151. package/layers/ui/app/utils/field-registry.ts +1 -1
  152. package/layers/ui/app/utils/icons.ts +2 -6
  153. package/layers/ui/app/utils/localized.ts +1 -1
  154. package/nuxt.config.ts +1 -1
  155. package/package.json +53 -19
  156. package/scripts/consumer-template-ci.mjs +157 -0
  157. package/scripts/coverage-all.mjs +163 -0
  158. package/scripts/dashboard.mjs +89 -0
  159. package/scripts/hash-password.mjs +1 -1
  160. package/scripts/lib/coverage-model.mjs +51 -0
  161. package/scripts/port-graph.mjs +350 -0
  162. package/scripts/typecheck.mjs +11 -0
  163. package/templates/starter/app/blocks/Prose.vue +1 -1
  164. package/layers/access/server/utils/csrf.ts +0 -39
  165. package/layers/access/server/utils/grant-registry.ts +0 -36
  166. package/layers/access/server/utils/guard.ts +0 -44
  167. package/layers/access/server/utils/ip-allowlist.ts +0 -79
  168. package/layers/access/server/utils/policy.ts +0 -98
  169. package/layers/access/server/utils/public-resources.ts +0 -11
  170. package/layers/access/server/utils/render-context.ts +0 -41
  171. package/layers/access/server/utils/require-admin.ts +0 -15
  172. package/layers/admin/app/pages/admin/[collection]/[id].nuxt.test.ts +0 -200
  173. package/layers/admin/app/pages/admin/[collection]/publish-preview.nuxt.test.ts +0 -142
  174. package/layers/auth/server/api/auth/login.post.ts +0 -38
  175. package/layers/auth/server/api/auth/logout.post.ts +0 -9
  176. package/layers/auth/server/api/auth/session.get.ts +0 -1
  177. package/layers/auth/server/utils/admin-credential.ts +0 -42
  178. package/layers/auth/server/utils/client-ip.ts +0 -88
  179. package/layers/auth/server/utils/login-throttle.ts +0 -126
  180. package/layers/auth/server/utils/password.ts +0 -52
  181. package/layers/auth/server/utils/session-cookie.ts +0 -44
  182. package/layers/auth/server/utils/session-epoch.ts +0 -96
  183. package/layers/auth/server/utils/session.ts +0 -88
  184. package/layers/collections/server/collections/pages.ts +0 -19
  185. package/layers/collections/server/utils/populate-relations.ts +0 -79
  186. package/layers/core/app/utils/bulk-actions.ts +0 -9
  187. package/layers/core/app/utils/filter-ops.ts +0 -98
  188. package/layers/core/app/utils/list-limits.ts +0 -27
  189. package/layers/core/app/utils/locale-path.ts +0 -12
  190. package/layers/core/app/utils/slugify.ts +0 -16
  191. package/layers/core/server/api/[collection]/[id]/dead-refs.get.ts +0 -8
  192. package/layers/core/server/api/[collection]/[id]/translations.get.ts +0 -4
  193. package/layers/core/server/api/[collection]/[id].delete.ts +0 -5
  194. package/layers/core/server/api/[collection]/[id].get.ts +0 -11
  195. package/layers/core/server/api/[collection]/[id].patch.ts +0 -8
  196. package/layers/core/server/api/[collection]/bulk.post.ts +0 -37
  197. package/layers/core/server/api/[collection]/index.get.ts +0 -24
  198. package/layers/core/server/api/[collection]/index.post.ts +0 -8
  199. package/layers/core/server/api/[collection]/index.put.test.ts +0 -76
  200. package/layers/core/server/api/[collection]/index.put.ts +0 -24
  201. package/layers/core/server/api/[collection]/options.get.test.ts +0 -60
  202. package/layers/core/server/api/[collection]/options.get.ts +0 -18
  203. package/layers/core/server/api/[collection]/populate-scope.test.ts +0 -119
  204. package/layers/core/server/api/[collection]/translations.get.test.ts +0 -90
  205. package/layers/core/server/api/[collection]/translations.get.ts +0 -25
  206. package/layers/core/server/api/collections/[name].get.ts +0 -4
  207. package/layers/core/server/api/collections/index.get.ts +0 -3
  208. package/layers/core/server/api/references/broken.get.ts +0 -9
  209. package/layers/core/server/api/references/referrers.get.ts +0 -24
  210. package/layers/core/server/database/record-refs.ts +0 -20
  211. package/layers/core/server/plugins/03.record-refs.ts +0 -12
  212. package/layers/core/server/schema/bootstrap.ts +0 -41
  213. package/layers/core/server/schema/desired.ts +0 -99
  214. package/layers/core/server/schema/dev-sync.ts +0 -23
  215. package/layers/core/server/schema/dialect.ts +0 -61
  216. package/layers/core/server/schema/diff.ts +0 -110
  217. package/layers/core/server/schema/introspect.ts +0 -68
  218. package/layers/core/server/schema/model.ts +0 -61
  219. package/layers/core/server/schema/render-sqlite.ts +0 -89
  220. package/layers/core/server/schema/sync.ts +0 -201
  221. package/layers/core/server/utils/blocks.ts +0 -6
  222. package/layers/core/server/utils/collection-types.ts +0 -28
  223. package/layers/core/server/utils/crud.ts +0 -578
  224. package/layers/core/server/utils/db.ts +0 -26
  225. package/layers/core/server/utils/defineCollection.ts +0 -213
  226. package/layers/core/server/utils/duplicate.ts +0 -162
  227. package/layers/core/server/utils/field-layout.ts +0 -125
  228. package/layers/core/server/utils/filter-predicate.ts +0 -101
  229. package/layers/core/server/utils/http.ts +0 -50
  230. package/layers/core/server/utils/if-unmodified.ts +0 -13
  231. package/layers/core/server/utils/kestrel-config.ts +0 -451
  232. package/layers/core/server/utils/key-lock.ts +0 -33
  233. package/layers/core/server/utils/locale.ts +0 -53
  234. package/layers/core/server/utils/page-route.ts +0 -58
  235. package/layers/core/server/utils/page-slug.ts +0 -127
  236. package/layers/core/server/utils/picker.ts +0 -82
  237. package/layers/core/server/utils/populate.ts +0 -50
  238. package/layers/core/server/utils/prerender-memo.ts +0 -23
  239. package/layers/core/server/utils/read-capture.ts +0 -37
  240. package/layers/core/server/utils/record-ref-index.ts +0 -200
  241. package/layers/core/server/utils/registry.ts +0 -24
  242. package/layers/core/server/utils/resolve-scope.ts +0 -79
  243. package/layers/core/server/utils/seo.ts +0 -42
  244. package/layers/core/server/utils/serialize-collection.ts +0 -147
  245. package/layers/core/server/utils/sql.ts +0 -2
  246. package/layers/core/server/utils/storage.local.ts +0 -117
  247. package/layers/core/server/utils/storage.s3.ts +0 -197
  248. package/layers/core/server/utils/storage.ts +0 -90
  249. package/layers/core/server/utils/write-effects.ts +0 -40
  250. package/layers/core/server/utils/write-events.ts +0 -31
  251. package/layers/fields/app/utils/condition.ts +0 -71
  252. package/layers/fields/app/utils/define-block.ts +0 -13
  253. package/layers/fields/app/utils/field-constraints.ts +0 -17
  254. package/layers/fields/app/utils/field-meta.ts +0 -33
  255. package/layers/fields/app/utils/field-validate.ts +0 -75
  256. package/layers/fields/app/utils/field-value.ts +0 -7
  257. package/layers/fields/app/utils/richtext-links.ts +0 -48
  258. package/layers/fields/server/api/blocks/index.get.ts +0 -9
  259. package/layers/fields/server/field-registry/index.ts +0 -249
  260. package/layers/fields/server/field-registry/naming.ts +0 -10
  261. package/layers/fields/server/field-registry/sanitize.ts +0 -43
  262. package/layers/fields/server/field-registry/types.ts +0 -19
  263. package/layers/fields/server/utils/block-ids.ts +0 -43
  264. package/layers/fields/server/utils/block-populate.ts +0 -41
  265. package/layers/fields/server/utils/buildCollection.ts +0 -90
  266. package/layers/fields/server/utils/buildFieldSchema.ts +0 -22
  267. package/layers/fields/server/utils/buildTable.ts +0 -113
  268. package/layers/fields/server/utils/conditional-required.ts +0 -25
  269. package/layers/fields/server/utils/defineBlock.ts +0 -86
  270. package/layers/fields/server/utils/defineFieldType.ts +0 -33
  271. package/layers/fields/server/utils/extract-refs.ts +0 -152
  272. package/layers/fields/server/utils/field-populate.ts +0 -68
  273. package/layers/media/server/api/media/[id]/usages.get.ts +0 -8
  274. package/layers/media/server/api/media/[id].delete.ts +0 -18
  275. package/layers/media/server/api/media/[id].get.ts +0 -10
  276. package/layers/media/server/api/media/[id].patch.test.ts +0 -66
  277. package/layers/media/server/api/media/[id].patch.ts +0 -65
  278. package/layers/media/server/api/media/backfill.post.ts +0 -18
  279. package/layers/media/server/api/media/copy.post.ts +0 -7
  280. package/layers/media/server/api/media/delete.post.ts +0 -13
  281. package/layers/media/server/api/media/folders.post.ts +0 -18
  282. package/layers/media/server/api/media/index.get.ts +0 -15
  283. package/layers/media/server/api/media/index.post.ts +0 -173
  284. package/layers/media/server/api/media/library.get.ts +0 -17
  285. package/layers/media/server/api/media/move.post.ts +0 -7
  286. package/layers/media/server/api/media/rename.post.ts +0 -7
  287. package/layers/media/server/api/media/resolve.get.ts +0 -13
  288. package/layers/media/server/collections/media-settings.ts +0 -25
  289. package/layers/media/server/collections/media.ts +0 -43
  290. package/layers/media/server/database/folders.ts +0 -10
  291. package/layers/media/server/plugins/03.media-cleanup.ts +0 -20
  292. package/layers/media/server/schema-tables/folders.ts +0 -4
  293. package/layers/media/server/utils/ai-disclosure-enabled.ts +0 -16
  294. package/layers/media/server/utils/ai-signal-detect.ts +0 -155
  295. package/layers/media/server/utils/backfill.ts +0 -144
  296. package/layers/media/server/utils/derive.ts +0 -59
  297. package/layers/media/server/utils/folder-paths.ts +0 -42
  298. package/layers/media/server/utils/folders.ts +0 -12
  299. package/layers/media/server/utils/library.ts +0 -91
  300. package/layers/media/server/utils/media-cleanup.ts +0 -27
  301. package/layers/media/server/utils/media-enabled.ts +0 -23
  302. package/layers/media/server/utils/media-ops.ts +0 -118
  303. package/layers/media/server/utils/media-write.ts +0 -13
  304. package/layers/media/server/utils/naming.ts +0 -52
  305. package/layers/media/server/utils/ondemand.ts +0 -69
  306. package/layers/media/server/utils/persist-upload.ts +0 -62
  307. package/layers/media/server/utils/populate.ts +0 -47
  308. package/layers/media/server/utils/record.ts +0 -71
  309. package/layers/media/server/utils/relocate-ops.ts +0 -341
  310. package/layers/media/server/utils/relocate-plan.ts +0 -63
  311. package/layers/media/server/utils/resolve.ts +0 -98
  312. package/layers/media/server/utils/sanitize-svg.ts +0 -12
  313. package/layers/media/server/utils/sniff.ts +0 -68
  314. package/layers/media/server/utils/storage-relocate.ts +0 -139
  315. package/layers/media/server/utils/translations.ts +0 -19
  316. package/layers/media/server/utils/usages.ts +0 -66
  317. package/layers/media/server/utils/variant-capture.ts +0 -70
  318. package/layers/media/server/utils/variants.ts +0 -123
  319. package/layers/public/modules/deploy-output/deploy-output.ts +0 -384
  320. package/layers/public/server/api/links/resolve.get.ts +0 -19
  321. package/layers/public/server/api/preview.get.ts +0 -28
  322. package/layers/public/server/api/preview.post.ts +0 -93
  323. package/layers/public/server/api/publish-status.get.ts +0 -58
  324. package/layers/public/server/api/publish.post.ts +0 -84
  325. package/layers/public/server/api/route.get.ts +0 -54
  326. package/layers/public/server/collections/redirects.ts +0 -75
  327. package/layers/public/server/collections/site.ts +0 -44
  328. package/layers/public/server/database/publish-deps.ts +0 -12
  329. package/layers/public/server/database/publish-status.ts +0 -19
  330. package/layers/public/server/schema-tables/publish-deps.ts +0 -4
  331. package/layers/public/server/schema-tables/publish-status.ts +0 -4
  332. package/layers/public/server/utils/link-resolve.ts +0 -38
  333. package/layers/public/server/utils/llms-full.ts +0 -125
  334. package/layers/public/server/utils/llms.ts +0 -52
  335. package/layers/public/server/utils/page-resolve.ts +0 -208
  336. package/layers/public/server/utils/populate-links.ts +0 -66
  337. package/layers/public/server/utils/preview-token.ts +0 -109
  338. package/layers/public/server/utils/publish/coalesce.ts +0 -22
  339. package/layers/public/server/utils/publish/deps-persistence.ts +0 -43
  340. package/layers/public/server/utils/publish/deps.ts +0 -87
  341. package/layers/public/server/utils/publish/invalidation.ts +0 -225
  342. package/layers/public/server/utils/publish/pending.ts +0 -74
  343. package/layers/public/server/utils/publish/publish-runtime.ts +0 -28
  344. package/layers/public/server/utils/publish/publish-status.ts +0 -72
  345. package/layers/public/server/utils/publish/publisher.ts +0 -362
  346. package/layers/public/server/utils/publish/queue.ts +0 -67
  347. package/layers/public/server/utils/publish/redirect-rules.ts +0 -221
  348. package/layers/public/server/utils/publish/redirects-artifact.ts +0 -20
  349. package/layers/public/server/utils/publish/route-for-record.ts +0 -16
  350. package/layers/public/server/utils/publish/route-keys.ts +0 -13
  351. package/layers/public/server/utils/richtext-markdown.ts +0 -260
  352. package/layers/public/server/utils/site-url.ts +0 -40
  353. package/layers/public/server/utils/sitemap.ts +0 -99
@@ -7,7 +7,7 @@ import UiButtonGroup from '../ui/ButtonGroup.vue'
7
7
  import UiCheckboxGroup from '../ui/CheckboxGroup.vue'
8
8
  import { resolveLocalized } from '../../utils/localized'
9
9
  import type { FieldComponentProps } from '../../utils/field-component'
10
- import type { FieldOf } from '../../../../core/server/utils/defineCollection'
10
+ import type { FieldOf } from '@michaelthielemann/kestrel-core'
11
11
 
12
12
  const props = defineProps<FieldComponentProps>()
13
13
  const model = defineModel<string | string[] | null>()
@@ -6,7 +6,7 @@ import UiTimeInput from '../ui/TimeInput.vue'
6
6
  import UiDatePicker from '../ui/DatePicker.vue'
7
7
  import UiDateRangePicker from '../ui/DateRangePicker.vue'
8
8
  import type { FieldComponentProps } from '../../utils/field-component'
9
- import type { FieldOf } from '../../../../core/server/utils/defineCollection'
9
+ import type { FieldOf } from '@michaelthielemann/kestrel-core'
10
10
 
11
11
  const { t } = useT()
12
12
  const props = defineProps<FieldComponentProps>()
@@ -2,8 +2,7 @@
2
2
  import { computed, ref, watch } from 'vue'
3
3
  import UiField from '../ui/Field.vue'
4
4
  import UiTextarea from '../ui/Textarea.vue'
5
- import { fieldConstraints } from '../../../../fields/app/utils/field-meta'
6
- import { tryParseJson } from '../../../../fields/app/utils/field-value'
5
+ import { fieldConstraints, tryParseJson } from '@michaelthielemann/kestrel-fields/client'
7
6
  import type { FieldComponentProps } from '../../utils/field-component'
8
7
 
9
8
  const props = defineProps<FieldComponentProps>()
@@ -2,11 +2,9 @@
2
2
  import { computed } from 'vue'
3
3
  import FieldRenderer from './Renderer.vue'
4
4
  import FieldDeadRefNote from './DeadRefNote.vue'
5
- import { isFieldVisible } from '../../../../fields/app/utils/condition'
5
+ import { isFieldVisible } from '@michaelthielemann/kestrel-core/client'
6
+ import type { FieldDef, LayoutNode, LayoutTrack, Localized } from '@michaelthielemann/kestrel-core'
6
7
  import { resolveLocalized } from '../../utils/localized'
7
- import type { FieldDef, Localized } from '../../../../core/server/utils/defineCollection'
8
- import type { LayoutNode, LayoutTrack } from '../../../../core/server/utils/field-layout'
9
-
10
8
  // The ONE shared field-list renderer, reused by every field-list surface (collection page fields, block
11
9
  // fields, repeater sub-fields). A FRAGMENT (no wrapper root) so its rows/groups drop straight into the
12
10
  // caller's existing gapped flex column — the per-context vertical rhythm is preserved. Imports are
@@ -6,7 +6,7 @@ import UiTextInput from '../ui/TextInput.vue'
6
6
  import LinkInternalPicker from './LinkInternalPicker.vue'
7
7
  import { useLinkField } from '../../composables/useLinkField'
8
8
  import type { FieldComponentProps } from '../../utils/field-component'
9
- import type { FieldOf, LinkType, LinkValue } from '../../../../core/server/utils/defineCollection'
9
+ import type { FieldOf, LinkType, LinkValue } from '@michaelthielemann/kestrel-core'
10
10
 
11
11
  const ALL: LinkType[] = ['external', 'email', 'tel', 'internal']
12
12
  const TYPE_KEYS: Record<LinkType, string> = {
@@ -4,7 +4,7 @@ import UiSelect from '../ui/Select.vue'
4
4
  import UiCombobox from '../ui/Combobox.vue'
5
5
  import { useRecordOptions } from '../../composables/useRecordOptions'
6
6
  import { resolveLocalized } from '../../utils/localized'
7
- import type { SerializedCollection } from '../../../../core/server/utils/serialize-collection'
7
+ import type { SerializedCollection } from '@michaelthielemann/kestrel-core'
8
8
 
9
9
  const { t, lang } = useT()
10
10
 
@@ -85,8 +85,8 @@ const { options, selected, loading, onSearch } = useRecordOptions(coll, ids, toR
85
85
  </template>
86
86
 
87
87
  <style lang="scss">
88
- // The internal arm stacks two block controls (collection select + record combobox) plus an error line;
89
- // without this they touched. Matches the `--space-1` rhythm the hash/label rows already use.
88
+ // The internal arm stacks two block controls (collection select + record combobox) plus an error line.
89
+ // Matches the `--space-1` rhythm the hash/label rows already use.
90
90
  .ui-link-internal {
91
91
  display: flex;
92
92
  flex-direction: column;
@@ -2,9 +2,9 @@
2
2
  import { computed } from 'vue'
3
3
  import UiField from '../ui/Field.vue'
4
4
  import UiNumberInput from '../ui/NumberInput.vue'
5
- import { fieldConstraints } from '../../../../fields/app/utils/field-meta'
5
+ import { fieldConstraints } from '@michaelthielemann/kestrel-fields/client'
6
6
  import type { FieldComponentProps } from '../../utils/field-component'
7
- import type { FieldOf } from '../../../../core/server/utils/defineCollection'
7
+ import type { FieldOf } from '@michaelthielemann/kestrel-core'
8
8
 
9
9
  const props = defineProps<FieldComponentProps>()
10
10
  const model = defineModel<number | null>()
@@ -4,7 +4,7 @@ import UiField from '../ui/Field.vue'
4
4
  import UiCombobox from '../ui/Combobox.vue'
5
5
  import { useRecordOptions } from '../../composables/useRecordOptions'
6
6
  import type { FieldComponentProps } from '../../utils/field-component'
7
- import type { FieldOf } from '../../../../core/server/utils/defineCollection'
7
+ import type { FieldOf } from '@michaelthielemann/kestrel-core'
8
8
 
9
9
  const props = defineProps<FieldComponentProps>()
10
10
  const model = defineModel<number | number[] | null>()
@@ -5,9 +5,7 @@ import FieldLayout from './Layout.vue'
5
5
  import { useRepeater } from '../../composables/useRepeater'
6
6
  import { useFieldA11y } from '../../utils/useFieldA11y'
7
7
  import type { FieldComponentProps } from '../../utils/field-component'
8
- import type { FieldDef, FieldOf } from '../../../../core/server/utils/defineCollection'
9
- import type { LayoutNode } from '../../../../core/server/utils/field-layout'
10
-
8
+ import type { FieldDef, FieldOf, LayoutNode } from '@michaelthielemann/kestrel-core'
11
9
  const props = defineProps<FieldComponentProps>()
12
10
  const model = defineModel<Record<string, unknown>[] | null>()
13
11
 
@@ -239,7 +237,7 @@ function insertRowAt(at: number) {
239
237
  </template>
240
238
 
241
239
  <style lang="scss">
242
- @use '../../assets/scss/mixins' as *;
240
+ @use '../../assets/scss/mixins';
243
241
 
244
242
  .ui-repeater {
245
243
  display: flex;
@@ -294,7 +292,7 @@ function insertRowAt(at: number) {
294
292
  }
295
293
 
296
294
  &__insert {
297
- @include focus-ring;
295
+ @include mixins.focus-ring;
298
296
  position: relative;
299
297
  z-index: 1;
300
298
  display: flex;
@@ -388,7 +386,7 @@ function insertRowAt(at: number) {
388
386
  &__move,
389
387
  &__duplicate,
390
388
  &__remove {
391
- @include focus-ring;
389
+ @include mixins.focus-ring;
392
390
  display: flex;
393
391
  align-items: center;
394
392
  justify-content: center;
@@ -428,7 +426,7 @@ function insertRowAt(at: number) {
428
426
  }
429
427
 
430
428
  &__add {
431
- @include focus-ring;
429
+ @include mixins.focus-ring;
432
430
  align-self: flex-start;
433
431
  border: 1px solid var(--color-border);
434
432
  background: var(--color-surface);
@@ -450,7 +448,7 @@ function insertRowAt(at: number) {
450
448
  }
451
449
 
452
450
  &__live {
453
- @include sr-only;
451
+ @include mixins.sr-only;
454
452
  }
455
453
 
456
454
  @media (prefers-reduced-motion: reduce) {
@@ -1,9 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
- import { slugify } from '../../../../core/app/utils/slugify'
3
+ import { slugify } from '@michaelthielemann/kestrel-core/client'
4
+ import type { FieldOf } from '@michaelthielemann/kestrel-core'
4
5
  import type { FieldComponentProps } from '../../utils/field-component'
5
- import type { FieldOf } from '../../../../core/server/utils/defineCollection'
6
-
7
6
  // Editor widget for the `slug` field type. Shows the (display-only) `prefix` before an editable slug input,
8
7
  // and normalizes the value to a real slug on blur. Leaving it blank is fine — the server auto-generates it
9
8
  // from `options.from` (e.g. the title) on save. `options.from`/`prefix` come from the field def.
@@ -3,7 +3,7 @@ import { computed } from 'vue'
3
3
  import UiField from '../ui/Field.vue'
4
4
  import UiTextInput from '../ui/TextInput.vue'
5
5
  import UiTextarea from '../ui/Textarea.vue'
6
- import { fieldConstraints } from '../../../../fields/app/utils/field-meta'
6
+ import { fieldConstraints } from '@michaelthielemann/kestrel-fields/client'
7
7
  import type { FieldComponentProps } from '../../utils/field-component'
8
8
 
9
9
  const props = defineProps<FieldComponentProps>()
@@ -29,10 +29,10 @@ withDefaults(
29
29
  </template>
30
30
 
31
31
  <style lang="scss">
32
- @use '../../assets/scss/mixins' as *;
32
+ @use '../../assets/scss/mixins';
33
33
 
34
34
  .ui-button {
35
- @include focus-ring;
35
+ @include mixins.focus-ring;
36
36
  display: inline-flex;
37
37
  align-items: center;
38
38
  justify-content: center;
@@ -49,7 +49,7 @@ function onUpdate(v: unknown) {
49
49
  </template>
50
50
 
51
51
  <style lang="scss">
52
- @use '../../assets/scss/mixins' as *;
52
+ @use '../../assets/scss/mixins';
53
53
 
54
54
  .ui-btngroup {
55
55
  display: inline-flex;
@@ -62,7 +62,7 @@ function onUpdate(v: unknown) {
62
62
  width: fit-content;
63
63
 
64
64
  &__item {
65
- @include focus-ring;
65
+ @include mixins.focus-ring;
66
66
  display: inline-flex;
67
67
  align-items: center;
68
68
  gap: var(--space-1);
@@ -36,10 +36,10 @@ watch(() => props.indeterminate, syncIndeterminate)
36
36
  </template>
37
37
 
38
38
  <style lang="scss">
39
- @use '../../assets/scss/mixins' as *;
39
+ @use '../../assets/scss/mixins';
40
40
 
41
41
  .ui-checkbox {
42
- @include focus-ring;
42
+ @include mixins.focus-ring;
43
43
  width: 1.25rem;
44
44
  height: 1.25rem;
45
45
  accent-color: var(--color-primary);
@@ -197,7 +197,7 @@ function moveChip(from: number, to: number) {
197
197
  </template>
198
198
 
199
199
  <style lang="scss">
200
- @use '../../assets/scss/mixins' as *;
200
+ @use '../../assets/scss/mixins';
201
201
 
202
202
  .ui-combobox {
203
203
  &__anchor {
@@ -232,7 +232,7 @@ function moveChip(from: number, to: number) {
232
232
  font-size: var(--text-sm);
233
233
  }
234
234
  &__chip-remove {
235
- @include focus-ring;
235
+ @include mixins.focus-ring;
236
236
  display: inline-flex;
237
237
  align-items: center;
238
238
  justify-content: center;
@@ -246,7 +246,7 @@ function moveChip(from: number, to: number) {
246
246
  line-height: 1;
247
247
  }
248
248
  &__chip-move {
249
- @include focus-ring;
249
+ @include mixins.focus-ring;
250
250
  display: inline-flex;
251
251
  align-items: center;
252
252
  justify-content: center;
@@ -269,7 +269,7 @@ function moveChip(from: number, to: number) {
269
269
 
270
270
  &:disabled { cursor: default; } // stays collapsed (the reveal selector excludes :disabled)
271
271
  }
272
- &__live { @include sr-only; }
272
+ &__live { @include mixins.sr-only; }
273
273
  &__chip {
274
274
  &:hover,
275
275
  &:focus-within {
@@ -302,7 +302,7 @@ function moveChip(from: number, to: number) {
302
302
  padding: var(--space-1) 0;
303
303
  }
304
304
  &__icon {
305
- @include focus-ring;
305
+ @include mixins.focus-ring;
306
306
  border: 0;
307
307
  background: transparent;
308
308
  cursor: pointer;
@@ -87,6 +87,6 @@ const value = computed<DateValue | undefined>({
87
87
 
88
88
  <style lang="scss">
89
89
  // Shared, global (non-scoped) datepicker chrome — the calendar is teleported to <body>.
90
- @use '../../assets/scss/datepicker' as *;
91
- @include ui-datepicker;
90
+ @use '../../assets/scss/datepicker';
91
+ @include datepicker.ui-datepicker;
92
92
  </style>
@@ -95,6 +95,6 @@ const value = computed<{ start: DateValue | undefined; end: DateValue | undefine
95
95
  // Shares the global (non-scoped) datepicker chrome — the calendar is teleported to <body>, so a
96
96
  // `scoped` block would silently drop these styles. Included explicitly rather than borrowing them
97
97
  // from DatePicker being present on the page.
98
- @use '../../assets/scss/datepicker' as *;
99
- @include ui-datepicker;
98
+ @use '../../assets/scss/datepicker';
99
+ @include datepicker.ui-datepicker;
100
100
  </style>
@@ -90,17 +90,17 @@ function onKeydown(e: KeyboardEvent) {
90
90
  </template>
91
91
 
92
92
  <style lang="scss">
93
- @use '../../assets/scss/mixins' as *;
93
+ @use '../../assets/scss/mixins';
94
94
 
95
95
  .ui-number {
96
- @include input-base;
96
+ @include mixins.input-base;
97
97
  width: 100%;
98
98
 
99
99
  &::placeholder {
100
100
  color: var(--color-text-muted);
101
101
  }
102
102
  &--slim {
103
- @include input-slim;
103
+ @include mixins.input-slim;
104
104
  }
105
105
  }
106
106
 
@@ -166,7 +166,7 @@ function onKeydown(e: KeyboardEvent) {
166
166
  // separate rule (not nested) so `.ui-number-wrap--slim .ui-number-wrap__input` outranks the base element rule.
167
167
  .ui-number-wrap--slim {
168
168
  .ui-number-wrap__input {
169
- @include input-slim;
169
+ @include mixins.input-slim;
170
170
  }
171
171
  .ui-number-wrap__suffix {
172
172
  padding-inline: var(--space-2);
@@ -8,7 +8,7 @@ import { Superscript } from '@tiptap/extension-superscript'
8
8
  import { TextAlign } from '@tiptap/extension-text-align'
9
9
  import RichtextToolbar from './RichtextToolbar.vue'
10
10
  import { RichtextSpanClass, RichtextBlockClass } from '../../utils/richtext-preserve-class'
11
- import { RICHTEXT_LINK_SCHEME } from '../../../../fields/app/utils/richtext-links'
11
+ import { RICHTEXT_LINK_SCHEME } from '@michaelthielemann/kestrel-core/client'
12
12
 
13
13
  const props = withDefaults(
14
14
  defineProps<{
@@ -7,7 +7,7 @@ import UiSelect from './Select.vue'
7
7
  import UiTextInput from './TextInput.vue'
8
8
  import LinkInternalPicker from '../field/LinkInternalPicker.vue'
9
9
  import type { IconName } from '../../utils/icons'
10
- import { richtextLinkHref, parseRichtextLinkHref } from '../../../../fields/app/utils/richtext-links'
10
+ import { richtextLinkHref, parseRichtextLinkHref } from '@michaelthielemann/kestrel-core/client'
11
11
 
12
12
  const props = defineProps<{ editor: Editor | undefined; locale?: string; disabled?: boolean }>()
13
13
 
@@ -236,8 +236,6 @@ function applyInternalLink() {
236
236
  align-items: center;
237
237
  }
238
238
 
239
- // Active toggle: a calm neutral fill with the accent carried only by the icon — matching the
240
- // calmed active-nav treatment, instead of a solid-indigo button per active mark.
241
239
  &__group .ui-button.rt-on {
242
240
  background: var(--color-active, var(--color-surface-2));
243
241
  color: var(--color-text);
@@ -27,7 +27,7 @@ const model = defineModel<string | null>()
27
27
  </template>
28
28
 
29
29
  <style lang="scss">
30
- @use '../../assets/scss/mixins' as *;
30
+ @use '../../assets/scss/mixins';
31
31
 
32
32
  .ui-select {
33
33
  position: relative;
@@ -35,7 +35,7 @@ const model = defineModel<string | null>()
35
35
  width: 100%;
36
36
  }
37
37
  .ui-select__field {
38
- @include input-base;
38
+ @include mixins.input-base;
39
39
  width: 100%;
40
40
  appearance: none;
41
41
  // leave room for the overlaid chevron so long values don't run under it
@@ -55,7 +55,7 @@ const inputType = computed(() => (revealable.value && revealed.value ? 'text' :
55
55
  </template>
56
56
 
57
57
  <style lang="scss">
58
- @use '../../assets/scss/mixins' as *;
58
+ @use '../../assets/scss/mixins';
59
59
 
60
60
  .ui-input-wrap {
61
61
  position: relative;
@@ -64,7 +64,7 @@ const inputType = computed(() => (revealable.value && revealed.value ? 'text' :
64
64
  }
65
65
 
66
66
  .ui-input {
67
- @include input-base;
67
+ @include mixins.input-base;
68
68
  width: 100%;
69
69
 
70
70
  &::placeholder {
@@ -72,7 +72,7 @@ const inputType = computed(() => (revealable.value && revealed.value ? 'text' :
72
72
  }
73
73
 
74
74
  &--slim {
75
- @include input-slim;
75
+ @include mixins.input-slim;
76
76
  }
77
77
 
78
78
  // Leave room for the inset reveal button so the value never sits under it. Declared after `--slim` so a
@@ -83,7 +83,7 @@ const inputType = computed(() => (revealable.value && revealed.value ? 'text' :
83
83
  }
84
84
 
85
85
  .ui-input__reveal {
86
- @include focus-ring;
86
+ @include mixins.focus-ring;
87
87
  position: absolute;
88
88
  inset-block: 1px;
89
89
  inset-inline-end: 1px;
@@ -23,10 +23,10 @@ const model = defineModel<string | null>()
23
23
  </template>
24
24
 
25
25
  <style lang="scss">
26
- @use '../../assets/scss/mixins' as *;
26
+ @use '../../assets/scss/mixins';
27
27
 
28
28
  .ui-textarea {
29
- @include input-base;
29
+ @include mixins.input-base;
30
30
  width: 100%;
31
31
  font-family: inherit;
32
32
  line-height: var(--leading-normal);
@@ -10,9 +10,9 @@ const model = defineModel<string | null>()
10
10
  </template>
11
11
 
12
12
  <style lang="scss">
13
- @use '../../assets/scss/mixins' as *;
13
+ @use '../../assets/scss/mixins';
14
14
 
15
15
  .ui-time {
16
- @include input-base;
16
+ @include mixins.input-base;
17
17
  }
18
18
  </style>
@@ -39,9 +39,9 @@ const { t } = useT()
39
39
  </template>
40
40
 
41
41
  <style lang="scss">
42
- @use '../../assets/scss/mixins' as *;
42
+ @use '../../assets/scss/mixins';
43
43
 
44
- .ui-toasts__live { @include sr-only; }
44
+ .ui-toasts__live { @include mixins.sr-only; }
45
45
 
46
46
  .ui-toasts {
47
47
  position: fixed;
@@ -76,7 +76,7 @@ const { t } = useT()
76
76
  &__msg { flex: 1 1 auto; min-width: 0; }
77
77
 
78
78
  &__close {
79
- @include focus-ring;
79
+ @include mixins.focus-ring;
80
80
  flex-shrink: 0;
81
81
  display: inline-flex;
82
82
  padding: var(--space-1);
@@ -1,7 +1,7 @@
1
1
  import { ref, computed, watch } from 'vue'
2
2
  import type { Ref } from 'vue'
3
3
  import { useEchoGuard } from './useEchoGuard'
4
- import type { LinkValue, LinkType } from '../../../core/server/utils/defineCollection'
4
+ import type { LinkValue, LinkType } from '@michaelthielemann/kestrel-core'
5
5
 
6
6
  export function useLinkField(
7
7
  model: Ref<LinkValue | null | undefined>,
@@ -1,6 +1,6 @@
1
1
  import { ref } from 'vue'
2
2
  import type { Ref } from 'vue'
3
- import type { FieldDef } from '../../../core/server/utils/defineCollection'
3
+ import type { FieldDef } from '@michaelthielemann/kestrel-core'
4
4
  import { reorder } from '../utils/reorder'
5
5
  import { useEchoGuard } from './useEchoGuard'
6
6
 
@@ -23,11 +23,9 @@ export const de: Catalog = {
23
23
  'a11y.rowActions': 'Aktionen',
24
24
  'lang.label': 'Sprache',
25
25
 
26
- // account menu (rail foot)
27
26
  'account.label': 'Konto',
28
27
  'account.name': 'Admin',
29
28
 
30
- // text input (password reveal)
31
29
  'input.showPassword': 'Passwort anzeigen',
32
30
  'input.hidePassword': 'Passwort verbergen',
33
31
 
@@ -53,6 +51,8 @@ export const de: Catalog = {
53
51
  'editor.fixBlockContent': 'Einige Block-Inhalte sind ungültig. Prüfe die markierten Block-Felder.',
54
52
  'editor.saveConflict': 'Das steht im Konflikt mit einem vorhandenen Datensatz.',
55
53
  'editor.saveFailed': 'Speichern fehlgeschlagen.',
54
+ 'editor.quarantined.title': 'Datensatz in Quarantäne',
55
+ 'editor.quarantined.desc': 'Dieser Datensatz entspricht nicht mehr dem Schema seiner Collection und wird schreibgeschützt angezeigt. Er kann erst bearbeitet oder gespeichert werden, wenn die zugrunde liegenden Daten korrigiert sind.',
56
56
  'toast.saved': 'Gespeichert',
57
57
  'toast.deleted': 'Gelöscht',
58
58
  'toast.duplicated': 'Dupliziert',
@@ -104,7 +104,9 @@ export const de: Catalog = {
104
104
  'list.result': '{total} Ergebnis',
105
105
  'list.results': '{total} Ergebnisse',
106
106
  'list.loadError': 'Diese Collection konnte nicht geladen werden.',
107
- // Zeilenauswahl + Sammelaktionen
107
+ 'list.quarantined': 'In Quarantäne',
108
+ 'list.quarantinedOne': '{n} Datensatz in Quarantäne',
109
+ 'list.quarantinedCount': '{n} Datensätze in Quarantäne',
108
110
  'list.selectAll': 'Alle Zeilen auf dieser Seite auswählen',
109
111
  'list.selectRow': '{name} auswählen',
110
112
  'list.selected': '{n} ausgewählt',
@@ -120,6 +122,8 @@ export const de: Catalog = {
120
122
  'list.deleteRefsUnverified': 'Verweise konnten nicht geprüft werden — einige Datensätze könnten mit einem defekten Verweis zurückbleiben. Mit Vorsicht fortfahren.',
121
123
  'list.opFailed': 'Die Aktion konnte nicht abgeschlossen werden.',
122
124
  'list.refCheckFailed': 'Verweise konnten nicht geprüft werden.',
125
+ 'list.actionConfirm': '"{name}" für {n} Datensatz/-sätze ausführen?',
126
+ 'list.actionDone': '{name} ausgeführt',
123
127
 
124
128
  'preview.ariaLabel': 'Live-Vorschau',
125
129
  'preview.label': 'Vorschau',
@@ -222,7 +226,6 @@ export const de: Catalog = {
222
226
  'blocks.fieldsLabel': 'Block-Felder',
223
227
  'blocks.treeLabel': 'Block-Struktur',
224
228
 
225
- // dead references (derived warnings)
226
229
  'deadRefs.listWarning': 'Verweist auf einen gelöschten oder unveröffentlichten Datensatz',
227
230
  'deadRefs.blockBadge': 'Defekter Verweis',
228
231
  'deadRefs.fieldNote': 'Verweist auf einen gelöschten oder unveröffentlichten Datensatz.',
@@ -247,7 +250,6 @@ export const de: Catalog = {
247
250
  'media.title': 'Medien',
248
251
  'media.picker.titleSingle': 'Datei auswählen',
249
252
  'media.picker.titleMultiple': 'Medien auswählen',
250
- // media — right-click context menu
251
253
  'media.menu.cut': 'Ausschneiden',
252
254
  'media.menu.copy': 'Kopieren',
253
255
  'media.menu.paste': 'Einfügen',
@@ -327,7 +329,6 @@ export const de: Catalog = {
327
329
  'mediaViewer.aiNote': 'Hinweis zur Kennzeichnung',
328
330
  'mediaViewer.aiNoteHint': 'Freitext, z. B. das verwendete Werkzeug. Beim Upload gefundene KI-Signale werden vorausgefüllt — nur Indizien, kein geprüfter Nachweis.',
329
331
 
330
- // richtext toolbar
331
332
  'richtext.bold': 'Fett',
332
333
  'richtext.italic': 'Kursiv',
333
334
  'richtext.underline': 'Unterstrichen',
@@ -362,7 +363,6 @@ export const de: Catalog = {
362
363
  'richtext.linkUrl': 'Link-URL',
363
364
  'richtext.apply': 'Übernehmen',
364
365
 
365
- // media toolbar
366
366
  'mediaToolbar.searchPlaceholder': 'Suchen…',
367
367
  'mediaToolbar.searchAriaLabel': 'Medien durchsuchen',
368
368
  'mediaToolbar.viewGrid': 'Raster',