@michaelthielemann/kestrel 4.1.2 → 5.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 (340) hide show
  1. package/README.md +26 -201
  2. package/dist/boot.d.ts +45 -0
  3. package/dist/boot.js +274 -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 +157 -0
  15. package/dist/defineConfig.js +41 -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/cron.d.ts +10 -0
  43. package/dist/triggers/cron.js +44 -0
  44. package/dist/triggers/cronMatch.d.ts +10 -0
  45. package/dist/triggers/cronMatch.js +68 -0
  46. package/dist/triggers/http.d.ts +58 -0
  47. package/dist/triggers/http.js +292 -0
  48. package/package.json +25 -151
  49. package/NOTICE +0 -12
  50. package/kestrel.config.ts +0 -28
  51. package/layers/access/nuxt.config.ts +0 -5
  52. package/layers/access/server/middleware/00.ip-allowlist.ts +0 -52
  53. package/layers/access/server/middleware/access-guard.ts +0 -32
  54. package/layers/access/server/types.d.ts +0 -7
  55. package/layers/admin/app/components/AdminAccount.vue +0 -212
  56. package/layers/admin/app/components/AdminNav.vue +0 -44
  57. package/layers/admin/app/components/BlockFields.vue +0 -67
  58. package/layers/admin/app/components/BlockPreview.vue +0 -413
  59. package/layers/admin/app/components/BlockTree.vue +0 -433
  60. package/layers/admin/app/components/BlocksBody.vue +0 -163
  61. package/layers/admin/app/components/CollectionDeleteDialog.vue +0 -38
  62. package/layers/admin/app/components/CollectionEditor.vue +0 -324
  63. package/layers/admin/app/components/CollectionList.vue +0 -429
  64. package/layers/admin/app/components/CollectionListBulkBar.vue +0 -62
  65. package/layers/admin/app/components/CollectionListColumnsPanel.vue +0 -37
  66. package/layers/admin/app/components/CollectionListFilterPanel.vue +0 -145
  67. package/layers/admin/app/components/CollectionListPager.vue +0 -70
  68. package/layers/admin/app/components/CollectionListTable.vue +0 -347
  69. package/layers/admin/app/components/EditorStatus.vue +0 -211
  70. package/layers/admin/app/components/EditorUnsupported.vue +0 -28
  71. package/layers/admin/app/components/FieldsBody.vue +0 -23
  72. package/layers/admin/app/components/LocaleBar.vue +0 -107
  73. package/layers/admin/app/components/PageFields.vue +0 -129
  74. package/layers/admin/app/components/PageFieldsPane.vue +0 -39
  75. package/layers/admin/app/components/SeoFields.vue +0 -210
  76. package/layers/admin/app/components/SingletonEditor.vue +0 -93
  77. package/layers/admin/app/composables/useAuth.ts +0 -68
  78. package/layers/admin/app/composables/useBlockTree.ts +0 -95
  79. package/layers/admin/app/composables/useBlocks.ts +0 -7
  80. package/layers/admin/app/composables/useCachedResource.ts +0 -35
  81. package/layers/admin/app/composables/useCollectionOps.ts +0 -102
  82. package/layers/admin/app/composables/useCollections.ts +0 -6
  83. package/layers/admin/app/composables/useContentLocales.ts +0 -23
  84. package/layers/admin/app/composables/useEditForm.ts +0 -402
  85. package/layers/admin/app/composables/useLinkResolver.ts +0 -40
  86. package/layers/admin/app/composables/useListBatchActions.ts +0 -78
  87. package/layers/admin/app/composables/useListColumns.ts +0 -53
  88. package/layers/admin/app/composables/useListFilterDraft.ts +0 -129
  89. package/layers/admin/app/composables/useListRows.ts +0 -75
  90. package/layers/admin/app/composables/useListSelection.ts +0 -28
  91. package/layers/admin/app/composables/useListUrlState.ts +0 -53
  92. package/layers/admin/app/composables/usePendingPublishPoll.ts +0 -15
  93. package/layers/admin/app/composables/usePreviewViewport.ts +0 -30
  94. package/layers/admin/app/composables/usePublishStatus.ts +0 -107
  95. package/layers/admin/app/composables/useRailCollapsed.ts +0 -14
  96. package/layers/admin/app/composables/useTheme.ts +0 -36
  97. package/layers/admin/app/composables/useToolbarPanel.ts +0 -38
  98. package/layers/admin/app/composables/useUnsavedGuard.ts +0 -22
  99. package/layers/admin/app/layouts/admin.vue +0 -266
  100. package/layers/admin/app/middleware/admin-auth.ts +0 -4
  101. package/layers/admin/app/pages/admin/[collection]/[id].vue +0 -203
  102. package/layers/admin/app/pages/admin/[collection]/index.vue +0 -62
  103. package/layers/admin/app/pages/admin/index.vue +0 -118
  104. package/layers/admin/app/pages/admin/login.vue +0 -68
  105. package/layers/admin/app/pages/admin/media.vue +0 -28
  106. package/layers/admin/app/pages/admin/references.vue +0 -146
  107. package/layers/admin/app/plugins/admin-style-guard.client.ts +0 -76
  108. package/layers/admin/app/plugins/reauth.client.ts +0 -20
  109. package/layers/admin/app/utils/admin-style-guard.ts +0 -20
  110. package/layers/admin/app/utils/block-tree.ts +0 -214
  111. package/layers/admin/app/utils/collection-ops.ts +0 -69
  112. package/layers/admin/app/utils/dead-refs.ts +0 -30
  113. package/layers/admin/app/utils/edit-form.ts +0 -267
  114. package/layers/admin/app/utils/editor-expose.ts +0 -36
  115. package/layers/admin/app/utils/editor-form-context.ts +0 -82
  116. package/layers/admin/app/utils/editor-registry.ts +0 -20
  117. package/layers/admin/app/utils/field-keys.ts +0 -11
  118. package/layers/admin/app/utils/list-cell.ts +0 -46
  119. package/layers/admin/app/utils/list-columns.ts +0 -142
  120. package/layers/admin/app/utils/list-query.ts +0 -96
  121. package/layers/admin/app/utils/nav-active.ts +0 -9
  122. package/layers/admin/app/utils/populate-blocks.ts +0 -288
  123. package/layers/admin/app/utils/preview-channel.ts +0 -62
  124. package/layers/admin/app/utils/preview-viewport.ts +0 -80
  125. package/layers/admin/app/utils/reauth.ts +0 -67
  126. package/layers/admin/app/utils/record-title.ts +0 -15
  127. package/layers/admin/app/utils/register-builtin-editors.ts +0 -9
  128. package/layers/admin/app/utils/safe-redirect.ts +0 -12
  129. package/layers/admin/nuxt.config.ts +0 -20
  130. package/layers/auth/nuxt.config.ts +0 -1
  131. package/layers/auth/server/plugins/01.register-auth-pipelines.ts +0 -7
  132. package/layers/collections/nuxt.config.ts +0 -1
  133. package/layers/collections/server/plugins/01.register.ts +0 -29
  134. package/layers/collections/server/plugins/02.register-relation-populate.ts +0 -18
  135. package/layers/core/app/composables/layouts.ts +0 -5
  136. package/layers/core/app/utils/field-factories.ts +0 -129
  137. package/layers/core/app/utils/layouts.ts +0 -38
  138. package/layers/core/modules/auto-discovery/extract-block.ts +0 -179
  139. package/layers/core/modules/auto-discovery/index.ts +0 -112
  140. package/layers/core/modules/auto-discovery/package-registry.ts +0 -19
  141. package/layers/core/modules/auto-discovery/scan.ts +0 -170
  142. package/layers/core/modules/auto-discovery/virtual.d.ts +0 -20
  143. package/layers/core/modules/component-namespace/index.ts +0 -44
  144. package/layers/core/modules/component-namespace/shared.ts +0 -24
  145. package/layers/core/modules/kestrel/app-shell.ts +0 -55
  146. package/layers/core/modules/kestrel/auto-imports.ts +0 -44
  147. package/layers/core/modules/kestrel/index.ts +0 -144
  148. package/layers/core/modules/plugin-order/index.ts +0 -69
  149. package/layers/core/modules/plugin-order/plugin-order.ts +0 -273
  150. package/layers/core/nuxt.config.ts +0 -10
  151. package/layers/core/server/api/[...path].ts +0 -98
  152. package/layers/core/server/plugins/00.config.ts +0 -12
  153. package/layers/core/server/plugins/00.migrate.ts +0 -12
  154. package/layers/core/server/plugins/01.register-introspection-pipeline.ts +0 -5
  155. package/layers/core/server/plugins/01.register-openapi-pipeline.ts +0 -5
  156. package/layers/core/server/plugins/01.register-tooling-pipelines.ts +0 -5
  157. package/layers/core/server/plugins/02.schema-sync.ts +0 -58
  158. package/layers/core/server/plugins/04.outbox-worker.ts +0 -31
  159. package/layers/core/server/plugins/05.reindex-refs.ts +0 -5
  160. package/layers/core/server/routes/__kestrel/dashboard.get.ts +0 -29
  161. package/layers/core/server/tasks/db/migrate-module.ts +0 -83
  162. package/layers/core/server/tasks/db/migrate-revisions.ts +0 -20
  163. package/layers/core/server/tasks/db/migrate.ts +0 -50
  164. package/layers/core/server/utils/server-config.ts +0 -81
  165. package/layers/fields/nuxt.config.ts +0 -1
  166. package/layers/fields/server/plugins/01.register-blocks-pipeline.ts +0 -7
  167. package/layers/fields/server/plugins/01.register-field-populate.ts +0 -11
  168. package/layers/media/app/components/KestrelImg.vue +0 -94
  169. package/layers/media/app/components/MediaConflictDialog.vue +0 -35
  170. package/layers/media/app/components/MediaDeleteDialog.vue +0 -52
  171. package/layers/media/app/components/MediaGrid.vue +0 -113
  172. package/layers/media/app/components/MediaImage.vue +0 -70
  173. package/layers/media/app/components/MediaLibrary.vue +0 -456
  174. package/layers/media/app/components/MediaNewFolderDialog.vue +0 -32
  175. package/layers/media/app/components/MediaPathBar.vue +0 -163
  176. package/layers/media/app/components/MediaPicker.vue +0 -19
  177. package/layers/media/app/components/MediaRenameDialog.vue +0 -32
  178. package/layers/media/app/components/MediaTable.vue +0 -75
  179. package/layers/media/app/components/MediaThumb.vue +0 -25
  180. package/layers/media/app/components/MediaToolbar.vue +0 -90
  181. package/layers/media/app/components/MediaUploadDialog.vue +0 -87
  182. package/layers/media/app/components/MediaViewer.vue +0 -137
  183. package/layers/media/app/components/field/Media.vue +0 -263
  184. package/layers/media/app/composables/useBlurUp.ts +0 -55
  185. package/layers/media/app/composables/useMediaClipboard.ts +0 -14
  186. package/layers/media/app/composables/useMediaContextMenu.ts +0 -56
  187. package/layers/media/app/composables/useMediaDnd.ts +0 -56
  188. package/layers/media/app/composables/useMediaLibrary.ts +0 -152
  189. package/layers/media/app/composables/useMediaOps.ts +0 -50
  190. package/layers/media/app/composables/useMediaRelocate.ts +0 -39
  191. package/layers/media/app/composables/useMediaResolver.ts +0 -50
  192. package/layers/media/app/composables/useMediaUpload.ts +0 -93
  193. package/layers/media/app/composables/useMediaVariant.ts +0 -34
  194. package/layers/media/app/plugins/field-media.client.ts +0 -6
  195. package/layers/media/app/utils/ai-disclosure.ts +0 -19
  196. package/layers/media/app/utils/dnd.ts +0 -69
  197. package/layers/media/app/utils/library.ts +0 -81
  198. package/layers/media/app/utils/ops.ts +0 -60
  199. package/layers/media/app/utils/picture-model.ts +0 -125
  200. package/layers/media/app/utils/thumbhash.ts +0 -19
  201. package/layers/media/app/utils/variant-name.ts +0 -20
  202. package/layers/media/nuxt.config.ts +0 -7
  203. package/layers/media/server/middleware/ondemand-variants.ts +0 -38
  204. package/layers/media/server/plugins/01.register-media-pipelines.ts +0 -7
  205. package/layers/media/server/plugins/02.register-media.ts +0 -22
  206. package/layers/media/server/plugins/04.variant-capture.ts +0 -27
  207. package/layers/media/server/plugins/05.media-cleanup.ts +0 -5
  208. package/layers/media/server/tasks/media/backfill.ts +0 -17
  209. package/layers/public/app/app.vue +0 -5
  210. package/layers/public/app/components/BlockRenderer.vue +0 -56
  211. package/layers/public/app/components/KestrelLink.vue +0 -26
  212. package/layers/public/app/components/KestrelPreviewBridge.vue +0 -133
  213. package/layers/public/app/composables/public-page.ts +0 -24
  214. package/layers/public/app/error.vue +0 -12
  215. package/layers/public/app/layouts/default.vue +0 -5
  216. package/layers/public/app/pages/[...slug].vue +0 -239
  217. package/layers/public/app/pages/__kestrel/preview.vue +0 -46
  218. package/layers/public/app/utils/block-edit-context.ts +0 -16
  219. package/layers/public/app/utils/json-ld.ts +0 -139
  220. package/layers/public/app/utils/link-href.ts +0 -46
  221. package/layers/public/app/utils/page-head.ts +0 -84
  222. package/layers/public/app/utils/page-layout.ts +0 -18
  223. package/layers/public/app/utils/preview-protocol.ts +0 -117
  224. package/layers/public/app/utils/public-route.ts +0 -46
  225. package/layers/public/app/utils/site-head.ts +0 -43
  226. package/layers/public/modules/deploy-output/index.ts +0 -80
  227. package/layers/public/modules/prerender-routes/discover.ts +0 -37
  228. package/layers/public/modules/prerender-routes/index.ts +0 -88
  229. package/layers/public/modules/prerender-routes/routes.ts +0 -16
  230. package/layers/public/modules/prune-media/index.ts +0 -48
  231. package/layers/public/modules/prune-media/prune-media.ts +0 -81
  232. package/layers/public/nuxt.config.ts +0 -14
  233. package/layers/public/server/delivery-live/serve.ts +0 -111
  234. package/layers/public/server/middleware/delivery-live-catchall.ts +0 -7
  235. package/layers/public/server/pipelines/index.ts +0 -23
  236. package/layers/public/server/plugins/00.ensure-snapshot-triggers.ts +0 -14
  237. package/layers/public/server/plugins/01.register-public-pipelines.ts +0 -7
  238. package/layers/public/server/plugins/02.register-links.ts +0 -16
  239. package/layers/public/server/plugins/03.redirects.ts +0 -55
  240. package/layers/public/server/plugins/05.plan-publish.ts +0 -5
  241. package/layers/public/server/plugins/zz.publish.ts +0 -127
  242. package/layers/public/server/routes/llms-full.txt.get.ts +0 -99
  243. package/layers/public/server/routes/llms.txt.get.ts +0 -66
  244. package/layers/public/server/routes/redirects.json.get.ts +0 -58
  245. package/layers/public/server/routes/robots.txt.get.ts +0 -11
  246. package/layers/public/server/routes/sitemap.xml.get.ts +0 -79
  247. package/layers/public/server/tasks/publish/run.ts +0 -32
  248. package/layers/public/server/utils/publish/render-live.ts +0 -26
  249. package/layers/ui/app/assets/scss/_base.scss +0 -16
  250. package/layers/ui/app/assets/scss/_datepicker.scss +0 -82
  251. package/layers/ui/app/assets/scss/_mixins.scss +0 -50
  252. package/layers/ui/app/assets/scss/_reset.scss +0 -65
  253. package/layers/ui/app/assets/scss/_tokens.scss +0 -183
  254. package/layers/ui/app/assets/scss/main.scss +0 -4
  255. package/layers/ui/app/components/field/Boolean.vue +0 -30
  256. package/layers/ui/app/components/field/Choice.vue +0 -73
  257. package/layers/ui/app/components/field/Datetime.vue +0 -98
  258. package/layers/ui/app/components/field/DeadRefNote.vue +0 -28
  259. package/layers/ui/app/components/field/Json.vue +0 -62
  260. package/layers/ui/app/components/field/Layout.vue +0 -138
  261. package/layers/ui/app/components/field/Link.vue +0 -112
  262. package/layers/ui/app/components/field/LinkInternalPicker.vue +0 -95
  263. package/layers/ui/app/components/field/Number.vue +0 -30
  264. package/layers/ui/app/components/field/Relation.vue +0 -49
  265. package/layers/ui/app/components/field/Renderer.vue +0 -30
  266. package/layers/ui/app/components/field/Repeater.vue +0 -460
  267. package/layers/ui/app/components/field/Richtext.vue +0 -26
  268. package/layers/ui/app/components/field/Slug.vue +0 -65
  269. package/layers/ui/app/components/field/Text.vue +0 -35
  270. package/layers/ui/app/components/field/Unsupported.vue +0 -34
  271. package/layers/ui/app/components/ui/Alert.vue +0 -50
  272. package/layers/ui/app/components/ui/Brand.vue +0 -22
  273. package/layers/ui/app/components/ui/Button.vue +0 -116
  274. package/layers/ui/app/components/ui/ButtonGroup.vue +0 -96
  275. package/layers/ui/app/components/ui/Checkbox.vue +0 -53
  276. package/layers/ui/app/components/ui/CheckboxGroup.vue +0 -51
  277. package/layers/ui/app/components/ui/Combobox.vue +0 -345
  278. package/layers/ui/app/components/ui/DatePicker.vue +0 -92
  279. package/layers/ui/app/components/ui/DateRangePicker.vue +0 -100
  280. package/layers/ui/app/components/ui/Dialog.vue +0 -107
  281. package/layers/ui/app/components/ui/EmptyState.vue +0 -46
  282. package/layers/ui/app/components/ui/Field.vue +0 -58
  283. package/layers/ui/app/components/ui/Fieldset.vue +0 -65
  284. package/layers/ui/app/components/ui/Icon.vue +0 -44
  285. package/layers/ui/app/components/ui/Menu.vue +0 -54
  286. package/layers/ui/app/components/ui/NumberInput.vue +0 -175
  287. package/layers/ui/app/components/ui/Richtext.vue +0 -141
  288. package/layers/ui/app/components/ui/RichtextToolbar.vue +0 -251
  289. package/layers/ui/app/components/ui/Select.vue +0 -52
  290. package/layers/ui/app/components/ui/TextInput.vue +0 -111
  291. package/layers/ui/app/components/ui/Textarea.vue +0 -39
  292. package/layers/ui/app/components/ui/TimeInput.vue +0 -18
  293. package/layers/ui/app/components/ui/Toasts.vue +0 -103
  294. package/layers/ui/app/components/ui/Tooltip.vue +0 -45
  295. package/layers/ui/app/composables/useAdminLang.ts +0 -8
  296. package/layers/ui/app/composables/useDragReorder.ts +0 -41
  297. package/layers/ui/app/composables/useEchoGuard.ts +0 -28
  298. package/layers/ui/app/composables/useLinkField.ts +0 -70
  299. package/layers/ui/app/composables/useRecordOptions.ts +0 -84
  300. package/layers/ui/app/composables/useRepeater.ts +0 -99
  301. package/layers/ui/app/composables/useT.ts +0 -32
  302. package/layers/ui/app/composables/useToast.ts +0 -46
  303. package/layers/ui/app/i18n/de.ts +0 -432
  304. package/layers/ui/app/i18n/en.ts +0 -434
  305. package/layers/ui/app/utils/boolean-field.ts +0 -10
  306. package/layers/ui/app/utils/field-component.ts +0 -22
  307. package/layers/ui/app/utils/field-empty.ts +0 -21
  308. package/layers/ui/app/utils/field-registry.ts +0 -36
  309. package/layers/ui/app/utils/humanize.ts +0 -14
  310. package/layers/ui/app/utils/icons.ts +0 -143
  311. package/layers/ui/app/utils/iso-date.ts +0 -18
  312. package/layers/ui/app/utils/localized.ts +0 -13
  313. package/layers/ui/app/utils/record-context.ts +0 -15
  314. package/layers/ui/app/utils/reorder.ts +0 -8
  315. package/layers/ui/app/utils/richtext-preserve-class.ts +0 -31
  316. package/layers/ui/app/utils/useFieldA11y.ts +0 -40
  317. package/layers/ui/nuxt.config.ts +0 -9
  318. package/nuxt.config.ts +0 -108
  319. package/scripts/consumer-template-ci.mjs +0 -160
  320. package/scripts/copy-create-payload.mjs +0 -49
  321. package/scripts/coverage-all.mjs +0 -163
  322. package/scripts/dashboard.mjs +0 -89
  323. package/scripts/hash-password.mjs +0 -29
  324. package/scripts/kestrel.mjs +0 -240
  325. package/scripts/lib/cli.mjs +0 -129
  326. package/scripts/lib/coverage-model.mjs +0 -51
  327. package/scripts/lib/password.mjs +0 -19
  328. package/scripts/lib/scaffold.mjs +0 -205
  329. package/scripts/port-graph.mjs +0 -350
  330. package/scripts/typecheck.mjs +0 -62
  331. package/templates/starter/README.md +0 -65
  332. package/templates/starter/_env.example +0 -17
  333. package/templates/starter/_gitignore +0 -26
  334. package/templates/starter/_package.json +0 -22
  335. package/templates/starter/app/app.vue +0 -7
  336. package/templates/starter/app/blocks/Prose.vue +0 -13
  337. package/templates/starter/app/layouts/default.vue +0 -6
  338. package/templates/starter/nuxt.config.ts +0 -23
  339. package/templates/starter/pnpm-workspace.yaml +0 -8
  340. 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>