@michaelthielemann/kestrel 4.1.3 → 5.0.2

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 (342) hide show
  1. package/README.md +26 -201
  2. package/dist/boot.d.ts +45 -0
  3. package/dist/boot.js +284 -0
  4. package/dist/cast.d.ts +3 -0
  5. package/dist/cast.js +6 -0
  6. package/dist/catalogue.d.ts +11 -0
  7. package/dist/catalogue.js +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +55 -0
  10. package/dist/context.d.ts +62 -0
  11. package/dist/context.js +61 -0
  12. package/dist/dataflow.d.ts +3 -0
  13. package/dist/dataflow.js +65 -0
  14. package/dist/defineConfig.d.ts +162 -0
  15. package/dist/defineConfig.js +42 -0
  16. package/dist/defineContract.d.ts +12 -0
  17. package/dist/defineContract.js +16 -0
  18. package/dist/defineModule.d.ts +75 -0
  19. package/dist/defineModule.js +9 -0
  20. package/dist/definePipeline.d.ts +7 -0
  21. package/dist/definePipeline.js +11 -0
  22. package/dist/errors.d.ts +26 -0
  23. package/dist/errors.js +48 -0
  24. package/dist/index.d.ts +19 -0
  25. package/dist/index.js +14 -0
  26. package/dist/load.d.ts +7 -0
  27. package/dist/load.js +79 -0
  28. package/dist/logger.d.ts +18 -0
  29. package/dist/logger.js +36 -0
  30. package/dist/registry.d.ts +14 -0
  31. package/dist/registry.js +87 -0
  32. package/dist/result.d.ts +19 -0
  33. package/dist/result.js +19 -0
  34. package/dist/runner.d.ts +29 -0
  35. package/dist/runner.js +133 -0
  36. package/dist/schema.d.ts +7 -0
  37. package/dist/schema.js +142 -0
  38. package/dist/sort.d.ts +2 -0
  39. package/dist/sort.js +50 -0
  40. package/dist/testing/runPipeline.d.ts +9 -0
  41. package/dist/testing/runPipeline.js +22 -0
  42. package/dist/triggers/allowlist.d.ts +9 -0
  43. package/dist/triggers/allowlist.js +49 -0
  44. package/dist/triggers/cron.d.ts +10 -0
  45. package/dist/triggers/cron.js +44 -0
  46. package/dist/triggers/cronMatch.d.ts +10 -0
  47. package/dist/triggers/cronMatch.js +68 -0
  48. package/dist/triggers/http.d.ts +59 -0
  49. package/dist/triggers/http.js +300 -0
  50. package/package.json +25 -151
  51. package/NOTICE +0 -12
  52. package/kestrel.config.ts +0 -28
  53. package/layers/access/nuxt.config.ts +0 -5
  54. package/layers/access/server/middleware/00.ip-allowlist.ts +0 -52
  55. package/layers/access/server/middleware/access-guard.ts +0 -32
  56. package/layers/access/server/types.d.ts +0 -7
  57. package/layers/admin/app/components/AdminAccount.vue +0 -212
  58. package/layers/admin/app/components/AdminNav.vue +0 -44
  59. package/layers/admin/app/components/BlockFields.vue +0 -67
  60. package/layers/admin/app/components/BlockPreview.vue +0 -413
  61. package/layers/admin/app/components/BlockTree.vue +0 -433
  62. package/layers/admin/app/components/BlocksBody.vue +0 -163
  63. package/layers/admin/app/components/CollectionDeleteDialog.vue +0 -38
  64. package/layers/admin/app/components/CollectionEditor.vue +0 -324
  65. package/layers/admin/app/components/CollectionList.vue +0 -429
  66. package/layers/admin/app/components/CollectionListBulkBar.vue +0 -62
  67. package/layers/admin/app/components/CollectionListColumnsPanel.vue +0 -37
  68. package/layers/admin/app/components/CollectionListFilterPanel.vue +0 -145
  69. package/layers/admin/app/components/CollectionListPager.vue +0 -70
  70. package/layers/admin/app/components/CollectionListTable.vue +0 -347
  71. package/layers/admin/app/components/EditorStatus.vue +0 -211
  72. package/layers/admin/app/components/EditorUnsupported.vue +0 -28
  73. package/layers/admin/app/components/FieldsBody.vue +0 -23
  74. package/layers/admin/app/components/LocaleBar.vue +0 -107
  75. package/layers/admin/app/components/PageFields.vue +0 -129
  76. package/layers/admin/app/components/PageFieldsPane.vue +0 -39
  77. package/layers/admin/app/components/SeoFields.vue +0 -210
  78. package/layers/admin/app/components/SingletonEditor.vue +0 -93
  79. package/layers/admin/app/composables/useAuth.ts +0 -68
  80. package/layers/admin/app/composables/useBlockTree.ts +0 -95
  81. package/layers/admin/app/composables/useBlocks.ts +0 -7
  82. package/layers/admin/app/composables/useCachedResource.ts +0 -35
  83. package/layers/admin/app/composables/useCollectionOps.ts +0 -102
  84. package/layers/admin/app/composables/useCollections.ts +0 -6
  85. package/layers/admin/app/composables/useContentLocales.ts +0 -23
  86. package/layers/admin/app/composables/useEditForm.ts +0 -402
  87. package/layers/admin/app/composables/useLinkResolver.ts +0 -40
  88. package/layers/admin/app/composables/useListBatchActions.ts +0 -78
  89. package/layers/admin/app/composables/useListColumns.ts +0 -53
  90. package/layers/admin/app/composables/useListFilterDraft.ts +0 -129
  91. package/layers/admin/app/composables/useListRows.ts +0 -75
  92. package/layers/admin/app/composables/useListSelection.ts +0 -28
  93. package/layers/admin/app/composables/useListUrlState.ts +0 -53
  94. package/layers/admin/app/composables/usePendingPublishPoll.ts +0 -15
  95. package/layers/admin/app/composables/usePreviewViewport.ts +0 -30
  96. package/layers/admin/app/composables/usePublishStatus.ts +0 -107
  97. package/layers/admin/app/composables/useRailCollapsed.ts +0 -14
  98. package/layers/admin/app/composables/useTheme.ts +0 -36
  99. package/layers/admin/app/composables/useToolbarPanel.ts +0 -38
  100. package/layers/admin/app/composables/useUnsavedGuard.ts +0 -22
  101. package/layers/admin/app/layouts/admin.vue +0 -266
  102. package/layers/admin/app/middleware/admin-auth.ts +0 -4
  103. package/layers/admin/app/pages/admin/[collection]/[id].vue +0 -203
  104. package/layers/admin/app/pages/admin/[collection]/index.vue +0 -62
  105. package/layers/admin/app/pages/admin/index.vue +0 -118
  106. package/layers/admin/app/pages/admin/login.vue +0 -68
  107. package/layers/admin/app/pages/admin/media.vue +0 -28
  108. package/layers/admin/app/pages/admin/references.vue +0 -146
  109. package/layers/admin/app/plugins/admin-style-guard.client.ts +0 -76
  110. package/layers/admin/app/plugins/reauth.client.ts +0 -20
  111. package/layers/admin/app/utils/admin-style-guard.ts +0 -20
  112. package/layers/admin/app/utils/block-tree.ts +0 -214
  113. package/layers/admin/app/utils/collection-ops.ts +0 -69
  114. package/layers/admin/app/utils/dead-refs.ts +0 -30
  115. package/layers/admin/app/utils/edit-form.ts +0 -267
  116. package/layers/admin/app/utils/editor-expose.ts +0 -36
  117. package/layers/admin/app/utils/editor-form-context.ts +0 -82
  118. package/layers/admin/app/utils/editor-registry.ts +0 -20
  119. package/layers/admin/app/utils/field-keys.ts +0 -11
  120. package/layers/admin/app/utils/list-cell.ts +0 -46
  121. package/layers/admin/app/utils/list-columns.ts +0 -142
  122. package/layers/admin/app/utils/list-query.ts +0 -96
  123. package/layers/admin/app/utils/nav-active.ts +0 -9
  124. package/layers/admin/app/utils/populate-blocks.ts +0 -288
  125. package/layers/admin/app/utils/preview-channel.ts +0 -62
  126. package/layers/admin/app/utils/preview-viewport.ts +0 -80
  127. package/layers/admin/app/utils/reauth.ts +0 -67
  128. package/layers/admin/app/utils/record-title.ts +0 -15
  129. package/layers/admin/app/utils/register-builtin-editors.ts +0 -9
  130. package/layers/admin/app/utils/safe-redirect.ts +0 -12
  131. package/layers/admin/nuxt.config.ts +0 -20
  132. package/layers/auth/nuxt.config.ts +0 -1
  133. package/layers/auth/server/plugins/01.register-auth-pipelines.ts +0 -7
  134. package/layers/collections/nuxt.config.ts +0 -1
  135. package/layers/collections/server/plugins/01.register.ts +0 -29
  136. package/layers/collections/server/plugins/02.register-relation-populate.ts +0 -18
  137. package/layers/core/app/composables/layouts.ts +0 -5
  138. package/layers/core/app/utils/field-factories.ts +0 -129
  139. package/layers/core/app/utils/layouts.ts +0 -38
  140. package/layers/core/modules/auto-discovery/extract-block.ts +0 -179
  141. package/layers/core/modules/auto-discovery/index.ts +0 -112
  142. package/layers/core/modules/auto-discovery/package-registry.ts +0 -19
  143. package/layers/core/modules/auto-discovery/scan.ts +0 -170
  144. package/layers/core/modules/auto-discovery/virtual.d.ts +0 -20
  145. package/layers/core/modules/component-namespace/index.ts +0 -44
  146. package/layers/core/modules/component-namespace/shared.ts +0 -24
  147. package/layers/core/modules/kestrel/app-shell.ts +0 -55
  148. package/layers/core/modules/kestrel/auto-imports.ts +0 -44
  149. package/layers/core/modules/kestrel/index.ts +0 -144
  150. package/layers/core/modules/plugin-order/index.ts +0 -69
  151. package/layers/core/modules/plugin-order/plugin-order.ts +0 -273
  152. package/layers/core/nuxt.config.ts +0 -10
  153. package/layers/core/server/api/[...path].ts +0 -98
  154. package/layers/core/server/plugins/00.config.ts +0 -12
  155. package/layers/core/server/plugins/00.migrate.ts +0 -12
  156. package/layers/core/server/plugins/01.register-introspection-pipeline.ts +0 -5
  157. package/layers/core/server/plugins/01.register-openapi-pipeline.ts +0 -5
  158. package/layers/core/server/plugins/01.register-tooling-pipelines.ts +0 -5
  159. package/layers/core/server/plugins/02.schema-sync.ts +0 -58
  160. package/layers/core/server/plugins/04.outbox-worker.ts +0 -31
  161. package/layers/core/server/plugins/05.reindex-refs.ts +0 -5
  162. package/layers/core/server/routes/__kestrel/dashboard.get.ts +0 -29
  163. package/layers/core/server/tasks/db/migrate-module.ts +0 -83
  164. package/layers/core/server/tasks/db/migrate-revisions.ts +0 -20
  165. package/layers/core/server/tasks/db/migrate.ts +0 -50
  166. package/layers/core/server/utils/server-config.ts +0 -81
  167. package/layers/fields/nuxt.config.ts +0 -1
  168. package/layers/fields/server/plugins/01.register-blocks-pipeline.ts +0 -7
  169. package/layers/fields/server/plugins/01.register-field-populate.ts +0 -11
  170. package/layers/media/app/components/KestrelImg.vue +0 -94
  171. package/layers/media/app/components/MediaConflictDialog.vue +0 -35
  172. package/layers/media/app/components/MediaDeleteDialog.vue +0 -52
  173. package/layers/media/app/components/MediaGrid.vue +0 -113
  174. package/layers/media/app/components/MediaImage.vue +0 -70
  175. package/layers/media/app/components/MediaLibrary.vue +0 -456
  176. package/layers/media/app/components/MediaNewFolderDialog.vue +0 -32
  177. package/layers/media/app/components/MediaPathBar.vue +0 -163
  178. package/layers/media/app/components/MediaPicker.vue +0 -19
  179. package/layers/media/app/components/MediaRenameDialog.vue +0 -32
  180. package/layers/media/app/components/MediaTable.vue +0 -75
  181. package/layers/media/app/components/MediaThumb.vue +0 -25
  182. package/layers/media/app/components/MediaToolbar.vue +0 -90
  183. package/layers/media/app/components/MediaUploadDialog.vue +0 -87
  184. package/layers/media/app/components/MediaViewer.vue +0 -137
  185. package/layers/media/app/components/field/Media.vue +0 -263
  186. package/layers/media/app/composables/useBlurUp.ts +0 -55
  187. package/layers/media/app/composables/useMediaClipboard.ts +0 -14
  188. package/layers/media/app/composables/useMediaContextMenu.ts +0 -56
  189. package/layers/media/app/composables/useMediaDnd.ts +0 -56
  190. package/layers/media/app/composables/useMediaLibrary.ts +0 -152
  191. package/layers/media/app/composables/useMediaOps.ts +0 -50
  192. package/layers/media/app/composables/useMediaRelocate.ts +0 -39
  193. package/layers/media/app/composables/useMediaResolver.ts +0 -50
  194. package/layers/media/app/composables/useMediaUpload.ts +0 -93
  195. package/layers/media/app/composables/useMediaVariant.ts +0 -34
  196. package/layers/media/app/plugins/field-media.client.ts +0 -6
  197. package/layers/media/app/utils/ai-disclosure.ts +0 -19
  198. package/layers/media/app/utils/dnd.ts +0 -69
  199. package/layers/media/app/utils/library.ts +0 -81
  200. package/layers/media/app/utils/ops.ts +0 -60
  201. package/layers/media/app/utils/picture-model.ts +0 -125
  202. package/layers/media/app/utils/thumbhash.ts +0 -19
  203. package/layers/media/app/utils/variant-name.ts +0 -20
  204. package/layers/media/nuxt.config.ts +0 -7
  205. package/layers/media/server/middleware/ondemand-variants.ts +0 -38
  206. package/layers/media/server/plugins/01.register-media-pipelines.ts +0 -7
  207. package/layers/media/server/plugins/02.register-media.ts +0 -22
  208. package/layers/media/server/plugins/04.variant-capture.ts +0 -27
  209. package/layers/media/server/plugins/05.media-cleanup.ts +0 -5
  210. package/layers/media/server/tasks/media/backfill.ts +0 -17
  211. package/layers/public/app/app.vue +0 -5
  212. package/layers/public/app/components/BlockRenderer.vue +0 -56
  213. package/layers/public/app/components/KestrelLink.vue +0 -26
  214. package/layers/public/app/components/KestrelPreviewBridge.vue +0 -133
  215. package/layers/public/app/composables/public-page.ts +0 -24
  216. package/layers/public/app/error.vue +0 -12
  217. package/layers/public/app/layouts/default.vue +0 -5
  218. package/layers/public/app/pages/[...slug].vue +0 -239
  219. package/layers/public/app/pages/__kestrel/preview.vue +0 -46
  220. package/layers/public/app/utils/block-edit-context.ts +0 -16
  221. package/layers/public/app/utils/json-ld.ts +0 -139
  222. package/layers/public/app/utils/link-href.ts +0 -46
  223. package/layers/public/app/utils/page-head.ts +0 -84
  224. package/layers/public/app/utils/page-layout.ts +0 -18
  225. package/layers/public/app/utils/preview-protocol.ts +0 -117
  226. package/layers/public/app/utils/public-route.ts +0 -46
  227. package/layers/public/app/utils/site-head.ts +0 -43
  228. package/layers/public/modules/deploy-output/index.ts +0 -80
  229. package/layers/public/modules/prerender-routes/discover.ts +0 -37
  230. package/layers/public/modules/prerender-routes/index.ts +0 -88
  231. package/layers/public/modules/prerender-routes/routes.ts +0 -16
  232. package/layers/public/modules/prune-media/index.ts +0 -48
  233. package/layers/public/modules/prune-media/prune-media.ts +0 -81
  234. package/layers/public/nuxt.config.ts +0 -14
  235. package/layers/public/server/delivery-live/serve.ts +0 -111
  236. package/layers/public/server/middleware/delivery-live-catchall.ts +0 -7
  237. package/layers/public/server/pipelines/index.ts +0 -23
  238. package/layers/public/server/plugins/00.ensure-snapshot-triggers.ts +0 -14
  239. package/layers/public/server/plugins/01.register-public-pipelines.ts +0 -7
  240. package/layers/public/server/plugins/02.register-links.ts +0 -16
  241. package/layers/public/server/plugins/03.redirects.ts +0 -55
  242. package/layers/public/server/plugins/05.plan-publish.ts +0 -5
  243. package/layers/public/server/plugins/zz.publish.ts +0 -127
  244. package/layers/public/server/routes/llms-full.txt.get.ts +0 -99
  245. package/layers/public/server/routes/llms.txt.get.ts +0 -66
  246. package/layers/public/server/routes/redirects.json.get.ts +0 -58
  247. package/layers/public/server/routes/robots.txt.get.ts +0 -11
  248. package/layers/public/server/routes/sitemap.xml.get.ts +0 -79
  249. package/layers/public/server/tasks/publish/run.ts +0 -32
  250. package/layers/public/server/utils/publish/render-live.ts +0 -26
  251. package/layers/ui/app/assets/scss/_base.scss +0 -16
  252. package/layers/ui/app/assets/scss/_datepicker.scss +0 -82
  253. package/layers/ui/app/assets/scss/_mixins.scss +0 -50
  254. package/layers/ui/app/assets/scss/_reset.scss +0 -65
  255. package/layers/ui/app/assets/scss/_tokens.scss +0 -183
  256. package/layers/ui/app/assets/scss/main.scss +0 -4
  257. package/layers/ui/app/components/field/Boolean.vue +0 -30
  258. package/layers/ui/app/components/field/Choice.vue +0 -73
  259. package/layers/ui/app/components/field/Datetime.vue +0 -98
  260. package/layers/ui/app/components/field/DeadRefNote.vue +0 -28
  261. package/layers/ui/app/components/field/Json.vue +0 -62
  262. package/layers/ui/app/components/field/Layout.vue +0 -138
  263. package/layers/ui/app/components/field/Link.vue +0 -112
  264. package/layers/ui/app/components/field/LinkInternalPicker.vue +0 -95
  265. package/layers/ui/app/components/field/Number.vue +0 -30
  266. package/layers/ui/app/components/field/Relation.vue +0 -49
  267. package/layers/ui/app/components/field/Renderer.vue +0 -30
  268. package/layers/ui/app/components/field/Repeater.vue +0 -460
  269. package/layers/ui/app/components/field/Richtext.vue +0 -26
  270. package/layers/ui/app/components/field/Slug.vue +0 -65
  271. package/layers/ui/app/components/field/Text.vue +0 -35
  272. package/layers/ui/app/components/field/Unsupported.vue +0 -34
  273. package/layers/ui/app/components/ui/Alert.vue +0 -50
  274. package/layers/ui/app/components/ui/Brand.vue +0 -22
  275. package/layers/ui/app/components/ui/Button.vue +0 -116
  276. package/layers/ui/app/components/ui/ButtonGroup.vue +0 -96
  277. package/layers/ui/app/components/ui/Checkbox.vue +0 -53
  278. package/layers/ui/app/components/ui/CheckboxGroup.vue +0 -51
  279. package/layers/ui/app/components/ui/Combobox.vue +0 -345
  280. package/layers/ui/app/components/ui/DatePicker.vue +0 -92
  281. package/layers/ui/app/components/ui/DateRangePicker.vue +0 -100
  282. package/layers/ui/app/components/ui/Dialog.vue +0 -107
  283. package/layers/ui/app/components/ui/EmptyState.vue +0 -46
  284. package/layers/ui/app/components/ui/Field.vue +0 -58
  285. package/layers/ui/app/components/ui/Fieldset.vue +0 -65
  286. package/layers/ui/app/components/ui/Icon.vue +0 -44
  287. package/layers/ui/app/components/ui/Menu.vue +0 -54
  288. package/layers/ui/app/components/ui/NumberInput.vue +0 -175
  289. package/layers/ui/app/components/ui/Richtext.vue +0 -141
  290. package/layers/ui/app/components/ui/RichtextToolbar.vue +0 -251
  291. package/layers/ui/app/components/ui/Select.vue +0 -52
  292. package/layers/ui/app/components/ui/TextInput.vue +0 -111
  293. package/layers/ui/app/components/ui/Textarea.vue +0 -39
  294. package/layers/ui/app/components/ui/TimeInput.vue +0 -18
  295. package/layers/ui/app/components/ui/Toasts.vue +0 -103
  296. package/layers/ui/app/components/ui/Tooltip.vue +0 -45
  297. package/layers/ui/app/composables/useAdminLang.ts +0 -8
  298. package/layers/ui/app/composables/useDragReorder.ts +0 -41
  299. package/layers/ui/app/composables/useEchoGuard.ts +0 -28
  300. package/layers/ui/app/composables/useLinkField.ts +0 -70
  301. package/layers/ui/app/composables/useRecordOptions.ts +0 -84
  302. package/layers/ui/app/composables/useRepeater.ts +0 -99
  303. package/layers/ui/app/composables/useT.ts +0 -32
  304. package/layers/ui/app/composables/useToast.ts +0 -46
  305. package/layers/ui/app/i18n/de.ts +0 -432
  306. package/layers/ui/app/i18n/en.ts +0 -434
  307. package/layers/ui/app/utils/boolean-field.ts +0 -10
  308. package/layers/ui/app/utils/field-component.ts +0 -22
  309. package/layers/ui/app/utils/field-empty.ts +0 -21
  310. package/layers/ui/app/utils/field-registry.ts +0 -36
  311. package/layers/ui/app/utils/humanize.ts +0 -14
  312. package/layers/ui/app/utils/icons.ts +0 -143
  313. package/layers/ui/app/utils/iso-date.ts +0 -18
  314. package/layers/ui/app/utils/localized.ts +0 -13
  315. package/layers/ui/app/utils/record-context.ts +0 -15
  316. package/layers/ui/app/utils/reorder.ts +0 -8
  317. package/layers/ui/app/utils/richtext-preserve-class.ts +0 -31
  318. package/layers/ui/app/utils/useFieldA11y.ts +0 -40
  319. package/layers/ui/nuxt.config.ts +0 -9
  320. package/nuxt.config.ts +0 -108
  321. package/scripts/consumer-template-ci.mjs +0 -160
  322. package/scripts/copy-create-payload.mjs +0 -49
  323. package/scripts/coverage-all.mjs +0 -163
  324. package/scripts/dashboard.mjs +0 -89
  325. package/scripts/hash-password.mjs +0 -29
  326. package/scripts/kestrel.mjs +0 -240
  327. package/scripts/lib/cli.mjs +0 -129
  328. package/scripts/lib/coverage-model.mjs +0 -51
  329. package/scripts/lib/password.mjs +0 -19
  330. package/scripts/lib/scaffold.mjs +0 -205
  331. package/scripts/port-graph.mjs +0 -350
  332. package/scripts/typecheck.mjs +0 -62
  333. package/templates/starter/README.md +0 -65
  334. package/templates/starter/_env.example +0 -17
  335. package/templates/starter/_gitignore +0 -26
  336. package/templates/starter/_package.json +0 -22
  337. package/templates/starter/app/app.vue +0 -7
  338. package/templates/starter/app/blocks/Prose.vue +0 -13
  339. package/templates/starter/app/layouts/default.vue +0 -6
  340. package/templates/starter/nuxt.config.ts +0 -23
  341. package/templates/starter/pnpm-workspace.yaml +0 -8
  342. package/templates/starter/tsconfig.json +0 -3
