@velastack/cms 0.5.0 → 0.6.0

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 (134) hide show
  1. package/README.md +104 -47
  2. package/dist/backend/index.d.ts +7 -5
  3. package/dist/backend/index.js +7 -5
  4. package/dist/backend/types.d.ts +1 -1
  5. package/dist/components/admin-bar/admin-bar-internal.svelte +54 -59
  6. package/dist/components/admin-bar/admin-bar.css +1 -1
  7. package/dist/components/admin-bar/admin-bar.svelte +11 -7
  8. package/dist/components/admin-bar/field-input.svelte +20 -1
  9. package/dist/components/admin-bar/field-input.svelte.d.ts +2 -1
  10. package/dist/components/admin-bar/fields-form.svelte +218 -0
  11. package/dist/components/admin-bar/fields-form.svelte.d.ts +16 -0
  12. package/dist/components/admin-bar/history-panel.svelte +4 -4
  13. package/dist/components/admin-bar/link-input.svelte +102 -0
  14. package/dist/components/admin-bar/link-input.svelte.d.ts +9 -0
  15. package/dist/components/admin-bar/list-field.svelte +93 -0
  16. package/dist/components/admin-bar/list-field.svelte.d.ts +20 -0
  17. package/dist/components/admin-bar/locales-panel.svelte +1 -1
  18. package/dist/components/admin-bar/page-options.d.ts +24 -0
  19. package/dist/components/admin-bar/page-options.js +53 -0
  20. package/dist/components/admin-bar/pages-panel.svelte +1 -1
  21. package/dist/components/admin-bar/rich-text-input.svelte +92 -0
  22. package/dist/components/admin-bar/rich-text-input.svelte.d.ts +9 -0
  23. package/dist/components/admin-bar/structured-editor-popover.svelte +15 -6
  24. package/dist/components/admin-bar/theme.svelte.js +1 -1
  25. package/dist/components/cms/cms-collection.svelte +46 -0
  26. package/dist/components/cms/cms-collection.svelte.d.ts +25 -0
  27. package/dist/components/cms/cms-contact.svelte +26 -0
  28. package/dist/components/cms/cms-contact.svelte.d.ts +14 -0
  29. package/dist/components/cms/cms-faq.svelte +22 -0
  30. package/dist/components/cms/cms-faq.svelte.d.ts +15 -0
  31. package/dist/components/cms/cms-gallery.svelte +31 -0
  32. package/dist/components/cms/cms-gallery.svelte.d.ts +16 -0
  33. package/dist/components/cms/cms-hours-editable.svelte +160 -0
  34. package/dist/components/cms/cms-hours-editable.svelte.d.ts +17 -0
  35. package/dist/components/cms/cms-hours.svelte +42 -0
  36. package/dist/components/cms/cms-hours.svelte.d.ts +19 -0
  37. package/dist/components/cms/cms-image.svelte +3 -25
  38. package/dist/components/cms/cms-image.svelte.d.ts +3 -11
  39. package/dist/components/cms/cms-link-editable.svelte +4 -60
  40. package/dist/components/cms/cms-link.svelte +3 -23
  41. package/dist/components/cms/cms-link.svelte.d.ts +3 -10
  42. package/dist/components/cms/cms-logos.svelte +29 -0
  43. package/dist/components/cms/cms-logos.svelte.d.ts +16 -0
  44. package/dist/components/cms/cms-nav.svelte +22 -0
  45. package/dist/components/cms/cms-nav.svelte.d.ts +15 -0
  46. package/dist/components/cms/cms-pricing.svelte +36 -0
  47. package/dist/components/cms/cms-pricing.svelte.d.ts +14 -0
  48. package/dist/components/cms/cms-schedule.svelte +35 -0
  49. package/dist/components/cms/cms-schedule.svelte.d.ts +16 -0
  50. package/dist/components/cms/cms-social-links.svelte +35 -0
  51. package/dist/components/cms/cms-social-links.svelte.d.ts +15 -0
  52. package/dist/components/cms/cms-stats.svelte +31 -0
  53. package/dist/components/cms/cms-stats.svelte.d.ts +16 -0
  54. package/dist/components/cms/cms-steps.svelte +31 -0
  55. package/dist/components/cms/cms-steps.svelte.d.ts +16 -0
  56. package/dist/components/cms/cms-store.svelte.js +8 -5
  57. package/dist/components/cms/cms-structured-editable.svelte +29 -0
  58. package/dist/components/cms/{cms-repeater-editable.svelte.d.ts → cms-structured-editable.svelte.d.ts} +12 -8
  59. package/dist/components/cms/cms-structured.svelte +50 -0
  60. package/dist/components/cms/cms-structured.svelte.d.ts +49 -0
  61. package/dist/components/cms/cms-team.svelte +22 -0
  62. package/dist/components/cms/cms-team.svelte.d.ts +15 -0
  63. package/dist/components/cms/cms-testimonials.svelte +36 -0
  64. package/dist/components/cms/cms-testimonials.svelte.d.ts +15 -0
  65. package/dist/components/cms/cms-timeline.svelte +35 -0
  66. package/dist/components/cms/cms-timeline.svelte.d.ts +16 -0
  67. package/dist/components/cms/index.d.ts +15 -1
  68. package/dist/components/cms/index.js +15 -1
  69. package/dist/core/cms-fetch.d.ts +16 -0
  70. package/dist/core/cms-fetch.js +24 -0
  71. package/dist/core/shapes/collection.d.ts +60 -0
  72. package/dist/core/shapes/collection.js +194 -0
  73. package/dist/core/shapes/contact.d.ts +70 -0
  74. package/dist/core/shapes/contact.js +214 -0
  75. package/dist/core/shapes/faq.d.ts +12 -0
  76. package/dist/core/shapes/faq.js +48 -0
  77. package/dist/core/shapes/form.d.ts +46 -0
  78. package/dist/core/shapes/form.js +7 -0
  79. package/dist/core/shapes/hours.d.ts +91 -0
  80. package/dist/core/shapes/hours.js +325 -0
  81. package/dist/core/shapes/image.d.ts +15 -0
  82. package/dist/core/shapes/image.js +27 -0
  83. package/dist/core/shapes/index.d.ts +31 -0
  84. package/dist/core/shapes/index.js +13 -0
  85. package/dist/core/shapes/link.d.ts +15 -0
  86. package/dist/core/shapes/link.js +42 -0
  87. package/dist/core/shapes/nav.d.ts +21 -0
  88. package/dist/core/shapes/nav.js +71 -0
  89. package/dist/core/shapes/presets.d.ts +65 -0
  90. package/dist/core/shapes/presets.js +152 -0
  91. package/dist/core/shapes/pricing.d.ts +40 -0
  92. package/dist/core/shapes/pricing.js +115 -0
  93. package/dist/core/shapes/social-links.d.ts +19 -0
  94. package/dist/core/shapes/social-links.js +83 -0
  95. package/dist/core/shapes/team.d.ts +22 -0
  96. package/dist/core/shapes/team.js +70 -0
  97. package/dist/core/shapes/testimonials.d.ts +24 -0
  98. package/dist/core/shapes/testimonials.js +86 -0
  99. package/dist/{components/cms → core}/structured-registry.d.ts +2 -2
  100. package/dist/core/structured.d.ts +33 -10
  101. package/dist/core/structured.js +98 -47
  102. package/dist/editor.d.ts +4 -0
  103. package/dist/editor.js +4 -0
  104. package/dist/index.d.ts +22 -8
  105. package/dist/index.js +24 -5
  106. package/dist/server/README.md +1 -1
  107. package/dist/server/api-adapter.js +1 -1
  108. package/dist/server/cms.d.ts +1 -1
  109. package/dist/server/cms.js +1 -1
  110. package/dist/server/load-cms.js +1 -1
  111. package/package.json +15 -11
  112. package/plugin/README.md +34 -4
  113. package/plugin/dist/adapter.d.ts +5 -5
  114. package/plugin/dist/adapter.js +21 -7
  115. package/plugin/dist/adapter.js.map +1 -1
  116. package/plugin/dist/build-state.d.ts +1 -0
  117. package/plugin/dist/build-state.js +10 -1
  118. package/plugin/dist/build-state.js.map +1 -1
  119. package/plugin/dist/build.d.ts +2 -2
  120. package/plugin/dist/build.js +8 -7
  121. package/plugin/dist/build.js.map +1 -1
  122. package/plugin/dist/index.js +5 -1
  123. package/plugin/dist/index.js.map +1 -1
  124. package/plugin/dist/manifest.d.ts +18 -0
  125. package/plugin/dist/manifest.js +78 -10
  126. package/plugin/dist/manifest.js.map +1 -1
  127. package/plugin/dist/transforms.d.ts +4 -4
  128. package/plugin/dist/transforms.js +4 -4
  129. package/dist/cms.d.ts +0 -6
  130. package/dist/cms.js +0 -27
  131. package/dist/components/cms/cms-repeater-editable.svelte +0 -137
  132. package/dist/components/cms/cms-repeater.svelte +0 -52
  133. package/dist/components/cms/cms-repeater.svelte.d.ts +0 -31
  134. /package/dist/{components/cms → core}/structured-registry.js +0 -0
