@elytracms/studio 0.0.70 → 0.0.72

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 (64) hide show
  1. package/convex/cms.ts +8 -0
  2. package/convex/delivery.ts +12 -2
  3. package/convex/write-limits.ts +62 -0
  4. package/dist/components/account/self-account-view-source.js +1 -0
  5. package/dist/components/account/self-account-view-source.js.map +1 -1
  6. package/dist/components/assets/assets-collection-view-source.js +1 -0
  7. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  8. package/dist/components/cms/collection-table.js +19 -2
  9. package/dist/components/cms/collection-table.js.map +1 -1
  10. package/dist/components/cms/detail-editor.js +73 -51
  11. package/dist/components/cms/detail-editor.js.map +1 -1
  12. package/dist/components/cms/duplicate-document.d.ts +45 -0
  13. package/dist/components/cms/duplicate-document.js +141 -0
  14. package/dist/components/cms/duplicate-document.js.map +1 -0
  15. package/dist/components/cms/field-anchors.d.ts +67 -0
  16. package/dist/components/cms/field-anchors.js +91 -0
  17. package/dist/components/cms/field-anchors.js.map +1 -0
  18. package/dist/components/cms/issue-copy.d.ts +45 -0
  19. package/dist/components/cms/issue-copy.js +89 -0
  20. package/dist/components/cms/issue-copy.js.map +1 -0
  21. package/dist/components/cms/issues-pane.d.ts +11 -2
  22. package/dist/components/cms/issues-pane.js +22 -4
  23. package/dist/components/cms/issues-pane.js.map +1 -1
  24. package/dist/components/cms/object-array-field.d.ts +2 -0
  25. package/dist/components/cms/object-array-field.js +75 -21
  26. package/dist/components/cms/object-array-field.js.map +1 -1
  27. package/dist/components/cms/row-actions.d.ts +10 -3
  28. package/dist/components/cms/row-actions.js +8 -0
  29. package/dist/components/cms/row-actions.js.map +1 -1
  30. package/dist/components/cms/use-field-focus.d.ts +25 -9
  31. package/dist/components/cms/use-field-focus.js +80 -15
  32. package/dist/components/cms/use-field-focus.js.map +1 -1
  33. package/dist/components/collection-view/cms-collection-view-source.js +1 -0
  34. package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
  35. package/dist/components/redirects/redirects-collection-view-source.js +1 -0
  36. package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
  37. package/dist/components/users/users-collection-view-source.js +1 -0
  38. package/dist/components/users/users-collection-view-source.js.map +1 -1
  39. package/dist/customization.d.ts +9 -0
  40. package/dist/customization.js +8 -0
  41. package/dist/customization.js.map +1 -1
  42. package/dist/lib/collection-view/collection-view-source.d.ts +8 -0
  43. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  44. package/dist/lib/i18n/messages.de.js +38 -1
  45. package/dist/lib/i18n/messages.de.js.map +1 -1
  46. package/dist/lib/i18n/messages.en.d.ts +34 -0
  47. package/dist/lib/i18n/messages.en.js +41 -1
  48. package/dist/lib/i18n/messages.en.js.map +1 -1
  49. package/dist/lib/persona/labels.d.ts +5 -1
  50. package/dist/lib/persona/labels.js +43 -30
  51. package/dist/lib/persona/labels.js.map +1 -1
  52. package/dist/previews/enumerate.d.ts +68 -0
  53. package/dist/previews/enumerate.js +78 -0
  54. package/dist/previews/enumerate.js.map +1 -0
  55. package/dist/previews/harness.d.ts +50 -0
  56. package/dist/previews/harness.js +123 -0
  57. package/dist/previews/harness.js.map +1 -0
  58. package/dist/previews/manifest-format.d.ts +57 -0
  59. package/dist/previews/manifest-format.js +59 -0
  60. package/dist/previews/manifest-format.js.map +1 -0
  61. package/dist/ui/field.d.ts +24 -1
  62. package/dist/ui/field.js +23 -2
  63. package/dist/ui/field.js.map +1 -1
  64. package/package.json +10 -6
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The previews-manifest format (EC-440) — the JSON contract between the
3
+ * `elytra previews` CLI (the writer) and a consumer's `studio.config.tsx`
4
+ * (the reader, via {@link generatedPreviewsFromManifest}).
5
+ *
6
+ * The CLI renders every canvas-eligible component's fixtures headlessly
7
+ * against the consumer theme, writes the images to the studio app's
8
+ * `public/elytra-previews/` (served at `/elytra-previews/...`), and writes
9
+ * this manifest next to `studio.config.tsx` (`studio/elytra-previews.json`)
10
+ * so it is importable as a plain JSON module. Everything in it is
11
+ * deterministic — no timestamps — so an unchanged re-run rewrites nothing.
12
+ */
13
+ /** Format version the CLI writes; bump on incompatible schema/hash changes. */
14
+ export const PREVIEWS_MANIFEST_VERSION = 1;
15
+ /** Tolerant entry guard — a malformed entry is skipped, never a crash. */
16
+ function isManifestEntry(value) {
17
+ if (typeof value !== 'object' || value === null)
18
+ return false;
19
+ const entry = value;
20
+ return (typeof entry.componentId === 'string' &&
21
+ typeof entry.fixture === 'string' &&
22
+ typeof entry.url === 'string');
23
+ }
24
+ /**
25
+ * Adapt a previews manifest (a plain JSON import) into the EC-439
26
+ * {@link GeneratedPreviewLookup} seam. The whole consumer wiring is ~2 lines:
27
+ *
28
+ * ```tsx
29
+ * // studio.config.tsx
30
+ * import previewsManifest from './elytra-previews.json'
31
+ * export default defineStudio({
32
+ * previews: { generated: generatedPreviewsFromManifest(previewsManifest) },
33
+ * })
34
+ * ```
35
+ *
36
+ * Tolerant by design (the input is a generated file a human may hand-edit or
37
+ * delete): a malformed document or entry yields lookup misses — the blocks
38
+ * panel then falls back to the icon+name placeholder — never a crash. A
39
+ * fixture-less component's single implicit variant is looked up (and written
40
+ * by the CLI) under the fixture name `'Default'`.
41
+ */
42
+ export function generatedPreviewsFromManifest(manifest) {
43
+ const byKey = new Map();
44
+ if (typeof manifest === 'object' && manifest !== null) {
45
+ const entries = manifest.entries;
46
+ if (Array.isArray(entries)) {
47
+ for (const entry of entries) {
48
+ if (isManifestEntry(entry))
49
+ byKey.set(lookupKey(entry.componentId, entry.fixture), entry.url);
50
+ }
51
+ }
52
+ }
53
+ return (componentId, fixtureName) => byKey.get(lookupKey(componentId, fixtureName));
54
+ }
55
+ function lookupKey(componentId, fixture) {
56
+ // NUL (\u0000) can appear in neither a component id nor a fixture name.
57
+ return `${componentId}\u0000${fixture}`;
58
+ }
59
+ //# sourceMappingURL=manifest-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-format.js","sourceRoot":"","sources":["../../src/previews/manifest-format.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAA;AA2B1C,0EAA0E;AAC1E,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAC7D,MAAM,KAAK,GAAG,KAAgC,CAAA;IAC9C,OAAO,CACL,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;QACrC,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,CAC9B,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAAiB;IAC7D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtD,MAAM,OAAO,GAAI,QAAkC,CAAC,OAAO,CAAA;QAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,eAAe,CAAC,KAAK,CAAC;oBAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;YAC/F,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAA;AACrF,CAAC;AAED,SAAS,SAAS,CAAC,WAAmB,EAAE,OAAe;IACrD,wEAAwE;IACxE,OAAO,GAAG,WAAW,SAAS,OAAO,EAAE,CAAA;AACzC,CAAC","sourcesContent":["import type { GeneratedPreviewLookup } from '../components/builder/blocks-model'\n\n/**\n * The previews-manifest format (EC-440) — the JSON contract between the\n * `elytra previews` CLI (the writer) and a consumer's `studio.config.tsx`\n * (the reader, via {@link generatedPreviewsFromManifest}).\n *\n * The CLI renders every canvas-eligible component's fixtures headlessly\n * against the consumer theme, writes the images to the studio app's\n * `public/elytra-previews/` (served at `/elytra-previews/...`), and writes\n * this manifest next to `studio.config.tsx` (`studio/elytra-previews.json`)\n * so it is importable as a plain JSON module. Everything in it is\n * deterministic — no timestamps — so an unchanged re-run rewrites nothing.\n */\n\n/** Format version the CLI writes; bump on incompatible schema/hash changes. */\nexport const PREVIEWS_MANIFEST_VERSION = 1\n\n/** One generated preview artifact: a component fixture and its rendered image. */\nexport interface GeneratedPreviewsManifestEntry {\n componentId: string\n /** The fixture name, or `'Default'` for a fixture-less component's implicit variant. */\n fixture: string\n /** Root-relative image URL (the file lives in the studio app's `public/`). */\n url: string\n /**\n * Input hash (sha-256 hex over the stable-serialized inputs: manifest prop +\n * slot shape, fixture props/slotChildren, engine version). `elytra previews`\n * skips entries whose hash is unchanged; `elytra previews --check` flags\n * entries whose hash drifted as stale. Deliberately NOT part of the lookup —\n * the studio only reads `componentId`/`fixture`/`url`.\n */\n hash: string\n}\n\n/** The manifest document `elytra previews` writes (`studio/elytra-previews.json`). */\nexport interface GeneratedPreviewsManifest {\n version: number\n /** The `@elytracms/studio` version the images were rendered with. */\n engine: string\n entries: GeneratedPreviewsManifestEntry[]\n}\n\n/** Tolerant entry guard — a malformed entry is skipped, never a crash. */\nfunction isManifestEntry(value: unknown): value is GeneratedPreviewsManifestEntry {\n if (typeof value !== 'object' || value === null) return false\n const entry = value as Record<string, unknown>\n return (\n typeof entry.componentId === 'string' &&\n typeof entry.fixture === 'string' &&\n typeof entry.url === 'string'\n )\n}\n\n/**\n * Adapt a previews manifest (a plain JSON import) into the EC-439\n * {@link GeneratedPreviewLookup} seam. The whole consumer wiring is ~2 lines:\n *\n * ```tsx\n * // studio.config.tsx\n * import previewsManifest from './elytra-previews.json'\n * export default defineStudio({\n * previews: { generated: generatedPreviewsFromManifest(previewsManifest) },\n * })\n * ```\n *\n * Tolerant by design (the input is a generated file a human may hand-edit or\n * delete): a malformed document or entry yields lookup misses — the blocks\n * panel then falls back to the icon+name placeholder — never a crash. A\n * fixture-less component's single implicit variant is looked up (and written\n * by the CLI) under the fixture name `'Default'`.\n */\nexport function generatedPreviewsFromManifest(manifest: unknown): GeneratedPreviewLookup {\n const byKey = new Map<string, string>()\n if (typeof manifest === 'object' && manifest !== null) {\n const entries = (manifest as { entries?: unknown }).entries\n if (Array.isArray(entries)) {\n for (const entry of entries) {\n if (isManifestEntry(entry)) byKey.set(lookupKey(entry.componentId, entry.fixture), entry.url)\n }\n }\n }\n return (componentId, fixtureName) => byKey.get(lookupKey(componentId, fixtureName))\n}\n\nfunction lookupKey(componentId: string, fixture: string): string {\n // NUL (\\u0000) can appear in neither a component id nor a fixture name.\n return `${componentId}\\u0000${fixture}`\n}\n"]}
@@ -1,4 +1,25 @@
1
1
  import type { LabelHTMLAttributes, ReactNode, SelectHTMLAttributes } from 'react';
