@velastack/cms 0.4.0 → 0.5.1

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 (205) hide show
  1. package/README.md +140 -28
  2. package/dist/backend/index.d.ts +1 -1
  3. package/dist/backend/routes/seed.d.ts +6 -0
  4. package/dist/backend/routes/seed.js +96 -0
  5. package/dist/backend/routes/table.js +3 -0
  6. package/dist/backend/store/queries.d.ts +6 -1
  7. package/dist/backend/store/queries.js +89 -0
  8. package/dist/cms.js +13 -16
  9. package/dist/components/admin-bar/admin-bar-internal.svelte +4 -4
  10. package/dist/components/admin-bar/admin-bar.css +1 -1
  11. package/dist/components/admin-bar/field-input.svelte +239 -0
  12. package/dist/components/admin-bar/field-input.svelte.d.ts +30 -0
  13. package/dist/components/admin-bar/fields-form.svelte +218 -0
  14. package/dist/components/admin-bar/fields-form.svelte.d.ts +16 -0
  15. package/dist/components/admin-bar/icons/pencil.svelte +19 -0
  16. package/dist/components/admin-bar/icons/pencil.svelte.d.ts +3 -0
  17. package/dist/components/admin-bar/keyboard-shortcuts-dialog.svelte +1 -1
  18. package/dist/components/admin-bar/link-input.svelte +102 -0
  19. package/dist/components/admin-bar/link-input.svelte.d.ts +9 -0
  20. package/dist/components/admin-bar/list-field.svelte +93 -0
  21. package/dist/components/admin-bar/list-field.svelte.d.ts +20 -0
  22. package/dist/components/admin-bar/locales-panel.svelte +42 -1
  23. package/dist/components/admin-bar/page-config.d.ts +8 -1
  24. package/dist/components/admin-bar/page-config.js +15 -2
  25. package/dist/components/admin-bar/page-options.d.ts +24 -0
  26. package/dist/components/admin-bar/page-options.js +53 -0
  27. package/dist/components/admin-bar/reorder-buttons.svelte +52 -0
  28. package/dist/components/admin-bar/reorder-buttons.svelte.d.ts +10 -0
  29. package/dist/components/admin-bar/rich-text-input.svelte +92 -0
  30. package/dist/components/admin-bar/rich-text-input.svelte.d.ts +9 -0
  31. package/dist/components/admin-bar/seo-panel.svelte +144 -189
  32. package/dist/components/admin-bar/site-options-panel.svelte +115 -0
  33. package/dist/components/admin-bar/site-options-panel.svelte.d.ts +7 -0
  34. package/dist/components/admin-bar/structured-editor-popover.svelte +259 -0
  35. package/dist/components/admin-bar/structured-editor-popover.svelte.d.ts +48 -0
  36. package/dist/components/admin-bar/ui/checkbox/checkbox.svelte +36 -0
  37. package/dist/components/admin-bar/ui/checkbox/checkbox.svelte.d.ts +5 -0
  38. package/dist/components/admin-bar/ui/checkbox/index.d.ts +2 -0
  39. package/dist/components/admin-bar/ui/checkbox/index.js +2 -0
  40. package/dist/components/admin-bar/ui/popover/index.d.ts +5 -0
  41. package/dist/components/admin-bar/ui/popover/index.js +7 -0
  42. package/dist/components/admin-bar/ui/popover/popover-close.svelte +7 -0
  43. package/dist/components/admin-bar/ui/popover/popover-close.svelte.d.ts +4 -0
  44. package/dist/components/admin-bar/ui/popover/popover-content.svelte +35 -0
  45. package/dist/components/admin-bar/ui/popover/popover-content.svelte.d.ts +10 -0
  46. package/dist/components/admin-bar/ui/popover/popover-trigger.svelte +7 -0
  47. package/dist/components/admin-bar/ui/popover/popover-trigger.svelte.d.ts +4 -0
  48. package/dist/components/admin-bar/ui/popover/popover.svelte +7 -0
  49. package/dist/components/admin-bar/ui/popover/popover.svelte.d.ts +3 -0
  50. package/dist/components/admin-bar/ui/sheet/index.d.ts +10 -0
  51. package/dist/components/admin-bar/ui/sheet/index.js +12 -0
  52. package/dist/components/admin-bar/ui/sheet/sheet-close.svelte +7 -0
  53. package/dist/components/admin-bar/ui/sheet/sheet-close.svelte.d.ts +4 -0
  54. package/dist/components/admin-bar/ui/sheet/sheet-content.svelte +64 -0
  55. package/dist/components/admin-bar/ui/sheet/sheet-content.svelte.d.ts +17 -0
  56. package/dist/components/admin-bar/ui/sheet/sheet-description.svelte +17 -0
  57. package/dist/components/admin-bar/ui/sheet/sheet-description.svelte.d.ts +4 -0
  58. package/dist/components/admin-bar/ui/sheet/sheet-footer.svelte +23 -0
  59. package/dist/components/admin-bar/ui/sheet/sheet-footer.svelte.d.ts +5 -0
  60. package/dist/components/admin-bar/ui/sheet/sheet-header.svelte +20 -0
  61. package/dist/components/admin-bar/ui/sheet/sheet-header.svelte.d.ts +5 -0
  62. package/dist/components/admin-bar/ui/sheet/sheet-overlay.svelte +20 -0
  63. package/dist/components/admin-bar/ui/sheet/sheet-overlay.svelte.d.ts +4 -0
  64. package/dist/components/admin-bar/ui/sheet/sheet-title.svelte +17 -0
  65. package/dist/components/admin-bar/ui/sheet/sheet-title.svelte.d.ts +4 -0
  66. package/dist/components/admin-bar/ui/sheet/sheet-trigger.svelte +7 -0
  67. package/dist/components/admin-bar/ui/sheet/sheet-trigger.svelte.d.ts +4 -0
  68. package/dist/components/admin-bar/ui/sheet/sheet.svelte +7 -0
  69. package/dist/components/admin-bar/ui/sheet/sheet.svelte.d.ts +3 -0
  70. package/dist/components/admin-bar/ui/switch/index.d.ts +2 -0
  71. package/dist/components/admin-bar/ui/switch/index.js +2 -0
  72. package/dist/components/admin-bar/ui/switch/switch.svelte +27 -0
  73. package/dist/components/admin-bar/ui/switch/switch.svelte.d.ts +5 -0
  74. package/dist/components/admin-bar/ui/tabs/index.d.ts +5 -0
  75. package/dist/components/admin-bar/ui/tabs/index.js +7 -0
  76. package/dist/components/admin-bar/ui/tabs/tabs-content.svelte +17 -0
  77. package/dist/components/admin-bar/ui/tabs/tabs-content.svelte.d.ts +4 -0
  78. package/dist/components/admin-bar/ui/tabs/tabs-list.svelte +16 -0
  79. package/dist/components/admin-bar/ui/tabs/tabs-list.svelte.d.ts +4 -0
  80. package/dist/components/admin-bar/ui/tabs/tabs-trigger.svelte +20 -0
  81. package/dist/components/admin-bar/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
  82. package/dist/components/admin-bar/ui/tabs/tabs.svelte +19 -0
  83. package/dist/components/admin-bar/ui/tabs/tabs.svelte.d.ts +4 -0
  84. package/dist/components/cms/cms-boolean.svelte +9 -18
  85. package/dist/components/cms/cms-boolean.svelte.d.ts +1 -0
  86. package/dist/components/cms/cms-collection.svelte +46 -0
  87. package/dist/components/cms/cms-collection.svelte.d.ts +25 -0
  88. package/dist/components/cms/cms-contact.svelte +26 -0
  89. package/dist/components/cms/cms-contact.svelte.d.ts +14 -0
  90. package/dist/components/cms/cms-date-time-editable.svelte +3 -3
  91. package/dist/components/cms/cms-date-time.svelte +17 -17
  92. package/dist/components/cms/cms-date-time.svelte.d.ts +2 -1
  93. package/dist/components/cms/cms-faq.svelte +22 -0
  94. package/dist/components/cms/cms-faq.svelte.d.ts +15 -0
  95. package/dist/components/cms/cms-gallery.svelte +31 -0
  96. package/dist/components/cms/cms-gallery.svelte.d.ts +16 -0
  97. package/dist/components/cms/cms-hours-editable.svelte +160 -0
  98. package/dist/components/cms/cms-hours-editable.svelte.d.ts +17 -0
  99. package/dist/components/cms/cms-hours.svelte +42 -0
  100. package/dist/components/cms/cms-hours.svelte.d.ts +19 -0
  101. package/dist/components/cms/cms-image-editable.svelte +13 -4
  102. package/dist/components/cms/cms-image.svelte +19 -34
  103. package/dist/components/cms/cms-image.svelte.d.ts +7 -8
  104. package/dist/components/cms/cms-link-editable.svelte +4 -60
  105. package/dist/components/cms/cms-link.svelte +15 -28
  106. package/dist/components/cms/cms-link.svelte.d.ts +4 -9
  107. package/dist/components/cms/cms-logos.svelte +29 -0
  108. package/dist/components/cms/cms-logos.svelte.d.ts +16 -0
  109. package/dist/components/cms/cms-markdown.svelte +15 -16
  110. package/dist/components/cms/cms-markdown.svelte.d.ts +1 -0
  111. package/dist/components/cms/cms-nav.svelte +22 -0
  112. package/dist/components/cms/cms-nav.svelte.d.ts +15 -0
  113. package/dist/components/cms/cms-number-editable.svelte +4 -2
  114. package/dist/components/cms/cms-number.svelte +18 -17
  115. package/dist/components/cms/cms-number.svelte.d.ts +2 -1
  116. package/dist/components/cms/cms-pricing.svelte +36 -0
  117. package/dist/components/cms/cms-pricing.svelte.d.ts +14 -0
  118. package/dist/components/cms/cms-rich-text.svelte +17 -22
  119. package/dist/components/cms/cms-rich-text.svelte.d.ts +1 -0
  120. package/dist/components/cms/cms-schedule.svelte +35 -0
  121. package/dist/components/cms/cms-schedule.svelte.d.ts +16 -0
  122. package/dist/components/cms/cms-social-links.svelte +35 -0
  123. package/dist/components/cms/cms-social-links.svelte.d.ts +15 -0
  124. package/dist/components/cms/cms-stats.svelte +31 -0
  125. package/dist/components/cms/cms-stats.svelte.d.ts +16 -0
  126. package/dist/components/cms/cms-steps.svelte +31 -0
  127. package/dist/components/cms/cms-steps.svelte.d.ts +16 -0
  128. package/dist/components/cms/cms-store.svelte.d.ts +14 -0
  129. package/dist/components/cms/cms-store.svelte.js +39 -2
  130. package/dist/components/cms/cms-structured-editable.svelte +29 -0
  131. package/dist/components/cms/{cms-repeater-editable.svelte.d.ts → cms-structured-editable.svelte.d.ts} +12 -8
  132. package/dist/components/cms/cms-structured.svelte +50 -0
  133. package/dist/components/cms/cms-structured.svelte.d.ts +49 -0
  134. package/dist/components/cms/cms-team.svelte +22 -0
  135. package/dist/components/cms/cms-team.svelte.d.ts +15 -0
  136. package/dist/components/cms/cms-testimonials.svelte +36 -0
  137. package/dist/components/cms/cms-testimonials.svelte.d.ts +15 -0
  138. package/dist/components/cms/cms-text.svelte +23 -30
  139. package/dist/components/cms/cms-text.svelte.d.ts +3 -1
  140. package/dist/components/cms/cms-timeline.svelte +35 -0
  141. package/dist/components/cms/cms-timeline.svelte.d.ts +16 -0
  142. package/dist/components/cms/index.d.ts +15 -1
  143. package/dist/components/cms/index.js +15 -1
  144. package/dist/components/cms/scope.d.ts +25 -5
  145. package/dist/components/cms/use-cms-field.svelte.d.ts +54 -0
  146. package/dist/components/cms/use-cms-field.svelte.js +83 -0
  147. package/dist/core/metadata.d.ts +44 -0
  148. package/dist/core/metadata.js +59 -0
  149. package/dist/core/shapes/collection.d.ts +60 -0
  150. package/dist/core/shapes/collection.js +194 -0
  151. package/dist/core/shapes/contact.d.ts +70 -0
  152. package/dist/core/shapes/contact.js +214 -0
  153. package/dist/core/shapes/faq.d.ts +12 -0
  154. package/dist/core/shapes/faq.js +48 -0
  155. package/dist/core/shapes/form.d.ts +46 -0
  156. package/dist/core/shapes/form.js +7 -0
  157. package/dist/core/shapes/hours.d.ts +91 -0
  158. package/dist/core/shapes/hours.js +325 -0
  159. package/dist/core/shapes/image.d.ts +15 -0
  160. package/dist/core/shapes/image.js +27 -0
  161. package/dist/core/shapes/index.d.ts +31 -0
  162. package/dist/core/shapes/index.js +13 -0
  163. package/dist/core/shapes/link.d.ts +15 -0
  164. package/dist/core/shapes/link.js +42 -0
  165. package/dist/core/shapes/nav.d.ts +21 -0
  166. package/dist/core/shapes/nav.js +71 -0
  167. package/dist/core/shapes/presets.d.ts +65 -0
  168. package/dist/core/shapes/presets.js +152 -0
  169. package/dist/core/shapes/pricing.d.ts +40 -0
  170. package/dist/core/shapes/pricing.js +115 -0
  171. package/dist/core/shapes/social-links.d.ts +19 -0
  172. package/dist/core/shapes/social-links.js +83 -0
  173. package/dist/core/shapes/team.d.ts +22 -0
  174. package/dist/core/shapes/team.js +70 -0
  175. package/dist/core/shapes/testimonials.d.ts +24 -0
  176. package/dist/core/shapes/testimonials.js +86 -0
  177. package/dist/core/structured-registry.d.ts +10 -0
  178. package/dist/core/structured-registry.js +6 -0
  179. package/dist/core/structured.d.ts +131 -0
  180. package/dist/core/structured.js +235 -0
  181. package/dist/core/wire.d.ts +31 -1
  182. package/dist/editor.d.ts +26 -0
  183. package/dist/editor.js +25 -0
  184. package/dist/index.d.ts +26 -4
  185. package/dist/index.js +31 -2
  186. package/dist/server/load-cms.js +1 -0
  187. package/package.json +12 -3
  188. package/plugin/README.md +22 -2
  189. package/plugin/dist/build.d.ts +74 -0
  190. package/plugin/dist/build.js +210 -0
  191. package/plugin/dist/build.js.map +1 -0
  192. package/plugin/dist/index.d.ts +10 -1
  193. package/plugin/dist/index.js +63 -4
  194. package/plugin/dist/index.js.map +1 -1
  195. package/plugin/dist/manifest.d.ts +31 -0
  196. package/plugin/dist/manifest.js +146 -41
  197. package/plugin/dist/manifest.js.map +1 -1
  198. package/plugin/dist/parse-svelte.d.ts +15 -0
  199. package/plugin/dist/parse-svelte.js +26 -16
  200. package/plugin/dist/parse-svelte.js.map +1 -1
  201. package/dist/components/admin-bar/site-settings-panel.svelte +0 -225
  202. package/dist/components/admin-bar/site-settings-panel.svelte.d.ts +0 -7
  203. package/dist/components/cms/cms-repeater-editable.svelte +0 -137
  204. package/dist/components/cms/cms-repeater.svelte +0 -55
  205. package/dist/components/cms/cms-repeater.svelte.d.ts +0 -30