@@ -31,18 +31,28 @@ export type TranslationOverlay = Record<string, Record<string, string>>;
31
31
  export type StructuredItem = {
32
32
  id: string;
33
33
  } & Record<string, unknown>;
34
+ /**
35
+ * One list of items inside a structured value. `key` names the array field,
36
+ * `translatable` the item fields holding translatable text (a string, or an
37
+ * array of strings, translated per index as `<field>.<i>`), and `items` any
38
+ * lists nested inside each item (a collection item's `details`, a pricing
39
+ * tier's `features`). Every item at every level carries a stable `id`.
40
+ */
41
+ export type ItemsSpec = {
42
+ key: string;
43
+ translatable: readonly string[];
44
+ items?: ItemsSpec | readonly ItemsSpec[];
45
+ };
34
46
  export type StructuredSchema<T extends Tree> = {
35
47
  /** Exported component name, e.g. `'CmsHours'`. Recorded in the manifest. */
36
48
  component: string;
37
49
  /** Current shape version, written as `v` on every value. */
38
50
  version: number;
39
- /** Root-level fields holding translatable strings, e.g. `['note']`. */
51
+ /** Root-level fields holding translatable strings, e.g. `['note']`. A
52
+ * dotted path addresses a nested field (`'labels.closed'`). */
40
53
  translatable: readonly string[];
41
- /** The item list, when the value has one, and the item fields that translate. */
42
- items?: {
43
- key: string;
44
- translatable: readonly string[];
45
- };
54
+ /** The value's item lists, when it has any, and the item fields that translate. */
55
+ items?: ItemsSpec | readonly ItemsSpec[];
46
56
  /**
47
57
  * Turn anything the store may hold — an older version, a hand-seeded
48
58
  * partial object, garbage — into the current shape. Must never throw.
@@ -63,21 +73,26 @@ export declare const isPlainObject: (v: unknown) => v is Tree;
63
73
  export declare const defineStructured: <T extends Tree>(schema: StructuredSchema<T>) => Structured<T>;
64
74
  /** Path of one translated string: `<name>.$t.<id>.<field>`. */
65
75
  export declare const translationPath: (name: string, id: string, field: string) => string;
66
- /** The `$t` branch of a stored value, when it is one. */
76
+ /** The `$t` branch of a stored value, when it is one. A dotted field
77
+ * (`labels.closed`, `tags.0`) is stored nested by the path-based writes and
78
+ * read back flat here. */
67
79
  export declare const extractTranslations: (raw: unknown) => TranslationOverlay | undefined;
68
80
  /** A copy of `raw` without its `$t` branch, so a whole-object write in the
69
81
  * default locale never carries another locale's strings. */
70
82
  export declare const stripTranslations: (raw: unknown) => unknown;
83
+ /** `set` that copies along the path instead of mutating shared branches. */
84
+ export declare const setIn: (obj: Tree, path: string, value: unknown) => Tree;
71
85
  /**
72
86
  * Fold a locale's string overlay into a normalized default-locale value.
73
- * Root fields come from the `_` id; item fields from the item's own `id`.
74
- * Only non-empty strings override, so an untranslated field shows the
75
- * default-locale text rather than a blank.
87
+ * Root fields come from the `_` id; item fields from the item's own `id`,
88
+ * at every nesting level. Only non-empty strings override, so an
89
+ * untranslated field shows the default-locale text rather than a blank.
76
90
  */
77
91
  export declare const applyTranslations: <T extends Tree>(value: T, overlay: TranslationOverlay | undefined, schema: Pick<StructuredSchema<T>, "translatable" | "items">) => T;
78
92
  export type TranslatableField = {
79
93
  /** Overlay id: an item id, or `_` for a root field. */
80
94
  id: string;
95
+ /** Field path within the object the id addresses (`note`, `labels.closed`, `tags.0`). */
81
96
  field: string;
82
97
  /** The default-locale string being translated. */
83
98
  source: string;
@@ -106,3 +121,11 @@ export declare const asItems: <I extends StructuredItem>(raw: unknown, normalize
106
121
  export declare const newItemId: () => string;
107
122
  /** Read a structured value's overlay for one locale out of a whole scope tree. */
108
123
  export declare const overlayAt: (tree: Tree | undefined, name: string) => TranslationOverlay | undefined;
124
+ /** A list of strings; anything else is dropped. */
125
+ export declare const asStrings: (v: unknown) => string[];
126
+ /** A nullable string: `null` (cleared) stays `null`; anything else is a string. */
127
+ export declare const asOptionalString: (v: unknown) => string | null;
128
+ /** One of a fixed set of strings, else the fallback. */
129
+ export declare const asEnum: <E extends string>(v: unknown, values: readonly E[], fallback: E) => E;
130
+ /** Move an item within a list, returning a new list. */
131
+ export declare const moveItem: <I>(list: readonly I[], from: number, to: number) => I[];
@@ -21,7 +21,7 @@
21
21
  * the default locale only, so the item list can never fork between
22
22
  * languages.
23
23
  */
24
- import { get } from './path.js';
24
+ import { get, leafPaths } from './path.js';
25
25
  /** Branch under a structured value that holds a locale's string overlay. */
26
26
  export const TRANSLATIONS_KEY = '$t';
27
27
  /** Overlay id that addresses the structured value's root fields. */
@@ -31,7 +31,7 @@ export const defineStructured = (schema) => ({
31
31
  ...schema,
32
32
  read: (raw, fallback) => {
33
33
  if (raw === undefined)
34
- return fallback === undefined ? schema.empty() : fallback;
34
+ return fallback === undefined ? schema.empty() : schema.normalize(fallback);
35
35
  if (raw === null)
36
36
  return schema.empty();
37
37
  const overlay = extractTranslations(raw);
@@ -41,7 +41,9 @@ export const defineStructured = (schema) => ({
41
41
  });
42
42
  /** Path of one translated string: `<name>.$t.<id>.<field>`. */
43
43
  export const translationPath = (name, id, field) => `${name}.${TRANSLATIONS_KEY}.${id}.${field}`;
44
- /** The `$t` branch of a stored value, when it is one. */
44
+ /** The `$t` branch of a stored value, when it is one. A dotted field
45
+ * (`labels.closed`, `tags.0`) is stored nested by the path-based writes and
46
+ * read back flat here. */
45
47
  export const extractTranslations = (raw) => {
46
48
  if (!isPlainObject(raw))
47
49
  return undefined;
@@ -53,9 +55,10 @@ export const extractTranslations = (raw) => {
53
55
  if (!isPlainObject(fields))
54
56
  continue;
55
57
  const strings = {};
56
- for (const [field, v] of Object.entries(fields)) {
58
+ for (const path of leafPaths(fields)) {
59
+ const v = get(fields, path);
57
60
  if (typeof v === 'string')
58
- strings[field] = v;
61
+ strings[path] = v;
59
62
  }
60
63
  out[id] = strings;
61
64
  }
@@ -73,38 +76,91 @@ const overlayString = (overlay, id, field) => {
73
76
  const v = overlay[id]?.[field];
74
77
  return typeof v === 'string' && v !== '' ? v : undefined;
75
78
  };
79
+ const specList = (items) => items === undefined ? [] : Array.isArray(items) ? items : [items];
80
+ const isStringArray = (v) => Array.isArray(v) && v.every((s) => typeof s === 'string');
81
+ /** `set` that copies along the path instead of mutating shared branches. */
82
+ export const setIn = (obj, path, value) => {
83
+ const [head, ...rest] = path.split('.');
84
+ const out = { ...obj };
85
+ if (rest.length === 0) {
86
+ out[head] = value;
87
+ return out;
88
+ }
89
+ const child = obj[head];
90
+ out[head] = setIn(isPlainObject(child) ? child : {}, rest.join('.'), value);
91
+ return out;
92
+ };
93
+ /**
94
+ * Fold a locale's string overlay into one object (the root value or an item)
95
+ * and, recursively, into the item lists it holds.
96
+ */
97
+ const applyTo = (obj, id, translatable, items, overlay) => {
98
+ let out = { ...obj };
99
+ for (const field of translatable) {
100
+ const cur = get(obj, field);
101
+ if (typeof cur === 'string') {
102
+ const s = overlayString(overlay, id, field);
103
+ if (s !== undefined)
104
+ out = setIn(out, field, s);
105
+ }
106
+ else if (isStringArray(cur)) {
107
+ let changed = false;
108
+ const next = cur.map((v, i) => {
109
+ const s = overlayString(overlay, id, `${field}.${i}`);
110
+ if (s === undefined)
111
+ return v;
112
+ changed = true;
113
+ return s;
114
+ });
115
+ if (changed)
116
+ out = setIn(out, field, next);
117
+ }
118
+ }
119
+ for (const spec of specList(items)) {
120
+ const list = obj[spec.key];
121
+ if (!Array.isArray(list))
122
+ continue;
123
+ out[spec.key] = list.map((item) => isPlainObject(item) && typeof item.id === 'string'
124
+ ? applyTo(item, item.id, spec.translatable, spec.items, overlay)
125
+ : item);
126
+ }
127
+ return out;
128
+ };
76
129
  /**
77
130
  * Fold a locale's string overlay into a normalized default-locale value.
78
- * Root fields come from the `_` id; item fields from the item's own `id`.
79
- * Only non-empty strings override, so an untranslated field shows the
80
- * default-locale text rather than a blank.
131
+ * Root fields come from the `_` id; item fields from the item's own `id`,
132
+ * at every nesting level. Only non-empty strings override, so an
133
+ * untranslated field shows the default-locale text rather than a blank.
81
134
  */
82
135
  export const applyTranslations = (value, overlay, schema) => {
83
136
  if (!overlay)
84
137
  return value;
85
- const out = { ...value };
86
- for (const field of schema.translatable) {
87
- const s = overlayString(overlay, ROOT_ID, field);
88
- if (s !== undefined)
89
- out[field] = s;
90
- }
91
- if (schema.items) {
92
- const list = value[schema.items.key];
93
- if (Array.isArray(list)) {
94
- out[schema.items.key] = list.map((item) => {
95
- if (!isPlainObject(item) || typeof item.id !== 'string')
96
- return item;
97
- const next = { ...item };
98
- for (const field of schema.items.translatable) {
99
- const s = overlayString(overlay, item.id, field);
100
- if (s !== undefined)
101
- next[field] = s;
102
- }
103
- return next;
138
+ return applyTo(value, ROOT_ID, schema.translatable, schema.items, overlay);
139
+ };
140
+ const collectFrom = (obj, id, translatable, items, out) => {
141
+ for (const field of translatable) {
142
+ const cur = get(obj, field);
143
+ if (typeof cur === 'string') {
144
+ if (cur !== '')
145
+ out.push({ id, field, source: cur });
146
+ }
147
+ else if (isStringArray(cur)) {
148
+ cur.forEach((s, i) => {
149
+ if (s !== '')
150
+ out.push({ id, field: `${field}.${i}`, source: s });
104
151
  });
105
152
  }
106
153
  }
107
- return out;
154
+ for (const spec of specList(items)) {
155
+ const list = obj[spec.key];
156
+ if (!Array.isArray(list))
157
+ continue;
158
+ for (const item of list) {
159
+ if (!isPlainObject(item) || typeof item.id !== 'string')
160
+ continue;
161
+ collectFrom(item, item.id, spec.translatable, spec.items, out);
162
+ }
163
+ }
108
164
  };
109
165
  /**
110
166
  * Every translatable, non-empty string in a normalized default-locale value.
@@ -112,25 +168,7 @@ export const applyTranslations = (value, overlay, schema) => {
112
168
  */
113
169
  export const translatableFields = (value, schema) => {
114
170
  const out = [];
115
- for (const field of schema.translatable) {
116
- const s = value[field];
117
- if (typeof s === 'string' && s !== '')
118
- out.push({ id: ROOT_ID, field, source: s });
119
- }
120
- if (schema.items) {
121
- const list = value[schema.items.key];
122
- if (Array.isArray(list)) {
123
- for (const item of list) {
124
- if (!isPlainObject(item) || typeof item.id !== 'string')
125
- continue;
126
- for (const field of schema.items.translatable) {
127
- const s = item[field];
128
- if (typeof s === 'string' && s !== '')
129
- out.push({ id: item.id, field, source: s });
130
- }
131
- }
132
- }
133
- }
171
+ collectFrom(value, ROOT_ID, schema.translatable, schema.items, out);
134
172
  return out;
135
173
  };
136
174
  /** How many translatable strings a locale's overlay still lacks. */
@@ -182,3 +220,16 @@ export const newItemId = () => {
182
220
  };
183
221
  /** Read a structured value's overlay for one locale out of a whole scope tree. */
184
222
  export const overlayAt = (tree, name) => extractTranslations(get(tree, name));
223
+ /** A list of strings; anything else is dropped. */
224
+ export const asStrings = (v) => Array.isArray(v) ? v.filter((s) => typeof s === 'string') : [];
225
+ /** A nullable string: `null` (cleared) stays `null`; anything else is a string. */
226
+ export const asOptionalString = (v) => typeof v === 'string' && v !== '' ? v : null;
227
+ /** One of a fixed set of strings, else the fallback. */
228
+ export const asEnum = (v, values, fallback) => typeof v === 'string' && values.includes(v) ? v : fallback;
229
+ /** Move an item within a list, returning a new list. */
230
+ export const moveItem = (list, from, to) => {
231
+ const next = [...list];
232
+ const [item] = next.splice(from, 1);
233
+ next.splice(to, 0, item);
234
+ return next;
235
+ };
package/dist/editor.d.ts CHANGED
@@ -10,6 +10,10 @@ export { default as StructuredEditorPopover } from './components/admin-bar/struc
10
10
  export { default as FieldInput } from './components/admin-bar/field-input.svelte';
11
11
  export type { FieldInputType } from './components/admin-bar/field-input.svelte';
12
12
  export { default as ReorderButtons } from './components/admin-bar/reorder-buttons.svelte';
13
+ export { default as FieldsForm } from './components/admin-bar/fields-form.svelte';
14
+ export { default as ListField } from './components/admin-bar/list-field.svelte';
15
+ export { default as LinkInput } from './components/admin-bar/link-input.svelte';
16
+ export { default as RichTextInput } from './components/admin-bar/rich-text-input.svelte';
13
17
  export { default as CssRoot } from './components/admin-bar/css-root.svelte';
14
18
  export { Button } from './components/admin-bar/ui/button/index.js';
15
19
  export { Input } from './components/admin-bar/ui/input/index.js';
package/dist/editor.js CHANGED
@@ -9,6 +9,10 @@
9
9
  export { default as StructuredEditorPopover } from './components/admin-bar/structured-editor-popover.svelte';
10
10
  export { default as FieldInput } from './components/admin-bar/field-input.svelte';
11
11
  export { default as ReorderButtons } from './components/admin-bar/reorder-buttons.svelte';
12
+ export { default as FieldsForm } from './components/admin-bar/fields-form.svelte';
13
+ export { default as ListField } from './components/admin-bar/list-field.svelte';
14
+ export { default as LinkInput } from './components/admin-bar/link-input.svelte';
15
+ export { default as RichTextInput } from './components/admin-bar/rich-text-input.svelte';
12
16
  export { default as CssRoot } from './components/admin-bar/css-root.svelte';
13
17
  export { Button } from './components/admin-bar/ui/button/index.js';
14
18
  export { Input } from './components/admin-bar/ui/input/index.js';
package/dist/index.d.ts CHANGED
@@ -7,23 +7,37 @@
7
7
  export { default as CmsText } from './components/cms/cms-text.svelte';
8
8
  export { default as CmsRichText } from './components/cms/cms-rich-text.svelte';
9
9
  export { default as CmsMarkdown } from './components/cms/cms-markdown.svelte';
10
- export { default as CmsImage, normalizeImage } from './components/cms/cms-image.svelte';
11
- export type { CmsImageValue } from './components/cms/cms-image.svelte';
10
+ export { default as CmsImage } from './components/cms/cms-image.svelte';
12
11
  export { default as CmsBoolean } from './components/cms/cms-boolean.svelte';
13
12
  export { default as CmsNumber } from './components/cms/cms-number.svelte';
14
13
  export type { CmsNumberProps } from './components/cms/cms-number.svelte';
15
14
  export { default as CmsDateTime } from './components/cms/cms-date-time.svelte';
16
15
  export type { CmsDateTimeMode, CmsDateTimeProps } from './components/cms/cms-date-time.svelte';
17
- export { default as CmsLink, normalizeLink } from './components/cms/cms-link.svelte';
18
- export type { CmsLinkValue, CmsLinkRenderProps } from './components/cms/cms-link.svelte';
19
- export { default as CmsRepeater } from './components/cms/cms-repeater.svelte';
16
+ export { default as CmsLink } from './components/cms/cms-link.svelte';
17
+ export type { CmsLinkRenderProps } from './components/cms/cms-link.svelte';
20
18
  export { default as CmsEntries } from './components/cms/cms-entries.svelte';
19
+ export { default as CmsNav } from './components/cms/cms-nav.svelte';
20
+ export { default as CmsHours } from './components/cms/cms-hours.svelte';
21
+ export { default as CmsContact } from './components/cms/cms-contact.svelte';
22
+ export { default as CmsSocialLinks } from './components/cms/cms-social-links.svelte';
23
+ export { default as CmsCollection } from './components/cms/cms-collection.svelte';
24
+ export { default as CmsTeam } from './components/cms/cms-team.svelte';
25
+ export { default as CmsTestimonials } from './components/cms/cms-testimonials.svelte';
26
+ export { default as CmsPricing } from './components/cms/cms-pricing.svelte';
27
+ export { default as CmsFaq } from './components/cms/cms-faq.svelte';
28
+ export { default as CmsStats } from './components/cms/cms-stats.svelte';
29
+ export { default as CmsSteps } from './components/cms/cms-steps.svelte';
30
+ export { default as CmsTimeline } from './components/cms/cms-timeline.svelte';
31
+ export { default as CmsGallery } from './components/cms/cms-gallery.svelte';
32
+ export { default as CmsLogos } from './components/cms/cms-logos.svelte';
33
+ export { default as CmsSchedule } from './components/cms/cms-schedule.svelte';
34
+ export * from './core/shapes/index.js';
21
35
  export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
22
36
  export { useCmsField, resolveScopeRef, ROOT_SCOPE_ID } from './components/cms/use-cms-field.svelte.js';
23
37
  export type { CmsField, CmsFieldInput } from './components/cms/use-cms-field.svelte.js';
24
- export { defineStructured, applyTranslations, countTranslations, extractTranslations, stripTranslations, translatableFields, translationPath, asBoolean, asItems, asNumber, asString, newItemId, ROOT_ID, TRANSLATIONS_KEY } from './core/structured.js';
25
- export type { Structured, StructuredItem, StructuredSchema, TranslatableField, TranslationOverlay } from './core/structured.js';
26
- export { registerStructured, getStructured } from './components/cms/structured-registry.js';
38
+ export { defineStructured, applyTranslations, countTranslations, extractTranslations, stripTranslations, translatableFields, translationPath, asBoolean, asEnum, asItems, asNumber, asOptionalString, asString, asStrings, moveItem, newItemId, setIn, ROOT_ID, TRANSLATIONS_KEY } from './core/structured.js';
39
+ export type { ItemsSpec, Structured, StructuredItem, StructuredSchema, TranslatableField, TranslationOverlay } from './core/structured.js';
40
+ export { registerStructured, getStructured } from './core/structured-registry.js';
27
41
  export { toMetaTags } from './core/metadata.js';
28
42
  export type { MetaTagsOptions, MetaTagsOutput } from './core/metadata.js';
29
43
  export { definePage, DEFAULT_METADATA_SCHEMA, metadataSchemaFor } from './components/admin-bar/page-config.js';
package/dist/index.js CHANGED
@@ -8,13 +8,32 @@
8
8
  export { default as CmsText } from './components/cms/cms-text.svelte';
9
9
  export { default as CmsRichText } from './components/cms/cms-rich-text.svelte';
10
10
  export { default as CmsMarkdown } from './components/cms/cms-markdown.svelte';
11
- export { default as CmsImage, normalizeImage } from './components/cms/cms-image.svelte';
11
+ export { default as CmsImage } from './components/cms/cms-image.svelte';
12
12
  export { default as CmsBoolean } from './components/cms/cms-boolean.svelte';
13
13
  export { default as CmsNumber } from './components/cms/cms-number.svelte';
14
14
  export { default as CmsDateTime } from './components/cms/cms-date-time.svelte';
15
- export { default as CmsLink, normalizeLink } from './components/cms/cms-link.svelte';
16
- export { default as CmsRepeater } from './components/cms/cms-repeater.svelte';
15
+ export { default as CmsLink } from './components/cms/cms-link.svelte';
17
16
  export { default as CmsEntries } from './components/cms/cms-entries.svelte';
17
+ // Structured editor components. Each reads one versioned value, opens a
18
+ // popover editor in edit mode and hands the typed view to `children`.
19
+ // Their value types, normalizers and JSON-LD helpers come from the shapes.
20
+ export { default as CmsNav } from './components/cms/cms-nav.svelte';
21
+ export { default as CmsHours } from './components/cms/cms-hours.svelte';
22
+ export { default as CmsContact } from './components/cms/cms-contact.svelte';
23
+ export { default as CmsSocialLinks } from './components/cms/cms-social-links.svelte';
24
+ export { default as CmsCollection } from './components/cms/cms-collection.svelte';
25
+ export { default as CmsTeam } from './components/cms/cms-team.svelte';
26
+ export { default as CmsTestimonials } from './components/cms/cms-testimonials.svelte';
27
+ export { default as CmsPricing } from './components/cms/cms-pricing.svelte';
28
+ export { default as CmsFaq } from './components/cms/cms-faq.svelte';
29
+ // List presets: fixed item shapes; a new shape is a new preset here.
30
+ export { default as CmsStats } from './components/cms/cms-stats.svelte';
31
+ export { default as CmsSteps } from './components/cms/cms-steps.svelte';
32
+ export { default as CmsTimeline } from './components/cms/cms-timeline.svelte';
33
+ export { default as CmsGallery } from './components/cms/cms-gallery.svelte';
34
+ export { default as CmsLogos } from './components/cms/cms-logos.svelte';
35
+ export { default as CmsSchedule } from './components/cms/cms-schedule.svelte';
36
+ export * from './core/shapes/index.js';
18
37
  // Admin bar (sync wrapper; admin-bar-internal + seo-panel are async chunks)
19
38
  export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
20
39
  // Building blocks for structured components: the field hook, the value
@@ -23,8 +42,8 @@ export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
23
42
  // lazily by editable siblings:
24
43
  // `import('@velastack/cms/editor')`
25
44
  export { useCmsField, resolveScopeRef, ROOT_SCOPE_ID } from './components/cms/use-cms-field.svelte.js';
26
- export { defineStructured, applyTranslations, countTranslations, extractTranslations, stripTranslations, translatableFields, translationPath, asBoolean, asItems, asNumber, asString, newItemId, ROOT_ID, TRANSLATIONS_KEY } from './core/structured.js';
27
- export { registerStructured, getStructured } from './components/cms/structured-registry.js';
45
+ export { defineStructured, applyTranslations, countTranslations, extractTranslations, stripTranslations, translatableFields, translationPath, asBoolean, asEnum, asItems, asNumber, asOptionalString, asString, asStrings, moveItem, newItemId, setIn, ROOT_ID, TRANSLATIONS_KEY } from './core/structured.js';
46
+ export { registerStructured, getStructured } from './core/structured-registry.js';
28
47
  // Page metadata → svelte-meta-tags props.
29
48
  export { toMetaTags } from './core/metadata.js';
30
49
  // Per-route page configuration — declare in `page.cms.ts` next to `+page.svelte`.
@@ -29,7 +29,7 @@ export const { load: loadCms, generateEntries } = createCms({
29
29
  ```ts
30
30
  // src/routes/+layout.server.ts
31
31
  import { error, redirect } from '@sveltejs/kit';
32
- import { loadCms } from '$lib/cms.js';
32
+ import { loadCms } from '#lib/cms.js';
33
33
 
34
34
  export const load = async (event) => {
35
35
  // Pick the locale however you like — pathname, Accept-Language, cookie, …
@@ -1,4 +1,4 @@
1
- import { building } from '$app/environment';
1
+ import { building } from '$app/env';
2
2
  import { buildConfig } from 'virtual:vela-cms/build-config';
3
3
  /**
4
4
  * The Vite plugin generates `virtual:vela-cms/build-config` with the
@@ -52,7 +52,7 @@ export type Cms = {
52
52
  * Typical usage:
53
53
  *
54
54
  * ```ts
55
- * // $lib/cms.ts
55
+ * // src/lib/cms.ts
56
56
  * import { createCms, mockAdapter } from '@velastack/cms/server';
57
57
  * export const { load: loadCms, generateEntries } = createCms({
58
58
  * adapter: mockAdapter({ layoutDocs, pageDocs }),
@@ -7,7 +7,7 @@ import { loadCms } from './load-cms.js';
7
7
  * Typical usage:
8
8
  *
9
9
  * ```ts
10
- * // $lib/cms.ts
10
+ * // src/lib/cms.ts
11
11
  * import { createCms, mockAdapter } from '@velastack/cms/server';
12
12
  * export const { load: loadCms, generateEntries } = createCms({
13
13
  * adapter: mockAdapter({ layoutDocs, pageDocs }),
@@ -1,6 +1,6 @@
1
1
  import { cmsManifest } from 'virtual:vela-cms/manifest';
2
2
  import { mergeLocaleDocs, mergeLocaleEntries } from '../components/cms/locale-merge.js';
3
- import { browser, building } from '$app/environment';
3
+ import { browser, building } from '$app/env';
4
4
  const builtManifest = cmsManifest;
5
5
  const DEFAULT_ENDPOINT = '/api/cms';
6
6
  const emptyPayload = (locale, locales, endpoint, site) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velastack/cms",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A scope-aware CMS for SvelteKit, for static or dynamic sites.",
5
5
  "keywords": [
6
6
  "svelte",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "type": "module",
24
24
  "engines": {
25
- "node": ">=22.12.0"
25
+ "node": ">=22.17.0"
26
26
  },
27
27
  "scripts": {
28
28
  "dev": "vite dev",
@@ -84,11 +84,11 @@
84
84
  }
85
85
  },
86
86
  "peerDependencies": {
87
- "@sveltejs/kit": "^2.0.0",
87
+ "@sveltejs/kit": "^3.0.0",
88
88
  "better-sqlite3": "^12.0.0 || ^13.0.0",
89
89
  "marked": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
90
- "svelte": "^5.0.0",
91
- "vite": "^8.0.0"
90
+ "svelte": "^5.57.1",
91
+ "vite": "^8.0.12"
92
92
  },
93
93
  "peerDependenciesMeta": {
94
94
  "better-sqlite3": {
@@ -100,10 +100,9 @@
100
100
  },
101
101
  "devDependencies": {
102
102
  "@internationalized/date": "^3.12.3",
103
- "@sveltejs/adapter-auto": "^7.0.1",
104
- "@sveltejs/kit": "^2.70.3",
105
- "@sveltejs/package": "^2.5.8",
106
- "@sveltejs/vite-plugin-svelte": "^7.3.0",
103
+ "@sveltejs/kit": "^3.0.0",
104
+ "@sveltejs/package": "^3.0.0",
105
+ "@sveltejs/vite-plugin-svelte": "^7.3.1",
107
106
  "@tailwindcss/cli": "^4.3.3",
108
107
  "@tailwindcss/vite": "^4.3.3",
109
108
  "@types/node": "^26.4.0",
@@ -112,7 +111,7 @@
112
111
  "prettier": "^3.9.6",
113
112
  "prettier-plugin-svelte": "^4.1.1",
114
113
  "publint": "^0.3.24",
115
- "svelte": "^5.57.0",
114
+ "svelte": "^5.57.1",
116
115
  "svelte-check": "^4.7.6",
117
116
  "svelte-meta-tags": "^5.0.2",
118
117
  "tailwindcss": "^4.3.3",
@@ -122,7 +121,7 @@
122
121
  "vitest": "^5.0.1"
123
122
  },
124
123
  "dependencies": {
125
- "@sveltejs/adapter-static": "^3.0.10",
124
+ "@sveltejs/adapter-static": "^4.0.0",
126
125
  "@tiptap/core": "^3.30.6",
127
126
  "@tiptap/extension-link": "^3.30.6",
128
127
  "@tiptap/pm": "^3.30.6",
@@ -130,5 +129,10 @@
130
129
  "bits-ui": "^2.19.0",
131
130
  "devalue": "^6.0.0",
132
131
  "tailwind-merge": "^3.6.0"
132
+ },
133
+ "imports": {
134
+ "#lib": "./src/lib/index.js",
135
+ "#lib/*": "./src/lib/*",
136
+ "#locales/*": "./src/locales/*"
133
137
  }
134
138
  }
package/plugin/README.md CHANGED
@@ -44,7 +44,10 @@ type ExternalCmsComponentSpec =
44
44
  ```
45
45
 
46
46
  - `routesDir` / `libDir` — only override if your project diverges from the
47
- SvelteKit defaults.
47
+ SvelteKit defaults. `#lib/…` imports resolve through the project's
48
+ `package.json` `imports` (read once when the config resolves), falling back
49
+ to `libDir` when `#lib` isn't declared there; `$lib/…` still resolves to
50
+ `libDir`.
48
51
  - `components` — third-party CMS component packs the walker should recognize.
49
52
  Auto-discovered for everything inside `<libDir>/components/cms/` and the
50
53
  `@velastack/cms` package itself; use this for components installed from npm.
@@ -111,7 +114,7 @@ instance script (or creates one if absent). Authors never write
111
114
  ### `routeId` into zero-arg `generateEntries()` calls
112
115
 
113
116
  In any `+page.ts` or `+page.server.ts` that imports `generateEntries` (from
114
- anywhere — typically `$lib/cms.ts`), zero-argument calls
117
+ anywhere — typically `#lib/cms.js`), zero-argument calls
115
118
  `generateEntries()` are rewritten to `generateEntries('<routeId>')`. Calls
116
119
  with any explicit argument are left alone — that's the documented opt-out.
117
120
 
@@ -123,8 +126,10 @@ Imports get classified as CMS components by:
123
126
 
124
127
  0. **`@velastack/cms` by name** — every named import from the package
125
128
  specifier, with no resolver needed.
126
- 1. **In-tree barrel** — anything from `<libDir>/components/cms/index.{ts,js}`.
127
- Named imports are CMS components; default isn't.
129
+ 1. **In-tree barrel** — anything from `<libDir>/components/cms/index.{ts,js}`,
130
+ however it's imported (`#lib/components/cms/index.js`, `$lib/components/cms`,
131
+ a relative path or another `#` subpath import). Named imports are CMS
132
+ components; default isn't.
128
133
  2. **In-tree files** — `.svelte` files directly under `<libDir>/components/cms/`.
129
134
  Default imports are CMS components.
130
135
  3. **`@velastack/cms` package** — any import resolving inside the `@velastack/cms`
@@ -139,6 +144,31 @@ own. Dynamic `name={expr}` props and `scope=` attributes naming a scope
139
144
  outside the route's chain are reported as warnings on the build result and
140
145
  logged by the plugin.
141
146
 
147
+ ## Static adapter
148
+
149
+ `@velastack/cms/adapter` wraps `@sveltejs/adapter-static` and takes the same
150
+ options. It goes in `sveltekit({...})` next to `cms()`:
151
+
152
+ ```ts
153
+ // vite.config.ts
154
+ import { defineConfig } from 'vite';
155
+ import { sveltekit } from '@sveltejs/kit/vite';
156
+ import { cms } from '@velastack/cms/vite';
157
+ import cmsAdapter from '@velastack/cms/adapter';
158
+
159
+ export default defineConfig({
160
+ plugins: [cms(), sveltekit({ adapter: cmsAdapter({ fallback: '200.html' }) })]
161
+ });
162
+ ```
163
+
164
+ After the inner adapter runs it writes `__fallback.json` beside each
165
+ parameterized route's parent directory (built from a prerendered
166
+ `__data.json`, with docs and metadata cleared and params replaced by
167
+ `%name%`), and injects `__velastack_manifest` plus a small fetch shim into the
168
+ `fallback` page. The manifest lists the prerendered entries of every
169
+ `creatable` `page.cms.ts` route; the plugin hands those routes to the adapter
170
+ in-process, so the adapter warns when the `cms()` plugin never ran.
171
+
142
172
  ## HMR
143
173
 
144
174
  The plugin invalidates `virtual:vela-cms/manifest` and
@@ -7,13 +7,13 @@
7
7
  * `%name%`. The runtime can fetch the fallback when navigating to a CMS page
8
8
  * that wasn't prerendered, then substitute the actual params client-side.
9
9
  *
10
+ * Everything else (`supports`, `emulate`, `vite`, whatever adapter-static
11
+ * grows) comes from the inner adapter unchanged.
12
+ *
10
13
  * @param {StaticAdapterOptions} [options]
14
+ * @returns {import('@sveltejs/kit').Adapter}
11
15
  */
12
- export default function adapter(options?: StaticAdapterOptions): {
13
- name: string;
14
- /** @param {import('@sveltejs/kit').Builder} builder */
15
- adapt(builder: import("@sveltejs/kit").Builder): Promise<void>;
16
- };
16
+ export default function adapter(options?: StaticAdapterOptions): import("@sveltejs/kit").Adapter;
17
17
  export function parseRouteSegments(routeId: string): ParsedSegment[];
18
18
  export function buildParamExtractor(segments: ParsedSegment[]): ((url: string) => Record<string, string> | null) | null;
19
19
  export type StaticAdapterOptions = Parameters<typeof staticAdapter>[0];