@elytracms/studio 0.0.98 → 0.0.100

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 (95) hide show
  1. package/convex/cms.ts +29 -2
  2. package/convex/ingest.test.ts +84 -7
  3. package/convex/ingest.ts +110 -31
  4. package/convex/path-segment-refusal.test.ts +201 -0
  5. package/convex/records-column-transform.convex.test.ts +149 -0
  6. package/convex/records-one-write.convex.test.ts +218 -0
  7. package/convex/schema.ts +1 -1
  8. package/convex/version-kind-stamping.test.ts +118 -0
  9. package/dist/addon.d.ts +5 -3
  10. package/dist/addon.js +4 -3
  11. package/dist/addon.js.map +1 -1
  12. package/dist/components/app-views/default-modules.d.ts +10 -3
  13. package/dist/components/app-views/default-modules.js +5 -1
  14. package/dist/components/app-views/default-modules.js.map +1 -1
  15. package/dist/components/app-views/module.d.ts +15 -8
  16. package/dist/components/app-views/module.js.map +1 -1
  17. package/dist/components/builder/reuse-context.d.ts +29 -1
  18. package/dist/components/builder/reuse-context.js +58 -12
  19. package/dist/components/builder/reuse-context.js.map +1 -1
  20. package/dist/components/cms/cms-store.d.ts +3 -2
  21. package/dist/components/cms/cms-store.js +8 -2
  22. package/dist/components/cms/cms-store.js.map +1 -1
  23. package/dist/components/cms/field-control.js +15 -5
  24. package/dist/components/cms/field-control.js.map +1 -1
  25. package/dist/components/cms/use-document-draft.d.ts +4 -1
  26. package/dist/components/cms/use-document-draft.js +4 -1
  27. package/dist/components/cms/use-document-draft.js.map +1 -1
  28. package/dist/components/cms/use-publish-controller.d.ts +7 -0
  29. package/dist/components/cms/use-publish-controller.js +17 -3
  30. package/dist/components/cms/use-publish-controller.js.map +1 -1
  31. package/dist/components/cms/versions-pane.js +8 -5
  32. package/dist/components/cms/versions-pane.js.map +1 -1
  33. package/dist/components/delivery-health-banner.d.ts +34 -16
  34. package/dist/components/delivery-health-banner.js +59 -27
  35. package/dist/components/delivery-health-banner.js.map +1 -1
  36. package/dist/components/issue-severity.d.ts +3 -2
  37. package/dist/components/issue-severity.js.map +1 -1
  38. package/dist/components/nav-issues-registry.d.ts +39 -24
  39. package/dist/components/nav-issues-registry.js +50 -21
  40. package/dist/components/nav-issues-registry.js.map +1 -1
  41. package/dist/components/nav-issues.d.ts +23 -50
  42. package/dist/components/nav-issues.js +31 -59
  43. package/dist/components/nav-issues.js.map +1 -1
  44. package/dist/components/notifications/notice-center.d.ts +61 -0
  45. package/dist/components/notifications/notice-center.js +119 -0
  46. package/dist/components/notifications/notice-center.js.map +1 -0
  47. package/dist/components/notifications/notice-model.d.ts +137 -0
  48. package/dist/components/notifications/notice-model.js +107 -0
  49. package/dist/components/notifications/notice-model.js.map +1 -0
  50. package/dist/components/notifications/outcome-notice.d.ts +16 -0
  51. package/dist/components/notifications/outcome-notice.js +34 -0
  52. package/dist/components/notifications/outcome-notice.js.map +1 -0
  53. package/dist/components/notifications/shell-findings.d.ts +36 -0
  54. package/dist/components/notifications/shell-findings.js +140 -0
  55. package/dist/components/notifications/shell-findings.js.map +1 -0
  56. package/dist/components/rail-view-source.js +5 -0
  57. package/dist/components/rail-view-source.js.map +1 -1
  58. package/dist/components/records/record-rail.d.ts +10 -2
  59. package/dist/components/records/record-rail.js +85 -4
  60. package/dist/components/records/record-rail.js.map +1 -1
  61. package/dist/components/section-nav.d.ts +15 -3
  62. package/dist/components/section-nav.js +39 -147
  63. package/dist/components/section-nav.js.map +1 -1
  64. package/dist/components/studio-project-screen.js.map +1 -1
  65. package/dist/components/studio-shell.js +8 -3
  66. package/dist/components/studio-shell.js.map +1 -1
  67. package/dist/components/use-narrow-viewport.d.ts +11 -0
  68. package/dist/components/use-narrow-viewport.js +25 -0
  69. package/dist/components/use-narrow-viewport.js.map +1 -0
  70. package/dist/components/users/role-policy-drift-issues.d.ts +14 -7
  71. package/dist/components/users/role-policy-drift-issues.js +17 -8
  72. package/dist/components/users/role-policy-drift-issues.js.map +1 -1
  73. package/dist/lib/collection-view/collection-view-source.d.ts +4 -2
  74. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  75. package/dist/lib/engine-version.d.ts +1 -1
  76. package/dist/lib/engine-version.js +1 -1
  77. package/dist/lib/engine-version.js.map +1 -1
  78. package/dist/lib/i18n/messages.de.js +22 -3
  79. package/dist/lib/i18n/messages.de.js.map +1 -1
  80. package/dist/lib/i18n/messages.en.d.ts +20 -2
  81. package/dist/lib/i18n/messages.en.js +25 -4
  82. package/dist/lib/i18n/messages.en.js.map +1 -1
  83. package/dist/lib/notices/toast-store.d.ts +61 -0
  84. package/dist/lib/notices/toast-store.js +116 -0
  85. package/dist/lib/notices/toast-store.js.map +1 -0
  86. package/dist/lib/operations/cms-persistence.d.ts +24 -4
  87. package/dist/lib/operations/cms-persistence.js +411 -80
  88. package/dist/lib/operations/cms-persistence.js.map +1 -1
  89. package/dist/lib/persistence/convex-adapter.d.ts +2 -0
  90. package/dist/lib/persistence/convex-adapter.js +127 -31
  91. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  92. package/dist/ui/toast.d.ts +24 -16
  93. package/dist/ui/toast.js +23 -32
  94. package/dist/ui/toast.js.map +1 -1
  95. package/package.json +3 -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.nav.issuesCount': import("./types.js").SourceMessage;
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.dismiss': import("./types.js").SourceMessage;
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;
@@ -472,6 +487,7 @@ declare const MESSAGES: {
472
487
  'cms.conflict.empty': import("./types.js").SourceMessage;
473
488
  'cms.conflict.structured': import("./types.js").SourceMessage;
474
489
  'cms.conflict.cellRefused': import("./types.js").SourceMessage;
490
+ 'cms.conflict.aliasRefused': import("./types.js").SourceMessage;
475
491
  'cms.conflict.unauthored': import("./types.js").SourceMessage;
476
492
  'cms.presence.inField': import("./types.js").SourceMessage;
477
493
  'cms.presence.inFieldMany': import("./types.js").SourceMessage;
@@ -494,6 +510,7 @@ declare const MESSAGES: {
494
510
  'cms.issueMessage.reservedFieldName': import("./types.js").SourceMessage;
495
511
  'cms.issueMessage.reservedFieldTypeOption': import("./types.js").SourceMessage;
496
512
  'cms.issueMessage.reservedPrototypeFieldName': import("./types.js").SourceMessage;
513
+ 'cms.issueMessage.reservedPrototypeMemberName': import("./types.js").SourceMessage;
497
514
  'cms.issueMessage.prototypeKey': import("./types.js").SourceMessage;
498
515
  'cms.issueMessage.duplicateSelectOption': import("./types.js").SourceMessage;
499
516
  'cms.issueMessage.duplicateObjectField': import("./types.js").SourceMessage;
@@ -654,6 +671,7 @@ declare const MESSAGES: {
654
671
  'cms.field.slug.regenerate': import("./types.js").SourceMessage;
655
672
  'cms.field.slug.taken': import("./types.js").SourceMessage;
656
673
  'cms.field.slug.takenGeneric': import("./types.js").SourceMessage;
674
+ 'cms.field.slug.routedComposesChildren': import("./types.js").SourceMessage;
657
675
  'cms.relation.change': import("./types.js").SourceMessage;
658
676
  'cms.relation.openTitle': import("./types.js").SourceMessage;
659
677
  '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
- 'shell.nav.issuesCount': msg('{count} sidebar issues', 'aria-label summarising rail customization issues on the collapsed rail.'),
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
- 'shell.deliveryHealth.dismiss': msg('Dismiss'),
149
- // EC-605 — the role-policy drift finding in the rail's issues foot (the deployment
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.'),
@@ -609,6 +626,7 @@ const MESSAGES = {
609
626
  'cms.conflict.empty': msg('(empty)', 'EC-595 A2: {value} when the stored value is empty.'),
610
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).'),
611
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.'),
612
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.'),
613
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.'),
614
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.'),
@@ -631,6 +649,7 @@ const MESSAGES = {
631
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.'),
632
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.'),
633
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.reservedPrototypeMemberName': msg('Field name “{field}” is reserved: every JavaScript object already answers to it (a member of Object.prototype), so a lookup by it finds the inherited member where nothing is held. Rename the field.', 'EC-697: {field} is a JavaScript member name such as constructor or toString, and Object.prototype is code (never translate either).'),
634
653
  '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).'),
635
654
  'cms.issueMessage.duplicateSelectOption': msg('Duplicate select option "{option}" on field "{field}".'),
636
655
  'cms.issueMessage.duplicateObjectField': msg('Duplicate field "{field}" in object field "{parent}".'),
@@ -642,7 +661,7 @@ const MESSAGES = {
642
661
  'cms.issueMessage.unknownVersion': msg('No version {version} recorded for document "{id}".'),
643
662
  'cms.issueMessage.routeConflict': msg('Routes "{first}" and "{second}" both match the same URLs (pattern "{pattern}").'),
644
663
  'cms.issueMessage.redirectLoop': msg('Redirect chain starting at "{from}" loops (…{chain}).'),
645
- 'cms.issueMessage.duplicateRedirect': msg('Redirect "{redirect}" (from "{redirectFrom}") duplicates redirect "{kept}" (from "{keptFrom}") — both are "{from}" once trailing slashes are ignored. "{kept}" applies; delete or change the other.'),
664
+ '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.'),
646
665
  '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.'),
647
666
  '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.'),
648
667
  '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.'),
@@ -814,6 +833,8 @@ const MESSAGES = {
814
833
  'cms.field.slug.regenerate': msg('Regenerate the slug', 'Tooltip on the ↻ button that re-derives the slug from its source field.'),
815
834
  'cms.field.slug.taken': msg('Already used by “{title}”', 'Inline warning when the slug collides with another document.'),
816
835
  'cms.field.slug.takenGeneric': msg('This slug is already in use.', 'Inline warning when a custom uniqueness check reports the slug is taken.'),
836
+ // EC-691 — a page an explicit route binds answers only at that route's address.
837
+ '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.'),
817
838
  // ─────────────────────────────────────────────────────────────────────────
818
839
  // CMS — relation picker
819
840
  // ─────────────────────────────────────────────────────────────────────────