@elytracms/studio 0.0.103 → 0.0.105

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 (34) hide show
  1. package/convex/ingest.test.ts +208 -13
  2. package/convex/ingest.ts +56 -35
  3. package/dist/components/app-views/module.d.ts +4 -0
  4. package/dist/components/app-views/module.js.map +1 -1
  5. package/dist/components/builder/canvas-theme-notice.js +4 -1
  6. package/dist/components/builder/canvas-theme-notice.js.map +1 -1
  7. package/dist/components/cms/document-diff-view.js +43 -7
  8. package/dist/components/cms/document-diff-view.js.map +1 -1
  9. package/dist/components/components/components-index.d.ts +0 -7
  10. package/dist/components/components/components-index.js +9 -2
  11. package/dist/components/components/components-index.js.map +1 -1
  12. package/dist/components/redirects/redirects-schema.d.ts +31 -0
  13. package/dist/components/redirects/redirects-schema.js +12 -6
  14. package/dist/components/redirects/redirects-schema.js.map +1 -1
  15. package/dist/lib/ai/translate-document.d.ts +7 -3
  16. package/dist/lib/ai/translate-document.js +63 -48
  17. package/dist/lib/ai/translate-document.js.map +1 -1
  18. package/dist/lib/ai/writer-form.d.ts +48 -5
  19. package/dist/lib/ai/writer-form.js +118 -64
  20. package/dist/lib/ai/writer-form.js.map +1 -1
  21. package/dist/lib/engine-version.d.ts +1 -1
  22. package/dist/lib/engine-version.js +1 -1
  23. package/dist/lib/engine-version.js.map +1 -1
  24. package/dist/lib/i18n/messages.de.js +25 -5
  25. package/dist/lib/i18n/messages.de.js.map +1 -1
  26. package/dist/lib/i18n/messages.en.d.ts +21 -1
  27. package/dist/lib/i18n/messages.en.js +29 -6
  28. package/dist/lib/i18n/messages.en.js.map +1 -1
  29. package/dist/lib/persona/labels.js +1 -0
  30. package/dist/lib/persona/labels.js.map +1 -1
  31. package/dist/lib/workspace/index.d.ts +9 -1
  32. package/dist/lib/workspace/index.js +13 -2
  33. package/dist/lib/workspace/index.js.map +1 -1
  34. package/package.json +3 -3
