@elytracms/studio 0.0.17 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/components/app-views/default-modules.d.ts +89 -26
  2. package/dist/components/app-views/default-modules.js +132 -49
  3. package/dist/components/app-views/default-modules.js.map +1 -1
  4. package/dist/components/app-views/module.d.ts +59 -50
  5. package/dist/components/app-views/module.js +25 -43
  6. package/dist/components/app-views/module.js.map +1 -1
  7. package/dist/components/app-views/sidebar.d.ts +54 -73
  8. package/dist/components/app-views/sidebar.js +92 -127
  9. package/dist/components/app-views/sidebar.js.map +1 -1
  10. package/dist/components/app-views/studio-config.d.ts +77 -0
  11. package/dist/components/app-views/studio-config.js +31 -0
  12. package/dist/components/app-views/studio-config.js.map +1 -0
  13. package/dist/components/cms/field-control.d.ts +8 -5
  14. package/dist/components/cms/field-control.js +21 -10
  15. package/dist/components/cms/field-control.js.map +1 -1
  16. package/dist/components/cms/field-renderer.d.ts +28 -30
  17. package/dist/components/cms/field-renderer.js +15 -20
  18. package/dist/components/cms/field-renderer.js.map +1 -1
  19. package/dist/components/section-nav.d.ts +8 -7
  20. package/dist/components/section-nav.js +27 -26
  21. package/dist/components/section-nav.js.map +1 -1
  22. package/dist/components/studio-project-screen.js +16 -16
  23. package/dist/components/studio-project-screen.js.map +1 -1
  24. package/dist/components/studio-shell.js +13 -13
  25. package/dist/components/studio-shell.js.map +1 -1
  26. package/dist/customization.d.ts +48 -0
  27. package/dist/customization.js +46 -0
  28. package/dist/customization.js.map +1 -0
  29. package/dist/lib/cms-views/view-model.d.ts +2 -2
  30. package/dist/lib/cms-views/views-store.d.ts +1 -1
  31. package/dist/lib/workspace/index.d.ts +32 -24
  32. package/dist/lib/workspace/index.js +35 -27
  33. package/dist/lib/workspace/index.js.map +1 -1
  34. package/dist/router/match.d.ts +16 -16
  35. package/dist/router/match.js +17 -17
  36. package/dist/router/match.js.map +1 -1
  37. package/dist/ui/badge.d.ts +1 -1
  38. package/package.json +6 -2
@@ -11,33 +11,33 @@
11
11
  * convention without needing a router instance.
12
12
  *
13
13
  * EC-239 (app-view modularization): the route table is no longer a hardcoded
14
- * per-section switch — the studio's surfaces are a config-extendable module
15
- * registry, so the matcher is now registry-AGNOSTIC. It splits the splat into a
16
- * module id (the first segment) + that module's trailing segments; the screen
17
- * resolver looks the id up in the registry and the module's own `parsePath`
14
+ * per-section switch — the studio's surfaces are a config-extendable VIEW registry
15
+ * derived from the section rail, so the matcher is registry-AGNOSTIC. It splits the
16
+ * splat into a view id (the first segment) + that view's trailing segments; the
17
+ * screen resolver looks the id up in the registry and the view's own `parsePath`
18
18
  * interprets the segments (CMS: collection/document; components: id; flat: none).
19
- * Keeping the matcher free of the surface list is what lets a consuming repo add
20
- * a routable custom module purely from config.
19
+ * Keeping the matcher free of the surface list is what lets a consuming repo add a
20
+ * routable custom view purely from config.
21
21
  *
22
- * /$projectId → { section: 'index' } (redirect to first module)
23
- * /$projectId/<id> → { section: 'module', moduleId: '<id>', segments: [] }
24
- * /$projectId/<id>/a/b → { section: 'module', moduleId: '<id>', segments: ['a','b'] }
22
+ * /$projectId → { section: 'index' } (redirect to first view)
23
+ * /$projectId/<id> → { section: 'view', viewId: '<id>', segments: [] }
24
+ * /$projectId/<id>/a/b → { section: 'view', viewId: '<id>', segments: ['a','b'] }
25
25
  *
