@elytracms/studio 0.0.97 → 0.0.99
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/convex/assets.ts +16 -2
- package/convex/ingest-attempt.test.ts +161 -0
- package/convex/ingest.test.ts +269 -16
- package/convex/ingest.ts +238 -47
- package/convex/path-segment-refusal.test.ts +201 -0
- package/convex/prototype-key.test.ts +92 -0
- package/convex/records-column-transform.convex.test.ts +147 -0
- package/convex/records-zod-refinements.convex.test.ts +94 -0
- package/convex/schema.ts +1 -1
- package/dist/addon.d.ts +5 -3
- package/dist/addon.js +4 -3
- package/dist/addon.js.map +1 -1
- package/dist/components/app-views/default-modules.d.ts +10 -3
- package/dist/components/app-views/default-modules.js +5 -1
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +15 -8
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/builder/reuse-context.d.ts +29 -1
- package/dist/components/builder/reuse-context.js +58 -12
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +5 -6
- package/dist/components/cms/cms-store.js +12 -24
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/field-control.d.ts +12 -1
- package/dist/components/cms/field-control.js +31 -6
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/import-batch.d.ts +23 -0
- package/dist/components/cms/import-batch.js +43 -0
- package/dist/components/cms/import-batch.js.map +1 -0
- package/dist/components/cms/rich-text/editor.js +13 -10
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/value.d.ts +9 -0
- package/dist/components/cms/rich-text/value.js +13 -0
- package/dist/components/cms/rich-text/value.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +39 -3
- package/dist/components/cms/use-document-draft.js +46 -16
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +7 -0
- package/dist/components/cms/use-publish-controller.js +17 -3
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/versions-pane.js +8 -5
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/delivery-health-banner.d.ts +34 -16
- package/dist/components/delivery-health-banner.js +59 -27
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/issue-severity.d.ts +3 -2
- package/dist/components/issue-severity.js.map +1 -1
- package/dist/components/nav-issues-registry.d.ts +39 -24
- package/dist/components/nav-issues-registry.js +50 -21
- package/dist/components/nav-issues-registry.js.map +1 -1
- package/dist/components/nav-issues.d.ts +23 -50
- package/dist/components/nav-issues.js +31 -59
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/notifications/notice-center.d.ts +61 -0
- package/dist/components/notifications/notice-center.js +119 -0
- package/dist/components/notifications/notice-center.js.map +1 -0
- package/dist/components/notifications/notice-model.d.ts +137 -0
- package/dist/components/notifications/notice-model.js +107 -0
- package/dist/components/notifications/notice-model.js.map +1 -0
- package/dist/components/notifications/outcome-notice.d.ts +16 -0
- package/dist/components/notifications/outcome-notice.js +34 -0
- package/dist/components/notifications/outcome-notice.js.map +1 -0
- package/dist/components/notifications/shell-findings.d.ts +36 -0
- package/dist/components/notifications/shell-findings.js +140 -0
- package/dist/components/notifications/shell-findings.js.map +1 -0
- package/dist/components/rail-view-source.js +5 -0
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/records/record-lens.js.map +1 -1
- package/dist/components/records/record-rail.d.ts +10 -2
- package/dist/components/records/record-rail.js +74 -4
- package/dist/components/records/record-rail.js.map +1 -1
- package/dist/components/section-nav.d.ts +15 -3
- package/dist/components/section-nav.js +39 -147
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +8 -3
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/components/use-narrow-viewport.d.ts +11 -0
- package/dist/components/use-narrow-viewport.js +25 -0
- package/dist/components/use-narrow-viewport.js.map +1 -0
- package/dist/components/users/role-policy-drift-issues.d.ts +14 -7
- package/dist/components/users/role-policy-drift-issues.js +17 -8
- package/dist/components/users/role-policy-drift-issues.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +4 -2
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +24 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +22 -2
- package/dist/lib/i18n/messages.en.js +27 -4
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/notices/toast-store.d.ts +61 -0
- package/dist/lib/notices/toast-store.js +116 -0
- package/dist/lib/notices/toast-store.js.map +1 -0
- package/dist/lib/operations/cms-persistence.d.ts +32 -7
- package/dist/lib/operations/cms-persistence.js +511 -71
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +61 -30
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persona/labels.js +1 -0
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/ui/toast.d.ts +24 -16
- package/dist/ui/toast.js +23 -32
- package/dist/ui/toast.js.map +1 -1
- package/package.json +4 -3
|
@@ -75,7 +75,20 @@ declare const MESSAGES: {
|
|
|
75
75
|
'shell.nav.planned': import("./types.js").SourceMessage;
|
|
76
76
|
'shell.nav.collapseView': import("./types.js").SourceMessage;
|
|
77
77
|
'shell.nav.expandView': import("./types.js").SourceMessage;
|
|
78
|
-
'shell.
|
|
78
|
+
'shell.notices.title': import("./types.js").SourceMessage;
|
|
79
|
+
'shell.notices.open': import("./types.js").SourceMessage;
|
|
80
|
+
'shell.notices.none': import("./types.js").SourceMessage;
|
|
81
|
+
'shell.notices.errors.one': import("./types.js").SourceMessage;
|
|
82
|
+
'shell.notices.errors.other': import("./types.js").SourceMessage;
|
|
83
|
+
'shell.notices.warnings.one': import("./types.js").SourceMessage;
|
|
84
|
+
'shell.notices.warnings.other': import("./types.js").SourceMessage;
|
|
85
|
+
'shell.notices.empty': import("./types.js").SourceMessage;
|
|
86
|
+
'shell.notices.goTo': import("./types.js").SourceMessage;
|
|
87
|
+
'shell.notices.dismiss': import("./types.js").SourceMessage;
|
|
88
|
+
'shell.notices.declaredInProject': import("./types.js").SourceMessage;
|
|
89
|
+
'shell.notices.declaredInStudio': import("./types.js").SourceMessage;
|
|
90
|
+
'shell.notices.deploymentSettings': import("./types.js").SourceMessage;
|
|
91
|
+
'shell.notices.reservedSource': import("./types.js").SourceMessage;
|
|
79
92
|
'shell.nav.orphanedCollections': import("./types.js").SourceMessage;
|
|
80
93
|
'shell.nav.orphanedCollections.item': import("./types.js").SourceMessage;
|
|
81
94
|
'shell.nav.orphanedCollections.itemPublished': import("./types.js").SourceMessage;
|
|
@@ -129,7 +142,9 @@ declare const MESSAGES: {
|
|
|
129
142
|
'shell.devLogin.hint': import("./types.js").SourceMessage;
|
|
130
143
|
'shell.playground.badge': import("./types.js").SourceMessage;
|
|
131
144
|
'shell.playground.hint': import("./types.js").SourceMessage;
|
|
132
|
-
'shell.deliveryHealth.
|
|
145
|
+
'shell.deliveryHealth.title': import("./types.js").SourceMessage;
|
|
146
|
+
'shell.deliveryHealth.missing': import("./types.js").SourceMessage;
|
|
147
|
+
'shell.deliveryHealth.unchecked': import("./types.js").SourceMessage;
|
|
133
148
|
'shell.rolePolicyDrift.title': import("./types.js").SourceMessage;
|
|
134
149
|
'customization.issue.noCollections': import("./types.js").SourceMessage;
|
|
135
150
|
'customization.issue.duplicateOverrideId': import("./types.js").SourceMessage;
|
|
@@ -399,6 +414,7 @@ declare const MESSAGES: {
|
|
|
399
414
|
'cms.issue.invalid-collection-config': import("./types.js").SourceMessage;
|
|
400
415
|
'cms.issue.invalid-field-config': import("./types.js").SourceMessage;
|
|
401
416
|
'cms.issue.reserved-field-name': import("./types.js").SourceMessage;
|
|
417
|
+
'cms.issue.prototype-key': import("./types.js").SourceMessage;
|
|
402
418
|
'cms.issue.missing-required-field': import("./types.js").SourceMessage;
|
|
403
419
|
'cms.issue.invalid-field-value': import("./types.js").SourceMessage;
|
|
404
420
|
'cms.issue.invalid-field-value.warning': import("./types.js").SourceMessage;
|
|
@@ -471,6 +487,7 @@ declare const MESSAGES: {
|
|
|
471
487
|
'cms.conflict.empty': import("./types.js").SourceMessage;
|
|
472
488
|
'cms.conflict.structured': import("./types.js").SourceMessage;
|
|
473
489
|
'cms.conflict.cellRefused': import("./types.js").SourceMessage;
|
|
490
|
+
'cms.conflict.aliasRefused': import("./types.js").SourceMessage;
|
|
474
491
|
'cms.conflict.unauthored': import("./types.js").SourceMessage;
|
|
475
492
|
'cms.presence.inField': import("./types.js").SourceMessage;
|
|
476
493
|
'cms.presence.inFieldMany': import("./types.js").SourceMessage;
|
|
@@ -492,6 +509,8 @@ declare const MESSAGES: {
|
|
|
492
509
|
'cms.issueMessage.unfilledSlot': import("./types.js").SourceMessage;
|
|
493
510
|
'cms.issueMessage.reservedFieldName': import("./types.js").SourceMessage;
|
|
494
511
|
'cms.issueMessage.reservedFieldTypeOption': import("./types.js").SourceMessage;
|
|
512
|
+
'cms.issueMessage.reservedPrototypeFieldName': import("./types.js").SourceMessage;
|
|
513
|
+
'cms.issueMessage.prototypeKey': import("./types.js").SourceMessage;
|
|
495
514
|
'cms.issueMessage.duplicateSelectOption': import("./types.js").SourceMessage;
|
|
496
515
|
'cms.issueMessage.duplicateObjectField': import("./types.js").SourceMessage;
|
|
497
516
|
'cms.issueMessage.assetCollectionSingleton': import("./types.js").SourceMessage;
|
|
@@ -651,6 +670,7 @@ declare const MESSAGES: {
|
|
|
651
670
|
'cms.field.slug.regenerate': import("./types.js").SourceMessage;
|
|
652
671
|
'cms.field.slug.taken': import("./types.js").SourceMessage;
|
|
653
672
|
'cms.field.slug.takenGeneric': import("./types.js").SourceMessage;
|
|
673
|
+
'cms.field.slug.routedComposesChildren': import("./types.js").SourceMessage;
|
|
654
674
|
'cms.relation.change': import("./types.js").SourceMessage;
|
|
655
675
|
'cms.relation.openTitle': import("./types.js").SourceMessage;
|
|
656
676
|
'cms.relation.pick': import("./types.js").SourceMessage;
|
|
@@ -87,7 +87,21 @@ const MESSAGES = {
|
|
|
87
87
|
'shell.nav.planned': msg('Planned', 'Tooltip on the status dot of a planned (not-yet-enabled) nav view.'),
|
|
88
88
|
'shell.nav.collapseView': msg('Collapse {name}', 'aria-label on a parent nav item disclosure; {name} is the view label.'),
|
|
89
89
|
'shell.nav.expandView': msg('Expand {name}', 'aria-label on a parent nav item disclosure; {name} is the view label.'),
|
|
90
|
-
|
|
90
|
+
// EC-690 — the ONE notification-and-issue surface: the header badge and its issues tray.
|
|
91
|
+
'shell.notices.title': msg('Issues', 'Title (and aria-label) of the issues tray the header badge opens.'),
|
|
92
|
+
'shell.notices.open': msg('Show issues: {summary}', 'aria-label of the header issues badge; {summary} is shell.notices.none or e.g. "2 errors, 1 warning".'),
|
|
93
|
+
'shell.notices.none': msg('No issues', 'The header badge summary when nothing needs attention.'),
|
|
94
|
+
'shell.notices.errors.one': msg('{count} error'),
|
|
95
|
+
'shell.notices.errors.other': msg('{count} errors'),
|
|
96
|
+
'shell.notices.warnings.one': msg('{count} warning'),
|
|
97
|
+
'shell.notices.warnings.other': msg('{count} warnings'),
|
|
98
|
+
'shell.notices.empty': msg('Nothing needs your attention.', 'The issues tray with no notice and no finding.'),
|
|
99
|
+
'shell.notices.goTo': msg('Go to', 'Link on a finding in the issues tray that opens where it is (a redirect row, a document).'),
|
|
100
|
+
'shell.notices.dismiss': msg('Dismiss', 'aria-label of the dismiss button on a notice in the issues tray.'),
|
|
101
|
+
'shell.notices.declaredInProject': msg('in {file}', 'Where a project-config finding is fixed; {file} is the project config file name (elytra.config.ts), untranslated.'),
|
|
102
|
+
'shell.notices.declaredInStudio': msg('in your defineStudio({…}) config', 'Where a studio-customization finding is fixed; defineStudio is code, untranslated.'),
|
|
103
|
+
'shell.notices.deploymentSettings': msg('Convex deployment: {settings}', 'Where a deployment finding is fixed; {settings} are environment variable names, untranslated.'),
|
|
104
|
+
'shell.notices.reservedSource': msg('An addon publishes findings under "{source}", a source name the engine reserves. Rename the source it passes to useNavIssues.', 'Error finding: an addon used an engine-reserved source name. {source} and useNavIssues are code, untranslated.'),
|
|
91
105
|
// EC-606 — stored documents whose collection the project no longer declares. One
|
|
92
106
|
// warning at the rail foot; {collections} joins one item message per collection.
|
|
93
107
|
'shell.nav.orphanedCollections': msg('Stored documents of collections this project does not declare are hidden from the studio: {collections}. To bring them back, declare the collection in "{file}" again (or restore its old id).', 'Warning at the rail foot. {collections} is a comma-separated list of shell.nav.orphanedCollections.item / .itemPublished; {file} is the project config file name (elytra.config.ts), untranslated.'),
|
|
@@ -145,8 +159,11 @@ const MESSAGES = {
|
|
|
145
159
|
'shell.devLogin.hint': msg('Dev auto-login: the studio signed in automatically from VITE_ELYTRA_DEV_LOGIN (dev builds only). Unset it to test the real sign-in flow.'),
|
|
146
160
|
'shell.playground.badge': msg('Playground', 'Product term — may stay untranslated.'),
|
|
147
161
|
'shell.playground.hint': msg('Playground mode: data lives in memory on this device and resets on reload. Nothing is saved to a backend.'),
|
|
148
|
-
|
|
149
|
-
|
|
162
|
+
// EC-690 — instant-publish health (EC-268), a finding of the issues tray (it was a banner).
|
|
163
|
+
'shell.deliveryHealth.title': msg('Instant publish not set up', 'Title of the instant-publish health finding.'),
|
|
164
|
+
'shell.deliveryHealth.missing': msg('{vars} not set on this Convex deployment. Publishes succeed, but the live site won’t update until you set them (see CONNECT.md) and re-push functions. A project that runs no revalidate host declares instantPublish: false in {file}.', '{vars} are environment variable names; {file} is elytra.config.ts; instantPublish: false is code — all untranslated.'),
|
|
165
|
+
'shell.deliveryHealth.unchecked': msg('Instant publish could not be checked: {error}', '{error} is the failed read’s message.'),
|
|
166
|
+
// EC-605 — the role-policy drift finding in the issues tray, for admins (the deployment
|
|
150
167
|
// enforces another role policy than elytra.config.ts declares). The doctor's verdict
|
|
151
168
|
// text (English, technical) follows it.
|
|
152
169
|
'shell.rolePolicyDrift.title': msg('This deployment enforces a different role policy than the project declares — permissions may not be what you expect.'),
|
|
@@ -511,6 +528,7 @@ const MESSAGES = {
|
|
|
511
528
|
'cms.issue.invalid-collection-config': msg('Invalid collection setup'),
|
|
512
529
|
'cms.issue.invalid-field-config': msg('Invalid field setup'),
|
|
513
530
|
'cms.issue.reserved-field-name': msg('Reserved field name'),
|
|
531
|
+
'cms.issue.prototype-key': msg('Key cannot be stored'),
|
|
514
532
|
'cms.issue.missing-required-field': msg('Missing required field'),
|
|
515
533
|
'cms.issue.invalid-field-value': msg('Invalid value'),
|
|
516
534
|
'cms.issue.invalid-field-value.warning': msg('Check this value', 'Title for a WARNING-severity invalid-field-value issue (EC-507). The value was accepted and stored — e.g. an implausible but legal date year — so "Invalid value" would contradict what the editor can see in the field.'),
|
|
@@ -608,6 +626,7 @@ const MESSAGES = {
|
|
|
608
626
|
'cms.conflict.empty': msg('(empty)', 'EC-595 A2: {value} when the stored value is empty.'),
|
|
609
627
|
'cms.conflict.structured': msg('a structured value', 'EC-595 A2: {value} when the stored value is not plain text (an object, a list, rich text).'),
|
|
610
628
|
'cms.conflict.cellRefused': msg('This field was changed by someone else since you loaded it — their value is shown.', 'EC-595 A2: a table cell or bulk edit refused because the field changed meanwhile; the stored value is kept.'),
|
|
629
|
+
'cms.conflict.aliasRefused': msg('The slot was changed by someone else since you chose it — theirs is kept. Choose it again to change it.', 'EC-686: a slot (alias) change refused because a colleague changed the slot first. The slot has no field of its own to hold a take-theirs / keep-mine choice, so the stored slot is kept. Shown on the persistence error banner.'),
|
|
611
630
|
'cms.conflict.unauthored': msg('A change was not saved: it does not say which version of the document it was made on, so it could overwrite a newer change. Reload the document and make it again.', 'EC-647: a write that names no revision it was authored against is refused, never sent at the newest one (it could silently overwrite a colleague). Shown on the persistence error banner.'),
|
|
612
631
|
'cms.presence.inField': msg('{name} is in this field', 'EC-595 A2 thin presence: another session has this field focused. {name} is their name.'),
|
|
613
632
|
'cms.presence.inFieldMany': msg('{names} are in this field', 'EC-595 A2 thin presence: several sessions have this field focused. {names} is a comma-separated list of names.'),
|
|
@@ -629,6 +648,8 @@ const MESSAGES = {
|
|
|
629
648
|
'cms.issueMessage.unfilledSlot': msg('The "{slot}" slot of "{collection}" has no document yet. Assign one so code that resolves "{slot}" finds it.'),
|
|
630
649
|
'cms.issueMessage.reservedFieldName': msg('Field name "{field}" is reserved (the self-describing node discriminator): a field named "{field}" may only be a select whose options exclude the engine\'s node types (reference, asset, block, elytra/*), and this one is a "{kind}" field.', 'EC-654: {kind} is a field type identifier (text, object, …); the parenthesised list is code — keep it untranslated.'),
|
|
631
650
|
'cms.issueMessage.reservedFieldTypeOption': msg('Select field "{field}" lists the option "{option}", an engine node type — a "{field}" of "{option}" would read as an engine node (reference, asset, block, elytra/*). Rename the option.', 'EC-654: {option} is the option value as declared; the parenthesised list is code — keep it untranslated.'),
|
|
651
|
+
'cms.issueMessage.reservedPrototypeFieldName': msg('Field name “{field}” is reserved: no value can be stored under it (a JavaScript object and the Convex store do not keep the key, and every write refuses it). Rename the field.', 'EC-684: {field} is the literal __proto__ (never translate it).'),
|
|
652
|
+
'cms.issueMessage.prototypeKey': msg('“{key}” cannot be used as a key: neither a JavaScript object nor the Convex store keeps it. Rename it.', 'EC-684: an own key in a value, or a path segment naming it, refused at a write or an import; {key} is the literal __proto__ (never translate it).'),
|
|
632
653
|
'cms.issueMessage.duplicateSelectOption': msg('Duplicate select option "{option}" on field "{field}".'),
|
|
633
654
|
'cms.issueMessage.duplicateObjectField': msg('Duplicate field "{field}" in object field "{parent}".'),
|
|
634
655
|
'cms.issueMessage.assetCollectionSingleton': msg('Asset collection "{collection}" cannot be a singleton.'),
|
|
@@ -639,7 +660,7 @@ const MESSAGES = {
|
|
|
639
660
|
'cms.issueMessage.unknownVersion': msg('No version {version} recorded for document "{id}".'),
|
|
640
661
|
'cms.issueMessage.routeConflict': msg('Routes "{first}" and "{second}" both match the same URLs (pattern "{pattern}").'),
|
|
641
662
|
'cms.issueMessage.redirectLoop': msg('Redirect chain starting at "{from}" loops (…{chain}).'),
|
|
642
|
-
'cms.issueMessage.duplicateRedirect': msg('Redirect
|
|
663
|
+
'cms.issueMessage.duplicateRedirect': msg('Redirect from "{redirectFrom}" duplicates the redirect from "{keptFrom}" — both are "{from}" once trailing slashes are ignored. The redirect from "{keptFrom}" applies; delete or change this one.'),
|
|
643
664
|
'cms.issueMessage.redirectUnreadable': msg('Stored redirect {redirect} could not be read and is not served ({reason}).', 'EC-628 (Codex F04): {redirect} is "id" (or #index), {reason} the column findings in English.'),
|
|
644
665
|
'cms.issueMessage.redirectReportUnreadable': msg("The deployment reported redirect issue #{index}, which this host cannot read — a redirect is not served; run the site on the deployment's engine version.", 'EC-628: the delivery snapshot carried a redirect issue the host (an older engine) cannot parse.'),
|
|
645
666
|
'cms.issueMessage.allDynamicShadowsMount': msg('Route "{route}" ("{pattern}") has only dynamic segments, so it matches EVERY {depth}-segment URL — hierarchy mount "{mountRoute}" ("{mountPattern}") is consulted at that depth only where the route resolves no document. Nothing is unconditionally unreachable, but precedence there is decided by CONTENT rather than by the routes: {shadowed} are silently shadowed wherever a composed path collides with {claimed}, and neither side is told. Give the route a static prefix ("/blog/:slug") so it claims a namespace instead of a whole depth.'),
|
|
@@ -811,6 +832,8 @@ const MESSAGES = {
|
|
|
811
832
|
'cms.field.slug.regenerate': msg('Regenerate the slug', 'Tooltip on the ↻ button that re-derives the slug from its source field.'),
|
|
812
833
|
'cms.field.slug.taken': msg('Already used by “{title}”', 'Inline warning when the slug collides with another document.'),
|
|
813
834
|
'cms.field.slug.takenGeneric': msg('This slug is already in use.', 'Inline warning when a custom uniqueness check reports the slug is taken.'),
|
|
835
|
+
// EC-691 — a page an explicit route binds answers only at that route's address.
|
|
836
|
+
'cms.field.slug.routedComposesChildren': msg('This page’s address is {path}. Its slug only composes the paths of its child pages.', 'Hint under the slug control of a page an explicit route binds (e.g. Home at /): the slug is not an address of its own.'),
|
|
814
837
|
// ─────────────────────────────────────────────────────────────────────────
|
|
815
838
|
// CMS — relation picker
|
|
816
839
|
// ─────────────────────────────────────────────────────────────────────────
|