2
+ /**
3
+ * EC-443 — THE nested-field anchor convention (one convention, used by both the
4
+ * stamping and the lookup — do not invent a second):
5
+ *
6
+ * data-ec-field-path="<field>(/<token>)*"
7
+ *
8
+ * Slash-joined tokens from the TOP-LEVEL field name down into the value —
9
+ * array indices are numeric tokens, object subfields their names — e.g.
10
+ * `links`, `links/3`, `links/3/badge`, `links/3/badge/text`. This mirrors the
11
+ * `values/…` reference-index path with the `values`/`localized/<locale>` prefix
12
+ * stripped: the locale axis is the studio's ambient locale switch, never part
13
+ * of the DOM anchor.
14
+ *
15
+ * Stamped by: the document form's top-level field rows (`detail-editor`'s
16
+ * `renderFieldRow` via the `fieldPath` prop below) and the object/repeater
17
+ * controls (`object-array-field`: item cards, table rows/cells, and nested
18
+ * subfield rows). Read by: `use-field-focus`'s deepest-available-anchor
19
+ * resolution (jump-to-field / `?field=` deep links) via the pure helpers in
20
+ * `components/cms/field-anchors.ts`.
21
+ */
22
+ export declare const FIELD_PATH_ATTR = "data-ec-field-path";
2
23
  /** A labelled form field group: label, control, and optional hint/error. */