26
- * The module decides whether its segments resolve; an unknown id or an
27
- * over-deep path becomes a not-found at the resolver, not here.
26
+ * The view decides whether its segments resolve; an unknown id or an over-deep
27
+ * path becomes a not-found at the resolver, not here.
28
28
  */
29
29
  export type StudioRouteMatch = {
30
30
  section: 'index';
31
31
  } | {
32
- section: 'module';
33
- moduleId: string;
32
+ section: 'view';
33
+ viewId: string;
34
34
  segments: string[];
35
35
  };
36
36
  /**
37
- * Match the trailing pathname of the project splat (`_splat`) into a module id +
37
+ * Match the trailing pathname of the project splat (`_splat`) into a view id +
38
38
  * its segments. `splat` is everything after `/$projectId/` — e.g. `'cms/page/abc'`
39
- * for `/p1/cms/page/abc` → `{ moduleId: 'cms', segments: ['page', 'abc'] }`. An
40
- * empty splat is the bare project index (which redirects to the first module).
39
+ * for `/p1/cms/page/abc` → `{ viewId: 'cms', segments: ['page', 'abc'] }`. An
40
+ * empty splat is the bare project index (which redirects to the first view).
41
41
  */
42
42
  export declare function matchStudioRoute(splat: string): StudioRouteMatch;
43
43
  /**
@@ -11,20 +11,20 @@
11
11
  * convention without needing a router instance.
12
12
  *
13
13
  * EC-239 (app-view modularization): the route table is no longer a hardcoded
14
- * per-section switch — the studio's surfaces are a config-extendable module
15
- * registry, so the matcher is now registry-AGNOSTIC. It splits the splat into a
16
- * module id (the first segment) + that module's trailing segments; the screen
17
- * resolver looks the id up in the registry and the module's own `parsePath`
14
+ * per-section switch — the studio's surfaces are a config-extendable VIEW registry
15
+ * derived from the section rail, so the matcher is registry-AGNOSTIC. It splits the
16
+ * splat into a view id (the first segment) + that view's trailing segments; the
17
+ * screen resolver looks the id up in the registry and the view's own `parsePath`
18
18
  * interprets the segments (CMS: collection/document; components: id; flat: none).
19
- * Keeping the matcher free of the surface list is what lets a consuming repo add
20
- * a routable custom module purely from config.
19
+ * Keeping the matcher free of the surface list is what lets a consuming repo add a
20
+ * routable custom view purely from config.
21
21
  *
22
- * /$projectId → { section: 'index' } (redirect to first module)
23
- * /$projectId/<id> → { section: 'module', moduleId: '<id>', segments: [] }
24
- * /$projectId/<id>/a/b → { section: 'module', moduleId: '<id>', segments: ['a','b'] }
22
+ * /$projectId → { section: 'index' } (redirect to first view)
23
+ * /$projectId/<id> → { section: 'view', viewId: '<id>', segments: [] }
24
+ * /$projectId/<id>/a/b → { section: 'view', viewId: '<id>', segments: ['a','b'] }
25
25
  *
26
- * The module decides whether its segments resolve; an unknown id or an
27
- * over-deep path becomes a not-found at the resolver, not here.
26
+ * The view decides whether its segments resolve; an unknown id or an over-deep
27
+ * path becomes a not-found at the resolver, not here.
28
28
  */
29
29
  /** Split a splat pathname into clean segments (drops empty/leading/trailing slashes). */
