@elytracms/studio 0.0.9 → 0.0.11
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/dist/components/cms/cms-store.js +10 -2
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/detail-editor.js +95 -37
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/details-pane.d.ts +29 -0
- package/dist/components/cms/details-pane.js +29 -0
- package/dist/components/cms/details-pane.js.map +1 -0
- package/dist/components/cms/field-control.js +6 -1
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/inspector/inspector-sidebar.d.ts +8 -1
- package/dist/components/inspector/inspector-sidebar.js +9 -3
- package/dist/components/inspector/inspector-sidebar.js.map +1 -1
- package/dist/lib/operations/cms-persistence.d.ts +7 -2
- package/dist/lib/operations/cms-persistence.js +6 -2
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/persona/labels.js +2 -0
- package/dist/lib/persona/labels.js.map +1 -1
- package/package.json +2 -2
|
@@ -2,14 +2,20 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Row, Text } from '@elytracms/studio/ui';
|
|
4
4
|
import { cn } from '@elytracms/studio/lib/utils';
|
|
5
|
-
export function InspectorSidebar({ panes, className, }) {
|
|
6
|
-
const [
|
|
5
|
+
export function InspectorSidebar({ panes, className, activeId: controlledId, onActiveChange, }) {
|
|
6
|
+
const [internalId, setInternalId] = useState(() => panes[0]?.id);
|
|
7
|
+
const activeId = controlledId ?? internalId;
|
|
8
|
+
const select = (id) => {
|
|
9
|
+
if (controlledId === undefined)
|
|
10
|
+
setInternalId(id);
|
|
11
|
+
onActiveChange?.(id);
|
|
12
|
+
};
|
|
7
13
|
// Derive the active pane defensively so a changed pane list (switching views)
|
|
8
14
|
// can never render a stale/absent tab — fall back to the first pane.
|
|
9
15
|
const active = panes.find((pane) => pane.id === activeId) ?? panes[0];
|
|
10
16
|
if (!active)
|
|
11
17
|
return null;
|
|
12
|
-
return (_jsxs("div", { className: cn('flex h-full min-h-0 flex-col bg-card', className), children: [_jsx(Row, { gap: "1", className: "shrink-0 border-b border-border p-1.5", children: panes.map((pane) => (_jsx(PaneTab, { pane: pane, active: pane.id === active.id, onClick: () =>
|
|
18
|
+
return (_jsxs("div", { className: cn('flex h-full min-h-0 flex-col bg-card', className), children: [_jsx(Row, { gap: "1", className: "shrink-0 border-b border-border p-1.5", children: panes.map((pane) => (_jsx(PaneTab, { pane: pane, active: pane.id === active.id, onClick: () => select(pane.id) }, pane.id))) }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: active.render() })] }));
|
|
13
19
|
}
|
|
14
20
|
function PaneTab({ pane, active, onClick, }) {
|
|
15
21
|
return (_jsxs("button", { type: "button", "aria-pressed": active, onClick: onClick, className: cn('inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 transition-colors', active ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted'), children: [pane.icon, _jsx(Text, { as: "span", size: "xs", weight: "medium", className: "leading-none", children: pane.label }), pane.badge] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-sidebar.js","sourceRoot":"","sources":["../../../src/components/inspector/inspector-sidebar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAA;AA0BhD,MAAM,UAAU,gBAAgB,CAAC,EAC/B,KAAK,EACL,SAAS,
|
|
1
|
+
{"version":3,"file":"inspector-sidebar.js","sourceRoot":"","sources":["../../../src/components/inspector/inspector-sidebar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAA;AA0BhD,MAAM,UAAU,gBAAgB,CAAC,EAC/B,KAAK,EACL,SAAS,EACT,QAAQ,EAAE,YAAY,EACtB,cAAc,GAWf;IACC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAqB,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACpF,MAAM,QAAQ,GAAG,YAAY,IAAI,UAAU,CAAA;IAC3C,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE;QAC5B,IAAI,YAAY,KAAK,SAAS;YAAE,aAAa,CAAC,EAAE,CAAC,CAAA;QACjD,cAAc,EAAE,CAAC,EAAE,CAAC,CAAA;IACtB,CAAC,CAAA;IACD,8EAA8E;IAC9E,qEAAqE;IACrE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAA;IACrE,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,sCAAsC,EAAE,SAAS,CAAC,aACnE,KAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,uCAAuC,YAC3D,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,KAAC,OAAO,IAEN,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,EAC7B,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAHzB,IAAI,CAAC,EAAE,CAIZ,CACH,CAAC,GACE,EACN,cAAK,SAAS,EAAC,8BAA8B,YAAE,MAAM,CAAC,MAAM,EAAE,GAAO,IACjE,CACP,CAAA;AACH,CAAC;AAED,SAAS,OAAO,CAAC,EACf,IAAI,EACJ,MAAM,EACN,OAAO,GAKR;IACC,OAAO,CACL,kBACE,IAAI,EAAC,QAAQ,kBACC,MAAM,EACpB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,EAAE,CACX,2EAA2E,EAC3E,MAAM,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,sCAAsC,CACrF,aAEA,IAAI,CAAC,IAAI,EACV,KAAC,IAAI,IAAC,EAAE,EAAC,MAAM,EAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,QAAQ,EAAC,SAAS,EAAC,cAAc,YAC/D,IAAI,CAAC,KAAK,GACN,EACN,IAAI,CAAC,KAAK,IACJ,CACV,CAAA;AACH,CAAC","sourcesContent":["import { useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { Row, Text } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\n\n/**\n * EC-224 — the shared, view-supplied inspector sidebar.\n *\n * A generic tabbed frame: it renders whatever {@link InspectorPane}s it is given\n * and owns nothing domain-specific. The OWNER composes the pane list — by\n * convention `panes = [...view.suppliedPanes, versionsPane]`, so a view (the\n * canvas) contributes its context pane (`Properties`) and the frame-owner (the\n * document editor) appends the document-level `Versions` pane. A future rich\n * field editor contributes its own pane the same way, without touching this\n * frame (AD-12 — the canvas is itself just a field editor).\n *\n * Each pane's `render` runs where the frame is mounted, so a pane that needs a\n * scoped context (e.g. the canvas `BuilderProvider` behind `PropertyInspector`)\n * must be supplied by a view that mounts the frame inside that provider.\n */\nexport interface InspectorPane {\n id: string\n label: string\n icon?: ReactNode\n /** Optional trailing adornment on the tab (e.g. a count badge for `Issues`). */\n badge?: ReactNode\n render: () => ReactNode\n}\n\nexport function InspectorSidebar({\n panes,\n className,\n activeId: controlledId,\n onActiveChange,\n}: {\n panes: readonly InspectorPane[]\n className?: string\n /**\n * Optionally control which pane is shown (EC-285): the document editor drives\n * this to open the `Details` pane when a jump-to-field targets a sidebar field.\n * Uncontrolled (internal state) when omitted.\n */\n activeId?: string\n onActiveChange?: (id: string) => void\n}) {\n const [internalId, setInternalId] = useState<string | undefined>(() => panes[0]?.id)\n const activeId = controlledId ?? internalId\n const select = (id: string) => {\n if (controlledId === undefined) setInternalId(id)\n onActiveChange?.(id)\n }\n // Derive the active pane defensively so a changed pane list (switching views)\n // can never render a stale/absent tab — fall back to the first pane.\n const active = panes.find((pane) => pane.id === activeId) ?? panes[0]\n if (!active) return null\n\n return (\n <div className={cn('flex h-full min-h-0 flex-col bg-card', className)}>\n <Row gap=\"1\" className=\"shrink-0 border-b border-border p-1.5\">\n {panes.map((pane) => (\n <PaneTab\n key={pane.id}\n pane={pane}\n active={pane.id === active.id}\n onClick={() => select(pane.id)}\n />\n ))}\n </Row>\n <div className=\"min-h-0 flex-1 overflow-auto\">{active.render()}</div>\n </div>\n )\n}\n\nfunction PaneTab({\n pane,\n active,\n onClick,\n}: {\n pane: InspectorPane\n active: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n aria-pressed={active}\n onClick={onClick}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 transition-colors',\n active ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted',\n )}\n >\n {pane.icon}\n <Text as=\"span\" size=\"xs\" weight=\"medium\" className=\"leading-none\">\n {pane.label}\n </Text>\n {pane.badge}\n </button>\n )\n}\n"]}
|
|
@@ -14,8 +14,13 @@ import type { OperationError, OperationsClient } from './operations-package';
|
|
|
14
14
|
export interface CmsPersister {
|
|
15
15
|
upsertDocument(doc: CmsDocument): void;
|
|
16
16
|
deleteDocument(ref: DocumentRef): void;
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Publish (EC-224): snapshot the current draft as a new version and pin it live.
|
|
19
|
+
* EC-273: an optional `snapshot` pins that exact document instead (the studio
|
|
20
|
+
* passes the draft with conditionally-hidden values stripped); the draft row is
|
|
21
|
+
* untouched.
|
|
22
|
+
*/
|
|
23
|
+
publishDocument(ref: DocumentRef, snapshot?: CmsDocument): void;
|
|
19
24
|
/** Unpublish (EC-224): clear the live version pin + any legacy published marker. */
|
|
20
25
|
unpublishDocument(ref: DocumentRef): void;
|
|
21
26
|
upsertCollection(collection: CollectionDef): void;
|
|
@@ -68,8 +68,12 @@ export function createCmsPersister(options) {
|
|
|
68
68
|
deleteDocument(ref) {
|
|
69
69
|
command((operations) => operations.documents.delete({ projectId: options.projectId, ref }));
|
|
70
70
|
},
|
|
71
|
-
publishDocument(ref) {
|
|
72
|
-
command((operations) => operations.documents.publish({
|
|
71
|
+
publishDocument(ref, snapshot) {
|
|
72
|
+
command((operations) => operations.documents.publish({
|
|
73
|
+
projectId: options.projectId,
|
|
74
|
+
ref,
|
|
75
|
+
...(snapshot ? { snapshot: selfType(snapshot) } : {}),
|
|
76
|
+
}));
|
|
73
77
|
},
|
|
74
78
|
unpublishDocument(ref) {
|
|
75
79
|
command((operations) => operations.documents.unpublish({ projectId: options.projectId, ref }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-persistence.js","sourceRoot":"","sources":["../../../src/lib/operations/cms-persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAQ3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"cms-persistence.js","sourceRoot":"","sources":["../../../src/lib/operations/cms-persistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { selfTypeDocument } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentRef,\n PropFieldLookup,\n} from '@elytracms/core/cms-core'\nimport type { OperationError, OperationResult, OperationsClient } from './operations-package'\nimport { createPersistQueue } from './persist-queue'\n\n/**\n * CMS session → operations persistence (EC-141). The CMS store keeps documents\n * and collections as optimistic session state (synchronous editing, validation\n * feedback while typing); every mutation is mirrored onto the operations layer\n * through `documents.*` / `schema.*` commands in dispatch order.\n *\n * Baseline: the persisted seed only covers the sample project. Before the\n * first mirrored command, the persister ensures the project has a persisted\n * schema + documents matching the session's starting state (kit imports,\n * sample fixtures), so deletes/updates address records that exist.\n */\nexport interface CmsPersister {\n upsertDocument(doc: CmsDocument): void\n deleteDocument(ref: DocumentRef): void\n /**\n * Publish (EC-224): snapshot the current draft as a new version and pin it live.\n * EC-273: an optional `snapshot` pins that exact document instead (the studio\n * passes the draft with conditionally-hidden values stripped); the draft row is\n * untouched.\n */\n publishDocument(ref: DocumentRef, snapshot?: CmsDocument): void\n /** Unpublish (EC-224): clear the live version pin + any legacy published marker. */\n unpublishDocument(ref: DocumentRef): void\n upsertCollection(collection: CollectionDef): void\n /** Run the baseline seed now (e.g. before applying an AI changeset). */\n ensureBaseline(): Promise<void>\n /** Await every queued command. */\n flush(): Promise<void>\n}\n\nexport interface CmsPersisterOptions {\n projectId: string\n getOperations: () => Promise<OperationsClient>\n /** Session collections/documents at baseline time (read lazily via refs). */\n getCollections: () => readonly CollectionDef[]\n getDocuments: () => readonly CmsDocument[]\n /**\n * Component-prop field-defs by component id (EC-277): the manifest half of the\n * write-time self-typing lens, so a composition's static relation/asset props are\n * self-typed too (\"schema leads, component adopts\"). Omit → document fields are\n * still self-typed; composition static props pass through.\n */\n lookupManifest?: PropFieldLookup\n onError: (error: OperationError) => void\n}\n\nexport function createCmsPersister(options: CmsPersisterOptions): CmsPersister {\n const queue = createPersistQueue()\n let baselineDone = false\n\n /**\n * EC-277: self-type a document through the schema-as-lens (+ the manifest for\n * composition static props) before it is persisted — the single write-time\n * wrapping point, so everything at rest self-describes and derivation stays\n * schema-free. Drift-tolerant: an undeclared collection passes through unchanged.\n */\n function selfType(doc: CmsDocument): CmsDocument {\n const collection = options.getCollections().find((c) => c.id === doc.collection)\n if (!collection) return doc\n return selfTypeDocument(\n doc,\n collection,\n options.lookupManifest ? { lookupManifest: options.lookupManifest } : {},\n )\n }\n\n async function ensureBaselineNow(): Promise<boolean> {\n if (baselineDone) return true\n const operations = await options.getOperations()\n const schema = await operations.schema.get({ projectId: options.projectId })\n if (schema.ok) {\n // A persisted schema exists (seeded sample project or an earlier\n // session) — the session started from the same fixtures, so commands\n // can apply directly.\n baselineDone = true\n return true\n }\n const saved = await operations.schema.save({\n projectId: options.projectId,\n collections: [...options.getCollections()],\n })\n if (!saved.ok) {\n options.onError(saved.error)\n return false\n }\n for (const document of options.getDocuments()) {\n const result = await operations.documents.upsert({\n projectId: options.projectId,\n document: selfType(document),\n })\n if (!result.ok) {\n options.onError(result.error)\n return false\n }\n }\n baselineDone = true\n return true\n }\n\n function command(run: (operations: OperationsClient) => Promise<OperationResult<unknown>>): void {\n queue.push(async () => {\n // A failed baseline already surfaced through onError — running the\n // command against missing records would only add a confusing follow-up.\n if (!(await ensureBaselineNow())) return\n const operations = await options.getOperations()\n const result = await run(operations)\n if (!result.ok) options.onError(result.error)\n })\n }\n\n return {\n upsertDocument(doc) {\n const document = selfType(doc)\n command((operations) =>\n operations.documents.upsert({ projectId: options.projectId, document }),\n )\n },\n deleteDocument(ref) {\n command((operations) => operations.documents.delete({ projectId: options.projectId, ref }))\n },\n publishDocument(ref, snapshot) {\n command((operations) =>\n operations.documents.publish({\n projectId: options.projectId,\n ref,\n ...(snapshot ? { snapshot: selfType(snapshot) } : {}),\n }),\n )\n },\n unpublishDocument(ref) {\n command((operations) =>\n operations.documents.unpublish({ projectId: options.projectId, ref }),\n )\n },\n upsertCollection(collection) {\n command((operations) =>\n operations.schema.upsertCollection({ projectId: options.projectId, collection }),\n )\n },\n async ensureBaseline() {\n queue.push(async () => {\n await ensureBaselineNow()\n })\n await queue.flush()\n },\n flush: () => queue.flush(),\n }\n}\n"]}
|
|
@@ -60,6 +60,8 @@ const CMS_ISSUE_LABELS = {
|
|
|
60
60
|
'duplicate-alias': 'Slot used twice',
|
|
61
61
|
'unknown-alias': 'Unknown slot',
|
|
62
62
|
'unfilled-slot': 'Empty slot',
|
|
63
|
+
'custom-validation': 'Validation rule',
|
|
64
|
+
'hidden-field-cleared': 'Cleared on publish',
|
|
63
65
|
'unknown-version': 'Unknown version',
|
|
64
66
|
};
|
|
65
67
|
const OPERATION_ERROR_LABELS = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/lib/persona/labels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IssueCode, ConditionOperator } from '@elytracms/core/project-graph'\nimport type { CmsIssueCode } from '@elytracms/core/cms-core'\nimport type { OperationErrorCode } from '@elytracms/core/operations'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { humanizeName } from '../utils'\n\n/**\n * Persona-aware labels (EC-192 item 2, Milestone B promise).\n *\n * A non-technical editor must never see a manifest/binding/path-token/validation\n * identifier on — or one click off — the editor path. The engine speaks in stable\n * machine codes (`issueCodeSchema`, `cmsIssueCodeSchema`, `operationErrorCodeSchema`)\n * and machine ids (`base.primitives.Heading`, `r-blog-slug-en`); this module is the\n * single translation layer the editor-facing surfaces route through.\n *\n * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a\n * code in `@elytracms/core/project-graph`, `@elytracms/core/cms-core`, or `@elytracms/core/operations`\n * fails typecheck here until it is given an editor-facing label — the persona\n * boundary cannot silently regress. Engineers keep the raw code: every surface that\n * shows a label also carries the code in a `title=`/`data-*` attribute (inspectable\n * via hover/devtools, never rendered as visible copy).\n */\n\nconst GRAPH_ISSUE_LABELS: Record<IssueCode, string> = {\n 'invalid-component-name': 'Invalid component',\n 'unknown-component': 'Unknown component',\n 'duplicate-node-id': 'Duplicate item',\n 'unknown-prop': 'Unknown setting',\n 'invalid-prop': 'Invalid setting',\n 'binding-not-allowed': 'Data not allowed here',\n 'unresolved-binding': 'Missing data',\n 'missing-required-slot': 'Missing content',\n 'unknown-slot': 'Unknown content area',\n 'forbidden-component': 'Not allowed here',\n 'server-only-component': 'Not available in the editor',\n 'unknown-layout': 'Unknown layout',\n 'route-conflict': 'Conflicting URL',\n 'invalid-container-direction': 'Invalid layout',\n 'invalid-container-alignment': 'Invalid alignment',\n 'invalid-container-gap': 'Invalid spacing',\n 'invalid-container-columns': 'Invalid columns',\n 'invalid-container-responsive': 'Invalid responsive setting',\n}\n\nconst CMS_ISSUE_LABELS: Record<CmsIssueCode, string> = {\n 'duplicate-collection': 'Duplicate collection',\n 'duplicate-field': 'Duplicate field',\n 'unknown-collection': 'Unknown collection',\n 'invalid-collection-config': 'Invalid collection setup',\n 'invalid-field-config': 'Invalid field setup',\n 'reserved-field-name': 'Reserved field name',\n 'missing-required-field': 'Missing required field',\n 'invalid-field-value': 'Invalid value',\n 'unknown-relation-target': 'Missing reference',\n 'unpublished-relation-target': 'Unpublished reference',\n 'cardinality-violation': 'Wrong number of values',\n 'unknown-asset': 'Missing asset',\n 'duplicate-document': 'Duplicate entry',\n 'unknown-locale': 'Unknown language',\n 'missing-localized-value': 'Missing translation',\n 'route-conflict': 'Conflicting URL',\n 'redirect-loop': 'Redirect loop',\n 'unknown-route-target': 'Missing page',\n 'hierarchy-cycle': 'Page nesting loop',\n 'document-nesting-depth': 'Content nested too deeply',\n 'singleton-document-drift': 'More than one document',\n 'duplicate-alias': 'Slot used twice',\n 'unknown-alias': 'Unknown slot',\n 'unfilled-slot': 'Empty slot',\n 'unknown-version': 'Unknown version',\n}\n\nconst OPERATION_ERROR_LABELS: Record<OperationErrorCode, string> = {\n validation: \"Couldn't save\",\n 'not-found': 'Not found',\n conflict: 'Already exists',\n 'backend-unavailable': 'Connection problem',\n unauthorized: 'Not allowed',\n unknown: 'Something went wrong',\n}\n\nconst EXPORT_TARGET_LABELS: Record<string, string> = {\n nextjs: 'Next.js',\n}\n\n/**\n * Plain-language labels for condition operators. Exhaustive over\n * `conditionOperatorSchema` — a new operator fails typecheck here until it has an\n * editor-facing label. The raw operator (`eq`, `gte`, …) is a machine token an\n * editor must never read in the inspector.\n */\nconst CONDITION_OPERATOR_LABELS: Record<ConditionOperator, string> = {\n truthy: 'Is set',\n exists: 'Exists',\n eq: 'Equals',\n neq: 'Does not equal',\n gt: 'Greater than',\n gte: 'At least',\n lt: 'Less than',\n lte: 'At most',\n}\n\n/** Plain-language title for a graph validation issue code. */\nexport function graphIssueLabel(code: IssueCode): string {\n return GRAPH_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for a CMS validation issue code. */\nexport function cmsIssueLabel(code: CmsIssueCode): string {\n return CMS_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for an operation failure code. */\nexport function operationErrorLabel(code: OperationErrorCode): string {\n return OPERATION_ERROR_LABELS[code] ?? OPERATION_ERROR_LABELS.unknown\n}\n\n/** Friendly name for a project export target (`nextjs` → `Next.js`). */\nexport function exportTargetLabel(target: string): string {\n return EXPORT_TARGET_LABELS[target] ?? humanizeName(target)\n}\n\n/** Plain-language label for a condition operator (`gte` → `At least`). */\nexport function conditionOperatorLabel(operator: ConditionOperator): string {\n return CONDITION_OPERATOR_LABELS[operator]\n}\n\n/** The leaf of a component id, namespace stripped (`base.primitives.Heading` → `Heading`). */\nexport function componentLeafId(componentId: string): string {\n const dot = componentId.lastIndexOf('.')\n return dot === -1 ? componentId : componentId.slice(dot + 1)\n}\n\n/**\n * The human display name of a component for editor-facing surfaces: the\n * manifest's authored `title` when present, else the humanized leaf of its id\n * (`base.primitives.Heading` → `Heading`, `project.MarketingHero` → `Marketing hero`).\n * Never the raw namespaced id.\n */\nexport function componentDisplayName(\n componentId: string,\n manifest?: ComponentManifest | undefined,\n): string {\n if (manifest?.title) return manifest.title\n return humanizeName(componentLeafId(componentId))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/lib/persona/labels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IssueCode, ConditionOperator } from '@elytracms/core/project-graph'\nimport type { CmsIssueCode } from '@elytracms/core/cms-core'\nimport type { OperationErrorCode } from '@elytracms/core/operations'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { humanizeName } from '../utils'\n\n/**\n * Persona-aware labels (EC-192 item 2, Milestone B promise).\n *\n * A non-technical editor must never see a manifest/binding/path-token/validation\n * identifier on — or one click off — the editor path. The engine speaks in stable\n * machine codes (`issueCodeSchema`, `cmsIssueCodeSchema`, `operationErrorCodeSchema`)\n * and machine ids (`base.primitives.Heading`, `r-blog-slug-en`); this module is the\n * single translation layer the editor-facing surfaces route through.\n *\n * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a\n * code in `@elytracms/core/project-graph`, `@elytracms/core/cms-core`, or `@elytracms/core/operations`\n * fails typecheck here until it is given an editor-facing label — the persona\n * boundary cannot silently regress. Engineers keep the raw code: every surface that\n * shows a label also carries the code in a `title=`/`data-*` attribute (inspectable\n * via hover/devtools, never rendered as visible copy).\n */\n\nconst GRAPH_ISSUE_LABELS: Record<IssueCode, string> = {\n 'invalid-component-name': 'Invalid component',\n 'unknown-component': 'Unknown component',\n 'duplicate-node-id': 'Duplicate item',\n 'unknown-prop': 'Unknown setting',\n 'invalid-prop': 'Invalid setting',\n 'binding-not-allowed': 'Data not allowed here',\n 'unresolved-binding': 'Missing data',\n 'missing-required-slot': 'Missing content',\n 'unknown-slot': 'Unknown content area',\n 'forbidden-component': 'Not allowed here',\n 'server-only-component': 'Not available in the editor',\n 'unknown-layout': 'Unknown layout',\n 'route-conflict': 'Conflicting URL',\n 'invalid-container-direction': 'Invalid layout',\n 'invalid-container-alignment': 'Invalid alignment',\n 'invalid-container-gap': 'Invalid spacing',\n 'invalid-container-columns': 'Invalid columns',\n 'invalid-container-responsive': 'Invalid responsive setting',\n}\n\nconst CMS_ISSUE_LABELS: Record<CmsIssueCode, string> = {\n 'duplicate-collection': 'Duplicate collection',\n 'duplicate-field': 'Duplicate field',\n 'unknown-collection': 'Unknown collection',\n 'invalid-collection-config': 'Invalid collection setup',\n 'invalid-field-config': 'Invalid field setup',\n 'reserved-field-name': 'Reserved field name',\n 'missing-required-field': 'Missing required field',\n 'invalid-field-value': 'Invalid value',\n 'unknown-relation-target': 'Missing reference',\n 'unpublished-relation-target': 'Unpublished reference',\n 'cardinality-violation': 'Wrong number of values',\n 'unknown-asset': 'Missing asset',\n 'duplicate-document': 'Duplicate entry',\n 'unknown-locale': 'Unknown language',\n 'missing-localized-value': 'Missing translation',\n 'route-conflict': 'Conflicting URL',\n 'redirect-loop': 'Redirect loop',\n 'unknown-route-target': 'Missing page',\n 'hierarchy-cycle': 'Page nesting loop',\n 'document-nesting-depth': 'Content nested too deeply',\n 'singleton-document-drift': 'More than one document',\n 'duplicate-alias': 'Slot used twice',\n 'unknown-alias': 'Unknown slot',\n 'unfilled-slot': 'Empty slot',\n 'custom-validation': 'Validation rule',\n 'hidden-field-cleared': 'Cleared on publish',\n 'unknown-version': 'Unknown version',\n}\n\nconst OPERATION_ERROR_LABELS: Record<OperationErrorCode, string> = {\n validation: \"Couldn't save\",\n 'not-found': 'Not found',\n conflict: 'Already exists',\n 'backend-unavailable': 'Connection problem',\n unauthorized: 'Not allowed',\n unknown: 'Something went wrong',\n}\n\nconst EXPORT_TARGET_LABELS: Record<string, string> = {\n nextjs: 'Next.js',\n}\n\n/**\n * Plain-language labels for condition operators. Exhaustive over\n * `conditionOperatorSchema` — a new operator fails typecheck here until it has an\n * editor-facing label. The raw operator (`eq`, `gte`, …) is a machine token an\n * editor must never read in the inspector.\n */\nconst CONDITION_OPERATOR_LABELS: Record<ConditionOperator, string> = {\n truthy: 'Is set',\n exists: 'Exists',\n eq: 'Equals',\n neq: 'Does not equal',\n gt: 'Greater than',\n gte: 'At least',\n lt: 'Less than',\n lte: 'At most',\n}\n\n/** Plain-language title for a graph validation issue code. */\nexport function graphIssueLabel(code: IssueCode): string {\n return GRAPH_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for a CMS validation issue code. */\nexport function cmsIssueLabel(code: CmsIssueCode): string {\n return CMS_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for an operation failure code. */\nexport function operationErrorLabel(code: OperationErrorCode): string {\n return OPERATION_ERROR_LABELS[code] ?? OPERATION_ERROR_LABELS.unknown\n}\n\n/** Friendly name for a project export target (`nextjs` → `Next.js`). */\nexport function exportTargetLabel(target: string): string {\n return EXPORT_TARGET_LABELS[target] ?? humanizeName(target)\n}\n\n/** Plain-language label for a condition operator (`gte` → `At least`). */\nexport function conditionOperatorLabel(operator: ConditionOperator): string {\n return CONDITION_OPERATOR_LABELS[operator]\n}\n\n/** The leaf of a component id, namespace stripped (`base.primitives.Heading` → `Heading`). */\nexport function componentLeafId(componentId: string): string {\n const dot = componentId.lastIndexOf('.')\n return dot === -1 ? componentId : componentId.slice(dot + 1)\n}\n\n/**\n * The human display name of a component for editor-facing surfaces: the\n * manifest's authored `title` when present, else the humanized leaf of its id\n * (`base.primitives.Heading` → `Heading`, `project.MarketingHero` → `Marketing hero`).\n * Never the raw namespaced id.\n */\nexport function componentDisplayName(\n componentId: string,\n manifest?: ComponentManifest | undefined,\n): string {\n if (manifest?.title) return manifest.title\n return humanizeName(componentLeafId(componentId))\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.
|
|
4
|
+
"version": "0.0.11",
|
|
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",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"lucide-react": "^1.17.0",
|
|
113
113
|
"tailwind-merge": "^3.6.0",
|
|
114
114
|
"zod": "^4.0.0",
|
|
115
|
-
"@elytracms/core": "^0.0.
|
|
115
|
+
"@elytracms/core": "^0.0.11"
|
|
116
116
|
},
|
|
117
117
|
"peerDependencies": {
|
|
118
118
|
"@tanstack/react-router": "^1.170.0",
|