3
24
  export interface FieldProps {
4
25
  label: ReactNode;
@@ -8,8 +29,10 @@ export interface FieldProps {
8
29
  required?: boolean;
9
30
  children: ReactNode;
10
31
  className?: string;
32
+ /** Stamps the {@link FIELD_PATH_ATTR} jump-to-field anchor (EC-443, see above). */
33
+ fieldPath?: string;
11
34
  }
12
- export declare function Field({ label, htmlFor, hint, error, required, children, className }: FieldProps): import("react").JSX.Element;
35
+ export declare function Field({ label, htmlFor, hint, error, required, children, className, fieldPath }: FieldProps): import("react").JSX.Element;
13
36
  export declare function FieldLabel({ className, ...props }: LabelHTMLAttributes<HTMLLabelElement>): import("react").JSX.Element;
14
37
  /** Native select styled to match Input — accessible and SSR-friendly. */
15
38
  export declare function Select({ className, children, ...props }: SelectHTMLAttributes<HTMLSelectElement>): import("react").JSX.Element;
package/dist/ui/field.js CHANGED
@@ -1,8 +1,29 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ChevronDown } from 'lucide-react';
3
3
  import { cn } from '../lib/utils.js';
4
- export function Field({ label, htmlFor, hint, error, required, children, className }) {
5
- return (_jsxs("div", { className: cn('flex flex-col gap-1.5', className), children: [_jsxs(FieldLabel, { htmlFor: htmlFor, children: [label, required ? _jsx("span", { className: "text-destructive", children: " *" }) : null] }), children, error ? (_jsx("p", { className: "text-xs font-medium text-destructive", role: "alert", children: error })) : hint ? (_jsx("p", { className: "text-xs text-muted-foreground", children: hint })) : null] }));
4
+ /**
5
+ * EC-443 — THE nested-field anchor convention (one convention, used by both the
6
+ * stamping and the lookup — do not invent a second):
7
+ *
8
+ * data-ec-field-path="<field>(/<token>)*"
9
+ *
10
+ * Slash-joined tokens from the TOP-LEVEL field name down into the value —
11
+ * array indices are numeric tokens, object subfields their names — e.g.
12
+ * `links`, `links/3`, `links/3/badge`, `links/3/badge/text`. This mirrors the
13
+ * `values/…` reference-index path with the `values`/`localized/<locale>` prefix
14
+ * stripped: the locale axis is the studio's ambient locale switch, never part
15
+ * of the DOM anchor.
16
+ *
17
+ * Stamped by: the document form's top-level field rows (`detail-editor`'s
18
+ * `renderFieldRow` via the `fieldPath` prop below) and the object/repeater
19
+ * controls (`object-array-field`: item cards, table rows/cells, and nested
20
+ * subfield rows). Read by: `use-field-focus`'s deepest-available-anchor
21
+ * resolution (jump-to-field / `?field=` deep links) via the pure helpers in
22
+ * `components/cms/field-anchors.ts`.
23
+ */
24
+ export const FIELD_PATH_ATTR = 'data-ec-field-path';
25
+ export function Field({ label, htmlFor, hint, error, required, children, className, fieldPath }) {
26
+ return (_jsxs("div", { className: cn('flex flex-col gap-1.5', className), ...(fieldPath !== undefined ? { [FIELD_PATH_ATTR]: fieldPath } : {}), children: [_jsxs(FieldLabel, { htmlFor: htmlFor, children: [label, required ? _jsx("span", { className: "text-destructive", children: " *" }) : null] }), children, error ? (_jsx("p", { className: "text-xs font-medium text-destructive", role: "alert", children: error })) : hint ? (_jsx("p", { className: "text-xs text-muted-foreground", children: hint })) : null] }));
6
27
  }
7
28
  export function FieldLabel({ className, ...props }) {
8
29
  return (_jsx("label", { className: cn('text-xs font-medium text-foreground/90', className), ...props }));
@@ -1 +1 @@
1
- {"version":3,"file":"field.js","sourceRoot":"","sources":["../../src/ui/field.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAajC,MAAM,UAAU,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAc;IAC9F,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,uBAAuB,EAAE,SAAS,CAAC,aACpD,MAAC,UAAU,IAAC,OAAO,EAAE,OAAO,aACzB,KAAK,EACL,QAAQ,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,kBAAkB,mBAAU,CAAC,CAAC,CAAC,IAAI,IACpD,EACZ,QAAQ,EACR,KAAK,CAAC,CAAC,CAAC,CACP,YAAG,SAAS,EAAC,sCAAsC,EAAC,IAAI,EAAC,OAAO,YAC7D,KAAK,GACJ,CACL,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACT,YAAG,SAAS,EAAC,+BAA+B,YAAE,IAAI,GAAK,CACxD,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAyC;IACvF,OAAO,CACL,gBACE,SAAS,EAAE,EAAE,CAAC,wCAAwC,EAAE,SAAS,CAAC,KAC9D,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,MAAM,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAA2C;IAC/F,OAAO,CACL,eAAK,SAAS,EAAC,UAAU,aACvB,iBACE,SAAS,EAAE,EAAE,CACX,8IAA8I,EAC9I,wFAAwF,EACxF,iDAAiD,EACjD,SAAS,CACV,KACG,KAAK,YAER,QAAQ,GACF,EACT,KAAC,WAAW,IAAC,SAAS,EAAC,gGAAgG,GAAG,IACtH,CACP,CAAA;AACH,CAAC","sourcesContent":["import { ChevronDown } from 'lucide-react'\nimport type { LabelHTMLAttributes, ReactNode, SelectHTMLAttributes } from 'react'\nimport { cn } from '../lib/utils'\n\n/** A labelled form field group: label, control, and optional hint/error. */\nexport interface FieldProps {\n label: ReactNode\n htmlFor?: string\n hint?: ReactNode\n error?: ReactNode\n required?: boolean\n children: ReactNode\n className?: string\n}\n\nexport function Field({ label, htmlFor, hint, error, required, children, className }: FieldProps) {\n return (\n <div className={cn('flex flex-col gap-1.5', className)}>\n <FieldLabel htmlFor={htmlFor}>\n {label}\n {required ? <span className=\"text-destructive\"> *</span> : null}\n </FieldLabel>\n {children}\n {error ? (\n <p className=\"text-xs font-medium text-destructive\" role=\"alert\">\n {error}\n </p>\n ) : hint ? (\n <p className=\"text-xs text-muted-foreground\">{hint}</p>\n ) : null}\n </div>\n )\n}\n\nexport function FieldLabel({ className, ...props }: LabelHTMLAttributes<HTMLLabelElement>) {\n return (\n <label\n className={cn('text-xs font-medium text-foreground/90', className)}\n {...props}\n />\n )\n}\n\n/** Native select styled to match Input — accessible and SSR-friendly. */\nexport function Select({ className, children, ...props }: SelectHTMLAttributes<HTMLSelectElement>) {\n return (\n <div className=\"relative\">\n <select\n className={cn(\n 'flex h-8 w-full appearance-none rounded-md border border-input bg-background pl-2.5 pr-8 text-sm text-foreground shadow-sm transition-colors',\n 'focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n {...props}\n >\n {children}\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n </div>\n )\n}\n"]}
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../../src/ui/field.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,oBAAoB,CAAA;AAenD,MAAM,UAAU,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAc;IACzG,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CAAC,uBAAuB,EAAE,SAAS,CAAC,KAC7C,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAErE,MAAC,UAAU,IAAC,OAAO,EAAE,OAAO,aACzB,KAAK,EACL,QAAQ,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,kBAAkB,mBAAU,CAAC,CAAC,CAAC,IAAI,IACpD,EACZ,QAAQ,EACR,KAAK,CAAC,CAAC,CAAC,CACP,YAAG,SAAS,EAAC,sCAAsC,EAAC,IAAI,EAAC,OAAO,YAC7D,KAAK,GACJ,CACL,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACT,YAAG,SAAS,EAAC,+BAA+B,YAAE,IAAI,GAAK,CACxD,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAyC;IACvF,OAAO,CACL,gBACE,SAAS,EAAE,EAAE,CAAC,wCAAwC,EAAE,SAAS,CAAC,KAC9D,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,MAAM,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAA2C;IAC/F,OAAO,CACL,eAAK,SAAS,EAAC,UAAU,aACvB,iBACE,SAAS,EAAE,EAAE,CACX,8IAA8I,EAC9I,wFAAwF,EACxF,iDAAiD,EACjD,SAAS,CACV,KACG,KAAK,YAER,QAAQ,GACF,EACT,KAAC,WAAW,IAAC,SAAS,EAAC,gGAAgG,GAAG,IACtH,CACP,CAAA;AACH,CAAC","sourcesContent":["import { ChevronDown } from 'lucide-react'\nimport type { LabelHTMLAttributes, ReactNode, SelectHTMLAttributes } from 'react'\nimport { cn } from '../lib/utils'\n\n/**\n * EC-443 — THE nested-field anchor convention (one convention, used by both the\n * stamping and the lookup — do not invent a second):\n *\n * data-ec-field-path=\"<field>(/<token>)*\"\n *\n * Slash-joined tokens from the TOP-LEVEL field name down into the value —\n * array indices are numeric tokens, object subfields their names — e.g.\n * `links`, `links/3`, `links/3/badge`, `links/3/badge/text`. This mirrors the\n * `values/…` reference-index path with the `values`/`localized/<locale>` prefix\n * stripped: the locale axis is the studio's ambient locale switch, never part\n * of the DOM anchor.\n *\n * Stamped by: the document form's top-level field rows (`detail-editor`'s\n * `renderFieldRow` via the `fieldPath` prop below) and the object/repeater\n * controls (`object-array-field`: item cards, table rows/cells, and nested\n * subfield rows). Read by: `use-field-focus`'s deepest-available-anchor\n * resolution (jump-to-field / `?field=` deep links) via the pure helpers in\n * `components/cms/field-anchors.ts`.\n */\nexport const FIELD_PATH_ATTR = 'data-ec-field-path'\n\n/** A labelled form field group: label, control, and optional hint/error. */\nexport interface FieldProps {\n label: ReactNode\n htmlFor?: string\n hint?: ReactNode\n error?: ReactNode\n required?: boolean\n children: ReactNode\n className?: string\n /** Stamps the {@link FIELD_PATH_ATTR} jump-to-field anchor (EC-443, see above). */\n fieldPath?: string\n}\n\nexport function Field({ label, htmlFor, hint, error, required, children, className, fieldPath }: FieldProps) {\n return (\n <div\n className={cn('flex flex-col gap-1.5', className)}\n {...(fieldPath !== undefined ? { [FIELD_PATH_ATTR]: fieldPath } : {})}\n >\n <FieldLabel htmlFor={htmlFor}>\n {label}\n {required ? <span className=\"text-destructive\"> *</span> : null}\n </FieldLabel>\n {children}\n {error ? (\n <p className=\"text-xs font-medium text-destructive\" role=\"alert\">\n {error}\n </p>\n ) : hint ? (\n <p className=\"text-xs text-muted-foreground\">{hint}</p>\n ) : null}\n </div>\n )\n}\n\nexport function FieldLabel({ className, ...props }: LabelHTMLAttributes<HTMLLabelElement>) {\n return (\n <label\n className={cn('text-xs font-medium text-foreground/90', className)}\n {...props}\n />\n )\n}\n\n/** Native select styled to match Input — accessible and SSR-friendly. */\nexport function Select({ className, children, ...props }: SelectHTMLAttributes<HTMLSelectElement>) {\n return (\n <div className=\"relative\">\n <select\n className={cn(\n 'flex h-8 w-full appearance-none rounded-md border border-input bg-background pl-2.5 pr-8 text-sm text-foreground shadow-sm transition-colors',\n 'focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n {...props}\n >\n {children}\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n </div>\n )\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elytracms/studio",
3
3
  "author": "finaldream <hi@finaldream.de>",
4
- "version": "0.0.70",
4
+ "version": "0.0.72",
5
5
  "private": false,
6
6
  "description": "The Elytra studio engine (UI + lib + routes + data-only Convex schema) as an importable package. Extraction staged in EXTRACTION.md (EC-223); Stages 1-2 = UI primitives + router-free lib leaves.",
7
7
  "license": "MIT",
@@ -32,10 +32,6 @@
32
32
  "types": "./dist/components/builder/canvas-stylesheet.d.ts",
33
33
  "default": "./dist/components/builder/canvas-stylesheet.js"
34
34
  },
35
- "./components/sections": {
36
- "types": "./dist/components/sections.d.ts",
37
- "default": "./dist/components/sections.js"
38
- },
39
35
  "./components/*": {
40
36
  "types": "./dist/components/*.d.ts",
41
37
  "default": "./dist/components/*.js"
@@ -108,6 +104,14 @@
108
104
  "types": "./dist/lib/*.d.ts",
109
105
  "default": "./dist/lib/*.js"
110
106
  },
107
+ "./previews/enumerate": {
108
+ "types": "./dist/previews/enumerate.d.ts",
109
+ "default": "./dist/previews/enumerate.js"
110
+ },
111
+ "./previews/harness": {
112
+ "types": "./dist/previews/harness.d.ts",
113
+ "default": "./dist/previews/harness.js"
114
+ },
111
115
  "./convex/*": "./convex/*.ts"
112
116
  },
113
117
  "files": [
@@ -146,7 +150,7 @@
146
150
  "lucide-react": "^1.23.0",
147
151
  "tailwind-merge": "^3.6.0",
148
152
  "zod": "^4.4.3",
149
- "@elytracms/core": "^0.0.70"
153
+ "@elytracms/core": "^0.0.72"
150
154
  },
151
155
  "peerDependencies": {
152
156
  "@tanstack/react-router": "^1.170.0",