30
30
  function segments(splat) {
@@ -34,16 +34,16 @@ function segments(splat) {
34
34
  .filter(Boolean);
35
35
  }
36
36
  /**
37
- * Match the trailing pathname of the project splat (`_splat`) into a module id +
37
+ * Match the trailing pathname of the project splat (`_splat`) into a view id +
38
38
  * its segments. `splat` is everything after `/$projectId/` — e.g. `'cms/page/abc'`
39
- * for `/p1/cms/page/abc` → `{ moduleId: 'cms', segments: ['page', 'abc'] }`. An
40
- * empty splat is the bare project index (which redirects to the first module).
39
+ * for `/p1/cms/page/abc` → `{ viewId: 'cms', segments: ['page', 'abc'] }`. An
40
+ * empty splat is the bare project index (which redirects to the first view).
41
41
  */
42
42
  export function matchStudioRoute(splat) {
43
- const [moduleId, ...rest] = segments(splat);
44
- if (moduleId === undefined)
43
+ const [viewId, ...rest] = segments(splat);
44
+ if (viewId === undefined)
45
45
  return { section: 'index' };
46
- return { section: 'module', moduleId, segments: rest };
46
+ return { section: 'view', viewId, segments: rest };
47
47
  }
48
48
  /**
49
49
  * The inverse of {@link matchStudioRoute}: build the `_splat` subpath for a
@@ -1 +1 @@
1
- {"version":3,"file":"match.js","sourceRoot":"","sources":["../../src/router/match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,yFAAyF;AACzF,SAAS,QAAQ,CAAC,KAAa;IAC7B,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;SAChC,MAAM,CAAC,OAAO,CAAC,CAAA;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAA;IACvD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;AACxD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE;IACf,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;IAC1B,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ;IACtB,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ;IACtB,UAAU,EAAE,GAAG,EAAE,CAAC,YAAY;IAC9B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;IACxB,UAAU,EAAE,GAAG,EAAE,CAAC,YAAY;IAC9B,SAAS,EAAE,CAAC,WAAmB,EAAE,EAAE,CAAC,cAAc,WAAW,EAAE;IAC/D,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;IAChB,aAAa,EAAE,CAAC,YAAoB,EAAE,EAAE,CAAC,OAAO,YAAY,EAAE;IAC9D,WAAW,EAAE,CAAC,YAAoB,EAAE,UAAkB,EAAE,EAAE,CACxD,OAAO,YAAY,IAAI,UAAU,EAAE;CAC7B,CAAA","sourcesContent":["/**\n * Studio route table (EC-223 Stage 6 — catch-all routing). The studio mounts as\n * a SINGLE splat route per project (`/$projectId/$`); this pure matcher turns the\n * splat's trailing pathname into a typed match that the screen resolver renders.\n * Keeping the URL→screen map inside the engine (instead of in per-consumer route\n * files) is what lets the package own its navigation with a near-empty consumer\n * route tree — the goal of Stage 6.\n *\n * Deterministic and dependency-free: every screen state is reproducible from a\n * pathname string (see `match.test.ts`), satisfying the fixtures-reproducibility\n * convention without needing a router instance.\n *\n * EC-239 (app-view modularization): the route table is no longer a hardcoded\n * per-section switch — the studio's surfaces are a config-extendable module\n * registry, so the matcher is now registry-AGNOSTIC. It splits the splat into a\n * module id (the first segment) + that module's trailing segments; the screen\n * resolver looks the id up in the registry and the module's own `parsePath`\n * interprets the segments (CMS: collection/document; components: id; flat: none).\n * Keeping the matcher free of the surface list is what lets a consuming repo add\n * a routable custom module purely from config.\n *\n * /$projectId → { section: 'index' } (redirect to first module)\n * /$projectId/<id> → { section: 'module', moduleId: '<id>', segments: [] }\n * /$projectId/<id>/a/b → { section: 'module', moduleId: '<id>', segments: ['a','b'] }\n *\n * The module decides whether its segments resolve; an unknown id or an\n * over-deep path becomes a not-found at the resolver, not here.\n */\n\nexport type StudioRouteMatch =\n | { section: 'index' }\n | { section: 'module'; moduleId: string; segments: string[] }\n\n/** Split a splat pathname into clean segments (drops empty/leading/trailing slashes). */\nfunction segments(splat: string): string[] {\n return splat\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean)\n}\n\n/**\n * Match the trailing pathname of the project splat (`_splat`) into a module id +\n * its segments. `splat` is everything after `/$projectId/` — e.g. `'cms/page/abc'`\n * for `/p1/cms/page/abc` → `{ moduleId: 'cms', segments: ['page', 'abc'] }`. An\n * empty splat is the bare project index (which redirects to the first module).\n */\nexport function matchStudioRoute(splat: string): StudioRouteMatch {\n const [moduleId, ...rest] = segments(splat)\n if (moduleId === undefined) return { section: 'index' }\n return { section: 'module', moduleId, segments: rest }\n}\n\n/**\n * The inverse of {@link matchStudioRoute}: build the `_splat` subpath for a\n * screen. Navigation addresses the single project splat as\n * `to={STUDIO_PROJECT_ROUTE} params={{ projectId, _splat: studioPath.X(...) }}`,\n * so the URL-construction lives in exactly one place — the same module that\n * parses it back. `matchStudioRoute(studioPath.cmsDocument(c, d))` round-trips\n * (pinned in `match.test.ts`), which is what keeps the two directions in sync.\n */\nexport const studioPath = {\n index: () => '',\n settings: () => 'settings',\n assets: () => 'assets',\n routes: () => 'routes',\n extensions: () => 'extensions',\n builder: () => 'builder',\n components: () => 'components',\n component: (componentId: string) => `components/${componentId}`,\n cms: () => 'cms',\n cmsCollection: (collectionId: string) => `cms/${collectionId}`,\n cmsDocument: (collectionId: string, documentId: string) =>\n `cms/${collectionId}/${documentId}`,\n} as const\n"]}
1
+ {"version":3,"file":"match.js","sourceRoot":"","sources":["../../src/router/match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,yFAAyF;AACzF,SAAS,QAAQ,CAAC,KAAa;IAC7B,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;SAChC,MAAM,CAAC,OAAO,CAAC,CAAA;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACzC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAA;IACrD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;AACpD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE;IACf,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;IAC1B,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ;IACtB,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ;IACtB,UAAU,EAAE,GAAG,EAAE,CAAC,YAAY;IAC9B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;IACxB,UAAU,EAAE,GAAG,EAAE,CAAC,YAAY;IAC9B,SAAS,EAAE,CAAC,WAAmB,EAAE,EAAE,CAAC,cAAc,WAAW,EAAE;IAC/D,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;IAChB,aAAa,EAAE,CAAC,YAAoB,EAAE,EAAE,CAAC,OAAO,YAAY,EAAE;IAC9D,WAAW,EAAE,CAAC,YAAoB,EAAE,UAAkB,EAAE,EAAE,CACxD,OAAO,YAAY,IAAI,UAAU,EAAE;CAC7B,CAAA","sourcesContent":["/**\n * Studio route table (EC-223 Stage 6 — catch-all routing). The studio mounts as\n * a SINGLE splat route per project (`/$projectId/$`); this pure matcher turns the\n * splat's trailing pathname into a typed match that the screen resolver renders.\n * Keeping the URL→screen map inside the engine (instead of in per-consumer route\n * files) is what lets the package own its navigation with a near-empty consumer\n * route tree — the goal of Stage 6.\n *\n * Deterministic and dependency-free: every screen state is reproducible from a\n * pathname string (see `match.test.ts`), satisfying the fixtures-reproducibility\n * convention without needing a router instance.\n *\n * EC-239 (app-view modularization): the route table is no longer a hardcoded\n * per-section switch — the studio's surfaces are a config-extendable VIEW registry\n * derived from the section rail, so the matcher is registry-AGNOSTIC. It splits the\n * splat into a view id (the first segment) + that view's trailing segments; the\n * screen resolver looks the id up in the registry and the view's own `parsePath`\n * interprets the segments (CMS: collection/document; components: id; flat: none).\n * Keeping the matcher free of the surface list is what lets a consuming repo add a\n * routable custom view purely from config.\n *\n * /$projectId → { section: 'index' } (redirect to first view)\n * /$projectId/<id> → { section: 'view', viewId: '<id>', segments: [] }\n * /$projectId/<id>/a/b → { section: 'view', viewId: '<id>', segments: ['a','b'] }\n *\n * The view decides whether its segments resolve; an unknown id or an over-deep\n * path becomes a not-found at the resolver, not here.\n */\n\nexport type StudioRouteMatch =\n | { section: 'index' }\n | { section: 'view'; viewId: string; segments: string[] }\n\n/** Split a splat pathname into clean segments (drops empty/leading/trailing slashes). */\nfunction segments(splat: string): string[] {\n return splat\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean)\n}\n\n/**\n * Match the trailing pathname of the project splat (`_splat`) into a view id +\n * its segments. `splat` is everything after `/$projectId/` — e.g. `'cms/page/abc'`\n * for `/p1/cms/page/abc` → `{ viewId: 'cms', segments: ['page', 'abc'] }`. An\n * empty splat is the bare project index (which redirects to the first view).\n */\nexport function matchStudioRoute(splat: string): StudioRouteMatch {\n const [viewId, ...rest] = segments(splat)\n if (viewId === undefined) return { section: 'index' }\n return { section: 'view', viewId, segments: rest }\n}\n\n/**\n * The inverse of {@link matchStudioRoute}: build the `_splat` subpath for a\n * screen. Navigation addresses the single project splat as\n * `to={STUDIO_PROJECT_ROUTE} params={{ projectId, _splat: studioPath.X(...) }}`,\n * so the URL-construction lives in exactly one place — the same module that\n * parses it back. `matchStudioRoute(studioPath.cmsDocument(c, d))` round-trips\n * (pinned in `match.test.ts`), which is what keeps the two directions in sync.\n */\nexport const studioPath = {\n index: () => '',\n settings: () => 'settings',\n assets: () => 'assets',\n routes: () => 'routes',\n extensions: () => 'extensions',\n builder: () => 'builder',\n components: () => 'components',\n component: (componentId: string) => `components/${componentId}`,\n cms: () => 'cms',\n cmsCollection: (collectionId: string) => `cms/${collectionId}`,\n cmsDocument: (collectionId: string, documentId: string) =>\n `cms/${collectionId}/${documentId}`,\n} as const\n"]}
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  import type { HTMLAttributes } from 'react';
3
3
  export declare const badgeVariants: (props?: ({
4
- variant?: "error" | "warning" | "default" | "success" | "primary" | "outline" | "info" | null | undefined;
4
+ variant?: "error" | "default" | "primary" | "outline" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {
7
7
  }
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@elytracms/studio",
3
3
  "author": "finaldream <hi@finaldream.de>",
4
- "version": "0.0.17",
4
+ "version": "0.0.18",
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",
8
8
  "type": "module",
9
9
  "exports": {
10
10
  "./styles.css": "./src/styles/studio.css",
11
+ "./customization": {
12
+ "types": "./dist/customization.d.ts",
13
+ "default": "./dist/customization.js"
14
+ },
11
15
  "./ui": {
12
16
  "types": "./dist/ui/index.d.ts",
13
17
  "default": "./dist/ui/index.js"
@@ -113,7 +117,7 @@
113
117
  "lucide-react": "^1.17.0",
114
118
  "tailwind-merge": "^3.6.0",
115
119
  "zod": "^4.0.0",
116
- "@elytracms/core": "^0.0.17"
120
+ "@elytracms/core": "^0.0.18"
117
121
  },
118
122
  "peerDependencies": {
119
123
  "@tanstack/react-router": "^1.170.0",