@elytracms/studio 0.0.62 → 0.0.63
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/account/credential-panel.js +6 -1
- package/dist/components/account/credential-panel.js.map +1 -1
- package/dist/components/auth-context.d.ts +14 -0
- package/dist/components/auth-context.js +32 -2
- package/dist/components/auth-context.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +7 -0
- package/dist/components/builder/blocks-model.js +5 -0
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.js +2 -2
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.d.ts +15 -0
- package/dist/components/builder/fixture-thumbnail.js +14 -0
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/cms/cms-index.js +6 -3
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +40 -8
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/components/operation-error.d.ts +7 -5
- package/dist/components/operation-error.js +19 -36
- package/dist/components/operation-error.js.map +1 -1
- package/dist/components/project-list.js +17 -1
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-switcher.d.ts +10 -0
- package/dist/components/project-switcher.js +13 -3
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/session-expired-overlay.d.ts +27 -0
- package/dist/components/session-expired-overlay.js +51 -0
- package/dist/components/session-expired-overlay.js.map +1 -0
- package/dist/components/sign-in-view.d.ts +13 -1
- package/dist/components/sign-in-view.js +27 -13
- package/dist/components/sign-in-view.js.map +1 -1
- package/dist/components/studio-project-screen.js +2 -2
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +40 -3
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/customization.d.ts +4 -1
- package/dist/customization.js +3 -0
- package/dist/customization.js.map +1 -1
- package/dist/lib/auth-errors.d.ts +58 -28
- package/dist/lib/auth-errors.js +93 -15
- package/dist/lib/auth-errors.js.map +1 -1
- package/dist/lib/i18n/index.d.ts +1 -0
- package/dist/lib/i18n/index.js.map +1 -1
- package/dist/lib/i18n/messages.de.d.ts +24 -0
- package/dist/lib/i18n/messages.de.js +1019 -0
- package/dist/lib/i18n/messages.de.js.map +1 -0
- package/dist/lib/i18n/messages.en.d.ts +880 -0
- package/dist/lib/i18n/messages.en.js +71 -3
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/studio-operations.js +7 -0
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/deployment-readiness.d.ts +8 -1
- package/dist/lib/persistence/deployment-readiness.js +16 -1
- package/dist/lib/persistence/deployment-readiness.js.map +1 -1
- package/dist/lib/session-expiry.d.ts +67 -0
- package/dist/lib/session-expiry.js +72 -0
- package/dist/lib/session-expiry.js.map +1 -0
- package/dist/lib/tab-title.d.ts +53 -0
- package/dist/lib/tab-title.js +77 -0
- package/dist/lib/tab-title.js.map +1 -0
- package/dist/lib/workspace/index.d.ts +1 -1
- package/dist/lib/workspace/index.js +1 -1
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/lib/workspace/switcher-source.d.ts +27 -0
- package/dist/lib/workspace/switcher-source.js +30 -0
- package/dist/lib/workspace/switcher-source.js.map +1 -1
- package/dist/router/index.d.ts +6 -0
- package/dist/router/index.js +6 -0
- package/dist/router/index.js.map +1 -1
- package/dist/router/launcher-visit.d.ts +13 -0
- package/dist/router/launcher-visit.js +34 -0
- package/dist/router/launcher-visit.js.map +1 -0
- package/dist/router/root.js +12 -2
- package/dist/router/root.js.map +1 -1
- package/dist/ui/dialog.d.ts +15 -0
- package/dist/ui/dialog.js +17 -0
- package/dist/ui/dialog.js.map +1 -1
- package/package.json +6 -2
|
@@ -16,6 +16,36 @@ export function selectSwitcherProjects(input) {
|
|
|
16
16
|
export function persistedProjects(entries) {
|
|
17
17
|
return entries.flatMap((entry) => (entry.kind === 'project' ? [entry.project] : []));
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* The workspace's sole navigable project, or `null` (EC-401). Non-null exactly
|
|
21
|
+
* when the workspace has ONE entry total and it is a real `project` — a single
|
|
22
|
+
* *unprovisioned* entry keeps this `null` (the launcher's setup gate must stay
|
|
23
|
+
* visible), and a second entry of any kind does too.
|
|
24
|
+
*/
|
|
25
|
+
export function soleWorkspaceProject(entries) {
|
|
26
|
+
const [only] = entries;
|
|
27
|
+
return entries.length === 1 && only?.kind === 'project' ? only.project : null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* EC-401 — the ONE single-project predicate, shared by the launcher (skip the
|
|
31
|
+
* dashboard hop) and the shell header (plain project name instead of the
|
|
32
|
+
* switcher dropdown) so the two surfaces can never disagree.
|
|
33
|
+
*/
|
|
34
|
+
export function isSingleProjectWorkspace(entries) {
|
|
35
|
+
return soleWorkspaceProject(entries) !== null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The launcher's auto-forward decision (EC-401), pure: the sole project to
|
|
39
|
+
* redirect to, or `null` to render the launcher. A forced `?state=` verification
|
|
40
|
+
* state and an explicit "all projects" navigation (see `launcherVisitState` in
|
|
41
|
+
* the router seam) both keep the launcher visible; otherwise a single-project
|
|
42
|
+
* workspace forwards straight into its project.
|
|
43
|
+
*/
|
|
44
|
+
export function launcherAutoForwardTarget(input) {
|
|
45
|
+
if (input.forcedState || input.explicitVisit)
|
|
46
|
+
return null;
|
|
47
|
+
return soleWorkspaceProject(input.entries);
|
|
48
|
+
}
|
|
19
49
|
/** The not-yet-provisioned config entries (rendered as explicit CLI-hint states). */
|
|
20
50
|
export function unprovisionedProjects(entries) {
|
|
21
51
|
return entries.flatMap((entry) => (entry.kind === 'unprovisioned' ? [entry.config] : []));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switcher-source.js","sourceRoot":"","sources":["../../../src/lib/workspace/switcher-source.ts"],"names":[],"mappings":"AAkCA,MAAM,UAAU,sBAAsB,CAAC,KAA0B;IAC/D,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,wEAAwE;QACxE,yEAAyE;QACzE,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACjF,OAAO,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QAC7C,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACzC,OAAO,SAAS;YACd,CAAC,CAAC,EAAE,IAAI,EAAE,SAAkB,EAAE,OAAO,EAAE,SAAS,EAAE;YAClD,CAAC,CAAC,EAAE,IAAI,EAAE,eAAwB,EAAE,MAAM,EAAE,CAAA;IAChD,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,iBAAiB,CAAC,OAAiC;IACjE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACtF,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,qBAAqB,CACnC,OAAiC;IAEjC,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAC3F,CAAC","sourcesContent":["import type { Project } from '@elytracms/core/studio-core'\nimport type { ResolvedWorkspaceProject } from '@elytracms/core/studio-core'\nimport type { WorkspaceState } from './workspace-source'\n\n/**\n * Config-driven switcher source (EC-154, reframed for EC-179 per-backend\n * projects). Pure logic deciding what the project switcher and launcher list:\n *\n * - **No workspace config** → exactly today's behavior: the adapter-listed\n * projects, untouched (single-project golden path).\n * - **Config present** → every configured project, in config order, as a\n * navigable entry; one that has no persisted counterpart yet is an explicit\n * `unprovisioned` state (\"run `builder init --project <slug>`\") rather than an\n * error. Persisted projects *not* in the config are not listed: the config is\n * the source of truth for what this workspace is.\n *\n * Visibility is **not** filtered by membership. One project per backend (vision\n * AD-8): a configured project lives on its own deployment, so the *active*\n * backend's memberships cannot speak to another project's. Access is enforced\n * where it belongs — per-backend sign-in plus the EC-150 role hook on entry —\n * not by hiding projects you'd authenticate into. (Hiding them was the\n * single-backend assumption; cross-backend it just made projects unreachable.)\n */\n\nexport type SwitcherEntry =\n | { kind: 'project'; project: Project }\n | { kind: 'unprovisioned'; config: ResolvedWorkspaceProject }\n\nexport interface SwitcherSourceInput {\n workspace: WorkspaceState\n /** The studio store's read model (config-derived under a workspace config). */\n persisted: readonly Project[]\n}\n\nexport function selectSwitcherProjects(input: SwitcherSourceInput): SwitcherEntry[] {\n if (input.workspace.kind !== 'config') {\n // No-config golden path (an invalid config never reaches the switcher —\n // the DeploymentSetupGate blocks boot): today's adapter-listed behavior.\n return input.persisted.map((project) => ({ kind: 'project', project }))\n }\n\n const bySlug = new Map(input.persisted.map((project) => [project.slug, project]))\n return input.workspace.projects.map((config) => {\n const persisted = bySlug.get(config.slug)\n return persisted\n ? { kind: 'project' as const, project: persisted }\n : { kind: 'unprovisioned' as const, config }\n })\n}\n\n/** The persisted projects among the entries (for list surfaces filtering by status). */\nexport function persistedProjects(entries: readonly SwitcherEntry[]): Project[] {\n return entries.flatMap((entry) => (entry.kind === 'project' ? [entry.project] : []))\n}\n\n/** The not-yet-provisioned config entries (rendered as explicit CLI-hint states). */\nexport function unprovisionedProjects(\n entries: readonly SwitcherEntry[],\n): ResolvedWorkspaceProject[] {\n return entries.flatMap((entry) => (entry.kind === 'unprovisioned' ? [entry.config] : []))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"switcher-source.js","sourceRoot":"","sources":["../../../src/lib/workspace/switcher-source.ts"],"names":[],"mappings":"AAkCA,MAAM,UAAU,sBAAsB,CAAC,KAA0B;IAC/D,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,wEAAwE;QACxE,yEAAyE;QACzE,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACjF,OAAO,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QAC7C,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACzC,OAAO,SAAS;YACd,CAAC,CAAC,EAAE,IAAI,EAAE,SAAkB,EAAE,OAAO,EAAE,SAAS,EAAE;YAClD,CAAC,CAAC,EAAE,IAAI,EAAE,eAAwB,EAAE,MAAM,EAAE,CAAA;IAChD,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,iBAAiB,CAAC,OAAiC;IACjE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACtF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAiC;IACpE,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;IACtB,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA;AAC/E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAiC;IACxE,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;AAC/C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAMzC;IACC,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,aAAa;QAAE,OAAO,IAAI,CAAA;IACzD,OAAO,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;AAC5C,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,qBAAqB,CACnC,OAAiC;IAEjC,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAC3F,CAAC","sourcesContent":["import type { Project } from '@elytracms/core/studio-core'\nimport type { ResolvedWorkspaceProject } from '@elytracms/core/studio-core'\nimport type { WorkspaceState } from './workspace-source'\n\n/**\n * Config-driven switcher source (EC-154, reframed for EC-179 per-backend\n * projects). Pure logic deciding what the project switcher and launcher list:\n *\n * - **No workspace config** → exactly today's behavior: the adapter-listed\n * projects, untouched (single-project golden path).\n * - **Config present** → every configured project, in config order, as a\n * navigable entry; one that has no persisted counterpart yet is an explicit\n * `unprovisioned` state (\"run `builder init --project <slug>`\") rather than an\n * error. Persisted projects *not* in the config are not listed: the config is\n * the source of truth for what this workspace is.\n *\n * Visibility is **not** filtered by membership. One project per backend (vision\n * AD-8): a configured project lives on its own deployment, so the *active*\n * backend's memberships cannot speak to another project's. Access is enforced\n * where it belongs — per-backend sign-in plus the EC-150 role hook on entry —\n * not by hiding projects you'd authenticate into. (Hiding them was the\n * single-backend assumption; cross-backend it just made projects unreachable.)\n */\n\nexport type SwitcherEntry =\n | { kind: 'project'; project: Project }\n | { kind: 'unprovisioned'; config: ResolvedWorkspaceProject }\n\nexport interface SwitcherSourceInput {\n workspace: WorkspaceState\n /** The studio store's read model (config-derived under a workspace config). */\n persisted: readonly Project[]\n}\n\nexport function selectSwitcherProjects(input: SwitcherSourceInput): SwitcherEntry[] {\n if (input.workspace.kind !== 'config') {\n // No-config golden path (an invalid config never reaches the switcher —\n // the DeploymentSetupGate blocks boot): today's adapter-listed behavior.\n return input.persisted.map((project) => ({ kind: 'project', project }))\n }\n\n const bySlug = new Map(input.persisted.map((project) => [project.slug, project]))\n return input.workspace.projects.map((config) => {\n const persisted = bySlug.get(config.slug)\n return persisted\n ? { kind: 'project' as const, project: persisted }\n : { kind: 'unprovisioned' as const, config }\n })\n}\n\n/** The persisted projects among the entries (for list surfaces filtering by status). */\nexport function persistedProjects(entries: readonly SwitcherEntry[]): Project[] {\n return entries.flatMap((entry) => (entry.kind === 'project' ? [entry.project] : []))\n}\n\n/**\n * The workspace's sole navigable project, or `null` (EC-401). Non-null exactly\n * when the workspace has ONE entry total and it is a real `project` — a single\n * *unprovisioned* entry keeps this `null` (the launcher's setup gate must stay\n * visible), and a second entry of any kind does too.\n */\nexport function soleWorkspaceProject(entries: readonly SwitcherEntry[]): Project | null {\n const [only] = entries\n return entries.length === 1 && only?.kind === 'project' ? only.project : null\n}\n\n/**\n * EC-401 — the ONE single-project predicate, shared by the launcher (skip the\n * dashboard hop) and the shell header (plain project name instead of the\n * switcher dropdown) so the two surfaces can never disagree.\n */\nexport function isSingleProjectWorkspace(entries: readonly SwitcherEntry[]): boolean {\n return soleWorkspaceProject(entries) !== null\n}\n\n/**\n * The launcher's auto-forward decision (EC-401), pure: the sole project to\n * redirect to, or `null` to render the launcher. A forced `?state=` verification\n * state and an explicit \"all projects\" navigation (see `launcherVisitState` in\n * the router seam) both keep the launcher visible; otherwise a single-project\n * workspace forwards straight into its project.\n */\nexport function launcherAutoForwardTarget(input: {\n entries: readonly SwitcherEntry[]\n /** Whether the launcher was opened with a forced `?state=` verification state. */\n forcedState: boolean\n /** Whether this location is an explicit \"all projects\" navigation. */\n explicitVisit: boolean\n}): Project | null {\n if (input.forcedState || input.explicitVisit) return null\n return soleWorkspaceProject(input.entries)\n}\n\n/** The not-yet-provisioned config entries (rendered as explicit CLI-hint states). */\nexport function unprovisionedProjects(\n entries: readonly SwitcherEntry[],\n): ResolvedWorkspaceProject[] {\n return entries.flatMap((entry) => (entry.kind === 'unprovisioned' ? [entry.config] : []))\n}\n"]}
|
package/dist/router/index.d.ts
CHANGED
|
@@ -30,3 +30,9 @@ export declare const STUDIO_PROJECT_ROUTE: "/$projectId/$";
|
|
|
30
30
|
* one place: `{ Link, STUDIO_PROJECT_ROUTE, studioPath }`.
|
|
31
31
|
*/
|
|
32
32
|
export { studioPath } from './match.js';
|
|
33
|
+
/**
|
|
34
|
+
* EC-401: history-state marker distinguishing an explicit "all projects"
|
|
35
|
+
* navigation from a bare launcher hit, so a single-project workspace's launcher
|
|
36
|
+
* auto-forward never swallows a deliberate visit to `/`.
|
|
37
|
+
*/
|
|
38
|
+
export { isExplicitLauncherVisit, launcherVisitState } from './launcher-visit.js';
|
package/dist/router/index.js
CHANGED
|
@@ -29,4 +29,10 @@ export const STUDIO_PROJECT_ROUTE = '/$projectId/$';
|
|
|
29
29
|
* one place: `{ Link, STUDIO_PROJECT_ROUTE, studioPath }`.
|
|
30
30
|
*/
|
|
31
31
|
export { studioPath } from './match.js';
|
|
32
|
+
/**
|
|
33
|
+
* EC-401: history-state marker distinguishing an explicit "all projects"
|
|
34
|
+
* navigation from a bare launcher hit, so a single-project workspace's launcher
|
|
35
|
+
* auto-forward never swallows a deliberate visit to `/`.
|
|
36
|
+
*/
|
|
37
|
+
export { isExplicitLauncherVisit, launcherVisitState } from './launcher-visit.js';
|
|
32
38
|
//# sourceMappingURL=index.js.map
|
package/dist/router/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,SAAS,EACT,SAAS,GACV,MAAM,wBAAwB,CAAA;AAG/B;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAwB,CAAA;AAE5D;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA","sourcesContent":["/**\n * Studio router adapter (EC-223 Stage 3 — the gating seam). The studio UI\n * navigates through these primitives instead of importing `@tanstack/react-router`\n * directly, so the engine owns its router CHOICE (TanStack Router) without being\n * hard-wired to a specific app: the consuming app provides the router INSTANCE +\n * the generated route tree (for Aurora, via TanStack Start). `@tanstack/react-router`\n * is a PEER dependency so there is exactly one instance — hence one router context\n * the re-exported hooks read.\n *\n * Only the runtime navigation primitives the studio components use are surfaced\n * here; route DEFINITION (`createFileRoute`/`createRouter`) stays in the consumer's\n * route files. Paths are addressed as plain strings (`to=\"/$projectId\"`), so the\n * components don't depend on the consumer's route-tree types.\n */\nexport {\n Link,\n Navigate,\n useBlocker,\n useLocation,\n useNavigate,\n useParams,\n useSearch,\n} from '@tanstack/react-router'\nexport type { LinkProps } from '@tanstack/react-router'\n\n/**\n * The studio's single in-project route (EC-223 Stage 6 — catch-all routing).\n * Every URL under a project is served by one splat, addressed by its `_splat`\n * subpath. By the route-table contract (`./match`), that subpath is the section\n * id for a top-level section (`'cms'`, `'settings'`, …) and the deep path for a\n * nested screen (`'cms/<collection>/<doc>'`). Navigate to a section with\n * `to={STUDIO_PROJECT_ROUTE} params={{ projectId, _splat }}` rather than a\n * per-section path that no longer exists in the consumer's route tree.\n */\nexport const STUDIO_PROJECT_ROUTE = '/$projectId/$' as const\n\n/**\n * Typed `_splat` builder for the studio splat — the inverse of the engine route\n * table (`./match`). Re-exported here so a component imports the link seam from\n * one place: `{ Link, STUDIO_PROJECT_ROUTE, studioPath }`.\n */\nexport { studioPath } from './match'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,SAAS,EACT,SAAS,GACV,MAAM,wBAAwB,CAAA;AAG/B;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAwB,CAAA;AAE5D;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEpC;;;;GAIG;AACH,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA","sourcesContent":["/**\n * Studio router adapter (EC-223 Stage 3 — the gating seam). The studio UI\n * navigates through these primitives instead of importing `@tanstack/react-router`\n * directly, so the engine owns its router CHOICE (TanStack Router) without being\n * hard-wired to a specific app: the consuming app provides the router INSTANCE +\n * the generated route tree (for Aurora, via TanStack Start). `@tanstack/react-router`\n * is a PEER dependency so there is exactly one instance — hence one router context\n * the re-exported hooks read.\n *\n * Only the runtime navigation primitives the studio components use are surfaced\n * here; route DEFINITION (`createFileRoute`/`createRouter`) stays in the consumer's\n * route files. Paths are addressed as plain strings (`to=\"/$projectId\"`), so the\n * components don't depend on the consumer's route-tree types.\n */\nexport {\n Link,\n Navigate,\n useBlocker,\n useLocation,\n useNavigate,\n useParams,\n useSearch,\n} from '@tanstack/react-router'\nexport type { LinkProps } from '@tanstack/react-router'\n\n/**\n * The studio's single in-project route (EC-223 Stage 6 — catch-all routing).\n * Every URL under a project is served by one splat, addressed by its `_splat`\n * subpath. By the route-table contract (`./match`), that subpath is the section\n * id for a top-level section (`'cms'`, `'settings'`, …) and the deep path for a\n * nested screen (`'cms/<collection>/<doc>'`). Navigate to a section with\n * `to={STUDIO_PROJECT_ROUTE} params={{ projectId, _splat }}` rather than a\n * per-section path that no longer exists in the consumer's route tree.\n */\nexport const STUDIO_PROJECT_ROUTE = '/$projectId/$' as const\n\n/**\n * Typed `_splat` builder for the studio splat — the inverse of the engine route\n * table (`./match`). Re-exported here so a component imports the link seam from\n * one place: `{ Link, STUDIO_PROJECT_ROUTE, studioPath }`.\n */\nexport { studioPath } from './match'\n\n/**\n * EC-401: history-state marker distinguishing an explicit \"all projects\"\n * navigation from a bare launcher hit, so a single-project workspace's launcher\n * auto-forward never swallows a deliberate visit to `/`.\n */\nexport { isExplicitLauncherVisit, launcherVisitState } from './launcher-visit'\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HistoryState } from '@tanstack/react-router';
|
|
2
|
+
/**
|
|
3
|
+
* `state` value for `Link`/`navigate` calls that mean "show me the project
|
|
4
|
+
* list": `<Link to="/" state={launcherVisitState}>` or
|
|
5
|
+
* `navigate({ to: '/', state: launcherVisitState })`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const launcherVisitState: HistoryState;
|
|
8
|
+
/**
|
|
9
|
+
* Whether a location's history state carries the explicit-visit marker.
|
|
10
|
+
* Takes `unknown`: `HistoryState` is an opaque (augmentable) interface, so the
|
|
11
|
+
* marker key is read structurally.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isExplicitLauncherVisit(state: unknown): boolean;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit-launcher-visit marker (EC-401). A single-project workspace
|
|
3
|
+
* auto-forwards the bare launcher hit (`/`) straight into its project — but an
|
|
4
|
+
* EXPLICIT "all projects" navigation (the shell's logo link, the switcher's
|
|
5
|
+
* "Browse all projects", a not-found screen's "Back to projects") must still
|
|
6
|
+
* land ON the launcher. Those navigations carry this history-state marker, and
|
|
7
|
+
* the launcher skips the auto-forward when it is present.
|
|
8
|
+
*
|
|
9
|
+
* History state (not a search param) deliberately: it never leaks into the URL,
|
|
10
|
+
* it needs no cooperation from the consumer route file's `validateSearch`, and
|
|
11
|
+
* it sticks to the history ENTRY — so back/forward returning to an explicit
|
|
12
|
+
* launcher visit (and even a reload of it) keeps showing the launcher, while a
|
|
13
|
+
* fresh address-bar hit of `/` carries no marker and forwards as designed.
|
|
14
|
+
*/
|
|
15
|
+
const LAUNCHER_VISIT_FLAG = 'elytraLauncherVisit';
|
|
16
|
+
/**
|
|
17
|
+
* `state` value for `Link`/`navigate` calls that mean "show me the project
|
|
18
|
+
* list": `<Link to="/" state={launcherVisitState}>` or
|
|
19
|
+
* `navigate({ to: '/', state: launcherVisitState })`.
|
|
20
|
+
*/
|
|
21
|
+
// `HistoryState` is an empty-but-augmentable interface; the cast carries the
|
|
22
|
+
// marker key without globally augmenting a transitive package's module.
|
|
23
|
+
export const launcherVisitState = { [LAUNCHER_VISIT_FLAG]: true };
|
|
24
|
+
/**
|
|
25
|
+
* Whether a location's history state carries the explicit-visit marker.
|
|
26
|
+
* Takes `unknown`: `HistoryState` is an opaque (augmentable) interface, so the
|
|
27
|
+
* marker key is read structurally.
|
|
28
|
+
*/
|
|
29
|
+
export function isExplicitLauncherVisit(state) {
|
|
30
|
+
return (typeof state === 'object' &&
|
|
31
|
+
state !== null &&
|
|
32
|
+
state[LAUNCHER_VISIT_FLAG] === true);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=launcher-visit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher-visit.js","sourceRoot":"","sources":["../../src/router/launcher-visit.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,MAAM,mBAAmB,GAAG,qBAAqB,CAAA;AAEjD;;;;GAIG;AACH,6EAA6E;AAC7E,wEAAwE;AACxE,MAAM,CAAC,MAAM,kBAAkB,GAAiB,EAAE,CAAC,mBAAmB,CAAC,EAAE,IAAI,EAAkB,CAAA;AAE/F;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAiC,CAAC,mBAAmB,CAAC,KAAK,IAAI,CACjE,CAAA;AACH,CAAC","sourcesContent":["import type { HistoryState } from '@tanstack/react-router'\n\n/**\n * Explicit-launcher-visit marker (EC-401). A single-project workspace\n * auto-forwards the bare launcher hit (`/`) straight into its project — but an\n * EXPLICIT \"all projects\" navigation (the shell's logo link, the switcher's\n * \"Browse all projects\", a not-found screen's \"Back to projects\") must still\n * land ON the launcher. Those navigations carry this history-state marker, and\n * the launcher skips the auto-forward when it is present.\n *\n * History state (not a search param) deliberately: it never leaks into the URL,\n * it needs no cooperation from the consumer route file's `validateSearch`, and\n * it sticks to the history ENTRY — so back/forward returning to an explicit\n * launcher visit (and even a reload of it) keeps showing the launcher, while a\n * fresh address-bar hit of `/` carries no marker and forwards as designed.\n */\nconst LAUNCHER_VISIT_FLAG = 'elytraLauncherVisit'\n\n/**\n * `state` value for `Link`/`navigate` calls that mean \"show me the project\n * list\": `<Link to=\"/\" state={launcherVisitState}>` or\n * `navigate({ to: '/', state: launcherVisitState })`.\n */\n// `HistoryState` is an empty-but-augmentable interface; the cast carries the\n// marker key without globally augmenting a transitive package's module.\nexport const launcherVisitState: HistoryState = { [LAUNCHER_VISIT_FLAG]: true } as HistoryState\n\n/**\n * Whether a location's history state carries the explicit-visit marker.\n * Takes `unknown`: `HistoryState` is an opaque (augmentable) interface, so the\n * marker key is read structurally.\n */\nexport function isExplicitLauncherVisit(state: unknown): boolean {\n return (\n typeof state === 'object' &&\n state !== null &&\n (state as Record<string, unknown>)[LAUNCHER_VISIT_FLAG] === true\n )\n}\n"]}
|
package/dist/router/root.js
CHANGED
|
@@ -2,14 +2,23 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { createRootRoute, HeadContent, Navigate, Outlet, Scripts, useLocation, } from '@tanstack/react-router';
|
|
3
3
|
import { AuthProvider, useAuth } from '@elytracms/studio/components/auth-context';
|
|
4
4
|
import { DeploymentSetupGate } from '@elytracms/studio/components/deployment-setup-gate';
|
|
5
|
+
import { SessionExpiredOverlay } from '@elytracms/studio/components/session-expired-overlay';
|
|
5
6
|
import { StudioProvider } from '@elytracms/studio/components/studio-context';
|
|
6
7
|
import { ThemeProvider } from '@elytracms/studio/components/theme-context';
|
|
7
8
|
import { Text } from '@elytracms/studio/ui';
|
|
8
9
|
import { SIGN_IN_PATH, resolveRouteGuard } from '@elytracms/studio/lib/auth';
|
|
10
|
+
import { useT } from '@elytracms/studio/lib/i18n';
|
|
11
|
+
import { setStudioBaseTitle } from '@elytracms/studio/lib/tab-title';
|
|
9
12
|
import { activeSlugForLocation, setActiveProjectSlug } from '@elytracms/studio/lib/workspace';
|
|
10
13
|
import { THEME_INIT_SCRIPT } from '@elytracms/studio/lib/theme';
|
|
11
14
|
export function createStudioRootRoute(options) {
|
|
12
15
|
const { appCssUrl, title = 'Elytra', htmlLang = 'en', meta = [], links = [], providers } = options;
|
|
16
|
+
// EC-402: register the base title for the shell's per-view tab-title effect
|
|
17
|
+
// (`StudioTabTitle` composes `deepest crumb · view · project · <this title>`
|
|
18
|
+
// and restores exactly this value when leaving a project view). Module-holder
|
|
19
|
+
// seam — same pattern as `setActiveProjectSlug` — so the shell never has to
|
|
20
|
+
// capture `document.title` and race the router's own head effect.
|
|
21
|
+
setStudioBaseTitle(title);
|
|
13
22
|
function RootComponent() {
|
|
14
23
|
const location = useLocation();
|
|
15
24
|
// EC-179: the active project selects its Convex backend. Resolve it from the
|
|
@@ -20,7 +29,7 @@ export function createStudioRootRoute(options) {
|
|
|
20
29
|
// re-select against the new backend and the session is restored per-backend.
|
|
21
30
|
const activeSlug = activeSlugForLocation(location.pathname, location.search.redirect);
|
|
22
31
|
setActiveProjectSlug(activeSlug);
|
|
23
|
-
return (_jsx(RootDocument, { htmlLang: htmlLang, children: _jsx(ThemeProvider, { children: _jsx(DeploymentSetupGate, { children: _jsx(AuthProvider, { children: _jsx(AuthGate, { children:
|
|
32
|
+
return (_jsx(RootDocument, { htmlLang: htmlLang, children: _jsx(ThemeProvider, { children: _jsx(DeploymentSetupGate, { children: _jsx(AuthProvider, { children: _jsx(AuthGate, { children: _jsxs(StudioProvider, { children: [providers ? providers(_jsx(Outlet, {})) : _jsx(Outlet, {}), _jsx(SessionExpiredOverlay, {})] }) }) }, activeSlug ?? '__launcher__') }) }) }));
|
|
24
33
|
}
|
|
25
34
|
return createRootRoute({
|
|
26
35
|
head: () => ({
|
|
@@ -56,7 +65,8 @@ function AuthGate({ children }) {
|
|
|
56
65
|
}
|
|
57
66
|
/** Deterministic session-restore state (also the SSR output for protected paths). */
|
|
58
67
|
function SessionLoading() {
|
|
59
|
-
|
|
68
|
+
const t = useT();
|
|
69
|
+
return (_jsx("div", { className: "grid min-h-dvh place-items-center bg-background", children: _jsx(Text, { size: "sm", tone: "muted", children: t('auth.boot.checkingSession') }) }));
|
|
60
70
|
}
|
|
61
71
|
function RootDocument({ children, htmlLang }) {
|
|
62
72
|
// Dark-first SSR baseline; THEME_INIT_SCRIPT flips the class pre-hydration
|
package/dist/router/root.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/router/root.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react'\nimport {\n createRootRoute,\n HeadContent,\n Navigate,\n Outlet,\n Scripts,\n useLocation,\n} from '@tanstack/react-router'\nimport { AuthProvider, useAuth } from '@elytracms/studio/components/auth-context'\nimport { DeploymentSetupGate } from '@elytracms/studio/components/deployment-setup-gate'\nimport { StudioProvider } from '@elytracms/studio/components/studio-context'\nimport { ThemeProvider } from '@elytracms/studio/components/theme-context'\nimport { Text } from '@elytracms/studio/ui'\nimport { SIGN_IN_PATH, resolveRouteGuard } from '@elytracms/studio/lib/auth'\nimport { activeSlugForLocation, setActiveProjectSlug } from '@elytracms/studio/lib/workspace'\nimport { THEME_INIT_SCRIPT } from '@elytracms/studio/lib/theme'\n\n/**\n * Studio root-route boot factory (EC-375; consolidated from the copy-pasted\n * consumer `__root.tsx` by EC-369 finding #7). It assembles the ONE canonical\n * provider/gate stack — ThemeProvider → DeploymentSetupGate → AuthProvider(keyed)\n * → AuthGate → StudioProvider → Outlet — plus the `RootDocument` shell\n * (html/head with the pre-hydration theme script + the chrome stylesheet).\n *\n * Its whole purpose is to make the NEXT required-gate addition automatic on\n * package upgrade: when EC-258 added `DeploymentSetupGate`, every hand-copied\n * `__root.tsx` had to be manually re-synced, and Konditorei's silently missed\n * it for five releases (EC-367). Owning the composition here means a consumer's\n * `__root.tsx` shrinks to a thin shell and picks up new gates for free.\n *\n * The consumer re-exports the returned route as `Route` (the generated route\n * tree imports `Route` from `./routes/__root`):\n *\n * ```tsx\n * import { createStudioRootRoute } from '@elytracms/studio/router/root'\n * import appCss from '../styles/app.css?url'\n * export const Route = createStudioRootRoute({ appCssUrl: appCss })\n * ```\n */\nexport interface StudioRootRouteOptions {\n /**\n * URL of the studio chrome stylesheet, from the consumer's Vite `?url` import\n * (`import appCss from '../styles/app.css?url'`). The one option every consumer\n * must pass — the sheet is project-owned (it carries the delivery theme).\n */\n appCssUrl: string\n /** Document `<title>`. Default: `'Elytra'`. */\n title?: string\n /** `<html lang>`. Default: `'en'`. */\n htmlLang?: string\n /**\n * Extra `<head>` meta entries appended after the defaults (charset, viewport,\n * title). Same shape TanStack Router's `head().meta` takes.\n */\n meta?: Array<Record<string, string>>\n /** Extra `<head>` link entries appended after the app stylesheet. */\n links?: Array<Record<string, string>>\n /**\n * Escape hatch: wrap additional project-specific providers around the routed\n * subtree. Rendered INSIDE `StudioProvider` and around `<Outlet />`, so nested\n * providers see the full studio/auth/theme context and remount with the active\n * project. Return the passed `children` wrapped in your providers.\n */\n providers?: (children: ReactNode) => ReactNode\n}\n\nexport function createStudioRootRoute(options: StudioRootRouteOptions) {\n const { appCssUrl, title = 'Elytra', htmlLang = 'en', meta = [], links = [], providers } = options\n\n function RootComponent() {\n const location = useLocation()\n // EC-179: the active project selects its Convex backend. Resolve it from the\n // URL (or the sign-in redirect target) and bind it *before* the auth and\n // persistence adapters are selected, so the initial sign-in authenticates\n // against the project the user is heading for. Keying the auth/studio subtree\n // by the active project remounts it on a project switch, so the adapters\n // re-select against the new backend and the session is restored per-backend.\n const activeSlug = activeSlugForLocation(\n location.pathname,\n (location.search as { redirect?: string }).redirect,\n )\n setActiveProjectSlug(activeSlug)\n\n return (\n <RootDocument htmlLang={htmlLang}>\n <ThemeProvider>\n {/* EC-258: one deployment-setup gate runs before auth — a misconfigured or\n unreachable backend, an invalid workspace config, or any failing readiness\n precondition (missing auth signing key, required collections, …) is a\n single visible boot screen with copy-pasteable fixes, never a silent\n in-memory fallback or a misleading downstream error. All checks passing\n renders children as before. */}\n <DeploymentSetupGate>\n <AuthProvider key={activeSlug ?? '__launcher__'}>\n <AuthGate>\n <StudioProvider>{providers ? providers(<Outlet />) : <Outlet />}</StudioProvider>\n </AuthGate>\n </AuthProvider>\n </DeploymentSetupGate>\n </ThemeProvider>\n </RootDocument>\n )\n }\n\n return createRootRoute({\n head: () => ({\n meta: [\n { charSet: 'utf-8' },\n { name: 'viewport', content: 'width=device-width, initial-scale=1' },\n { title },\n ...meta,\n ],\n links: [{ rel: 'stylesheet', href: appCssUrl }, ...links],\n }),\n component: RootComponent,\n })\n}\n\n/**\n * Route guard (EC-150): one chokepoint in the root layout. Unauthenticated\n * visitors on protected paths are redirected to the sign-in screen (with the\n * intended target preserved); signed-in users never see the sign-in screen.\n * The decision logic is the pure, unit-tested `resolveRouteGuard`.\n */\nfunction AuthGate({ children }: { children: ReactNode }) {\n const { status } = useAuth()\n const { pathname } = useLocation()\n const decision = resolveRouteGuard(pathname, status)\n\n if (decision.kind === 'pending') return <SessionLoading />\n if (decision.kind === 'redirect-to-sign-in') {\n return <Navigate to={SIGN_IN_PATH} search={{ redirect: decision.redirect }} replace />\n }\n if (decision.kind === 'redirect-home') return <Navigate to=\"/\" replace />\n return children\n}\n\n/** Deterministic session-restore state (also the SSR output for protected paths). */\nfunction SessionLoading() {\n return (\n <div className=\"grid min-h-dvh place-items-center bg-background\">\n <Text size=\"sm\" tone=\"muted\">\n Checking session…\n </Text>\n </div>\n )\n}\n\nfunction RootDocument({ children, htmlLang }: { children: ReactNode; htmlLang: string }) {\n // Dark-first SSR baseline; THEME_INIT_SCRIPT flips the class pre-hydration\n // from the stored preference, and ThemeProvider keeps it reactive thereafter.\n // suppressHydrationWarning: the script intentionally mutates the class before\n // React hydrates, so a className mismatch on <html> is expected, not a bug.\n // The app stylesheet is emitted via head().links and rendered by <HeadContent />.\n return (\n <html lang={htmlLang} className=\"dark\" suppressHydrationWarning>\n <head>\n <HeadContent />\n <script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} />\n </head>\n <body className=\"min-h-screen bg-background font-sans text-foreground antialiased\">\n {children}\n <Scripts />\n </body>\n </html>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/router/root.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react'\nimport {\n createRootRoute,\n HeadContent,\n Navigate,\n Outlet,\n Scripts,\n useLocation,\n} from '@tanstack/react-router'\nimport { AuthProvider, useAuth } from '@elytracms/studio/components/auth-context'\nimport { DeploymentSetupGate } from '@elytracms/studio/components/deployment-setup-gate'\nimport { SessionExpiredOverlay } from '@elytracms/studio/components/session-expired-overlay'\nimport { StudioProvider } from '@elytracms/studio/components/studio-context'\nimport { ThemeProvider } from '@elytracms/studio/components/theme-context'\nimport { Text } from '@elytracms/studio/ui'\nimport { SIGN_IN_PATH, resolveRouteGuard } from '@elytracms/studio/lib/auth'\nimport { useT } from '@elytracms/studio/lib/i18n'\nimport { setStudioBaseTitle } from '@elytracms/studio/lib/tab-title'\nimport { activeSlugForLocation, setActiveProjectSlug } from '@elytracms/studio/lib/workspace'\nimport { THEME_INIT_SCRIPT } from '@elytracms/studio/lib/theme'\n\n/**\n * Studio root-route boot factory (EC-375; consolidated from the copy-pasted\n * consumer `__root.tsx` by EC-369 finding #7). It assembles the ONE canonical\n * provider/gate stack — ThemeProvider → DeploymentSetupGate → AuthProvider(keyed)\n * → AuthGate → StudioProvider → Outlet — plus the `RootDocument` shell\n * (html/head with the pre-hydration theme script + the chrome stylesheet).\n *\n * Its whole purpose is to make the NEXT required-gate addition automatic on\n * package upgrade: when EC-258 added `DeploymentSetupGate`, every hand-copied\n * `__root.tsx` had to be manually re-synced, and Konditorei's silently missed\n * it for five releases (EC-367). Owning the composition here means a consumer's\n * `__root.tsx` shrinks to a thin shell and picks up new gates for free.\n *\n * The consumer re-exports the returned route as `Route` (the generated route\n * tree imports `Route` from `./routes/__root`):\n *\n * ```tsx\n * import { createStudioRootRoute } from '@elytracms/studio/router/root'\n * import appCss from '../styles/app.css?url'\n * export const Route = createStudioRootRoute({ appCssUrl: appCss })\n * ```\n */\nexport interface StudioRootRouteOptions {\n /**\n * URL of the studio chrome stylesheet, from the consumer's Vite `?url` import\n * (`import appCss from '../styles/app.css?url'`). The one option every consumer\n * must pass — the sheet is project-owned (it carries the delivery theme).\n */\n appCssUrl: string\n /** Document `<title>`. Default: `'Elytra'`. */\n title?: string\n /** `<html lang>`. Default: `'en'`. */\n htmlLang?: string\n /**\n * Extra `<head>` meta entries appended after the defaults (charset, viewport,\n * title). Same shape TanStack Router's `head().meta` takes.\n */\n meta?: Array<Record<string, string>>\n /** Extra `<head>` link entries appended after the app stylesheet. */\n links?: Array<Record<string, string>>\n /**\n * Escape hatch: wrap additional project-specific providers around the routed\n * subtree. Rendered INSIDE `StudioProvider` and around `<Outlet />`, so nested\n * providers see the full studio/auth/theme context and remount with the active\n * project. Return the passed `children` wrapped in your providers.\n */\n providers?: (children: ReactNode) => ReactNode\n}\n\nexport function createStudioRootRoute(options: StudioRootRouteOptions) {\n const { appCssUrl, title = 'Elytra', htmlLang = 'en', meta = [], links = [], providers } = options\n\n // EC-402: register the base title for the shell's per-view tab-title effect\n // (`StudioTabTitle` composes `deepest crumb · view · project · <this title>`\n // and restores exactly this value when leaving a project view). Module-holder\n // seam — same pattern as `setActiveProjectSlug` — so the shell never has to\n // capture `document.title` and race the router's own head effect.\n setStudioBaseTitle(title)\n\n function RootComponent() {\n const location = useLocation()\n // EC-179: the active project selects its Convex backend. Resolve it from the\n // URL (or the sign-in redirect target) and bind it *before* the auth and\n // persistence adapters are selected, so the initial sign-in authenticates\n // against the project the user is heading for. Keying the auth/studio subtree\n // by the active project remounts it on a project switch, so the adapters\n // re-select against the new backend and the session is restored per-backend.\n const activeSlug = activeSlugForLocation(\n location.pathname,\n (location.search as { redirect?: string }).redirect,\n )\n setActiveProjectSlug(activeSlug)\n\n return (\n <RootDocument htmlLang={htmlLang}>\n <ThemeProvider>\n {/* EC-258: one deployment-setup gate runs before auth — a misconfigured or\n unreachable backend, an invalid workspace config, or any failing readiness\n precondition (missing auth signing key, required collections, …) is a\n single visible boot screen with copy-pasteable fixes, never a silent\n in-memory fallback or a misleading downstream error. All checks passing\n renders children as before. */}\n <DeploymentSetupGate>\n <AuthProvider key={activeSlug ?? '__launcher__'}>\n <AuthGate>\n <StudioProvider>\n {providers ? providers(<Outlet />) : <Outlet />}\n {/* EC-403: the blocking in-place re-auth overlay — mounted over\n every route, INSIDE StudioProvider (it invalidates the studio\n query cache on re-auth) and inside AuthProvider (it reads the\n sessionExpired state). Renders null unless the session died. */}\n <SessionExpiredOverlay />\n </StudioProvider>\n </AuthGate>\n </AuthProvider>\n </DeploymentSetupGate>\n </ThemeProvider>\n </RootDocument>\n )\n }\n\n return createRootRoute({\n head: () => ({\n meta: [\n { charSet: 'utf-8' },\n { name: 'viewport', content: 'width=device-width, initial-scale=1' },\n { title },\n ...meta,\n ],\n links: [{ rel: 'stylesheet', href: appCssUrl }, ...links],\n }),\n component: RootComponent,\n })\n}\n\n/**\n * Route guard (EC-150): one chokepoint in the root layout. Unauthenticated\n * visitors on protected paths are redirected to the sign-in screen (with the\n * intended target preserved); signed-in users never see the sign-in screen.\n * The decision logic is the pure, unit-tested `resolveRouteGuard`.\n */\nfunction AuthGate({ children }: { children: ReactNode }) {\n const { status } = useAuth()\n const { pathname } = useLocation()\n const decision = resolveRouteGuard(pathname, status)\n\n if (decision.kind === 'pending') return <SessionLoading />\n if (decision.kind === 'redirect-to-sign-in') {\n return <Navigate to={SIGN_IN_PATH} search={{ redirect: decision.redirect }} replace />\n }\n if (decision.kind === 'redirect-home') return <Navigate to=\"/\" replace />\n return children\n}\n\n/** Deterministic session-restore state (also the SSR output for protected paths). */\nfunction SessionLoading() {\n const t = useT()\n return (\n <div className=\"grid min-h-dvh place-items-center bg-background\">\n <Text size=\"sm\" tone=\"muted\">\n {t('auth.boot.checkingSession')}\n </Text>\n </div>\n )\n}\n\nfunction RootDocument({ children, htmlLang }: { children: ReactNode; htmlLang: string }) {\n // Dark-first SSR baseline; THEME_INIT_SCRIPT flips the class pre-hydration\n // from the stored preference, and ThemeProvider keeps it reactive thereafter.\n // suppressHydrationWarning: the script intentionally mutates the class before\n // React hydrates, so a className mismatch on <html> is expected, not a bug.\n // The app stylesheet is emitted via head().links and rendered by <HeadContent />.\n return (\n <html lang={htmlLang} className=\"dark\" suppressHydrationWarning>\n <head>\n <HeadContent />\n <script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} />\n </head>\n <body className=\"min-h-screen bg-background font-sans text-foreground antialiased\">\n {children}\n <Scripts />\n </body>\n </html>\n )\n}\n"]}
|
package/dist/ui/dialog.d.ts
CHANGED
|
@@ -6,6 +6,21 @@ export declare const DialogClose: import("react").ForwardRefExoticComponent<Dial
|
|
|
6
6
|
export declare const DialogPortal: import("react").FC<DialogPrimitive.DialogPortalProps>;
|
|
7
7
|
export declare function DialogOverlay({ className, ...props }: ComponentProps<typeof DialogPrimitive.Overlay>): import("react").JSX.Element;
|
|
8
8
|
export declare function DialogContent({ className, children, ...props }: ComponentProps<typeof DialogPrimitive.Content>): import("react").JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* Full-viewport, NON-dismissible modal content (EC-403: the blocking
|
|
11
|
+
* session-expired re-auth overlay). Escape, backdrop clicks, and outside
|
|
12
|
+
* interaction are all consumed — the only way out is the flow rendered inside
|
|
13
|
+
* (or the owner clearing the controlled `open`). Radix still provides the
|
|
14
|
+
* modal guarantees the blocking treatment relies on: focus trap, scroll lock,
|
|
15
|
+
* `aria-modal`, and outside pointer-event lockout, so the studio beneath stays
|
|
16
|
+
* fully inert while the React tree (and all unsaved editor state) lives on.
|
|
17
|
+
*
|
|
18
|
+
* Stacking (EC-392 discipline): mounted last and modal, it is the topmost
|
|
19
|
+
* layer — its Escape handler `preventDefault`s, which the `ExpandableSurface`
|
|
20
|
+
* window-bubble handler reads as "handled above me", so sheets open beneath
|
|
21
|
+
* the blocker stay open instead of swallowing keys.
|
|
22
|
+
*/
|
|
23
|
+
export declare function DialogBlockingContent({ className, children, ...props }: ComponentProps<typeof DialogPrimitive.Content>): import("react").JSX.Element;
|
|
9
24
|
export declare function DialogHeader({ className, ...props }: ComponentProps<'div'>): import("react").JSX.Element;
|
|
10
25
|
export declare function DialogFooter({ className, ...props }: ComponentProps<'div'>): import("react").JSX.Element;
|
|
11
26
|
export declare function DialogTitle({ className, ...props }: ComponentProps<typeof DialogPrimitive.Title>): import("react").JSX.Element;
|
package/dist/ui/dialog.js
CHANGED
|
@@ -12,6 +12,23 @@ export function DialogOverlay({ className, ...props }) {
|
|
|
12
12
|
export function DialogContent({ className, children, ...props }) {
|
|
13
13
|
return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { className: cn('fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl border border-border bg-popover p-5 text-popover-foreground shadow-2xl', 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95', className), ...props, children: [children, _jsxs(DialogPrimitive.Close, { className: "absolute right-3.5 top-3.5 rounded-sm text-muted-foreground opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring/60 outline-none", children: [_jsx(X, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "Close" })] })] })] }));
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Full-viewport, NON-dismissible modal content (EC-403: the blocking
|
|
17
|
+
* session-expired re-auth overlay). Escape, backdrop clicks, and outside
|
|
18
|
+
* interaction are all consumed — the only way out is the flow rendered inside
|
|
19
|
+
* (or the owner clearing the controlled `open`). Radix still provides the
|
|
20
|
+
* modal guarantees the blocking treatment relies on: focus trap, scroll lock,
|
|
21
|
+
* `aria-modal`, and outside pointer-event lockout, so the studio beneath stays
|
|
22
|
+
* fully inert while the React tree (and all unsaved editor state) lives on.
|
|
23
|
+
*
|
|
24
|
+
* Stacking (EC-392 discipline): mounted last and modal, it is the topmost
|
|
25
|
+
* layer — its Escape handler `preventDefault`s, which the `ExpandableSurface`
|
|
26
|
+
* window-bubble handler reads as "handled above me", so sheets open beneath
|
|
27
|
+
* the blocker stay open instead of swallowing keys.
|
|
28
|
+
*/
|
|
29
|
+
export function DialogBlockingContent({ className, children, ...props }) {
|
|
30
|
+
return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsx(DialogPrimitive.Content, { onEscapeKeyDown: (event) => event.preventDefault(), onPointerDownOutside: (event) => event.preventDefault(), onInteractOutside: (event) => event.preventDefault(), className: cn('fixed inset-0 z-50 overflow-y-auto outline-none', className), ...props, children: children })] }));
|
|
31
|
+
}
|
|
15
32
|
export function DialogHeader({ className, ...props }) {
|
|
16
33
|
return _jsx("div", { className: cn('flex flex-col gap-1.5', className), ...props });
|
|
17
34
|
}
|
package/dist/ui/dialog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../src/ui/dialog.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAEjC,MAAM,CAAC,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAA;AAC1C,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC,OAAO,CAAA;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAA;AAChD,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAA;AAElD,MAAM,UAAU,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAkD;IACnG,OAAO,CACL,KAAC,eAAe,CAAC,OAAO,IACtB,SAAS,EAAE,EAAE,CACX,4KAA4K,EAC5K,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAC5B,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACuC;IAC/C,OAAO,CACL,MAAC,YAAY,eACX,KAAC,aAAa,KAAG,EACjB,MAAC,eAAe,CAAC,OAAO,IACtB,SAAS,EAAE,EAAE,CACX,4KAA4K,EAC5K,uFAAuF,EACvF,gGAAgG,EAChG,SAAS,CACV,KACG,KAAK,aAER,QAAQ,EACT,MAAC,eAAe,CAAC,KAAK,IAAC,SAAS,EAAC,0KAA0K,aACzM,KAAC,CAAC,IAAC,SAAS,EAAC,QAAQ,GAAG,EACxB,eAAM,SAAS,EAAC,SAAS,sBAAa,IAChB,IACA,IACb,CAChB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAyB;IACzE,OAAO,cAAK,SAAS,EAAE,EAAE,CAAC,uBAAuB,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAAA;AAC9E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAyB;IACzE,OAAO,cAAK,SAAS,EAAE,EAAE,CAAC,0CAA0C,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAAA;AACjG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAgD;IAC/F,OAAO,KAAC,eAAe,CAAC,KAAK,IAAC,SAAS,EAAE,EAAE,CAAC,wCAAwC,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAAA;AACjH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAChC,SAAS,EACT,GAAG,KAAK,EAC2C;IACnD,OAAO,KAAC,eAAe,CAAC,WAAW,IAAC,SAAS,EAAE,EAAE,CAAC,+BAA+B,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAAA;AAC9G,CAAC","sourcesContent":["import * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { X } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../lib/utils'\n\nexport const Dialog = DialogPrimitive.Root\nexport const DialogTrigger = DialogPrimitive.Trigger\nexport const DialogClose = DialogPrimitive.Close\nexport const DialogPortal = DialogPrimitive.Portal\n\nexport function DialogOverlay({ className, ...props }: ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n className={cn(\n 'fixed inset-0 z-50 bg-black/55 backdrop-blur-[2px] data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0',\n className,\n )}\n {...props}\n />\n )\n}\n\nexport function DialogContent({\n className,\n children,\n ...props\n}: ComponentProps<typeof DialogPrimitive.Content>) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n className={cn(\n 'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl border border-border bg-popover p-5 text-popover-foreground shadow-2xl',\n 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',\n 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-3.5 top-3.5 rounded-sm text-muted-foreground opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring/60 outline-none\">\n <X className=\"size-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n}\n\nexport function DialogHeader({ className, ...props }: ComponentProps<'div'>) {\n return <div className={cn('flex flex-col gap-1.5', className)} {...props} />\n}\n\nexport function DialogFooter({ className, ...props }: ComponentProps<'div'>) {\n return <div className={cn('flex items-center justify-end gap-2 pt-2', className)} {...props} />\n}\n\nexport function DialogTitle({ className, ...props }: ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={cn('text-base font-semibold tracking-tight', className)} {...props} />\n}\n\nexport function DialogDescription({\n className,\n ...props\n}: ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={cn('text-sm text-muted-foreground', className)} {...props} />\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../src/ui/dialog.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAEjC,MAAM,CAAC,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAA;AAC1C,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC,OAAO,CAAA;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAA;AAChD,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAA;AAElD,MAAM,UAAU,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAkD;IACnG,OAAO,CACL,KAAC,eAAe,CAAC,OAAO,IACtB,SAAS,EAAE,EAAE,CACX,4KAA4K,EAC5K,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAC5B,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACuC;IAC/C,OAAO,CACL,MAAC,YAAY,eACX,KAAC,aAAa,KAAG,EACjB,MAAC,eAAe,CAAC,OAAO,IACtB,SAAS,EAAE,EAAE,CACX,4KAA4K,EAC5K,uFAAuF,EACvF,gGAAgG,EAChG,SAAS,CACV,KACG,KAAK,aAER,QAAQ,EACT,MAAC,eAAe,CAAC,KAAK,IAAC,SAAS,EAAC,0KAA0K,aACzM,KAAC,CAAC,IAAC,SAAS,EAAC,QAAQ,GAAG,EACxB,eAAM,SAAS,EAAC,SAAS,sBAAa,IAChB,IACA,IACb,CAChB,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,EACpC,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACuC;IAC/C,OAAO,CACL,MAAC,YAAY,eACX,KAAC,aAAa,KAAG,EACjB,KAAC,eAAe,CAAC,OAAO,IACtB,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EAClD,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EACvD,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EACpD,SAAS,EAAE,EAAE,CAAC,iDAAiD,EAAE,SAAS,CAAC,KACvE,KAAK,YAER,QAAQ,GACe,IACb,CAChB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAyB;IACzE,OAAO,cAAK,SAAS,EAAE,EAAE,CAAC,uBAAuB,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAAA;AAC9E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAyB;IACzE,OAAO,cAAK,SAAS,EAAE,EAAE,CAAC,0CAA0C,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAAA;AACjG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAgD;IAC/F,OAAO,KAAC,eAAe,CAAC,KAAK,IAAC,SAAS,EAAE,EAAE,CAAC,wCAAwC,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAAA;AACjH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAChC,SAAS,EACT,GAAG,KAAK,EAC2C;IACnD,OAAO,KAAC,eAAe,CAAC,WAAW,IAAC,SAAS,EAAE,EAAE,CAAC,+BAA+B,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAAA;AAC9G,CAAC","sourcesContent":["import * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { X } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../lib/utils'\n\nexport const Dialog = DialogPrimitive.Root\nexport const DialogTrigger = DialogPrimitive.Trigger\nexport const DialogClose = DialogPrimitive.Close\nexport const DialogPortal = DialogPrimitive.Portal\n\nexport function DialogOverlay({ className, ...props }: ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n className={cn(\n 'fixed inset-0 z-50 bg-black/55 backdrop-blur-[2px] data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0',\n className,\n )}\n {...props}\n />\n )\n}\n\nexport function DialogContent({\n className,\n children,\n ...props\n}: ComponentProps<typeof DialogPrimitive.Content>) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n className={cn(\n 'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl border border-border bg-popover p-5 text-popover-foreground shadow-2xl',\n 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',\n 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-3.5 top-3.5 rounded-sm text-muted-foreground opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring/60 outline-none\">\n <X className=\"size-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n}\n\n/**\n * Full-viewport, NON-dismissible modal content (EC-403: the blocking\n * session-expired re-auth overlay). Escape, backdrop clicks, and outside\n * interaction are all consumed — the only way out is the flow rendered inside\n * (or the owner clearing the controlled `open`). Radix still provides the\n * modal guarantees the blocking treatment relies on: focus trap, scroll lock,\n * `aria-modal`, and outside pointer-event lockout, so the studio beneath stays\n * fully inert while the React tree (and all unsaved editor state) lives on.\n *\n * Stacking (EC-392 discipline): mounted last and modal, it is the topmost\n * layer — its Escape handler `preventDefault`s, which the `ExpandableSurface`\n * window-bubble handler reads as \"handled above me\", so sheets open beneath\n * the blocker stay open instead of swallowing keys.\n */\nexport function DialogBlockingContent({\n className,\n children,\n ...props\n}: ComponentProps<typeof DialogPrimitive.Content>) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n onEscapeKeyDown={(event) => event.preventDefault()}\n onPointerDownOutside={(event) => event.preventDefault()}\n onInteractOutside={(event) => event.preventDefault()}\n className={cn('fixed inset-0 z-50 overflow-y-auto outline-none', className)}\n {...props}\n >\n {children}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n}\n\nexport function DialogHeader({ className, ...props }: ComponentProps<'div'>) {\n return <div className={cn('flex flex-col gap-1.5', className)} {...props} />\n}\n\nexport function DialogFooter({ className, ...props }: ComponentProps<'div'>) {\n return <div className={cn('flex items-center justify-end gap-2 pt-2', className)} {...props} />\n}\n\nexport function DialogTitle({ className, ...props }: ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={cn('text-base font-semibold tracking-tight', className)} {...props} />\n}\n\nexport function DialogDescription({\n className,\n ...props\n}: ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={cn('text-sm text-muted-foreground', className)} {...props} />\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.63",
|
|
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",
|
|
@@ -56,6 +56,10 @@
|
|
|
56
56
|
"types": "./dist/lib/i18n/index.d.ts",
|
|
57
57
|
"default": "./dist/lib/i18n/index.js"
|
|
58
58
|
},
|
|
59
|
+
"./locales/de": {
|
|
60
|
+
"types": "./dist/lib/i18n/messages.de.d.ts",
|
|
61
|
+
"default": "./dist/lib/i18n/messages.de.js"
|
|
62
|
+
},
|
|
59
63
|
"./lib/ai": {
|
|
60
64
|
"types": "./dist/lib/ai/index.d.ts",
|
|
61
65
|
"default": "./dist/lib/ai/index.js"
|
|
@@ -142,7 +146,7 @@
|
|
|
142
146
|
"lucide-react": "^1.23.0",
|
|
143
147
|
"tailwind-merge": "^3.6.0",
|
|
144
148
|
"zod": "^4.4.3",
|
|
145
|
-
"@elytracms/core": "^0.0.
|
|
149
|
+
"@elytracms/core": "^0.0.63"
|
|
146
150
|
},
|
|
147
151
|
"peerDependencies": {
|
|
148
152
|
"@tanstack/react-router": "^1.170.0",
|