@@ -450,6 +450,7 @@ declare const MESSAGES: {
450
450
  'cms.issue.route-conflict': import("./types.js").SourceMessage;
451
451
  'cms.issue.redirect-loop': import("./types.js").SourceMessage;
452
452
  'cms.issue.invalid-redirect': import("./types.js").SourceMessage;
453
+ 'cms.issue.invalid-route': import("./types.js").SourceMessage;
453
454
  'cms.issue.unknown-route-target': import("./types.js").SourceMessage;
454
455
  'cms.issue.hierarchy-cycle': import("./types.js").SourceMessage;
455
456
  'cms.issue.document-nesting-depth': import("./types.js").SourceMessage;
@@ -544,14 +545,27 @@ declare const MESSAGES: {
544
545
  'cms.issueMessage.routeConflict': import("./types.js").SourceMessage;
545
546
  'cms.issueMessage.redirectLoop': import("./types.js").SourceMessage;
546
547
  'cms.issueMessage.duplicateRedirect': import("./types.js").SourceMessage;
548
+ 'cms.issueMessage.redirectFromMalformed': import("./types.js").SourceMessage;
549
+ 'cms.issueMessage.redirectToRefused': import("./types.js").SourceMessage;
550
+ 'cms.issueDetail.redirectToRelative': import("./types.js").SourceMessage;
551
+ 'cms.issueDetail.redirectToOtherHost': import("./types.js").SourceMessage;
552
+ 'cms.issueDetail.redirectToScheme': import("./types.js").SourceMessage;
553
+ 'cms.issueDetail.redirectToNotUrl': import("./types.js").SourceMessage;
554
+ 'cms.issueDetail.redirectToMalformedPercent': import("./types.js").SourceMessage;
555
+ 'cms.issueDetail.redirectToBrokenCharacter': import("./types.js").SourceMessage;
556
+ 'cms.issueDetail.redirectToDotSegment': import("./types.js").SourceMessage;
547
557
  'cms.issueMessage.redirectUnreadable': import("./types.js").SourceMessage;
548
558
  'cms.issueMessage.redirectReportUnreadable': import("./types.js").SourceMessage;
549
559
  'cms.issueMessage.allDynamicShadowsMount': import("./types.js").SourceMessage;
550
560
  'cms.issueMessage.mountCannotServe': import("./types.js").SourceMessage;
561
+ 'cms.issueMessage.routeSegmentNotText': import("./types.js").SourceMessage;
562
+ 'cms.issueMessage.routePatternDelimiter': import("./types.js").SourceMessage;
563
+ 'cms.issueMessage.routeParameterReserved': import("./types.js").SourceMessage;
564
+ 'cms.issueMessage.routeParameterNotIdentity': import("./types.js").SourceMessage;
565
+ 'cms.issueMessage.routeLiteralWithParameter': import("./types.js").SourceMessage;
551
566
  'cms.issueMessage.deadCatchAll': import("./types.js").SourceMessage;
552
567
  'cms.issueMessage.mountConflict': import("./types.js").SourceMessage;
553
568
  'cms.issueMessage.routeBoundDocumentNotFound': import("./types.js").SourceMessage;
554
- 'cms.issueMessage.routeParamNotCaptured': import("./types.js").SourceMessage;
555
569
  'cms.issueMessage.collectionNotDefined': import("./types.js").SourceMessage;
556
570
  'cms.issueMessage.storedDocumentUnreadable': import("./types.js").SourceMessage;
557
571
  'cms.issueMessage.unaddressableLocaleCollision': import("./types.js").SourceMessage;
@@ -838,6 +852,10 @@ declare const MESSAGES: {
838
852
  'redirects.field.to.label': import("./types.js").SourceMessage;
839
853
  'redirects.field.permanent.label': import("./types.js").SourceMessage;
840
854
  'redirects.field.enabled.label': import("./types.js").SourceMessage;
855
+ 'redirects.field.from.description': import("./types.js").SourceMessage;
856
+ 'redirects.field.to.description': import("./types.js").SourceMessage;
857
+ 'redirects.field.permanent.description': import("./types.js").SourceMessage;
858
+ 'redirects.field.enabled.description': import("./types.js").SourceMessage;
841
859
  'references.title': import("./types.js").SourceMessage;
842
860
  'references.hintOutbound': import("./types.js").SourceMessage;
843
861
  'references.emptyOutbound': import("./types.js").SourceMessage;
@@ -1214,6 +1232,8 @@ declare const MESSAGES: {
1214
1232
  'cms.issueMessage.assetHoldsUrl': import("./types.js").SourceMessage;
1215
1233
  'cms.issueMessage.slugNotSegment': import("./types.js").SourceMessage;
1216
1234
  'cms.issueMessage.slugNotAddressable': import("./types.js").SourceMessage;
1235
+ 'cms.issueMessage.slugNotEncodable': import("./types.js").SourceMessage;
1236
+ 'cms.issueMessage.documentIdNotSegment': import("./types.js").SourceMessage;
1217
1237
  'cms.issueMessage.emptyAllow': import("./types.js").SourceMessage;
1218
1238
  'cms.issueMessage.unknownAllowComponent': import("./types.js").SourceMessage;
1219
1239
  'cms.issueMessage.blocksNotTopLevel': import("./types.js").SourceMessage;
@@ -569,7 +569,8 @@ const MESSAGES = {
569
569
  'cms.issue.missing-localized-value': msg('Missing translation'),
570
570
  'cms.issue.route-conflict': msg('Conflicting URL'),
571
571
  'cms.issue.redirect-loop': msg('Redirect loop'),
572
- 'cms.issue.invalid-redirect': msg('Invalid redirect', 'EC-590/EC-628: a duplicate `from`, or a stored redirect that cannot be read (and is not served).'),
572
+ 'cms.issue.invalid-redirect': msg('Invalid redirect', 'EC-590/EC-628/EC-771: a duplicate `from`, a stored redirect that cannot be read, or a destination that is not a path or an http(s) URL (the last two are not served).'),
573
+ 'cms.issue.invalid-route': msg('Invalid route', 'EC-770: a route declaration the route gate refuses — it serves nothing (e.g. a pattern holding a ? or a %, a mount no collection can back).'),
573
574
  'cms.issue.unknown-route-target': msg('Missing page'),
574
575
  'cms.issue.hierarchy-cycle': msg('Page nesting loop'),
575
576
  'cms.issue.document-nesting-depth': msg('Content nested too deeply'),
@@ -688,15 +689,30 @@ const MESSAGES = {
688
689
  'cms.issueMessage.unknownVersion': msg('No version {version} recorded for document "{id}".'),
689
690
  'cms.issueMessage.routeConflict': msg('Routes "{first}" and "{second}" both match the same URLs (pattern "{pattern}").'),
690
691
  'cms.issueMessage.redirectLoop': msg('Redirect chain starting at "{from}" loops (…{chain}).'),
691
- '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.'),
692
+ 'cms.issueMessage.duplicateRedirect': msg('Redirect from "{redirectFrom}" duplicates the redirect from "{keptFrom}" — both are "{from}" once trailing slashes are ignored and percent-encoding is decoded. The redirect from "{keptFrom}" applies; delete or change this one.'),
693
+ // EC-760 — a redirect `from` that is not a valid percent-encoding matches no request
694
+ 'cms.issueMessage.redirectFromMalformed': msg('Redirect from "{from}" holds a "%" that is not a percent-encoding, so it can match no request and is not served. Write the character itself, or "%25" for a percent sign.'),
695
+ 'cms.issueMessage.redirectToRefused': msg('Redirect from "{from}" is not served: its destination {reason}. A destination is a path on this site starting with "/" (e.g. "/new-page") or a full http:// or https:// URL.', 'EC-771: {reason} is one of the cms.issueDetail.redirectTo* fragments; "/new-page", http:// and https:// are code — keep them as written.'),
696
+ 'cms.issueDetail.redirectToRelative': msg('is neither a path starting with "/" nor a full http:// or https:// URL'),
697
+ 'cms.issueDetail.redirectToOtherHost': msg('starts with "//" or "/\\", which a browser reads as another host'),
698
+ 'cms.issueDetail.redirectToScheme': msg('uses the scheme "{scheme}", and only http:// and https:// are served', 'EC-771: {scheme} is a URL scheme such as javascript: or mailto: (code — never translate it).'),
699
+ 'cms.issueDetail.redirectToNotUrl': msg('is not a valid http:// or https:// URL'),
700
+ 'cms.issueDetail.redirectToMalformedPercent': msg('holds a "%" that is not a percent-encoding (write the character itself, or "%25" for a percent sign)'),
701
+ 'cms.issueDetail.redirectToBrokenCharacter': msg('holds half of a character (a lone UTF-16 surrogate), which no URL can carry'),
702
+ 'cms.issueDetail.redirectToDotSegment': msg('holds a "." or ".." segment, which a browser removes before the request'),
692
703
  '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.'),
693
704
  '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.'),
694
705
  '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.'),
695
706
  'cms.issueMessage.mountCannotServe': msg('Hierarchy mount "{route}" ("{pattern}") can never serve a document: {why}. The resolver skips such a mount silently, so every URL under "{space}" 404s with no issue raised.'),
707
+ // EC-760 — the route gate: a static pattern segment is text
708
+ 'cms.issueMessage.routeSegmentNotText': msg('Route "{route}" ("{pattern}") is not served: a pattern segment is not URL text ({segments}). A pattern is written as text — "/über-uns", not "/%C3%BCber-uns" — and its links are encoded, so a "%" is refused; so are a "." or ".." segment (a browser removes it before the request) and half of a character (no URL can carry it). Rewrite the segment.', 'EC-760: {segments} lists the refused pattern segments, each quoted as written in code; "/über-uns" and "/%C3%BCber-uns" are code — keep them as written.'),
709
+ 'cms.issueMessage.routePatternDelimiter': msg('Route "{route}" ("{pattern}") is not served: the pattern holds {delimiters}, which a URL reads as the start of its query or fragment, so the route would end there. A pattern is a path — remove the character.', 'EC-760: {delimiters} is "?" and/or "#", quoted and comma-separated — the characters are code; keep them as written.'),
710
+ 'cms.issueMessage.routeParameterReserved': msg('Route "{route}" ("{pattern}") is not served: a parameter name is reserved ({names}) — 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 parameter.', 'EC-773: {names} lists the reserved parameter names, quoted — JavaScript member names such as __proto__ or constructor; they and Object.prototype are code (never translate them).'),
711
+ 'cms.issueMessage.routeParameterNotIdentity': msg('Route "{route}" ("{pattern}") is not served: its document is found by the parameter ":{identity}" alone, so the pattern must hold exactly that one parameter. Any other parameter is never compared with the document — every value of it would address the same page. Make ":{identity}" the pattern\'s only parameter, or bind no document.', 'EC-772: {identity} is the parameter name the route\'s document id placeholder names (e.g. slug); a pattern and ":{identity}" are code — keep them as written.'),
712
+ 'cms.issueMessage.routeLiteralWithParameter': msg('Route "{route}" ("{pattern}") is not served: it is bound to the one document "{id}", so its pattern may hold no parameter — every value of one would address that same page. Remove the parameter, or bind the document by it (a document id ":name").', 'EC-772: {id} is the document id the route names; ":name" is code — keep it as written.'),
696
713
  'cms.issueMessage.deadCatchAll': msg('Route "{route}" ("{pattern}") serves nothing in either direction: it ends in a catch-all, so every router drops it, but its prefix has a dynamic segment ({dynamic}), so no hierarchy mount is built for it either. A mount\'s prefix must be static — declare one mount per locale (`{ pattern: \'/de/:path*\', locale: \'de\' }`), or drop the route.'),
697
714
  'cms.issueMessage.mountConflict': msg('{opening} — an exact tie, separated only by declaration order, so "{earlier}" (collection "{earlierCollection}") claims every URL there its own tree resolves, and "{laterCollection}" documents at those URLs are unreachable.'),
698
715
  'cms.issueMessage.routeBoundDocumentNotFound': msg('Route-bound document "{collection}:{id}" was not found.'),
699
- 'cms.issueMessage.routeParamNotCaptured': msg('Route "{route}" binds its document to URL parameter ":{param}", which pattern "{pattern}" does not capture.'),
700
716
  'cms.issueMessage.collectionNotDefined': msg('Collection "{collection}" is not defined.'),
701
717
  'cms.issueMessage.storedDocumentUnreadable': msg('Stored document {document} could not be read and is not delivered ({reason}).', 'EC-585 (Codex F12): {document} is collection:id (or #index), {reason} the schema findings in English.'),
702
718
  'cms.issueMessage.unaddressableLocaleCollision': msg('No URL reaches "{id}" in locale "{locale}": its address would be "{url}", but the first segment "{segment}" is a declared locale code, so the site reads that URL as locale "{segment}", path "{readPath}". Rename that segment — on this document or on the ancestor that owns it — so it does not collide with a locale code.'),
@@ -1046,6 +1062,10 @@ const MESSAGES = {
1046
1062
  'redirects.field.to.label': msg('To'),
1047
1063
  'redirects.field.permanent.label': msg('Permanent (301)'),
1048
1064
  'redirects.field.enabled.label': msg('Enabled'),
1065
+ 'redirects.field.from.description': msg('The path to forward, e.g. /old-page.'),
1066
+ 'redirects.field.to.description': msg('A path on this site starting with / (e.g. /new-page), or a full https:// URL.'),
1067
+ 'redirects.field.permanent.description': msg('On: 301 permanent. Off: 302 temporary.'),
1068
+ 'redirects.field.enabled.description': msg('Only enabled redirects are served on the live site.'),
1049
1069
  // ─────────────────────────────────────────────────────────────────────────
1050
1070
  // References panel
1051
1071
  // ─────────────────────────────────────────────────────────────────────────
@@ -1087,7 +1107,7 @@ const MESSAGES = {
1087
1107
  'components.index.byCategory': msg('By category'),
1088
1108
  'components.index.guide.title': msg('Registering & extending components'),
1089
1109
  'components.index.guide.badge': msg('for developers'),
1090
- 'components.index.guide.intro': msg('Components are code, owned by your repo. Register a manifest (prop schemas, defaults, inspector controls, slots, and preview fixtures) and the studio browses, inspects, and previews it. These guides, in the Elytra engine repository’s docs, cover the details:'),
1110
+ 'components.index.guide.intro': msg('Components are code, owned by your repo. Register a manifest (prop schemas, defaults, inspector controls, slots, and preview fixtures) and the studio browses, inspects, and previews it. These chapters of the Elytra guide, which installs with @elytracms/core, cover the details:'),
1091
1111
  'components.index.guide.doc.authoring': msg('How to declare and register a component so the studio can browse, inspect, and preview it.'),
1092
1112
  'components.index.guide.doc.components': msg('The guide’s components chapter: the manifest + implementation pair, registering the list, fixtures, reusable elements, styling.'),
1093
1113
  'components.index.guide.doc.guide': msg('The guide’s overview and chapter index: setup, the config API, delivery, the CLI and environment reference.'),
@@ -1276,9 +1296,9 @@ const MESSAGES = {
1276
1296
  'builder.canvas.insertAfter': msg('Insert block after'),
1277
1297
  'builder.canvas.insertInto': msg('Insert block inside'),
1278
1298
  // Canvas-theme classification notices (EC-414 AD-D) — dev-only, never blocking.
1279
- 'builder.canvasTheme.legacyNotice': msg('This project’s canvas.css still bundles the studio design system with the site theme (the legacy merged shape). Point canvas.css at your site’s own full-Tailwind theme entry instead — see the theming guide (docs/theming.md). Everything keeps working meanwhile.', 'Dev-only migration pointer when the canvas stylesheet is the pre-EC-414 merged build.'),
1299
+ 'builder.canvasTheme.legacyNotice': msg('This project’s canvas.css still bundles the studio design system with the site theme (the legacy merged shape). Point canvas.css at your site’s own full-Tailwind theme entry instead — see the guide’s theming chapter ({guide}). Everything keeps working meanwhile.', 'Dev-only migration pointer when the canvas stylesheet is the pre-EC-414 merged build. {guide} is the theming chapter’s path in the project’s node_modules — keep it verbatim.'),
1280
1300
  'builder.canvasTheme.stylesheetErrorNotice': msg('The canvas stylesheet failed to load — blocks render unstyled. Check the studio’s canvas.css build.'),
1281
- 'builder.canvasTheme.noThemeTokensNotice': msg('The canvas stylesheet defines no theme tokens. Make canvas.css import your site’s full-Tailwind theme entry (see docs/theming.md).'),
1301
+ 'builder.canvasTheme.noThemeTokensNotice': msg('The canvas stylesheet defines no theme tokens. Make canvas.css import your site’s full-Tailwind theme entry (see the guide’s theming chapter, {guide}).', '{guide} is the theming chapter’s path in the project’s node_modules — keep it verbatim.'),
1282
1302
  'builder.canvasTheme.noStylesheetNotice': msg('No canvas stylesheet is configured — blocks render unstyled. The app must supply one via setCanvasStylesheetUrl (the create-elytra template wires this).'),
1283
1303
  // Mixed-sequence honesty notice (EC-549 interim): a blocks value carrying
1284
1304
  // typography items the canvas cannot render yet must SAY so — count + pointer,
@@ -1485,6 +1505,9 @@ const MESSAGES = {
1485
1505
  'cms.issueMessage.slugNotSegment': msg('Slug field "{field}" holds "{value}", which is not a slug by the field\'s slug rule — the rule writes "{slug}" for it. Store the slug the rule writes.'),
1486
1506
  // EC-749 — a slug the route codec cannot carry as one URL segment
1487
1507
  'cms.issueMessage.slugNotAddressable': msg('Slug field "{field}" holds "{value}", which cannot be one URL segment: a browser removes a "." or ".." segment before the request, and a "/" reads as a nested path. No URL is built from it — change the slug.'),
1508
+ // EC-761 — a slug the route codec cannot encode at all
1509
+ 'cms.issueMessage.slugNotEncodable': msg('Slug field "{field}" holds a broken character — half of a character such as an emoji (a lone UTF-16 surrogate), which no URL can carry. No URL is built from it — change the slug.', 'EC-761: the stored slug holds half of a two-part character (what cutting a string in the middle of an emoji yields); the value itself is not shown because half a character cannot be displayed.'),
1510
+ 'cms.issueMessage.documentIdNotSegment': msg('A document id must be usable as one URL segment — not "." or "..", no "/", and no half of a character (a lone UTF-16 surrogate). A route falls back to the id where a document has no slug, and no URL can carry this one. Choose another id.', 'EC-772: refused when a document is created with an explicit id; "." , ".." and "/" are code — keep them as written.'),
1488
1511
  // EC-584 — the AllowList parse (config load / registration).
1489
1512
  'cms.issueMessage.emptyAllow': msg('Field "{field}" declares an empty "allow" — a list permits exactly the ids it names, so an empty one permits nothing. Omit "allow" for the type\'s default vocabulary, or list the component ids.'),
1490
1513
  'cms.issueMessage.unknownAllowComponent': msg('Field "{field}" allows "{component}", which is not a component of this project — check the id, or remove it from "allow".', '{component} is the component id as declared in the field\'s allow list.'),