@michaelthielemann/kestrel 3.0.1 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -110
- package/kestrel.config.ts +1 -1
- package/layers/access/server/middleware/00.ip-allowlist.ts +3 -18
- package/layers/access/server/middleware/access-guard.ts +25 -23
- package/layers/access/server/types.d.ts +1 -2
- package/layers/admin/app/components/AdminAccount.vue +1 -1
- package/layers/admin/app/components/BlockFields.vue +1 -2
- package/layers/admin/app/components/BlockPreview.vue +17 -5
- package/layers/admin/app/components/BlocksBody.vue +1 -1
- package/layers/admin/app/components/CollectionEditor.vue +35 -16
- package/layers/admin/app/components/CollectionList.vue +34 -3
- package/layers/admin/app/components/CollectionListBulkBar.vue +17 -3
- package/layers/admin/app/components/CollectionListFilterPanel.vue +1 -1
- package/layers/admin/app/components/CollectionListPager.vue +1 -1
- package/layers/admin/app/components/CollectionListTable.vue +41 -2
- package/layers/admin/app/components/EditorStatus.vue +8 -7
- package/layers/admin/app/components/LocaleBar.vue +3 -1
- package/layers/admin/app/components/PageFields.vue +2 -4
- package/layers/admin/app/components/SeoFields.vue +2 -4
- package/layers/admin/app/components/SingletonEditor.vue +3 -2
- package/layers/admin/app/composables/useAuth.ts +3 -3
- package/layers/admin/app/composables/useBlockTree.ts +1 -1
- package/layers/admin/app/composables/useBlocks.ts +1 -1
- package/layers/admin/app/composables/useCollectionOps.ts +45 -8
- package/layers/admin/app/composables/useCollections.ts +1 -2
- package/layers/admin/app/composables/useEditForm.ts +35 -17
- package/layers/admin/app/composables/useLinkResolver.ts +1 -1
- package/layers/admin/app/composables/useListBatchActions.ts +20 -3
- package/layers/admin/app/composables/useListColumns.ts +1 -1
- package/layers/admin/app/composables/useListFilterDraft.ts +3 -2
- package/layers/admin/app/composables/useListRows.ts +7 -2
- package/layers/admin/app/composables/useListUrlState.ts +1 -1
- package/layers/admin/app/composables/usePublishStatus.ts +2 -2
- package/layers/admin/app/layouts/admin.vue +0 -1
- package/layers/admin/app/pages/admin/[collection]/[id].vue +5 -4
- package/layers/admin/app/pages/admin/index.vue +1 -1
- package/layers/admin/app/pages/admin/references.vue +2 -2
- package/layers/admin/app/utils/block-tree.ts +1 -1
- package/layers/admin/app/utils/collection-ops.ts +28 -3
- package/layers/admin/app/utils/edit-form.ts +34 -2
- package/layers/admin/app/utils/editor-expose.ts +2 -0
- package/layers/admin/app/utils/editor-form-context.ts +1 -2
- package/layers/admin/app/utils/field-keys.ts +1 -1
- package/layers/admin/app/utils/list-columns.ts +3 -2
- package/layers/admin/app/utils/list-query.ts +2 -2
- package/layers/admin/app/utils/populate-blocks.ts +2 -2
- package/layers/admin/app/utils/reauth.ts +5 -6
- package/layers/auth/server/plugins/01.register-auth-pipelines.ts +7 -0
- package/layers/collections/server/plugins/01.register.ts +9 -12
- package/layers/collections/server/plugins/02.register-relation-populate.ts +5 -7
- package/layers/{fields → core}/app/utils/field-factories.ts +1 -1
- package/layers/core/modules/auto-discovery/extract-block.ts +3 -3
- package/layers/core/modules/auto-discovery/index.ts +69 -11
- package/layers/core/modules/auto-discovery/package-registry.ts +19 -0
- package/layers/core/modules/auto-discovery/scan.ts +92 -0
- package/layers/core/modules/auto-discovery/virtual.d.ts +5 -0
- package/layers/core/modules/component-namespace/index.ts +24 -0
- package/layers/core/modules/kestrel/index.ts +6 -1
- package/layers/core/modules/plugin-order/index.ts +69 -0
- package/layers/core/modules/plugin-order/plugin-order.ts +273 -0
- package/layers/core/nuxt.config.ts +1 -0
- package/layers/core/server/api/[...path].ts +98 -0
- package/layers/core/server/plugins/00.config.ts +12 -0
- package/layers/core/server/plugins/00.migrate.ts +1 -0
- package/layers/core/server/plugins/01.register-introspection-pipeline.ts +5 -0
- package/layers/core/server/plugins/01.register-openapi-pipeline.ts +5 -0
- package/layers/core/server/plugins/01.register-tooling-pipelines.ts +5 -0
- package/layers/core/server/plugins/02.schema-sync.ts +2 -7
- package/layers/core/server/plugins/04.outbox-worker.ts +31 -0
- package/layers/core/server/plugins/05.reindex-refs.ts +5 -0
- package/layers/core/server/routes/__kestrel/dashboard.get.ts +29 -0
- package/layers/core/server/tasks/db/migrate-module.ts +83 -0
- package/layers/core/server/tasks/db/migrate-revisions.ts +20 -0
- package/layers/core/server/tasks/db/migrate.ts +2 -6
- package/layers/core/server/utils/server-config.ts +66 -3
- package/layers/fields/server/plugins/01.register-blocks-pipeline.ts +7 -0
- package/layers/fields/server/plugins/01.register-field-populate.ts +2 -2
- package/layers/media/app/components/KestrelImg.vue +3 -3
- package/layers/media/app/components/MediaLibrary.vue +2 -2
- package/layers/media/app/components/field/Media.vue +1 -1
- package/layers/media/app/composables/useMediaResolver.ts +1 -1
- package/layers/media/app/composables/useMediaUpload.ts +1 -1
- package/layers/media/app/composables/useMediaVariant.ts +2 -2
- package/layers/media/app/utils/ai-disclosure.ts +1 -1
- package/layers/media/app/utils/library.ts +1 -1
- package/layers/media/app/utils/ops.ts +1 -3
- package/layers/media/app/utils/picture-model.ts +2 -2
- package/layers/media/app/utils/variant-name.ts +1 -1
- package/layers/media/server/middleware/ondemand-variants.ts +6 -5
- package/layers/media/server/plugins/01.register-media-pipelines.ts +7 -0
- package/layers/media/server/plugins/02.register-media.ts +10 -7
- package/layers/media/server/plugins/04.variant-capture.ts +12 -8
- package/layers/media/server/plugins/05.media-cleanup.ts +5 -0
- package/layers/media/server/tasks/media/backfill.ts +3 -4
- package/layers/public/app/components/KestrelLink.vue +1 -1
- package/layers/public/app/pages/[...slug].vue +1 -1
- package/layers/public/app/pages/__kestrel/preview.vue +2 -2
- package/layers/public/app/utils/json-ld.ts +1 -1
- package/layers/public/app/utils/link-href.ts +1 -1
- package/layers/public/app/utils/page-head.ts +1 -1
- package/layers/public/modules/deploy-output/index.ts +4 -6
- package/layers/public/modules/prerender-routes/index.ts +4 -4
- package/layers/public/modules/prerender-routes/routes.ts +1 -1
- package/layers/public/modules/prune-media/index.ts +2 -2
- package/layers/public/server/delivery-live/serve.ts +111 -0
- package/layers/public/server/middleware/delivery-live-catchall.ts +7 -0
- package/layers/public/server/pipelines/index.ts +23 -0
- package/layers/public/server/plugins/00.ensure-snapshot-triggers.ts +14 -0
- package/layers/public/server/plugins/01.register-public-pipelines.ts +7 -0
- package/layers/public/server/plugins/02.register-links.ts +2 -3
- package/layers/public/server/plugins/03.redirects.ts +41 -23
- package/layers/public/server/plugins/05.plan-publish.ts +5 -0
- package/layers/public/server/plugins/zz.publish.ts +79 -24
- package/layers/public/server/routes/llms-full.txt.get.ts +5 -5
- package/layers/public/server/routes/llms.txt.get.ts +4 -2
- package/layers/public/server/routes/redirects.json.get.ts +2 -2
- package/layers/public/server/routes/robots.txt.get.ts +2 -0
- package/layers/public/server/routes/sitemap.xml.get.ts +16 -6
- package/layers/public/server/tasks/publish/run.ts +9 -4
- package/layers/public/server/utils/publish/render-live.ts +26 -0
- package/layers/ui/app/components/field/Choice.vue +1 -1
- package/layers/ui/app/components/field/Datetime.vue +1 -1
- package/layers/ui/app/components/field/Json.vue +1 -2
- package/layers/ui/app/components/field/Layout.vue +2 -4
- package/layers/ui/app/components/field/Link.vue +1 -1
- package/layers/ui/app/components/field/LinkInternalPicker.vue +3 -3
- package/layers/ui/app/components/field/Number.vue +2 -2
- package/layers/ui/app/components/field/Relation.vue +1 -1
- package/layers/ui/app/components/field/Repeater.vue +1 -3
- package/layers/ui/app/components/field/Slug.vue +2 -3
- package/layers/ui/app/components/field/Text.vue +1 -1
- package/layers/ui/app/components/ui/Richtext.vue +1 -1
- package/layers/ui/app/components/ui/RichtextToolbar.vue +1 -3
- package/layers/ui/app/composables/useLinkField.ts +1 -1
- package/layers/ui/app/composables/useRepeater.ts +1 -1
- package/layers/ui/app/i18n/de.ts +7 -7
- package/layers/ui/app/i18n/en.ts +7 -39
- package/layers/ui/app/utils/field-component.ts +1 -1
- package/layers/ui/app/utils/field-registry.ts +1 -1
- package/layers/ui/app/utils/icons.ts +2 -6
- package/layers/ui/app/utils/localized.ts +1 -1
- package/nuxt.config.ts +1 -1
- package/package.json +53 -19
- package/scripts/consumer-template-ci.mjs +160 -0
- package/scripts/coverage-all.mjs +163 -0
- package/scripts/dashboard.mjs +89 -0
- package/scripts/hash-password.mjs +1 -1
- package/scripts/lib/coverage-model.mjs +51 -0
- package/scripts/port-graph.mjs +350 -0
- package/scripts/typecheck.mjs +11 -0
- package/templates/starter/app/blocks/Prose.vue +1 -1
- package/layers/access/server/utils/csrf.ts +0 -39
- package/layers/access/server/utils/grant-registry.ts +0 -36
- package/layers/access/server/utils/guard.ts +0 -44
- package/layers/access/server/utils/ip-allowlist.ts +0 -79
- package/layers/access/server/utils/policy.ts +0 -98
- package/layers/access/server/utils/public-resources.ts +0 -11
- package/layers/access/server/utils/render-context.ts +0 -41
- package/layers/access/server/utils/require-admin.ts +0 -15
- package/layers/admin/app/pages/admin/[collection]/[id].nuxt.test.ts +0 -200
- package/layers/admin/app/pages/admin/[collection]/publish-preview.nuxt.test.ts +0 -142
- package/layers/auth/server/api/auth/login.post.ts +0 -38
- package/layers/auth/server/api/auth/logout.post.ts +0 -9
- package/layers/auth/server/api/auth/session.get.ts +0 -1
- package/layers/auth/server/utils/admin-credential.ts +0 -42
- package/layers/auth/server/utils/client-ip.ts +0 -88
- package/layers/auth/server/utils/login-throttle.ts +0 -126
- package/layers/auth/server/utils/password.ts +0 -52
- package/layers/auth/server/utils/session-cookie.ts +0 -44
- package/layers/auth/server/utils/session-epoch.ts +0 -96
- package/layers/auth/server/utils/session.ts +0 -88
- package/layers/collections/server/collections/pages.ts +0 -19
- package/layers/collections/server/utils/populate-relations.ts +0 -79
- package/layers/core/app/utils/bulk-actions.ts +0 -9
- package/layers/core/app/utils/filter-ops.ts +0 -98
- package/layers/core/app/utils/list-limits.ts +0 -27
- package/layers/core/app/utils/locale-path.ts +0 -12
- package/layers/core/app/utils/slugify.ts +0 -16
- package/layers/core/server/api/[collection]/[id]/dead-refs.get.ts +0 -8
- package/layers/core/server/api/[collection]/[id]/translations.get.ts +0 -4
- package/layers/core/server/api/[collection]/[id].delete.ts +0 -5
- package/layers/core/server/api/[collection]/[id].get.ts +0 -11
- package/layers/core/server/api/[collection]/[id].patch.ts +0 -8
- package/layers/core/server/api/[collection]/bulk.post.ts +0 -37
- package/layers/core/server/api/[collection]/index.get.ts +0 -24
- package/layers/core/server/api/[collection]/index.post.ts +0 -8
- package/layers/core/server/api/[collection]/index.put.test.ts +0 -76
- package/layers/core/server/api/[collection]/index.put.ts +0 -24
- package/layers/core/server/api/[collection]/options.get.test.ts +0 -60
- package/layers/core/server/api/[collection]/options.get.ts +0 -18
- package/layers/core/server/api/[collection]/populate-scope.test.ts +0 -119
- package/layers/core/server/api/[collection]/translations.get.test.ts +0 -90
- package/layers/core/server/api/[collection]/translations.get.ts +0 -25
- package/layers/core/server/api/collections/[name].get.ts +0 -4
- package/layers/core/server/api/collections/index.get.ts +0 -3
- package/layers/core/server/api/references/broken.get.ts +0 -9
- package/layers/core/server/api/references/referrers.get.ts +0 -24
- package/layers/core/server/database/record-refs.ts +0 -20
- package/layers/core/server/plugins/03.record-refs.ts +0 -12
- package/layers/core/server/schema/bootstrap.ts +0 -41
- package/layers/core/server/schema/desired.ts +0 -99
- package/layers/core/server/schema/dev-sync.ts +0 -23
- package/layers/core/server/schema/dialect.ts +0 -61
- package/layers/core/server/schema/diff.ts +0 -110
- package/layers/core/server/schema/introspect.ts +0 -68
- package/layers/core/server/schema/model.ts +0 -61
- package/layers/core/server/schema/render-sqlite.ts +0 -89
- package/layers/core/server/schema/sync.ts +0 -201
- package/layers/core/server/utils/blocks.ts +0 -6
- package/layers/core/server/utils/collection-types.ts +0 -28
- package/layers/core/server/utils/crud.ts +0 -578
- package/layers/core/server/utils/db.ts +0 -26
- package/layers/core/server/utils/defineCollection.ts +0 -213
- package/layers/core/server/utils/duplicate.ts +0 -162
- package/layers/core/server/utils/field-layout.ts +0 -125
- package/layers/core/server/utils/filter-predicate.ts +0 -101
- package/layers/core/server/utils/http.ts +0 -50
- package/layers/core/server/utils/if-unmodified.ts +0 -13
- package/layers/core/server/utils/kestrel-config.ts +0 -451
- package/layers/core/server/utils/key-lock.ts +0 -33
- package/layers/core/server/utils/locale.ts +0 -53
- package/layers/core/server/utils/page-route.ts +0 -58
- package/layers/core/server/utils/page-slug.ts +0 -127
- package/layers/core/server/utils/picker.ts +0 -82
- package/layers/core/server/utils/populate.ts +0 -50
- package/layers/core/server/utils/prerender-memo.ts +0 -23
- package/layers/core/server/utils/read-capture.ts +0 -37
- package/layers/core/server/utils/record-ref-index.ts +0 -200
- package/layers/core/server/utils/registry.ts +0 -24
- package/layers/core/server/utils/resolve-scope.ts +0 -79
- package/layers/core/server/utils/seo.ts +0 -42
- package/layers/core/server/utils/serialize-collection.ts +0 -147
- package/layers/core/server/utils/sql.ts +0 -2
- package/layers/core/server/utils/storage.local.ts +0 -117
- package/layers/core/server/utils/storage.s3.ts +0 -197
- package/layers/core/server/utils/storage.ts +0 -90
- package/layers/core/server/utils/write-effects.ts +0 -40
- package/layers/core/server/utils/write-events.ts +0 -31
- package/layers/fields/app/utils/condition.ts +0 -71
- package/layers/fields/app/utils/define-block.ts +0 -13
- package/layers/fields/app/utils/field-constraints.ts +0 -17
- package/layers/fields/app/utils/field-meta.ts +0 -33
- package/layers/fields/app/utils/field-validate.ts +0 -75
- package/layers/fields/app/utils/field-value.ts +0 -7
- package/layers/fields/app/utils/richtext-links.ts +0 -48
- package/layers/fields/server/api/blocks/index.get.ts +0 -9
- package/layers/fields/server/field-registry/index.ts +0 -249
- package/layers/fields/server/field-registry/naming.ts +0 -10
- package/layers/fields/server/field-registry/sanitize.ts +0 -43
- package/layers/fields/server/field-registry/types.ts +0 -19
- package/layers/fields/server/utils/block-ids.ts +0 -43
- package/layers/fields/server/utils/block-populate.ts +0 -41
- package/layers/fields/server/utils/buildCollection.ts +0 -90
- package/layers/fields/server/utils/buildFieldSchema.ts +0 -22
- package/layers/fields/server/utils/buildTable.ts +0 -113
- package/layers/fields/server/utils/conditional-required.ts +0 -25
- package/layers/fields/server/utils/defineBlock.ts +0 -86
- package/layers/fields/server/utils/defineFieldType.ts +0 -33
- package/layers/fields/server/utils/extract-refs.ts +0 -152
- package/layers/fields/server/utils/field-populate.ts +0 -68
- package/layers/media/server/api/media/[id]/usages.get.ts +0 -8
- package/layers/media/server/api/media/[id].delete.ts +0 -18
- package/layers/media/server/api/media/[id].get.ts +0 -10
- package/layers/media/server/api/media/[id].patch.test.ts +0 -66
- package/layers/media/server/api/media/[id].patch.ts +0 -65
- package/layers/media/server/api/media/backfill.post.ts +0 -18
- package/layers/media/server/api/media/copy.post.ts +0 -7
- package/layers/media/server/api/media/delete.post.ts +0 -13
- package/layers/media/server/api/media/folders.post.ts +0 -18
- package/layers/media/server/api/media/index.get.ts +0 -15
- package/layers/media/server/api/media/index.post.ts +0 -173
- package/layers/media/server/api/media/library.get.ts +0 -17
- package/layers/media/server/api/media/move.post.ts +0 -7
- package/layers/media/server/api/media/rename.post.ts +0 -7
- package/layers/media/server/api/media/resolve.get.ts +0 -13
- package/layers/media/server/collections/media-settings.ts +0 -25
- package/layers/media/server/collections/media.ts +0 -43
- package/layers/media/server/database/folders.ts +0 -10
- package/layers/media/server/plugins/03.media-cleanup.ts +0 -20
- package/layers/media/server/schema-tables/folders.ts +0 -4
- package/layers/media/server/utils/ai-disclosure-enabled.ts +0 -16
- package/layers/media/server/utils/ai-signal-detect.ts +0 -155
- package/layers/media/server/utils/backfill.ts +0 -144
- package/layers/media/server/utils/derive.ts +0 -59
- package/layers/media/server/utils/folder-paths.ts +0 -42
- package/layers/media/server/utils/folders.ts +0 -12
- package/layers/media/server/utils/library.ts +0 -91
- package/layers/media/server/utils/media-cleanup.ts +0 -27
- package/layers/media/server/utils/media-enabled.ts +0 -23
- package/layers/media/server/utils/media-ops.ts +0 -118
- package/layers/media/server/utils/media-write.ts +0 -13
- package/layers/media/server/utils/naming.ts +0 -52
- package/layers/media/server/utils/ondemand.ts +0 -69
- package/layers/media/server/utils/persist-upload.ts +0 -62
- package/layers/media/server/utils/populate.ts +0 -47
- package/layers/media/server/utils/record.ts +0 -71
- package/layers/media/server/utils/relocate-ops.ts +0 -341
- package/layers/media/server/utils/relocate-plan.ts +0 -63
- package/layers/media/server/utils/resolve.ts +0 -98
- package/layers/media/server/utils/sanitize-svg.ts +0 -12
- package/layers/media/server/utils/sniff.ts +0 -68
- package/layers/media/server/utils/storage-relocate.ts +0 -139
- package/layers/media/server/utils/translations.ts +0 -19
- package/layers/media/server/utils/usages.ts +0 -66
- package/layers/media/server/utils/variant-capture.ts +0 -70
- package/layers/media/server/utils/variants.ts +0 -123
- package/layers/public/modules/deploy-output/deploy-output.ts +0 -384
- package/layers/public/server/api/links/resolve.get.ts +0 -19
- package/layers/public/server/api/preview.get.ts +0 -28
- package/layers/public/server/api/preview.post.ts +0 -93
- package/layers/public/server/api/publish-status.get.ts +0 -58
- package/layers/public/server/api/publish.post.ts +0 -84
- package/layers/public/server/api/route.get.ts +0 -54
- package/layers/public/server/collections/redirects.ts +0 -75
- package/layers/public/server/collections/site.ts +0 -44
- package/layers/public/server/database/publish-deps.ts +0 -12
- package/layers/public/server/database/publish-status.ts +0 -19
- package/layers/public/server/schema-tables/publish-deps.ts +0 -4
- package/layers/public/server/schema-tables/publish-status.ts +0 -4
- package/layers/public/server/utils/link-resolve.ts +0 -38
- package/layers/public/server/utils/llms-full.ts +0 -125
- package/layers/public/server/utils/llms.ts +0 -52
- package/layers/public/server/utils/page-resolve.ts +0 -208
- package/layers/public/server/utils/populate-links.ts +0 -66
- package/layers/public/server/utils/preview-token.ts +0 -109
- package/layers/public/server/utils/publish/coalesce.ts +0 -22
- package/layers/public/server/utils/publish/deps-persistence.ts +0 -43
- package/layers/public/server/utils/publish/deps.ts +0 -87
- package/layers/public/server/utils/publish/invalidation.ts +0 -225
- package/layers/public/server/utils/publish/pending.ts +0 -74
- package/layers/public/server/utils/publish/publish-runtime.ts +0 -28
- package/layers/public/server/utils/publish/publish-status.ts +0 -72
- package/layers/public/server/utils/publish/publisher.ts +0 -362
- package/layers/public/server/utils/publish/queue.ts +0 -67
- package/layers/public/server/utils/publish/redirect-rules.ts +0 -221
- package/layers/public/server/utils/publish/redirects-artifact.ts +0 -20
- package/layers/public/server/utils/publish/route-for-record.ts +0 -16
- package/layers/public/server/utils/publish/route-keys.ts +0 -13
- package/layers/public/server/utils/richtext-markdown.ts +0 -260
- package/layers/public/server/utils/site-url.ts +0 -40
- package/layers/public/server/utils/sitemap.ts +0 -99
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DepsStore } from '
|
|
3
|
-
import { createSqlitePersistence } from '../../utils/publish/deps-persistence'
|
|
1
|
+
import { renderRouteLive } from '../../utils/publish/render-live'
|
|
2
|
+
import { publishFull, outputDriver, DepsStore, createSqlitePersistence, usePublishingDb, setRenderRouteLive } from '@michaelthielemann/kestrel-publishing'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* The shared publish engine, exposed as a Nitro task. Renders every published page (+ sitemap/robots)
|
|
@@ -17,9 +16,15 @@ import { createSqlitePersistence } from '../../utils/publish/deps-persistence'
|
|
|
17
16
|
export default defineTask({
|
|
18
17
|
meta: { name: 'publish:run', description: 'Render all published pages to the configured static output (HTML + _nuxt + assets)' },
|
|
19
18
|
async run() {
|
|
19
|
+
// Wired explicitly here (not a module-load side effect): Nitro dev re-evaluates a task's module graph
|
|
20
|
+
// per invocation for hot-reload, which can give this call and `publisher.ts`'s a differently-scoped
|
|
21
|
+
// `@michaelthielemann/kestrel-publishing` instance if the wiring only ran once, earlier, at a boot-time import — calling
|
|
22
|
+
// the setter fresh, synchronously, right before the only call that needs it removes that dependency on
|
|
23
|
+
// import-order/instance-identity entirely. Idempotent: safe to call before every run.
|
|
24
|
+
setRenderRouteLive(renderRouteLive)
|
|
20
25
|
// The same durable store the boot-publish plugin uses, so a manually-triggered run also prunes
|
|
21
26
|
// routes that left the published set and records deps for later incremental publishes.
|
|
22
|
-
const deps = new DepsStore(createSqlitePersistence(
|
|
27
|
+
const deps = new DepsStore(createSqlitePersistence(usePublishingDb().db))
|
|
23
28
|
const result = await publishFull(outputDriver(), deps)
|
|
24
29
|
console.info(`[kestrel] publish:run rendered ${result.rendered} route(s), pruned ${result.pruned}`)
|
|
25
30
|
return { result }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { runAsRenderer } from '@michaelthielemann/kestrel-access'
|
|
2
|
+
|
|
3
|
+
/** Render a public route via the in-process server, returning its HTML buffer (200) or a null body with the
|
|
4
|
+
* HTTP status, so the caller can tell a broken page (5xx — a server-error RESPONSE, not a throw) from an
|
|
5
|
+
* expected non-200 (a draft / unpublish race → 404 / redirect). The PRODUCER's own render primitive — the
|
|
6
|
+
* delivery-static side never calls this; see `@michaelthielemann/kestrel-delivery-static`'s `render-route.ts`. `@michaelthielemann/kestrel-publishing`
|
|
7
|
+
* never imports this directly (a package cannot reach `useNitroApp`) — each real
|
|
8
|
+
* entry point (`zz.publish.ts`, `tasks/publish/run.ts`) instead calls `setRenderRouteLive(renderRouteLive)`
|
|
9
|
+
* itself, right before the call that needs it (see either file's own comment for why NOT a module-load
|
|
10
|
+
* side effect: Nitro dev can re-evaluate a task's module graph per invocation). */
|
|
11
|
+
export async function renderRouteLive(route: string): Promise<{ body: Buffer | null; status: number }> {
|
|
12
|
+
// Run as the renderer principal so the nested $fetch('/api/route') passes the access guard (it is not a
|
|
13
|
+
// public endpoint). The ALS context propagates through localFetch → the page render → the nested fetch.
|
|
14
|
+
// useNitroApp is imported LAZILY (not via the Nitro auto-import) on purpose: a static import gives this
|
|
15
|
+
// module an edge to nitropack's app.mjs, which both defines useNitroApp AND loads the plugin registry
|
|
16
|
+
// that reaches this file (via zz.publish) — a build-time circular dependency. A dynamic import is a chunk
|
|
17
|
+
// boundary, so that cycle disappears from the build graph; it's the same nitroApp singleton either way
|
|
18
|
+
// (Rollup bundles the literal specifier; useNitroApp is only CALLED here at publish time, long after boot).
|
|
19
|
+
const { useNitroApp } = await import('nitropack/runtime')
|
|
20
|
+
// Every consumer that needs to know "is this OUR OWN in-process render" (delivery-live's catch-all,
|
|
21
|
+
// media's variant-capture scan) reads `isRendererContext()`/`import.meta.prerender` instead of a header —
|
|
22
|
+
// both are set by `runAsRenderer` below and an external request can't forge either.
|
|
23
|
+
const res = await runAsRenderer(() => useNitroApp().localFetch(route, { method: 'GET' }))
|
|
24
|
+
if (res.status !== 200) return { body: null, status: res.status }
|
|
25
|
+
return { body: Buffer.from(await res.arrayBuffer()), status: res.status }
|
|
26
|
+
}
|
|
@@ -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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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
|
-
//
|
|
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 '
|
|
5
|
+
import { fieldConstraints } from '@michaelthielemann/kestrel-fields/client'
|
|
6
6
|
import type { FieldComponentProps } from '../../utils/field-component'
|
|
7
|
-
import type { FieldOf } from '
|
|
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 '
|
|
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 '
|
|
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
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
|
-
import { slugify } from '
|
|
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 '
|
|
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>()
|
|
@@ -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 '
|
|
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 '
|
|
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);
|
|
@@ -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 '
|
|
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 '
|
|
3
|
+
import type { FieldDef } from '@michaelthielemann/kestrel-core'
|
|
4
4
|
import { reorder } from '../utils/reorder'
|
|
5
5
|
import { useEchoGuard } from './useEchoGuard'
|
|
6
6
|
|
package/layers/ui/app/i18n/de.ts
CHANGED
|
@@ -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
|
-
|
|
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',
|
package/layers/ui/app/i18n/en.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { Catalog } from '../composables/useT'
|
|
|
4
4
|
// carry `{name}` placeholders interpolated by `useT().t`. Content (collection data) is NOT translated
|
|
5
5
|
// here — only the dashboard chrome. Keep `en` the complete set; other catalogs may be partial.
|
|
6
6
|
export const en: Catalog = {
|
|
7
|
-
// common / shared
|
|
8
7
|
'common.new': 'New {label}',
|
|
9
8
|
'common.save': 'Save',
|
|
10
9
|
'common.publish': 'Publish',
|
|
@@ -14,7 +13,6 @@ export const en: Catalog = {
|
|
|
14
13
|
'common.close': 'Close',
|
|
15
14
|
'common.retry': 'Retry',
|
|
16
15
|
|
|
17
|
-
// navigation rail + shell
|
|
18
16
|
'nav.dashboard': 'Dashboard',
|
|
19
17
|
'nav.signOut': 'Sign out',
|
|
20
18
|
'theme.dark': 'Dark mode',
|
|
@@ -27,21 +25,17 @@ export const en: Catalog = {
|
|
|
27
25
|
'a11y.rowActions': 'Actions',
|
|
28
26
|
'lang.label': 'Language',
|
|
29
27
|
|
|
30
|
-
// account menu (rail foot)
|
|
31
28
|
'account.label': 'Account',
|
|
32
29
|
'account.name': 'Admin',
|
|
33
30
|
|
|
34
|
-
// text input (password reveal)
|
|
35
31
|
'input.showPassword': 'Show password',
|
|
36
32
|
'input.hidePassword': 'Hide password',
|
|
37
33
|
|
|
38
|
-
// login
|
|
39
34
|
'login.signIn': 'Sign in',
|
|
40
35
|
'login.password': 'Password',
|
|
41
36
|
'login.invalid': 'Invalid credentials',
|
|
42
37
|
'login.error': 'Something went wrong — please try again',
|
|
43
38
|
|
|
44
|
-
// dashboard
|
|
45
39
|
'dash.title': 'Dashboard',
|
|
46
40
|
'dash.lede': 'Choose a collection to manage its content.',
|
|
47
41
|
'dash.empty.title': 'No collections yet',
|
|
@@ -49,10 +43,8 @@ export const en: Catalog = {
|
|
|
49
43
|
'dash.mode.singleton': 'Singleton',
|
|
50
44
|
'dash.mode.collection': 'Collection',
|
|
51
45
|
|
|
52
|
-
// collection (list page wrapper)
|
|
53
46
|
'collection.unknown': 'Unknown collection: {name}',
|
|
54
47
|
|
|
55
|
-
// record editor page + toasts
|
|
56
48
|
'editor.back': '{collection}',
|
|
57
49
|
'editor.newRecord': 'New {collection}',
|
|
58
50
|
'editor.editRecord': 'Edit {collection} #{id}',
|
|
@@ -61,6 +53,8 @@ export const en: Catalog = {
|
|
|
61
53
|
'editor.fixBlockContent': 'Some block content is invalid. Check the highlighted block fields.',
|
|
62
54
|
'editor.saveConflict': 'This conflicts with an existing record.',
|
|
63
55
|
'editor.saveFailed': 'Save failed.',
|
|
56
|
+
'editor.quarantined.title': 'Record quarantined',
|
|
57
|
+
'editor.quarantined.desc': 'This record no longer matches its collection’s schema and is shown read-only. It cannot be edited or saved until the underlying data is fixed.',
|
|
64
58
|
'toast.saved': 'Saved',
|
|
65
59
|
'toast.deleted': 'Deleted',
|
|
66
60
|
'toast.duplicated': 'Duplicated',
|
|
@@ -69,7 +63,6 @@ export const en: Catalog = {
|
|
|
69
63
|
'toast.region': 'Notifications',
|
|
70
64
|
'toast.dismiss': 'Dismiss',
|
|
71
65
|
|
|
72
|
-
// collection list
|
|
73
66
|
'list.translations': 'Translations',
|
|
74
67
|
'list.deadRefs': 'Refs',
|
|
75
68
|
'list.filter': 'Filter',
|
|
@@ -113,7 +106,9 @@ export const en: Catalog = {
|
|
|
113
106
|
'list.result': '{total} result',
|
|
114
107
|
'list.results': '{total} results',
|
|
115
108
|
'list.loadError': "Couldn't load this collection.",
|
|
116
|
-
|
|
109
|
+
'list.quarantined': 'Quarantined',
|
|
110
|
+
'list.quarantinedOne': '{n} quarantined record',
|
|
111
|
+
'list.quarantinedCount': '{n} quarantined records',
|
|
117
112
|
'list.selectAll': 'Select all rows on this page',
|
|
118
113
|
'list.selectRow': 'Select {name}',
|
|
119
114
|
'list.selected': '{n} selected',
|
|
@@ -129,8 +124,9 @@ export const en: Catalog = {
|
|
|
129
124
|
'list.deleteRefsUnverified': 'References could not be verified — some records may be left with a broken link. Proceed with caution.',
|
|
130
125
|
'list.opFailed': 'The action could not be completed.',
|
|
131
126
|
'list.refCheckFailed': 'References could not be checked.',
|
|
127
|
+
'list.actionConfirm': 'Run "{name}" on {n} record(s)?',
|
|
128
|
+
'list.actionDone': '{name} done',
|
|
132
129
|
|
|
133
|
-
// editor preview pane
|
|
134
130
|
'preview.ariaLabel': 'Live preview',
|
|
135
131
|
'preview.label': 'Preview',
|
|
136
132
|
'preview.deviceLabel': 'Preview size',
|
|
@@ -142,7 +138,6 @@ export const en: Catalog = {
|
|
|
142
138
|
'preview.width': 'Width',
|
|
143
139
|
'preview.height': 'Height',
|
|
144
140
|
|
|
145
|
-
// page settings (system fields on pageLike collections)
|
|
146
141
|
'pageSettings.slugLabel': 'Slug',
|
|
147
142
|
'pageSettings.slugHint': 'URL path, e.g. /about (blank = auto-generated from the title)',
|
|
148
143
|
'pageSettings.layoutLabel': 'Layout',
|
|
@@ -153,7 +148,6 @@ export const en: Catalog = {
|
|
|
153
148
|
'pageSettings.statusDraft': 'Draft',
|
|
154
149
|
'pageSettings.statusPublished': 'Published',
|
|
155
150
|
|
|
156
|
-
// editor action bar — live preview + status "Ampel"
|
|
157
151
|
'editor.openInNewTab': 'Open in new tab',
|
|
158
152
|
'editor.previewUnsaved': 'Preview unsaved changes in a new tab',
|
|
159
153
|
'editor.previewFailed': 'Could not open the preview.',
|
|
@@ -189,7 +183,6 @@ export const en: Catalog = {
|
|
|
189
183
|
'editorStatus.live.draft': 'Draft — not generated. It stays off the live site until published.',
|
|
190
184
|
'editorStatus.live.updatedAt': 'Last published {time}',
|
|
191
185
|
|
|
192
|
-
// SEO section + Google preview
|
|
193
186
|
'seo.sectionLabel': 'SEO',
|
|
194
187
|
'seo.metaTitle': 'Meta title',
|
|
195
188
|
'seo.metaDescription': 'Meta description',
|
|
@@ -208,7 +201,6 @@ export const en: Catalog = {
|
|
|
208
201
|
'seo.keywords': 'Keywords',
|
|
209
202
|
'seo.keywordsHint': 'Comma-separated topics for this page.',
|
|
210
203
|
|
|
211
|
-
// locale bar (translations)
|
|
212
204
|
'localeBar.fieldLabel': 'Locale',
|
|
213
205
|
'localeBar.groupLabel': 'Content locale',
|
|
214
206
|
'localeBar.editLocale': 'Edit {loc}',
|
|
@@ -216,12 +208,10 @@ export const en: Catalog = {
|
|
|
216
208
|
'localeBar.createTranslation': 'Create {loc} translation',
|
|
217
209
|
'localeBar.copyFailed': "Couldn't copy from {locale}.",
|
|
218
210
|
|
|
219
|
-
// editor history (undo/redo)
|
|
220
211
|
'history.label': 'Edit history',
|
|
221
212
|
'history.undo': 'Undo',
|
|
222
213
|
'history.redo': 'Redo',
|
|
223
214
|
|
|
224
|
-
// block editor
|
|
225
215
|
'blocks.empty': 'No blocks yet.',
|
|
226
216
|
'blocks.moveUp': 'Move block {n} up',
|
|
227
217
|
'blocks.moveDown': 'Move block {n} down',
|
|
@@ -238,7 +228,6 @@ export const en: Catalog = {
|
|
|
238
228
|
'blocks.fieldsLabel': 'Block fields',
|
|
239
229
|
'blocks.treeLabel': 'Block structure',
|
|
240
230
|
|
|
241
|
-
// dead references (derived stale-reference warnings)
|
|
242
231
|
'deadRefs.listWarning': 'Links to a deleted or unpublished record',
|
|
243
232
|
'deadRefs.blockBadge': 'Broken reference',
|
|
244
233
|
'deadRefs.fieldNote': 'Points at a deleted or unpublished record.',
|
|
@@ -256,16 +245,13 @@ export const en: Catalog = {
|
|
|
256
245
|
'refs.reason.unpublished': 'Unpublished',
|
|
257
246
|
'refs.dashAlert': '{n} broken reference(s) — review',
|
|
258
247
|
|
|
259
|
-
// field gallery
|
|
260
248
|
'gallery.title': 'Field gallery',
|
|
261
249
|
'gallery.lede.before': 'Every implemented field widget, live. The relation and internal-link pickers query ',
|
|
262
250
|
'gallery.lede.after': ' — create a post or two to see results.',
|
|
263
251
|
|
|
264
|
-
// media library
|
|
265
252
|
'media.title': 'Media',
|
|
266
253
|
'media.picker.titleSingle': 'Select a file',
|
|
267
254
|
'media.picker.titleMultiple': 'Select media',
|
|
268
|
-
// media — right-click context menu
|
|
269
255
|
'media.menu.cut': 'Cut',
|
|
270
256
|
'media.menu.copy': 'Copy',
|
|
271
257
|
'media.menu.paste': 'Paste',
|
|
@@ -295,7 +281,6 @@ export const en: Catalog = {
|
|
|
295
281
|
'media.selected': 'selected',
|
|
296
282
|
'media.useSelected': 'Use selected',
|
|
297
283
|
'media.dropToUpload': 'Drop files to upload',
|
|
298
|
-
// media — upload conflict resolution
|
|
299
284
|
'media.conflicts.title': 'Resolve upload conflicts',
|
|
300
285
|
'media.conflicts.desc': '{count} file(s) already exist. Choose how to resolve each.',
|
|
301
286
|
'media.conflicts.applyAll': 'Apply to all:',
|
|
@@ -306,20 +291,17 @@ export const en: Catalog = {
|
|
|
306
291
|
'media.conflicts.overwrite': 'Overwrite',
|
|
307
292
|
'media.conflicts.rename': 'Rename',
|
|
308
293
|
'media.conflicts.skip': 'Skip',
|
|
309
|
-
// media — new folder / rename dialogs
|
|
310
294
|
'media.newFolder': 'New folder',
|
|
311
295
|
'media.folderName': 'Folder name',
|
|
312
296
|
'media.folderNamePlaceholder': 'e.g. holiday',
|
|
313
297
|
'media.rename': 'Rename',
|
|
314
298
|
'media.newName': 'New name',
|
|
315
|
-
// media — delete dialog
|
|
316
299
|
'media.deleteSummary': 'Delete {files} {filesUnit} and {folders} {foldersUnit} — {size}.',
|
|
317
300
|
'media.file': 'file',
|
|
318
301
|
'media.files': 'files',
|
|
319
302
|
'media.folder': 'folder',
|
|
320
303
|
'media.folders': 'folders',
|
|
321
304
|
'media.usageWarning': 'These files are used by published content and links will break:',
|
|
322
|
-
// media — move/copy conflict dialog
|
|
323
305
|
'media.conflictTitleMove': 'Move — conflicts',
|
|
324
306
|
'media.conflictTitleCopy': 'Copy — conflicts',
|
|
325
307
|
'media.conflictItems': '{count} item(s) already exist in "{dest}":',
|
|
@@ -329,7 +311,6 @@ export const en: Catalog = {
|
|
|
329
311
|
'media.skip': 'Skip',
|
|
330
312
|
'media.replace': 'Replace',
|
|
331
313
|
'media.keepBoth': 'Keep both',
|
|
332
|
-
// media — table view
|
|
333
314
|
'media.colName': 'Name',
|
|
334
315
|
'media.colType': 'Type',
|
|
335
316
|
'media.colSize': 'Size',
|
|
@@ -337,7 +318,6 @@ export const en: Catalog = {
|
|
|
337
318
|
'media.typeFolder': 'Folder',
|
|
338
319
|
'media.parentFolder': 'Go to the parent folder',
|
|
339
320
|
|
|
340
|
-
// media — viewer
|
|
341
321
|
'mediaViewer.folder': 'Folder',
|
|
342
322
|
'mediaViewer.uploaded': 'Uploaded',
|
|
343
323
|
'mediaViewer.alt': 'Alt text',
|
|
@@ -351,7 +331,6 @@ export const en: Catalog = {
|
|
|
351
331
|
'mediaViewer.aiNote': 'Disclosure note',
|
|
352
332
|
'mediaViewer.aiNoteHint': 'Free text, e.g. the tool used. Pre-filled with any AI signals found in the file at upload — evidence only, not a verified claim.',
|
|
353
333
|
|
|
354
|
-
// richtext toolbar
|
|
355
334
|
'richtext.bold': 'Bold',
|
|
356
335
|
'richtext.italic': 'Italic',
|
|
357
336
|
'richtext.underline': 'Underline',
|
|
@@ -386,7 +365,6 @@ export const en: Catalog = {
|
|
|
386
365
|
'richtext.linkUrl': 'Link URL',
|
|
387
366
|
'richtext.apply': 'Apply',
|
|
388
367
|
|
|
389
|
-
// media toolbar
|
|
390
368
|
'mediaToolbar.searchPlaceholder': 'Search…',
|
|
391
369
|
'mediaToolbar.searchAriaLabel': 'Search media',
|
|
392
370
|
'mediaToolbar.viewGrid': 'Grid',
|
|
@@ -395,18 +373,14 @@ export const en: Catalog = {
|
|
|
395
373
|
'mediaToolbar.upload': 'Upload',
|
|
396
374
|
'mediaToolbar.newFolder': 'New folder',
|
|
397
375
|
|
|
398
|
-
// media path bar
|
|
399
376
|
'mediaPath.navAriaLabel': 'Path',
|
|
400
377
|
'mediaPath.editAriaLabel': 'Edit path',
|
|
401
378
|
'mediaPath.inputAriaLabel': 'Enter path',
|
|
402
379
|
|
|
403
|
-
// collections nav
|
|
404
380
|
'nav.collections': 'Collections',
|
|
405
381
|
|
|
406
|
-
// field widgets — datetime
|
|
407
382
|
'field.datetime.range_start': 'Start',
|
|
408
383
|
'field.datetime.range_end': 'End',
|
|
409
|
-
// field widgets — link
|
|
410
384
|
'field.link.type_internal': 'Internal',
|
|
411
385
|
'field.link.type_url': 'URL',
|
|
412
386
|
'field.link.type_email': 'Email',
|
|
@@ -415,14 +389,11 @@ export const en: Catalog = {
|
|
|
415
389
|
'field.link.url_placeholder': 'https://…',
|
|
416
390
|
'field.link.label_placeholder': 'Link text (optional)',
|
|
417
391
|
'field.link.hash_placeholder': 'Anchor (optional, e.g. section-id)',
|
|
418
|
-
// field widgets — relation
|
|
419
392
|
'field.relation.placeholder': 'Search…',
|
|
420
|
-
// field widgets — internal-link picker
|
|
421
393
|
'field.linkPicker.loadError': 'Could not load collections.',
|
|
422
394
|
'field.linkPicker.chooseCollection': 'Choose a collection…',
|
|
423
395
|
'field.linkPicker.collectionLabel': 'Collection',
|
|
424
396
|
'field.linkPicker.searchRecords': 'Search records…',
|
|
425
|
-
// field widgets — repeater
|
|
426
397
|
'field.repeater.moved': 'Moved item to position {n} of {total}',
|
|
427
398
|
'field.repeater.removed': 'Item {n} removed, {remaining} remaining',
|
|
428
399
|
'field.repeater.duplicated': 'Duplicated item {n}',
|
|
@@ -435,7 +406,6 @@ export const en: Catalog = {
|
|
|
435
406
|
'field.repeater.remove_label': 'Remove item {n}',
|
|
436
407
|
'field.repeater.empty': 'No items yet.',
|
|
437
408
|
'field.repeater.add': 'Add item',
|
|
438
|
-
// field widgets — media
|
|
439
409
|
'field.media.empty': 'No media selected.',
|
|
440
410
|
'field.media.add': 'Add media',
|
|
441
411
|
'field.media.replace': 'Replace',
|
|
@@ -446,7 +416,6 @@ export const en: Catalog = {
|
|
|
446
416
|
'field.media.move_later': 'Move {name} later',
|
|
447
417
|
'field.media.moved': 'Moved {name} to position {pos} of {total}',
|
|
448
418
|
|
|
449
|
-
// combobox
|
|
450
419
|
'combobox.moved': 'Moved {label} to position {pos} of {total}',
|
|
451
420
|
'combobox.move_earlier': 'Move {label} earlier',
|
|
452
421
|
'combobox.move_later': 'Move {label} later',
|
|
@@ -456,7 +425,6 @@ export const en: Catalog = {
|
|
|
456
425
|
'combobox.loading': 'Loading…',
|
|
457
426
|
'combobox.no_results': 'No results',
|
|
458
427
|
|
|
459
|
-
// date pickers
|
|
460
428
|
'datePicker.openCalendar': 'Open calendar',
|
|
461
429
|
'datePicker.previousMonth': 'Previous month',
|
|
462
430
|
'datePicker.nextMonth': 'Next month',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FieldDef } from '
|
|
1
|
+
import type { FieldDef } from '@michaelthielemann/kestrel-core'
|
|
2
2
|
|
|
3
3
|
/** A record reference: its id plus a human label. Shared by UiCombobox and FieldRelation. */
|
|
4
4
|
export interface FieldOption { value: number; label: string }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineAsyncComponent, type Component } from 'vue'
|
|
2
|
-
import type { FieldType } from '
|
|
2
|
+
import type { FieldType } from '@michaelthielemann/kestrel-core'
|
|
3
3
|
import FieldText from '../components/field/Text.vue'
|
|
4
4
|
import FieldNumber from '../components/field/Number.vue'
|
|
5
5
|
import FieldBoolean from '../components/field/Boolean.vue'
|
|
@@ -3,19 +3,14 @@ export type IconName =
|
|
|
3
3
|
| 'copy' | 'trash' | 'x' | 'plus' | 'pencil' | 'check'
|
|
4
4
|
| 'home' | 'file-text' | 'folder' | 'folder-plus' | 'panel-left-close' | 'panel-left-open' | 'log-out'
|
|
5
5
|
| 'sun' | 'moon' | 'calendar' | 'upload' | 'layout-grid' | 'table'
|
|
6
|
-
// collection icons + list toolbar
|
|
7
6
|
| 'settings' | 'image' | 'newspaper' | 'columns' | 'filter' | 'search' | 'sliders'
|
|
8
|
-
// richtext toolbar
|
|
9
7
|
| 'bold' | 'italic' | 'underline' | 'strikethrough' | 'code' | 'square-code' | 'highlighter'
|
|
10
8
|
| 'subscript' | 'superscript' | 'list' | 'list-ordered' | 'quote' | 'minus' | 'message-square'
|
|
11
9
|
| 'align-left' | 'align-center' | 'align-right' | 'align-justify'
|
|
12
10
|
| 'eraser' | 'undo' | 'redo' | 'link' | 'link-2'
|
|
13
|
-
| 'external-link' | 'triangle-alert'
|
|
14
|
-
// password reveal
|
|
11
|
+
| 'external-link' | 'triangle-alert' | 'globe'
|
|
15
12
|
| 'eye' | 'eye-off'
|
|
16
|
-
// preview device widths + reload
|
|
17
13
|
| 'monitor' | 'tablet' | 'smartphone' | 'rotate-cw'
|
|
18
|
-
// block-type tiles
|
|
19
14
|
| 'clock' | 'user'
|
|
20
15
|
|
|
21
16
|
export const icons: Record<IconName, string> = {
|
|
@@ -74,6 +69,7 @@ export const icons: Record<IconName, string> = {
|
|
|
74
69
|
'link': '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
|
|
75
70
|
'link-2': '<path d="M9 17H7A5 5 0 0 1 7 7h2"/><path d="M15 7h2a5 5 0 1 1 0 10h-2"/><line x1="8" x2="16" y1="12" y2="12"/>',
|
|
76
71
|
'external-link': '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6"/>',
|
|
72
|
+
'globe': '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>',
|
|
77
73
|
'triangle-alert': '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
|
|
78
74
|
'settings': '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>',
|
|
79
75
|
'image': '<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/>',
|