@@ -1,65 +0,0 @@
1
- *,
2
- *::before,
3
- *::after {
4
- box-sizing: border-box;
5
- }
6
-
7
- * {
8
- margin: 0;
9
- }
10
-
11
- html {
12
- -webkit-text-size-adjust: 100%;
13
- }
14
-
15
- body {
16
- min-height: 100svh;
17
- line-height: var(--leading-normal);
18
- -webkit-font-smoothing: antialiased;
19
- }
20
-
21
- img,
22
- picture,
23
- svg,
24
- video,
25
- canvas {
26
- display: block;
27
- max-width: 100%;
28
- }
29
-
30
- input,
31
- button,
32
- textarea,
33
- select {
34
- font: inherit;
35
- color: inherit;
36
- }
37
-
38
- button {
39
- cursor: pointer;
40
- }
41
-
42
- button:disabled {
43
- cursor: not-allowed;
44
- }
45
-
46
- p,
47
- h1,
48
- h2,
49
- h3,
50
- h4,
51
- h5,
52
- h6 {
53
- overflow-wrap: break-word;
54
- }
55
-
56
- @media (prefers-reduced-motion: reduce) {
57
- *,
58
- *::before,
59
- *::after {
60
- animation-duration: 0.01ms !important;
61
- animation-iteration-count: 1 !important;
62
- transition-duration: 0.01ms !important;
63
- scroll-behavior: auto !important;
64
- }
65
- }
@@ -1,183 +0,0 @@
1
- /* Design tokens.
2
- *
3
- * `:root` holds the SHARED structural scale (space / type / radius / shadow / z / motion / layout)
4
- * plus the PUBLIC SSG site's colour defaults — left untouched so the generated site is unaffected.
5
- *
6
- * The admin shell opts into a richer, neutral "depth" palette (and dark mode) by setting
7
- * `data-theme="light|dark"` on <html> (see `useTheme` + the admin layout). Because the attribute
8
- * lives on <html>, teleported widgets (dialogs, comboboxes) resolve the same tokens.
9
- */
10
- :root {
11
- /* ---- color: public defaults (do not retune — drives the SSG site) ---- */
12
- --color-bg: #ffffff;
13
- --color-surface: #ffffff;
14
- --color-surface-2: #f4f4f5;
15
- --color-text: #1a1a1a;
16
- --color-text-muted: #5c5c5c;
17
- --color-text-subtle: #a1a1aa;
18
- --color-border: #d4d4d8;
19
- --color-border-strong: #b4b4bb;
20
- --color-primary: #2563eb;
21
- --color-primary-hover: #1d4ed8;
22
- --color-on-primary: #ffffff;
23
- --color-primary-solid: #2563eb; /* fill for solid primary controls (keeps AA with on-primary text) */
24
- --color-danger: #dc2626;
25
- --color-danger-solid: #dc2626; /* fill for solid danger controls (keeps AA with on-danger text) */
26
- --color-on-danger: #ffffff;
27
- --color-success: #16a34a;
28
- --color-warning: #d97706; /* amber accent for icons/borders (non-text, ≥3:1) */
29
- --color-warning-text: #b45309; /* darker amber for warning TEXT (≥4.5:1 on surface, WCAG 1.4.3) */
30
- --color-highlight: #fef08a; /* <mark> background */
31
- --color-on-highlight: #1a1a1a;
32
- --color-focus: #2563eb;
33
- --color-scrim: rgba(0, 0, 0, 0.5);
34
- --color-rail-bg: #f7f7f8;
35
- --color-hover: rgba(24, 24, 27, 0.06);
36
- --color-primary-soft: rgba(37, 99, 235, 0.1);
37
-
38
- /* space (rem step scale) */
39
- --space-1: 0.25rem;
40
- --space-2: 0.5rem;
41
- --space-3: 0.75rem;
42
- --space-4: 1rem;
43
- --space-5: 1.5rem;
44
- --space-6: 2rem;
45
- --space-7: 3rem;
46
- --space-8: 4rem;
47
-
48
- /* type */
49
- --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
50
- --text-xs: 0.75rem;
51
- --text-sm: 0.875rem;
52
- --text-base: 1rem;
53
- --text-lg: 1.125rem;
54
- --text-xl: 1.5rem;
55
- --leading-normal: 1.5;
56
- --weight-normal: 400;
57
- --weight-medium: 500;
58
- --weight-bold: 700;
59
-
60
- /* radius */
61
- --radius-sm: 4px;
62
- --radius-md: 8px;
63
- --radius-lg: 12px;
64
- --radius-full: 9999px;
65
-
66
- /* shadow */
67
- --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
68
- --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
69
- --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.16);
70
-
71
- /* z-index */
72
- --z-dropdown: 1000;
73
- --z-overlay: 1100;
74
- --z-dialog: 1200;
75
- --z-toast: 1300;
76
-
77
- /* motion */
78
- --motion-fast: 120ms;
79
- --motion-base: 200ms;
80
- --ease-standard: cubic-bezier(0.2, 0, 0, 1);
81
-
82
- /* layout */
83
- --rail-width: 15rem;
84
- --rail-width-collapsed: 4rem;
85
- --content-measure: 72rem;
86
- }
87
-
88
- /* ===================================================================== *
89
- * Admin theme — Light (neutral zinc + calm indigo, soft depth)
90
- * ===================================================================== */
91
- :root[data-theme="light"] {
92
- color-scheme: light;
93
-
94
- --color-bg: #fafafa; /* tinted app canvas so surfaces lift off it */
95
- --color-surface: #ffffff; /* cards, panels, inputs-on-card */
96
- --color-surface-2: #f4f4f5; /* sunken / secondary fills, input wells */
97
- --color-text: #18181b; /* zinc-900 */
98
- --color-text-muted: #71717a; /* zinc-500 */
99
- --color-text-subtle: #a1a1aa; /* zinc-400 — hints, placeholders */
100
- --color-border: #e4e4e7; /* zinc-200 — hairline (decorative) */
101
- --color-border-strong: #d4d4d8; /* zinc-300 — dividers */
102
- --color-control-border: #8e8e96; /* interactive control boundary — ~3.3:1 on surface (WCAG 1.4.11) */
103
- --color-primary: #4f46e5; /* indigo-600 */
104
- --color-primary-hover: #4338ca; /* indigo-700 */
105
- --color-on-primary: #ffffff;
106
- --color-primary-solid: #4f46e5; /* solid indigo fill (white text = 6.29:1, AA) */
107
- --color-primary-soft: rgba(79, 70, 229, 0.1);
108
- --color-danger: #dc2626;
109
- --color-danger-solid: #dc2626;
110
- --color-on-danger: #ffffff;
111
- --color-success: #16a34a;
112
- --color-warning: #d97706;
113
- --color-warning-text: #b45309; /* amber-700 — warning text ≈4.9:1 on the light surfaces */
114
- --color-highlight: #fde68a;
115
- --color-on-highlight: #18181b;
116
- --color-focus: #4f46e5;
117
- --color-link: #1a0dab; /* SERP-style link (SEO Google preview) — high contrast on light */
118
- --color-scrim: rgba(9, 9, 11, 0.45);
119
- --color-rail-bg: #ffffff;
120
- --color-hover: rgba(24, 24, 27, 0.05);
121
- --color-active: #f1f1f4; /* selected-row / active-nav neutral fill */
122
- --color-primary-on-fill: #4f46e5; /* primary-tinted text/icon on an active/raised fill, ≥4.5:1 */
123
-
124
- --shadow-sm: 0 1px 1px rgba(16, 24, 40, 0.04);
125
- --shadow-md: 0 2px 6px -2px rgba(16, 24, 40, 0.08), 0 1px 2px rgba(16, 24, 40, 0.04);
126
- --shadow-lg: 0 10px 28px -10px rgba(16, 24, 40, 0.16);
127
- }
128
-
129
- /* ===================================================================== *
130
- * Admin theme — Dark (zinc-950 canvas, brighter indigo, border-led depth)
131
- * ===================================================================== */
132
- :root[data-theme="dark"] {
133
- color-scheme: dark;
134
-
135
- --color-bg: #09090b; /* zinc-950 */
136
- --color-surface: #18181b; /* zinc-900 */
137
- --color-surface-2: #27272a; /* zinc-800 */
138
- --color-text: #fafafa;
139
- --color-text-muted: #a1a1aa; /* zinc-400 */
140
- --color-text-subtle: #71717a; /* zinc-500 */
141
- --color-border: #27272a; /* zinc-800 — hairline (decorative) */
142
- --color-border-strong: #3f3f46; /* zinc-700 */
143
- --color-control-border: #71717a; /* zinc-500 — ~3.6:1 on the dark surface (WCAG 1.4.11) */
144
- --color-primary: #6366f1; /* indigo-500 — lifts on dark */
145
- --color-primary-hover: #818cf8; /* indigo-400 */
146
- --color-on-primary: #ffffff;
147
- --color-primary-solid: #4f46e5; /* stays indigo-600 (not primary-500) so white text keeps AA (6.29:1) */
148
- --color-primary-soft: rgba(99, 102, 241, 0.16);
149
- --color-danger: #f87171;
150
- --color-danger-solid: #dc2626; /* deep enough for white text on the dark canvas */
151
- --color-on-danger: #ffffff;
152
- --color-success: #4ade80;
153
- --color-warning: #fbbf24;
154
- --color-warning-text: #fbbf24; /* on the dark canvas the bright amber already clears 4.5:1 */
155
- --color-highlight: #854d0e;
156
- --color-on-highlight: #fef3c7;
157
- --color-focus: #818cf8;
158
- --color-link: #8ab4f8; /* SERP-style link, dark mode — ~8:1 on the dark surface (WCAG AA) */
159
- --color-scrim: rgba(0, 0, 0, 0.6);
160
- --color-rail-bg: #111113;
161
- --color-hover: rgba(255, 255, 255, 0.06);
162
-
163
- --shadow-sm: 0 1px 1px rgba(0, 0, 0, 0.4);
164
- --shadow-md: 0 2px 8px -2px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4);
165
- --shadow-lg: 0 10px 28px -8px rgba(0, 0, 0, 0.6);
166
-
167
- /* active nav / selected-row fill reads against the darker rail */
168
- --color-active: #232327;
169
- --color-primary-on-fill: #818cf8; /* indigo-400 — 5.25:1 on --color-active (--color-primary is only ~3.5:1) */
170
- }
171
-
172
- /* ===================================================================== *
173
- * Admin shell — structural refinements (border-led depth, tighter radii)
174
- *
175
- * Scoped to the themed admin shell (any `data-theme`) so the public SSG
176
- * site keeps the looser `:root` radii. The admin leans on hairline borders
177
- * rather than soft shadows; the tighter radii read as a tool, not a card UI.
178
- * ===================================================================== */
179
- :root[data-theme] {
180
- --radius-sm: 4px;
181
- --radius-md: 6px;
182
- --radius-lg: 8px;
183
- }
@@ -1,4 +0,0 @@
1
- @use 'tokens';
2
- @use 'reset';
3
- @use 'base';
4
- @use 'mixins';
@@ -1,30 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import UiFieldset from '../ui/Fieldset.vue'
4
- import UiButtonGroup from '../ui/ButtonGroup.vue'
5
- import { nextBooleanValue } from '../../utils/boolean-field'
6
- import type { FieldComponentProps } from '../../utils/field-component'
7
-
8
- const props = defineProps<FieldComponentProps>()
9
- const model = defineModel<boolean>()
10
-
11
- const { t } = useT()
12
- const boolOptions = computed(() => [
13
- { label: t('field.boolean.no'), value: 'false' },
14
- { label: t('field.boolean.yes'), value: 'true' },
15
- ])
16
-
17
- const proxy = computed<string | string[] | null>({
18
- get: () => (model.value === true ? 'true' : model.value === false ? 'false' : null),
19
- set: (v) => { model.value = nextBooleanValue(model.value, v) },
20
- })
21
- const required = computed(() => !!props.field.required)
22
- </script>
23
-
24
- <template>
25
- <UiFieldset :id="id" :label="name" :error="error" :required="required">
26
- <template #default="f">
27
- <UiButtonGroup v-model="proxy" :options="boolOptions" :disabled="disabled" v-bind="f" />
28
- </template>
29
- </UiFieldset>
30
- </template>
@@ -1,73 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import UiField from '../ui/Field.vue'
4
- import UiFieldset from '../ui/Fieldset.vue'
5
- import UiSelect from '../ui/Select.vue'
6
- import UiButtonGroup from '../ui/ButtonGroup.vue'
7
- import UiCheckboxGroup from '../ui/CheckboxGroup.vue'
8
- import { resolveLocalized } from '../../utils/localized'
9
- import type { FieldComponentProps } from '../../utils/field-component'
10
- import type { FieldOf } from '@michaelthielemann/kestrel-core'
11
-
12
- const props = defineProps<FieldComponentProps>()
13
- const model = defineModel<string | string[] | null>()
14
-
15
- const { lang } = useT()
16
-
17
- const cfg = computed(() => {
18
- if (props.field.type !== 'choice') return { choices: [] as { label: string; value: string }[], multiple: false, buttons: false }
19
- const o = (props.field as FieldOf<'choice'>).options
20
- return {
21
- // A choice label is `Localized` like every other author-supplied label. Resolving it HERE (not in the
22
- // controls) is what keeps it out of the markup as a JSON blob — `{{ o.label }}` would stringify the map.
23
- choices: o.choices.map((c) => ({ ...c, label: resolveLocalized(c.label, lang.value) ?? c.value })),
24
- multiple: !!o.multiple,
25
- buttons: o.display === 'buttons',
26
- }
27
- })
28
- const required = computed(() => !!props.field.required)
29
-
30
- // Narrowing proxies so each control gets its precise model type. For the single <select>, map null↔''
31
- // (the empty option's value) so an optional choice can REPRESENT "no value" (null shows the empty option
32
- // instead of defaulting to the first choice) and be CLEARED back to null by re-selecting it.
33
- const single = computed<string | null>({
34
- get: () => (typeof model.value === 'string' ? model.value : ''),
35
- set: (v) => { model.value = v ? v : null },
36
- })
37
- // An optional single choice gets a leading empty option so null is selectable/clearable; a required one
38
- // has none (validation enforces a pick), keeping its existing behaviour.
39
- const singleOptions = computed(() =>
40
- required.value ? cfg.value.choices : [{ label: '—', value: '' }, ...cfg.value.choices],
41
- )
42
- const multi = computed<string[]>({
43
- get: () => (Array.isArray(model.value) ? model.value : []),
44
- set: (v) => { model.value = v },
45
- })
46
- </script>
47
-
48
- <template>
49
- <UiField v-if="!cfg.multiple && !cfg.buttons" :id="id" :label="name" :error="error" :required="required">
50
- <template #default="f">
51
- <UiSelect v-model="single" :options="singleOptions" :disabled="disabled" v-bind="f" />
52
- </template>
53
- </UiField>
54
- <UiFieldset v-else :id="id" :label="name" :error="error" :required="required">
55
- <template #default="f">
56
- <UiButtonGroup
57
- v-if="cfg.buttons"
58
- v-model="model"
59
- :options="cfg.choices"
60
- :multiple="cfg.multiple"
61
- :disabled="disabled"
62
- v-bind="f"
63
- />
64
- <UiCheckboxGroup
65
- v-else
66
- v-model="multi"
67
- :options="cfg.choices"
68
- :disabled="disabled"
69
- v-bind="f"
70
- />
71
- </template>
72
- </UiFieldset>
73
- </template>
@@ -1,98 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import UiField from '../ui/Field.vue'
4
- import UiFieldset from '../ui/Fieldset.vue'
5
- import UiTimeInput from '../ui/TimeInput.vue'
6
- import UiDatePicker from '../ui/DatePicker.vue'
7
- import UiDateRangePicker from '../ui/DateRangePicker.vue'
8
- import type { FieldComponentProps } from '../../utils/field-component'
9
- import type { FieldOf } from '@michaelthielemann/kestrel-core'
10
-
11
- const { t } = useT()
12
- const props = defineProps<FieldComponentProps>()
13
- const model = defineModel<string | { start: string; end: string } | null>()
14
-
15
- const cfg = computed(() => {
16
- if (props.field.type !== 'datetime') return { precision: 'datetime' as const, range: false }
17
- const o = (props.field as FieldOf<'datetime'>).options
18
- return { precision: o?.precision ?? 'datetime', range: !!o?.range }
19
- })
20
- const required = computed(() => !!props.field.required)
21
- const isTime = computed(() => cfg.value.precision === 'time')
22
- const pickerPrecision = computed<'date' | 'datetime'>(() => (cfg.value.precision === 'date' ? 'date' : 'datetime'))
23
-
24
- const single = computed<string | null>({
25
- get: () => (typeof model.value === 'string' ? model.value : null),
26
- set: (v) => { model.value = v },
27
- })
28
- const range = computed<{ start: string; end: string } | null>({
29
- get: () => (model.value && typeof model.value === 'object' ? model.value : null),
30
- set: (v) => { model.value = v },
31
- })
32
- const rangeStart = computed<string | null>({
33
- get: () => range.value?.start ?? null,
34
- set: (v) => { model.value = { start: v ?? '', end: range.value?.end ?? '' } },
35
- })
36
- const rangeEnd = computed<string | null>({
37
- get: () => range.value?.end ?? null,
38
- set: (v) => { model.value = { start: range.value?.start ?? '', end: v ?? '' } },
39
- })
40
- </script>
41
-
42
- <template>
43
- <UiField v-if="!cfg.range" :id="id" :label="name" :error="error" :required="required">
44
- <template #default="f">
45
- <UiTimeInput v-if="isTime" v-model="single" :disabled="disabled" v-bind="f" />
46
- <UiDatePicker
47
- v-else
48
- v-model="single"
49
- :precision="pickerPrecision"
50
- :disabled="disabled"
51
- :aria-label="name"
52
- :describedby="f['aria-describedby']"
53
- :invalid="f['aria-invalid'] === 'true'"
54
- :required="!!f.required"
55
- />
56
- </template>
57
- </UiField>
58
-
59
- <UiFieldset v-else :id="id" :label="name" :error="error" :required="required">
60
- <template #default="f">
61
- <template v-if="isTime">
62
- <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -- native wrapping label around a custom UiTimeInput; no `for`/`id` pair needed, invisible to static analysis -->
63
- <label class="field-datetime__sub">
64
- <span class="field-datetime__sublabel">{{ t('field.datetime.range_start') }}</span>
65
- <UiTimeInput v-model="rangeStart" :disabled="disabled" v-bind="f" />
66
- </label>
67
- <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -- native wrapping label around a custom UiTimeInput; no `for`/`id` pair needed, invisible to static analysis -->
68
- <label class="field-datetime__sub">
69
- <span class="field-datetime__sublabel">{{ t('field.datetime.range_end') }}</span>
70
- <UiTimeInput v-model="rangeEnd" :disabled="disabled" v-bind="f" />
71
- </label>
72
- </template>
73
- <UiDateRangePicker
74
- v-else
75
- v-model="range"
76
- :precision="pickerPrecision"
77
- :disabled="disabled"
78
- :aria-label="name"
79
- :describedby="f['aria-describedby']"
80
- :invalid="f['aria-invalid'] === 'true'"
81
- :required="!!f.required"
82
- />
83
- </template>
84
- </UiFieldset>
85
- </template>
86
-
87
- <style lang="scss">
88
- .field-datetime__sub {
89
- display: flex;
90
- align-items: center;
91
- gap: var(--space-2);
92
- }
93
- .field-datetime__sublabel {
94
- font-size: var(--text-sm);
95
- color: var(--color-text-muted);
96
- min-width: 3rem;
97
- }
98
- </style>
@@ -1,28 +0,0 @@
1
- <script setup lang="ts">
2
- // The per-field stale-reference warning, rendered by the shared FieldLayout. Non-blocking note,
3
- // distinct from the field's red validation error. Lives in the ui layer so the shared renderer
4
- // references it without a ui→admin dependency; its path-derived auto-import name stays
5
- // `FieldDeadRefNote`, so callers are unchanged.
6
- defineProps<{ show: boolean }>()
7
- const { t } = useT()
8
- </script>
9
-
10
- <template>
11
- <p v-if="show" class="field-dead-ref" role="note">
12
- <KestrelUiIcon name="triangle-alert" :size="13" class="field-dead-ref__icon" />{{ t('deadRefs.fieldNote') }}
13
- </p>
14
- </template>
15
-
16
- <style lang="scss">
17
- .field-dead-ref {
18
- display: flex;
19
- align-items: center;
20
- gap: var(--space-1);
21
- margin: 0;
22
- font-size: var(--text-xs);
23
- color: var(--color-warning-text); /* warning conveyed as TEXT → needs 4.5:1, not the 3:1 icon accent */
24
- }
25
- .field-dead-ref__icon {
26
- flex-shrink: 0;
27
- }
28
- </style>
@@ -1,62 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed, ref, watch } from 'vue'
3
- import UiField from '../ui/Field.vue'
4
- import UiTextarea from '../ui/Textarea.vue'
5
- import { fieldConstraints, tryParseJson } from '@michaelthielemann/kestrel-fields/client'
6
- import type { FieldComponentProps } from '../../utils/field-component'
7
-
8
- const props = defineProps<FieldComponentProps>()
9
- const model = defineModel<unknown>()
10
- const c = computed(() => fieldConstraints(props.field))
11
-
12
- const serialize = (v: unknown) => (v == null ? '' : JSON.stringify(v, null, 2))
13
- const raw = ref(serialize(model.value))
14
- const localError = ref<string | null>(null)
15
-
16
- // Reseed only when the model genuinely differs from what the textarea holds, so our own valid edits
17
- // never clobber the user's in-progress text. The watch only fires on a model CHANGE, and onInput never
18
- // writes `model` while the text is invalid — so a fire while the textarea holds invalid JSON is always
19
- // an EXTERNAL change (applyFrom / reset). That invalid text is not an edit worth protecting: take the
20
- // external value and clear the stale error, rather than silently dropping the update.
21
- watch(model, (v) => {
22
- const current = tryParseJson(raw.value)
23
- if (!current.ok) {
24
- raw.value = serialize(v)
25
- localError.value = null
26
- return
27
- }
28
- if (JSON.stringify(v) !== JSON.stringify(current.value)) raw.value = serialize(v)
29
- })
30
-
31
- function onInput(value: string | null | undefined) {
32
- raw.value = value ?? ''
33
- if (raw.value.trim() === '') {
34
- localError.value = null
35
- model.value = null
36
- return
37
- }
38
- const parsed = tryParseJson(raw.value)
39
- if (parsed.ok) {
40
- localError.value = null
41
- model.value = parsed.value
42
- } else {
43
- localError.value = 'Invalid JSON.'
44
- }
45
- }
46
-
47
- const shownError = computed(() => localError.value ?? props.error ?? null)
48
- </script>
49
-
50
- <template>
51
- <UiField :id="id" :label="name" :error="shownError" :required="c.required">
52
- <template #default="f">
53
- <UiTextarea
54
- :model-value="raw"
55
- :rows="6"
56
- :disabled="disabled"
57
- v-bind="f"
58
- @update:model-value="onInput"
59
- />
60
- </template>
61
- </UiField>
62
- </template>