@@ -0,0 +1,31 @@
1
+ <script lang="ts" module>
2
+ /** `CmsSteps` preset: title, body, image. */
3
+ export type { CmsStepsValue, StepItem } from '../../core/shapes/presets.js';
4
+ export { cmsSteps } from '../../core/shapes/presets.js';
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import type { Snippet } from 'svelte';
9
+ import Structured from './cms-structured.svelte';
10
+ import { cmsSteps, type CmsStepsValue, type StepItem } from '../../core/shapes/presets.js';
11
+
12
+ type Props = {
13
+ name: string;
14
+ scope?: string;
15
+ /** The full value, or a bare item list. */
16
+ fallback?: Partial<CmsStepsValue> | Partial<StepItem>[];
17
+ value?: unknown;
18
+ children: Snippet<[StepItem[]]>;
19
+ };
20
+ let { name, scope, fallback, value, children }: Props = $props();
21
+ </script>
22
+
23
+ <Structured
24
+ schema={cmsSteps}
25
+ {name}
26
+ {scope}
27
+ {fallback}
28
+ {value}
29
+ view={(v: CmsStepsValue) => v.items}
30
+ {children}
31
+ />
@@ -0,0 +1,16 @@
1
+ /** `CmsSteps` preset: title, body, image. */
2
+ export type { CmsStepsValue, StepItem } from '../../core/shapes/presets.js';
3
+ export { cmsSteps } from '../../core/shapes/presets.js';
4
+ import type { Snippet } from 'svelte';
5
+ import { type CmsStepsValue, type StepItem } from '../../core/shapes/presets.js';
6
+ type Props = {
7
+ name: string;
8
+ scope?: string;
9
+ /** The full value, or a bare item list. */
10
+ fallback?: Partial<CmsStepsValue> | Partial<StepItem>[];
11
+ value?: unknown;
12
+ children: Snippet<[StepItem[]]>;
13
+ };
14
+ declare const CmsSteps: import("svelte").Component<Props, {}, "">;
15
+ type CmsSteps = ReturnType<typeof CmsSteps>;
16
+ export default CmsSteps;
@@ -38,9 +38,23 @@ declare class CmsStore {
38
38
  private _activeLocale;
39
39
  private overlayFetchToken;
40
40
  private entriesFetchToken;
41
+ private localeOverlayTokens;
41
42
  toggleEdit(): void;
42
43
  setActiveLocale(locale: string | null): void;
43
44
  private effectiveLocale;
45
+ /** The locale edits currently target: the editor's `?locale=` override, else the page's. */
46
+ get activeLocale(): string;
47
+ /** The project's default locale, `cms.locales[0]`. */
48
+ get defaultLocale(): string;
49
+ /** Every supported locale from the payload. */
50
+ get locales(): string[];
51
+ /**
52
+ * Make sure the overlay for `locale` is loaded for the current route's
53
+ * scopes, so a structured editor's translation tab and the Locales panel
54
+ * can read another locale's strings without switching the page. Uses the
55
+ * open release's preview key when there is one.
56
+ */
57
+ loadLocaleOverlay(locale: string): Promise<void>;
44
58
  /** Base doc (server-rendered) for a scope. Already locale-resolved by `resolveCmsPayload`. */
45
59
  private baseDoc;
46
60
  /** Merged read tree: base ∪ overlay (∪ draft when editing) for the given locale. */
@@ -105,6 +105,7 @@ class CmsStore {
105
105
  _activeLocale = $state(null);
106
106
  overlayFetchToken = 0;
107
107
  entriesFetchToken = 0;
108
+ localeOverlayTokens = {};
108
109
  toggleEdit() {
109
110
  this.isEditing = !this.isEditing;
110
111
  }
@@ -114,6 +115,38 @@ class CmsStore {
114
115
  effectiveLocale() {
115
116
  return this._activeLocale ?? pageLocale();
116
117
  }
118
+ /** The locale edits currently target: the editor's `?locale=` override, else the page's. */
119
+ get activeLocale() {
120
+ return this.effectiveLocale();
121
+ }
122
+ /** The project's default locale, `cms.locales[0]`. */
123
+ get defaultLocale() {
124
+ return pageDefaultLocale();
125
+ }
126
+ /** Every supported locale from the payload. */
127
+ get locales() {
128
+ return page.data?.cms?.locales ?? [];
129
+ }
130
+ /**
131
+ * Make sure the overlay for `locale` is loaded for the current route's
132
+ * scopes, so a structured editor's translation tab and the Locales panel
133
+ * can read another locale's strings without switching the page. Uses the
134
+ * open release's preview key when there is one.
135
+ */
136
+ async loadLocaleOverlay(locale) {
137
+ const cms = page.data?.cms;
138
+ if (!cms)
139
+ return;
140
+ const scopes = Object.values(cms.scopes);
141
+ const loaded = scopes.every((s) => composeKey({ scopeId: s.scopeId, routeId: s.routeId, params: s.params }, locale) in
142
+ this.overlay);
143
+ if (loaded)
144
+ return;
145
+ await this.loadAndApplyOverlay(cms.endpoint, scopes, this.openRelease?.preview_key ?? null, {
146
+ locale,
147
+ defaultLocale: cms.locales[0]
148
+ });
149
+ }
117
150
  /** Base doc (server-rendered) for a scope. Already locale-resolved by `resolveCmsPayload`. */
118
151
  baseDoc(scope) {
119
152
  const cms = page.data?.cms;
@@ -234,7 +267,11 @@ class CmsStore {
234
267
  * overlays applied during navigation while new-page scopes load.
235
268
  */
236
269
  async loadAndApplyOverlay(endpoint, scopes, previewKey, opts) {
237
- const token = ++this.overlayFetchToken;
270
+ // One in-flight fetch per locale: a translation tab loading `es` must
271
+ // not cancel the page's own `en` overlay load, and vice versa.
272
+ const token = (this.localeOverlayTokens[opts.locale] =
273
+ (this.localeOverlayTokens[opts.locale] ?? 0) + 1);
274
+ this.overlayFetchToken = token;
238
275
  const versionKey = opts.versionKey ?? null;
239
276
  const locale = opts.locale;
240
277
  const defaultLocale = opts.defaultLocale ?? pageDefaultLocale() ?? locale;
@@ -271,7 +308,7 @@ class CmsStore {
271
308
  ? Promise.all(scopes.map((s) => fetchScope(s, defaultLocale)))
272
309
  : Promise.resolve(null)
273
310
  ]);
274
- if (token !== this.overlayFetchToken)
311
+ if (token !== this.localeOverlayTokens[locale])
275
312
  return;
276
313
  const merged = mergeLocaleDocs(collect(requestedPairs), fallbackPairs ? collect(fallbackPairs) : null);
277
314
  const scopeIndex = new Map();
@@ -0,0 +1,29 @@
1
+ <script lang="ts" generics="T extends Record<string, unknown>">
2
+ /**
3
+ * The generic editable sibling of a structured component: the popover
4
+ * over the shape's declared fields. Loaded lazily, only in edit mode.
5
+ */
6
+ import type { Snippet } from 'svelte';
7
+ import type { FormShape } from '../../core/shapes/form.js';
8
+ import type { Tree } from '../../core/path.js';
9
+ import type { CmsField } from './use-cms-field.svelte.js';
10
+ import StructuredEditorPopover from '../admin-bar/structured-editor-popover.svelte';
11
+ import FieldsForm from '../admin-bar/fields-form.svelte';
12
+
13
+ type Props = {
14
+ field: CmsField<T>;
15
+ schema: FormShape<T>;
16
+ preview: Snippet;
17
+ };
18
+ let { field, schema, preview }: Props = $props();
19
+ </script>
20
+
21
+ <StructuredEditorPopover {field} {schema} label={schema.label} {preview}>
22
+ {#snippet editor(draft: T, update: (next: T) => void)}
23
+ <FieldsForm
24
+ fields={schema.fields}
25
+ value={draft as Tree}
26
+ onChange={(next) => update(next as T)}
27
+ />
28
+ {/snippet}
29
+ </StructuredEditorPopover>
@@ -1,11 +1,15 @@
1
+ /**
2
+ * The generic editable sibling of a structured component: the popover
3
+ * over the shape's declared fields. Loaded lazily, only in edit mode.
4
+ */
1
5
  import type { Snippet } from 'svelte';
2
- import { type CmsScopeRef } from './cms-store.svelte.js';
6
+ import type { FormShape } from '../../core/shapes/form.js';
7
+ import type { CmsField } from './use-cms-field.svelte.js';
3
8
  declare function $$render<T extends Record<string, unknown>>(): {
4
9
  props: {
5
- scope: CmsScopeRef;
6
- name: string;
7
- items: T[];
8
- children: Snippet<[T, number]>;
10
+ field: CmsField<T>;
11
+ schema: FormShape<T>;
12
+ preview: Snippet;
9
13
  };
10
14
  exports: {};
11
15
  bindings: "";
@@ -26,6 +30,6 @@ interface $$IsomorphicComponent {
26
30
  <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
27
31
  z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
28
32
  }
29
- declare const CmsRepeaterEditable: $$IsomorphicComponent;
30
- type CmsRepeaterEditable<T extends Record<string, unknown>> = InstanceType<typeof CmsRepeaterEditable<T>>;
31
- export default CmsRepeaterEditable;
33
+ declare const CmsStructuredEditable: $$IsomorphicComponent;
34
+ type CmsStructuredEditable<T extends Record<string, unknown>> = InstanceType<typeof CmsStructuredEditable<T>>;
35
+ export default CmsStructuredEditable;
@@ -0,0 +1,50 @@
1
+ <script lang="ts" generics="T extends Record<string, unknown>, V">
2
+ /**
3
+ * The one display component behind every structured editor component.
4
+ * It reads the value through the shape, derives the template's view of
5
+ * it and renders `children(view)`; in edit mode it swaps in the lazily
6
+ * loaded editable sibling (the generic form, or a bespoke one).
7
+ */
8
+ import type { Component, Snippet } from 'svelte';
9
+ import type { FormShape } from '../../core/shapes/form.js';
10
+ import { useCmsField, type CmsField } from './use-cms-field.svelte.js';
11
+
12
+ type EditableProps = { field: CmsField<T>; schema: FormShape<T>; preview: Snippet };
13
+ type Props = {
14
+ schema: FormShape<T>;
15
+ name: string;
16
+ scope?: string;
17
+ fallback?: unknown;
18
+ value?: unknown;
19
+ view: (value: T) => V;
20
+ children: Snippet<[V]>;
21
+ /** Loader for a bespoke editable sibling; defaults to the generic form. */
22
+ editable?: () => Promise<{ default: Component<EditableProps> }>;
23
+ };
24
+ let { schema, name, scope, fallback, value, view, children, editable }: Props = $props();
25
+
26
+ const field = useCmsField(
27
+ () => ({ name, scope, value }),
28
+ (raw) => schema.read(raw, fallback as T | undefined)
29
+ );
30
+ const current = $derived(view(field.current));
31
+ const load = $derived(
32
+ editable ??
33
+ (() =>
34
+ import('./cms-structured-editable.svelte') as unknown as Promise<{
35
+ default: Component<EditableProps>;
36
+ }>)
37
+ );
38
+ </script>
39
+
40
+ {#snippet preview()}
41
+ {@render children(current)}
42
+ {/snippet}
43
+
44
+ {#if field.editable}
45
+ {#await load() then { default: Editable }}
46
+ <Editable {field} {schema} {preview} />
47
+ {/await}
48
+ {:else}
49
+ {@render children(current)}
50
+ {/if}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The one display component behind every structured editor component.
3
+ * It reads the value through the shape, derives the template's view of
4
+ * it and renders `children(view)`; in edit mode it swaps in the lazily
5
+ * loaded editable sibling (the generic form, or a bespoke one).
6
+ */
7
+ import type { Component, Snippet } from 'svelte';
8
+ import type { FormShape } from '../../core/shapes/form.js';
9
+ import { type CmsField } from './use-cms-field.svelte.js';
10
+ declare function $$render<T extends Record<string, unknown>, V>(): {
11
+ props: {
12
+ schema: FormShape<T>;
13
+ name: string;
14
+ scope?: string;
15
+ fallback?: unknown;
16
+ value?: unknown;
17
+ view: (value: T) => V;
18
+ children: Snippet<[V]>;
19
+ /** Loader for a bespoke editable sibling; defaults to the generic form. */
20
+ editable?: () => Promise<{
21
+ default: Component<{
22
+ field: CmsField<T>;
23
+ schema: FormShape<T>;
24
+ preview: Snippet;
25
+ }>;
26
+ }>;
27
+ };
28
+ exports: {};
29
+ bindings: "";
30
+ slots: {};
31
+ events: {};
32
+ };
33
+ declare class __sveltets_Render<T extends Record<string, unknown>, V> {
34
+ props(): ReturnType<typeof $$render<T, V>>['props'];
35
+ events(): ReturnType<typeof $$render<T, V>>['events'];
36
+ slots(): ReturnType<typeof $$render<T, V>>['slots'];
37
+ bindings(): "";
38
+ exports(): {};
39
+ }
40
+ interface $$IsomorphicComponent {
41
+ new <T extends Record<string, unknown>, V>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T, V>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T, V>['props']>, ReturnType<__sveltets_Render<T, V>['events']>, ReturnType<__sveltets_Render<T, V>['slots']>> & {
42
+ $$bindings?: ReturnType<__sveltets_Render<T, V>['bindings']>;
43
+ } & ReturnType<__sveltets_Render<T, V>['exports']>;
44
+ <T extends Record<string, unknown>, V>(internal: unknown, props: ReturnType<__sveltets_Render<T, V>['props']> & {}): ReturnType<__sveltets_Render<T, V>['exports']>;
45
+ z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
46
+ }
47
+ declare const CmsStructured: $$IsomorphicComponent;
48
+ type CmsStructured<T extends Record<string, unknown>, V> = InstanceType<typeof CmsStructured<T, V>>;
49
+ export default CmsStructured;
@@ -0,0 +1,22 @@
1
+ <script lang="ts" module>
2
+ /** `CmsTeam`: people with a portrait, role and bio. */
3
+ export * from '../../core/shapes/team.js';
4
+ </script>
5
+
6
+ <script lang="ts">
7
+ import type { Snippet } from 'svelte';
8
+ import Structured from './cms-structured.svelte';
9
+ import { cmsTeam, teamView, type CmsTeamValue, type TeamMember } from '../../core/shapes/team.js';
10
+
11
+ type Props = {
12
+ name: string;
13
+ scope?: string;
14
+ /** The full value, or a bare item list. */
15
+ fallback?: Partial<CmsTeamValue> | Array<Partial<CmsTeamValue['items'][number]>>;
16
+ value?: unknown;
17
+ children: Snippet<[TeamMember[]]>;
18
+ };
19
+ let { name, scope, fallback, value, children }: Props = $props();
20
+ </script>
21
+
22
+ <Structured schema={cmsTeam} {name} {scope} {fallback} {value} view={teamView} {children} />
@@ -0,0 +1,15 @@
1
+ /** `CmsTeam`: people with a portrait, role and bio. */
2
+ export * from '../../core/shapes/team.js';
3
+ import type { Snippet } from 'svelte';
4
+ import { type CmsTeamValue, type TeamMember } from '../../core/shapes/team.js';
5
+ type Props = {
6
+ name: string;
7
+ scope?: string;
8
+ /** The full value, or a bare item list. */
9
+ fallback?: Partial<CmsTeamValue> | Array<Partial<CmsTeamValue['items'][number]>>;
10
+ value?: unknown;
11
+ children: Snippet<[TeamMember[]]>;
12
+ };
13
+ declare const CmsTeam: import("svelte").Component<Props, {}, "">;
14
+ type CmsTeam = ReturnType<typeof CmsTeam>;
15
+ export default CmsTeam;
@@ -0,0 +1,36 @@
1
+ <script lang="ts" module>
2
+ /** `CmsTestimonials`: quotes with author, rating and source. */
3
+ export * from '../../core/shapes/testimonials.js';
4
+ </script>
5
+
6
+ <script lang="ts">
7
+ import type { Snippet } from 'svelte';
8
+ import Structured from './cms-structured.svelte';
9
+ import {
10
+ cmsTestimonials,
11
+ testimonialsView,
12
+ type CmsTestimonialsValue,
13
+ type Testimonial
14
+ } from '../../core/shapes/testimonials.js';
15
+
16
+ type Props = {
17
+ name: string;
18
+ scope?: string;
19
+ /** The full value, or a bare item list. */
20
+ fallback?:
21
+ Partial<CmsTestimonialsValue> | Array<Partial<CmsTestimonialsValue['items'][number]>>;
22
+ value?: unknown;
23
+ children: Snippet<[Testimonial[]]>;
24
+ };
25
+ let { name, scope, fallback, value, children }: Props = $props();
26
+ </script>
27
+
28
+ <Structured
29
+ schema={cmsTestimonials}
30
+ {name}
31
+ {scope}
32
+ {fallback}
33
+ {value}
34
+ view={testimonialsView}
35
+ {children}
36
+ />
@@ -0,0 +1,15 @@
1
+ /** `CmsTestimonials`: quotes with author, rating and source. */
2
+ export * from '../../core/shapes/testimonials.js';
3
+ import type { Snippet } from 'svelte';
4
+ import { type CmsTestimonialsValue, type Testimonial } from '../../core/shapes/testimonials.js';
5
+ type Props = {
6
+ name: string;
7
+ scope?: string;
8
+ /** The full value, or a bare item list. */
9
+ fallback?: Partial<CmsTestimonialsValue> | Array<Partial<CmsTestimonialsValue['items'][number]>>;
10
+ value?: unknown;
11
+ children: Snippet<[Testimonial[]]>;
12
+ };
13
+ declare const CmsTestimonials: import("svelte").Component<Props, {}, "">;
14
+ type CmsTestimonials = ReturnType<typeof CmsTestimonials>;
15
+ export default CmsTestimonials;
@@ -1,30 +1,29 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import { getCmsScope } from './scope.js';
4
- import { cmsStore, type CmsScopeRef } from './cms-store.svelte.js';
3
+ import { useCmsField } from './use-cms-field.svelte.js';
5
4
 
6
5
  type Props = {
7
6
  name: string;
7
+ /** `'root'` for the root layout scope, or an explicit scope id. */
8
+ scope?: string;
8
9
  fallback?: string;
9
- /** Per-item override used by `CmsRepeater`. */
10
+ /** Per-item override used by structured components. */
10
11
  value?: unknown;
11
12
  children?: Snippet;
12
13
  };
13
14
 
14
- let { name, fallback, value, children }: Props = $props();
15
+ let { name, scope, fallback, value, children }: Props = $props();
15
16
 
16
- const scope = getCmsScope();
17
- const ref = $derived<CmsScopeRef | null>(
18
- scope ? { scopeId: scope.scopeId, routeId: scope.routeId, params: scope.params } : null
17
+ // `undefined` → fallback (or children); `null` → cleared, shown empty.
18
+ const field = useCmsField(
19
+ () => ({ name, scope, value }),
20
+ (raw): string | undefined => {
21
+ if (raw === undefined) return fallback;
22
+ return typeof raw === 'string' ? raw : '';
23
+ }
19
24
  );
20
25
 
21
- const resolved = $derived.by(() => {
22
- if (value !== undefined) return value;
23
- return ref ? cmsStore.getValue(ref, name) : undefined;
24
- });
25
-
26
- const display = $derived(typeof resolved === 'string' ? resolved : (fallback ?? ''));
27
- const editable = $derived(cmsStore.isEditing && value === undefined && !!ref);
26
+ const display = $derived(field.current ?? '');
28
27
 
29
28
  let el = $state<HTMLSpanElement>();
30
29
  let childrenEl = $state<HTMLElement>();
@@ -32,17 +31,13 @@
32
31
  let composing = false;
33
32
 
34
33
  $effect.pre(() => {
35
- if (editable && childrenEl && initialFromChildren === undefined) {
34
+ if (field.editable && childrenEl && initialFromChildren === undefined) {
36
35
  const text = childrenEl.textContent?.trim();
37
36
  initialFromChildren = text || undefined;
38
37
  }
39
38
  });
40
39
 
41
- const editValue = $derived.by(() => {
42
- if (typeof resolved === 'string') return resolved;
43
- if (fallback !== undefined) return fallback;
44
- return initialFromChildren ?? '';
45
- });
40
+ const editValue = $derived(field.current ?? initialFromChildren ?? '');
46
41
 
47
42
  $effect(() => {
48
43
  if (!el) return;
@@ -52,14 +47,9 @@
52
47
  el.textContent = target;
53
48
  });
54
49
 
55
- const writeValue = (text: string) => {
56
- if (!ref) return;
57
- cmsStore.setValue(ref, name, text);
58
- };
59
-
60
50
  const onInput = (e: Event) => {
61
51
  if (composing) return;
62
- writeValue((e.currentTarget as HTMLElement).textContent ?? '');
52
+ field.set((e.currentTarget as HTMLElement).textContent ?? '');
63
53
  };
64
54
 
65
55
  const onKeydown = (e: KeyboardEvent) => {
@@ -68,11 +58,11 @@
68
58
 
69
59
  const onCompositionEnd = (e: CompositionEvent) => {
70
60
  composing = false;
71
- writeValue((e.currentTarget as HTMLElement).textContent ?? '');
61
+ field.set((e.currentTarget as HTMLElement).textContent ?? '');
72
62
  };
73
63
  </script>
74
64
 
75
- {#if editable && ref}
65
+ {#if field.editable && field.ref}
76
66
  <!-- Mounted straight into edit mode (navigation while editing): the
77
67
  children branch below never rendered, so render it hidden once to
78
68
  capture the default text. Removed as soon as it's captured. -->
@@ -85,7 +75,7 @@
85
75
  contenteditable="plaintext-only"
86
76
  data-placeholder={name}
87
77
  data-cms-name={name}
88
- data-cms-scope={scope?.scopeId ?? '?'}
78
+ data-cms-scope={field.ref.scopeId}
89
79
  role="textbox"
90
80
  tabindex="0"
91
81
  aria-multiline="false"
@@ -98,10 +88,13 @@
98
88
  ></span>
99
89
  {:else if display}
100
90
  {display}
91
+ {:else if field.raw === null}
92
+ <!-- cleared by the editor: render nothing -->
101
93
  {:else if children}
102
94
  <span bind:this={childrenEl} class="cms-text-children">{@render children()}</span>
103
95
  {:else}
104
- <span class="cms-missing" data-cms-name={name} data-cms-scope={scope?.scopeId ?? '?'}>{name}</span
96
+ <span class="cms-missing" data-cms-name={name} data-cms-scope={field.ref?.scopeId ?? '?'}
97
+ >{name}</span
105
98
  >
106
99
  {/if}
107
100
 
@@ -1,8 +1,10 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  type Props = {
3
3
  name: string;
4
+ /** `'root'` for the root layout scope, or an explicit scope id. */
5
+ scope?: string;
4
6
  fallback?: string;
5
- /** Per-item override used by `CmsRepeater`. */
7
+ /** Per-item override used by structured components. */
6
8
  value?: unknown;
7
9
  children?: Snippet;
8
10
  };
@@ -0,0 +1,35 @@
1
+ <script lang="ts" module>
2
+ /** `CmsTimeline` preset: date, title, body, image. */
3
+ export type { CmsTimelineValue, TimelineItem } from '../../core/shapes/presets.js';
4
+ export { cmsTimeline } from '../../core/shapes/presets.js';
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import type { Snippet } from 'svelte';
9
+ import Structured from './cms-structured.svelte';
10
+ import {
11
+ cmsTimeline,
12
+ type CmsTimelineValue,
13
+ type TimelineItem
14
+ } from '../../core/shapes/presets.js';
15
+
16
+ type Props = {
17
+ name: string;
18
+ scope?: string;
19
+ /** The full value, or a bare item list. */
20
+ fallback?: Partial<CmsTimelineValue> | Partial<TimelineItem>[];
21
+ value?: unknown;
22
+ children: Snippet<[TimelineItem[]]>;
23
+ };
24
+ let { name, scope, fallback, value, children }: Props = $props();
25
+ </script>
26
+
27
+ <Structured
28
+ schema={cmsTimeline}
29
+ {name}
30
+ {scope}
31
+ {fallback}
32
+ {value}
33
+ view={(v: CmsTimelineValue) => v.items}
34
+ {children}
35
+ />
@@ -0,0 +1,16 @@
1
+ /** `CmsTimeline` preset: date, title, body, image. */
2
+ export type { CmsTimelineValue, TimelineItem } from '../../core/shapes/presets.js';
3
+ export { cmsTimeline } from '../../core/shapes/presets.js';
4
+ import type { Snippet } from 'svelte';
5
+ import { type CmsTimelineValue, type TimelineItem } from '../../core/shapes/presets.js';
6
+ type Props = {
7
+ name: string;
8
+ scope?: string;
9
+ /** The full value, or a bare item list. */
10
+ fallback?: Partial<CmsTimelineValue> | Partial<TimelineItem>[];
11
+ value?: unknown;
12
+ children: Snippet<[TimelineItem[]]>;
13
+ };
14
+ declare const CmsTimeline: import("svelte").Component<Props, {}, "">;
15
+ type CmsTimeline = ReturnType<typeof CmsTimeline>;
16
+ export default CmsTimeline;
@@ -7,7 +7,21 @@ export { default as CmsNumber } from './cms-number.svelte';
7
7
  export { default as CmsDateTime } from './cms-date-time.svelte';
8
8
  export { default as CmsLink } from './cms-link.svelte';
9
9
  export type { CmsLinkValue, CmsLinkRenderProps } from './cms-link.svelte';
10
- export { default as CmsRepeater } from './cms-repeater.svelte';
10
+ export { default as CmsNav } from './cms-nav.svelte';
11
+ export { default as CmsHours } from './cms-hours.svelte';
12
+ export { default as CmsContact } from './cms-contact.svelte';
13
+ export { default as CmsSocialLinks } from './cms-social-links.svelte';
14
+ export { default as CmsCollection } from './cms-collection.svelte';
15
+ export { default as CmsTeam } from './cms-team.svelte';
16
+ export { default as CmsTestimonials } from './cms-testimonials.svelte';
17
+ export { default as CmsPricing } from './cms-pricing.svelte';
18
+ export { default as CmsFaq } from './cms-faq.svelte';
19
+ export { default as CmsStats } from './cms-stats.svelte';
20
+ export { default as CmsSteps } from './cms-steps.svelte';
21
+ export { default as CmsTimeline } from './cms-timeline.svelte';
22
+ export { default as CmsGallery } from './cms-gallery.svelte';
23
+ export { default as CmsLogos } from './cms-logos.svelte';
24
+ export { default as CmsSchedule } from './cms-schedule.svelte';
11
25
  export { default as CmsEntries } from './cms-entries.svelte';
12
26
  export { CMS_SCOPE, getCmsScope, getCmsValue } from './scope.js';
13
27
  export type { CmsScope, CmsPayload, CmsEntry } from './scope.js';
@@ -6,6 +6,20 @@ export { default as CmsBoolean } from './cms-boolean.svelte';
6
6
  export { default as CmsNumber } from './cms-number.svelte';
7
7
  export { default as CmsDateTime } from './cms-date-time.svelte';
8
8
  export { default as CmsLink } from './cms-link.svelte';
9
- export { default as CmsRepeater } from './cms-repeater.svelte';
9
+ export { default as CmsNav } from './cms-nav.svelte';
10
+ export { default as CmsHours } from './cms-hours.svelte';
11
+ export { default as CmsContact } from './cms-contact.svelte';
12
+ export { default as CmsSocialLinks } from './cms-social-links.svelte';
13
+ export { default as CmsCollection } from './cms-collection.svelte';
14
+ export { default as CmsTeam } from './cms-team.svelte';
15
+ export { default as CmsTestimonials } from './cms-testimonials.svelte';
16
+ export { default as CmsPricing } from './cms-pricing.svelte';
17
+ export { default as CmsFaq } from './cms-faq.svelte';
18
+ export { default as CmsStats } from './cms-stats.svelte';
19
+ export { default as CmsSteps } from './cms-steps.svelte';
20
+ export { default as CmsTimeline } from './cms-timeline.svelte';
21
+ export { default as CmsGallery } from './cms-gallery.svelte';
22
+ export { default as CmsLogos } from './cms-logos.svelte';
23
+ export { default as CmsSchedule } from './cms-schedule.svelte';
10
24
  export { default as CmsEntries } from './cms-entries.svelte';
11
25
  export { CMS_SCOPE, getCmsScope, getCmsValue } from './scope.js';