includio-cms 0.7.2 → 0.13.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 (185) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/ROADMAP.md +54 -2
  3. package/dist/admin/api/generate-styles.d.ts +2 -0
  4. package/dist/admin/api/generate-styles.js +32 -0
  5. package/dist/admin/api/handler.js +33 -0
  6. package/dist/admin/api/media-gc.js +10 -4
  7. package/dist/admin/api/rest/handler.js +17 -0
  8. package/dist/admin/api/rest/routes/collections.js +25 -13
  9. package/dist/admin/api/rest/routes/entries.d.ts +1 -1
  10. package/dist/admin/api/rest/routes/entries.js +10 -10
  11. package/dist/admin/api/rest/routes/media.d.ts +2 -0
  12. package/dist/admin/api/rest/routes/media.js +9 -0
  13. package/dist/admin/api/rest/routes/schema.d.ts +5 -0
  14. package/dist/admin/api/rest/routes/schema.js +152 -0
  15. package/dist/admin/api/rest/routes/singletons.d.ts +1 -1
  16. package/dist/admin/api/rest/routes/singletons.js +8 -7
  17. package/dist/admin/api/rest/routes/upload.d.ts +2 -0
  18. package/dist/admin/api/rest/routes/upload.js +28 -0
  19. package/dist/admin/api/upload.js +13 -0
  20. package/dist/admin/client/collection/collection-entries.svelte +35 -13
  21. package/dist/admin/client/entry/entry.svelte +21 -23
  22. package/dist/admin/client/entry/header/a11y-validator.js +2 -2
  23. package/dist/admin/client/entry/header/publish-panel.svelte +33 -85
  24. package/dist/admin/client/entry/header/status-badge.svelte +2 -2
  25. package/dist/admin/client/entry/header/version-history-sheet.svelte +9 -9
  26. package/dist/admin/client/entry/header/visibility.svelte +16 -10
  27. package/dist/admin/client/entry/utils.d.ts +3 -0
  28. package/dist/admin/client/entry/utils.js +22 -4
  29. package/dist/admin/client/form/form-submission/form-submission-page.svelte +4 -1
  30. package/dist/admin/client/form/form-submission/submission-field.svelte +10 -0
  31. package/dist/admin/client/index.d.ts +1 -0
  32. package/dist/admin/client/index.js +1 -0
  33. package/dist/admin/client/maintenance/maintenance-page.svelte +146 -2
  34. package/dist/admin/client/users/users-page.svelte +5 -6
  35. package/dist/admin/client/users/users-page.svelte.d.ts +1 -4
  36. package/dist/admin/components/fields/block-picker-modal.svelte +13 -4
  37. package/dist/admin/components/fields/blocks-field.svelte +40 -19
  38. package/dist/admin/components/fields/field-renderer.svelte +4 -8
  39. package/dist/admin/components/fields/object-field.svelte +7 -12
  40. package/dist/admin/components/fields/select-field.svelte +8 -2
  41. package/dist/admin/components/fields/seo-field.svelte +40 -93
  42. package/dist/admin/components/fields/simple-array-field.svelte +27 -16
  43. package/dist/admin/components/fields/text-field-wrapper.svelte +52 -197
  44. package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
  45. package/dist/admin/components/fields/url-field-wrapper.svelte +15 -25
  46. package/dist/admin/components/fields/url-field.svelte +61 -72
  47. package/dist/admin/components/layout/layout-renderer.svelte +10 -4
  48. package/dist/admin/components/media/file-preview.svelte +10 -1
  49. package/dist/admin/components/media/file-upload.svelte +5 -1
  50. package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
  51. package/dist/admin/components/media/files-list.svelte +12 -3
  52. package/dist/admin/components/media/media-library.svelte +109 -37
  53. package/dist/admin/components/media/media-selector.svelte +90 -16
  54. package/dist/admin/components/media/tag-sidebar.svelte +10 -6
  55. package/dist/admin/components/media/tag-sidebar.svelte.d.ts +7 -2
  56. package/dist/admin/components/tiptap/FigureNodeView.svelte +15 -10
  57. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +53 -94
  58. package/dist/admin/components/tiptap/SlashCommandPopup.svelte +8 -3
  59. package/dist/admin/components/tiptap/editor-toolbar.svelte +28 -23
  60. package/dist/admin/components/tiptap/image-dialog.svelte +12 -7
  61. package/dist/admin/components/tiptap/inline-block-node.js +6 -5
  62. package/dist/admin/components/tiptap/lang.d.ts +77 -0
  63. package/dist/admin/components/tiptap/lang.js +170 -0
  64. package/dist/admin/components/tiptap/link-dialog.svelte +31 -28
  65. package/dist/admin/components/tiptap/slash-command.js +27 -23
  66. package/dist/admin/components/tiptap/table-dialog.svelte +9 -4
  67. package/dist/admin/components/tiptap/video-dialog.svelte +6 -1
  68. package/dist/admin/remote/email.remote.d.ts +1 -0
  69. package/dist/admin/remote/email.remote.js +5 -0
  70. package/dist/admin/remote/entry.remote.d.ts +2 -5
  71. package/dist/admin/remote/entry.remote.js +23 -28
  72. package/dist/admin/remote/index.d.ts +1 -0
  73. package/dist/admin/remote/index.js +1 -0
  74. package/dist/admin/remote/media.remote.d.ts +15 -0
  75. package/dist/admin/remote/media.remote.js +18 -2
  76. package/dist/admin/remote/preview.remote.js +3 -1
  77. package/dist/admin/utils/entryLabel.js +9 -6
  78. package/dist/admin/utils/translationStatus.js +1 -2
  79. package/dist/cli/scaffold/admin.js +34 -2
  80. package/dist/cms/runtime/api.d.ts +16 -12
  81. package/dist/cms/runtime/api.js +7 -6
  82. package/dist/cms/runtime/remote.js +2 -2
  83. package/dist/cms/runtime/schemas.d.ts +1 -1
  84. package/dist/cms/runtime/schemas.js +1 -1
  85. package/dist/cms/runtime/types.d.ts +118 -112
  86. package/dist/cms/runtime/types.js +0 -12
  87. package/dist/core/cms.d.ts +3 -1
  88. package/dist/core/cms.js +30 -0
  89. package/dist/core/fields/fieldSchemaToTs.js +9 -15
  90. package/dist/core/fields/formFieldSchemaToTs.js +7 -0
  91. package/dist/core/server/entries/operations/create.js +10 -4
  92. package/dist/core/server/entries/operations/get.d.ts +1 -0
  93. package/dist/core/server/entries/operations/get.js +186 -191
  94. package/dist/core/server/entries/operations/update.d.ts +6 -7
  95. package/dist/core/server/entries/operations/update.js +20 -38
  96. package/dist/core/server/fields/populateEntry.js +16 -52
  97. package/dist/core/server/fields/resolveImageFields.js +69 -120
  98. package/dist/core/server/fields/resolveRelationFields.js +30 -51
  99. package/dist/core/server/fields/resolveRichtextLinks.js +46 -100
  100. package/dist/core/server/fields/resolveTypographyOrphans.bench.d.ts +1 -0
  101. package/dist/core/server/fields/resolveTypographyOrphans.bench.js +87 -0
  102. package/dist/core/server/fields/resolveTypographyOrphans.d.ts +3 -0
  103. package/dist/core/server/fields/resolveTypographyOrphans.js +128 -0
  104. package/dist/core/server/fields/resolveUrlFields.js +47 -56
  105. package/dist/core/server/fields/utils/fixOrphans.d.ts +5 -0
  106. package/dist/core/server/fields/utils/fixOrphans.js +12 -0
  107. package/dist/core/server/fields/utils/imageStyles.d.ts +4 -2
  108. package/dist/core/server/fields/utils/imageStyles.js +41 -25
  109. package/dist/core/server/fields/utils/resolveMedia.js +1 -6
  110. package/dist/core/server/forms/submissions/operations/delete.js +26 -2
  111. package/dist/core/server/forms/submissions/utils/parseMultipart.d.ts +2 -0
  112. package/dist/core/server/forms/submissions/utils/parseMultipart.js +75 -0
  113. package/dist/core/server/generator/fields.d.ts +6 -0
  114. package/dist/core/server/generator/fields.js +43 -5
  115. package/dist/core/server/generator/formFieldSchemaToString.js +10 -0
  116. package/dist/core/server/generator/formFields.js +1 -0
  117. package/dist/core/server/generator/generator.js +98 -30
  118. package/dist/core/server/media/operations/getFiles.d.ts +5 -0
  119. package/dist/core/server/media/operations/getFiles.js +6 -0
  120. package/dist/core/server/media/operations/uploadPrivateFile.d.ts +4 -0
  121. package/dist/core/server/media/operations/uploadPrivateFile.js +8 -0
  122. package/dist/core/server/media/styles/operations/batchGenerateStyles.d.ts +16 -0
  123. package/dist/core/server/media/styles/operations/batchGenerateStyles.js +144 -0
  124. package/dist/db-postgres/index.js +303 -37
  125. package/dist/db-postgres/schema/entry.d.ts +0 -94
  126. package/dist/db-postgres/schema/entry.js +0 -6
  127. package/dist/db-postgres/schema/entryVersion.d.ts +17 -0
  128. package/dist/db-postgres/schema/entryVersion.js +1 -0
  129. package/dist/entity/index.d.ts +9 -4
  130. package/dist/entity/index.js +24 -24
  131. package/dist/files-local/index.js +43 -0
  132. package/dist/paraglide/messages/_index.d.ts +36 -3
  133. package/dist/paraglide/messages/_index.js +71 -3
  134. package/dist/paraglide/messages/en.d.ts +5 -0
  135. package/dist/paraglide/messages/en.js +14 -0
  136. package/dist/paraglide/messages/pl.d.ts +5 -0
  137. package/dist/paraglide/messages/pl.js +14 -0
  138. package/dist/sveltekit/components/preview.svelte +2 -326
  139. package/dist/sveltekit/components/preview.svelte.d.ts +5 -16
  140. package/dist/sveltekit/server/index.d.ts +2 -1
  141. package/dist/sveltekit/server/index.js +2 -1
  142. package/dist/sveltekit/server/preview.js +4 -7
  143. package/dist/types/adapters/db.d.ts +15 -1
  144. package/dist/types/adapters/files.d.ts +6 -0
  145. package/dist/types/cms.d.ts +5 -0
  146. package/dist/types/entries.d.ts +54 -18
  147. package/dist/types/fields.d.ts +14 -24
  148. package/dist/types/formFields.d.ts +7 -2
  149. package/dist/types/index.d.ts +2 -2
  150. package/dist/types/layout.d.ts +0 -1
  151. package/dist/types/structured-content.d.ts +5 -0
  152. package/dist/updates/0.10.0/index.d.ts +2 -0
  153. package/dist/updates/0.10.0/index.js +15 -0
  154. package/dist/updates/0.11.0/index.d.ts +2 -0
  155. package/dist/updates/0.11.0/index.js +12 -0
  156. package/dist/updates/0.12.0/index.d.ts +2 -0
  157. package/dist/updates/0.12.0/index.js +12 -0
  158. package/dist/updates/0.13.0/index.d.ts +2 -0
  159. package/dist/updates/0.13.0/index.js +10 -0
  160. package/dist/updates/0.13.1/index.d.ts +2 -0
  161. package/dist/updates/0.13.1/index.js +20 -0
  162. package/dist/updates/0.7.3/index.d.ts +2 -0
  163. package/dist/updates/0.7.3/index.js +10 -0
  164. package/dist/updates/0.8.0/index.d.ts +2 -0
  165. package/dist/updates/0.8.0/index.js +18 -0
  166. package/dist/updates/0.8.0/migrate.d.ts +2 -0
  167. package/dist/updates/0.8.0/migrate.js +101 -0
  168. package/dist/updates/0.9.0/index.d.ts +2 -0
  169. package/dist/updates/0.9.0/index.js +38 -0
  170. package/dist/updates/index.js +9 -1
  171. package/package.json +7 -6
  172. package/dist/admin/components/fields/image-field.svelte +0 -198
  173. package/dist/admin/components/fields/image-field.svelte.d.ts +0 -8
  174. package/dist/admin/components/fields/richtext-field.svelte +0 -13
  175. package/dist/admin/components/fields/richtext-field.svelte.d.ts +0 -8
  176. package/dist/admin/components/tiptap.svelte +0 -11
  177. package/dist/admin/components/tiptap.svelte.d.ts +0 -6
  178. package/dist/core/server/entries/utils/getEntryTranslation.d.ts +0 -1
  179. package/dist/core/server/entries/utils/getEntryTranslation.js +0 -18
  180. package/dist/paraglide/messages/hello_world.d.ts +0 -5
  181. package/dist/paraglide/messages/hello_world.js +0 -33
  182. package/dist/paraglide/messages/login_hello.d.ts +0 -16
  183. package/dist/paraglide/messages/login_hello.js +0 -34
  184. package/dist/paraglide/messages/login_please_login.d.ts +0 -16
  185. package/dist/paraglide/messages/login_please_login.js +0 -34
@@ -1,9 +1,5 @@
1
- <script lang="ts" generics="T extends { data: PopulatedEntryData }">
2
- import type { PopulatedEntryData } from '../../types/entries.js';
1
+ <script lang="ts" generics="T extends Record<string, unknown>">
3
2
  import { onMount, type Snippet } from 'svelte';
4
- import { enableHybridEditing } from './hybrid-context.js';
5
-
6
- enableHybridEditing();
7
3
 
8
4
  type Props = {
9
5
  entry: T;
@@ -13,230 +9,10 @@
13
9
  let { entry, child }: Props = $props();
14
10
 
15
11
  let previewData: T = $state(entry);
16
- let hybridModeEnabled = $state(false);
17
- let highlightedPath = $state<string | null>(null);
18
12
  let parentOrigin: string | null = null;
19
13
 
20
- /**
21
- * Checks if a value looks like a UUID reference
22
- */
23
- function isUUID(value: unknown): value is string {
24
- if (typeof value !== 'string') return false;
25
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
26
- }
27
-
28
- /**
29
- * Checks if a value is a block with slug and data (e.g., {slug: "card", data: {...}})
30
- */
31
- function isBlock(value: unknown): value is { slug: string; data: Record<string, unknown> } {
32
- return (
33
- typeof value === 'object' &&
34
- value !== null &&
35
- 'slug' in value &&
36
- 'data' in value &&
37
- typeof (value as Record<string, unknown>).slug === 'string'
38
- );
39
- }
40
-
41
- /**
42
- * Checks if a value is a resolved media/file object with nested data
43
- * e.g., { data: { id: "...", url: "...", ... }, styles: {} }
44
- */
45
- function isResolvedMediaBlock(
46
- value: unknown
47
- ): value is { data: { id: string; [key: string]: unknown }; [key: string]: unknown } {
48
- return (
49
- typeof value === 'object' &&
50
- value !== null &&
51
- 'data' in value &&
52
- typeof (value as Record<string, unknown>).data === 'object' &&
53
- (value as Record<string, unknown>).data !== null &&
54
- 'id' in ((value as Record<string, unknown>).data as Record<string, unknown>) &&
55
- 'url' in ((value as Record<string, unknown>).data as Record<string, unknown>)
56
- );
57
- }
58
-
59
- /**
60
- * Checks if a value is a simple resolved reference (has id UUID at top level)
61
- * e.g., { id: "...", slug: "...", type: "collection" } or { id: "...", url: "...", width: 800 }
62
- */
63
- function isResolvedReference(
64
- value: unknown
65
- ): value is { id: string; [key: string]: unknown } {
66
- return (
67
- typeof value === 'object' &&
68
- value !== null &&
69
- 'id' in value &&
70
- typeof (value as Record<string, unknown>).id === 'string' &&
71
- isUUID((value as Record<string, unknown>).id)
72
- );
73
- }
74
-
75
- /**
76
- * Checks if a value looks like a localized object (has language keys like 'pl', 'en')
77
- */
78
- function isLocalizedObject(value: unknown): value is Record<string, unknown> {
79
- if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;
80
- const keys = Object.keys(value);
81
- // Common language codes
82
- const langCodes = ['pl', 'en', 'de', 'fr', 'es', 'it', 'uk', 'cs', 'sk'];
83
- return keys.length > 0 && keys.every((k) => langCodes.includes(k));
84
- }
85
-
86
- /**
87
- * Deep merges form data with original populated data, preserving resolved references
88
- * when the ID hasn't changed. This allows live preview to work without losing
89
- * populated image/file/relation data.
90
- */
91
- function deepMergeWithReferences(
92
- original: PopulatedEntryData,
93
- updates: Record<string, unknown>
94
- ): PopulatedEntryData {
95
- const result: PopulatedEntryData = { ...original };
96
-
97
- for (const key in updates) {
98
- const updateValue = updates[key];
99
- const originalValue = original[key];
100
-
101
- // Case 1: Update is a UUID string, original is a resolved media block { data: { id, url, ... }, styles: {} }
102
- if (isUUID(updateValue) && isResolvedMediaBlock(originalValue)) {
103
- if (originalValue.data.id === updateValue) {
104
- // Same ID - keep the fully populated original
105
- continue;
106
- } else {
107
- // Different ID - mark as pending
108
- result[key] = { data: { id: updateValue, _pending: true }, styles: {} };
109
- }
110
- continue;
111
- }
112
-
113
- // Case 2: Update is a UUID string, original is a simple resolved reference { id, url, ... }
114
- if (isUUID(updateValue) && isResolvedReference(originalValue)) {
115
- if (originalValue.id === updateValue) {
116
- // Same ID - keep the fully populated original
117
- continue;
118
- } else {
119
- // Different ID - mark as pending
120
- result[key] = { id: updateValue, _pending: true };
121
- }
122
- continue;
123
- }
124
-
125
- // Case 3: Both are arrays - merge based on index and slug matching for blocks
126
- if (Array.isArray(updateValue) && Array.isArray(originalValue)) {
127
- const mergedArray: unknown[] = [];
128
-
129
- for (let i = 0; i < updateValue.length; i++) {
130
- const updateItem = updateValue[i];
131
-
132
- // Handle UUID in array
133
- if (isUUID(updateItem)) {
134
- // Find matching resolved object in original by ID
135
- const matchingOriginal = originalValue.find((orig) => {
136
- if (isResolvedMediaBlock(orig)) return orig.data.id === updateItem;
137
- if (isResolvedReference(orig)) return orig.id === updateItem;
138
- return false;
139
- });
140
- if (matchingOriginal) {
141
- mergedArray.push(matchingOriginal);
142
- } else {
143
- mergedArray.push({ id: updateItem, _pending: true });
144
- }
145
- continue;
146
- }
147
-
148
- // Handle blocks with slug and data
149
- if (isBlock(updateItem)) {
150
- // Try to find matching block in original by same index and slug
151
- const originalItem = originalValue[i];
152
- if (isBlock(originalItem) && originalItem.slug === updateItem.slug) {
153
- // Recursively merge the data inside the block
154
- mergedArray.push({
155
- slug: updateItem.slug,
156
- data: deepMergeWithReferences(
157
- originalItem.data as PopulatedEntryData,
158
- updateItem.data
159
- )
160
- });
161
- } else {
162
- // No matching block, use update as-is
163
- mergedArray.push(updateItem);
164
- }
165
- continue;
166
- }
167
-
168
- // Handle other nested objects
169
- if (typeof updateItem === 'object' && updateItem !== null) {
170
- const originalItem = originalValue[i];
171
- if (typeof originalItem === 'object' && originalItem !== null) {
172
- mergedArray.push(
173
- deepMergeWithReferences(
174
- originalItem as PopulatedEntryData,
175
- updateItem as Record<string, unknown>
176
- )
177
- );
178
- } else {
179
- mergedArray.push(updateItem);
180
- }
181
- continue;
182
- }
183
-
184
- // Primitive value
185
- mergedArray.push(updateItem);
186
- }
187
-
188
- result[key] = mergedArray;
189
- continue;
190
- }
191
-
192
- // Case 4: Update is localized object {pl: "..."}, original is plain string
193
- // Use the current language value or first available
194
- if (isLocalizedObject(updateValue) && typeof originalValue === 'string') {
195
- const langs = Object.keys(updateValue);
196
- // Try 'pl' first, then first available
197
- result[key] = updateValue['pl'] ?? updateValue[langs[0]] ?? originalValue;
198
- continue;
199
- }
200
-
201
- // Case 5: Both are blocks with slug/data - merge recursively
202
- if (
203
- isBlock(updateValue) &&
204
- isBlock(originalValue) &&
205
- updateValue.slug === originalValue.slug
206
- ) {
207
- result[key] = {
208
- slug: updateValue.slug,
209
- data: deepMergeWithReferences(originalValue.data as PopulatedEntryData, updateValue.data)
210
- };
211
- continue;
212
- }
213
-
214
- // Case 6: Both are nested objects (but not arrays) - recursively merge
215
- if (
216
- typeof updateValue === 'object' &&
217
- updateValue !== null &&
218
- !Array.isArray(updateValue) &&
219
- typeof originalValue === 'object' &&
220
- originalValue !== null &&
221
- !Array.isArray(originalValue)
222
- ) {
223
- result[key] = deepMergeWithReferences(
224
- originalValue as PopulatedEntryData,
225
- updateValue as Record<string, unknown>
226
- );
227
- continue;
228
- }
229
-
230
- // Case 7: Simple value replacement (strings, numbers, booleans, etc.)
231
- result[key] = updateValue;
232
- }
233
-
234
- return result;
235
- }
236
-
237
14
  onMount(() => {
238
15
  function handleMessage(e: MessageEvent) {
239
- // Accept first message from any origin (handshake), then lock to that origin
240
16
  if (!parentOrigin) {
241
17
  parentOrigin = e.origin;
242
18
  } else if (e.origin !== parentOrigin) {
@@ -244,103 +20,17 @@
244
20
  }
245
21
 
246
22
  if (e.data.type === 'preview-update') {
247
- // Merge form data with current preview data, preserving resolved references
248
- previewData = {
249
- ...previewData,
250
- data: deepMergeWithReferences(previewData.data as PopulatedEntryData, e.data.data)
251
- } as T;
252
- }
253
-
254
- if (e.data.type === 'hybrid-mode-enable') {
255
- hybridModeEnabled = e.data.enabled;
256
- updateHybridClickHandlers();
257
- }
258
-
259
- // Highlight from form focus
260
- if (e.data.type === 'hybrid-highlight') {
261
- highlightedPath = e.data.path;
262
- updateHighlight();
23
+ previewData = { ...previewData, ...e.data.data } as T;
263
24
  }
264
25
  }
265
26
 
266
27
  window.addEventListener('message', handleMessage);
267
- // Block link clicks in hybrid mode
268
- document.addEventListener('click', handleLinkClick, true);
269
-
270
- // Signal to parent that preview is ready to receive data
271
28
  window.parent.postMessage({ type: 'preview-ready' }, parentOrigin || '*');
272
29
 
273
30
  return () => {
274
31
  window.removeEventListener('message', handleMessage);
275
- document.removeEventListener('click', handleLinkClick, true);
276
32
  };
277
33
  });
278
-
279
- function updateHybridClickHandlers() {
280
- // Remove existing handlers and styles
281
- document.querySelectorAll('[data-hybrid-path]').forEach((el) => {
282
- el.removeEventListener('click', handleHybridClick as EventListener);
283
- (el as HTMLElement).classList.remove('hybrid-editable');
284
- });
285
-
286
- if (hybridModeEnabled) {
287
- // Add click handlers and styles for hybrid elements
288
- document.querySelectorAll('[data-hybrid-path]').forEach((el) => {
289
- el.addEventListener('click', handleHybridClick as EventListener);
290
- (el as HTMLElement).classList.add('hybrid-editable');
291
- });
292
- }
293
- }
294
-
295
- // Block all link navigation when in hybrid mode
296
- function handleLinkClick(e: MouseEvent) {
297
- if (!hybridModeEnabled) return;
298
-
299
- const link = (e.target as HTMLElement).closest('a');
300
- if (link && link.href) {
301
- e.preventDefault();
302
- e.stopPropagation();
303
- }
304
- }
305
-
306
- function updateHighlight() {
307
- // Remove all highlights
308
- document.querySelectorAll('[data-hybrid-path]').forEach((el) => {
309
- (el as HTMLElement).classList.remove('hybrid-highlighted');
310
- });
311
-
312
- // Add highlight to matching element
313
- if (highlightedPath) {
314
- const el = document.querySelector(`[data-hybrid-path="${highlightedPath}"]`) as HTMLElement;
315
- if (el) {
316
- el.classList.add('hybrid-highlighted');
317
- }
318
- }
319
- }
320
-
321
- function handleHybridClick(e: MouseEvent) {
322
- e.preventDefault();
323
- e.stopPropagation();
324
-
325
- // Find the closest element with data-hybrid-path
326
- const target = (e.target as HTMLElement).closest('[data-hybrid-path]') as HTMLElement;
327
- if (!target) return;
328
-
329
- const path = target.dataset.hybridPath;
330
- if (path) {
331
- // Normalize to dot notation (hero[0].data → hero.0.data)
332
- const normalized = path.replace(/\[(\d+)\]/g, '.$1');
333
- window.parent.postMessage({ type: 'hybrid-focus', path: normalized }, parentOrigin || '*');
334
- }
335
- }
336
-
337
- // Re-apply handlers when previewData changes
338
- $effect(() => {
339
- if (previewData && hybridModeEnabled) {
340
- // Small delay to ensure DOM is updated
341
- setTimeout(updateHybridClickHandlers, 100);
342
- }
343
- });
344
34
  </script>
345
35
 
346
36
  {#if child}
@@ -350,17 +40,3 @@
350
40
  {:else}
351
41
  <pre>{JSON.stringify(previewData, null, 2)}</pre>
352
42
  {/if}
353
-
354
- <style>
355
- :global(.hybrid-editable) {
356
- cursor: pointer;
357
- outline: 2px dashed transparent;
358
- outline-offset: 4px;
359
- transition: outline-color 0.15s ease;
360
- }
361
-
362
- :global(.hybrid-editable:hover),
363
- :global(.hybrid-highlighted) {
364
- outline-color: #3b82f6;
365
- }
366
- </style>
@@ -1,8 +1,5 @@
1
- import type { PopulatedEntryData } from '../../types/entries.js';
2
1
  import { type Snippet } from 'svelte';
3
- declare function $$render<T extends {
4
- data: PopulatedEntryData;
5
- }>(): {
2
+ declare function $$render<T extends Record<string, unknown>>(): {
6
3
  props: {
7
4
  entry: T;
8
5
  child?: Snippet<[{
@@ -14,9 +11,7 @@ declare function $$render<T extends {
14
11
  slots: {};
15
12
  events: {};
16
13
  };
17
- declare class __sveltets_Render<T extends {
18
- data: PopulatedEntryData;
19
- }> {
14
+ declare class __sveltets_Render<T extends Record<string, unknown>> {
20
15
  props(): ReturnType<typeof $$render<T>>['props'];
21
16
  events(): ReturnType<typeof $$render<T>>['events'];
22
17
  slots(): ReturnType<typeof $$render<T>>['slots'];
@@ -24,18 +19,12 @@ declare class __sveltets_Render<T extends {
24
19
  exports(): {};
25
20
  }
26
21
  interface $$IsomorphicComponent {
27
- new <T extends {
28
- data: PopulatedEntryData;
29
- }>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
22
+ new <T extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
30
23
  $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
31
24
  } & ReturnType<__sveltets_Render<T>['exports']>;
32
- <T extends {
33
- data: PopulatedEntryData;
34
- }>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
25
+ <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
35
26
  z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
36
27
  }
37
28
  declare const Preview: $$IsomorphicComponent;
38
- type Preview<T extends {
39
- data: PopulatedEntryData;
40
- }> = InstanceType<typeof Preview<T>>;
29
+ type Preview<T extends Record<string, unknown>> = InstanceType<typeof Preview<T>>;
41
30
  export default Preview;
@@ -1,5 +1,6 @@
1
1
  export { includioCMS } from './handle.js';
2
- export { getEntry, getEntries } from '../../core/server/entries/operations/get.js';
2
+ export { getEntry, getEntries, countEntries } from '../../core/server/entries/operations/get.js';
3
3
  export { createFormSubmission } from '../../core/server/forms/submissions/operations/create.js';
4
+ export { parseFormDataForSubmission } from '../../core/server/forms/submissions/utils/parseMultipart.js';
4
5
  export { createConsentLog } from '../../core/server/consentLogs/operations/create.js';
5
6
  export { getPreviewEntry } from './preview.js';
@@ -1,5 +1,6 @@
1
1
  export { includioCMS } from './handle.js';
2
- export { getEntry, getEntries } from '../../core/server/entries/operations/get.js';
2
+ export { getEntry, getEntries, countEntries } from '../../core/server/entries/operations/get.js';
3
3
  export { createFormSubmission } from '../../core/server/forms/submissions/operations/create.js';
4
+ export { parseFormDataForSubmission } from '../../core/server/forms/submissions/utils/parseMultipart.js';
4
5
  export { createConsentLog } from '../../core/server/consentLogs/operations/create.js';
5
6
  export { getPreviewEntry } from './preview.js';
@@ -9,11 +9,8 @@ export async function getPreviewEntry(event, options) {
9
9
  if (!authLocals.user || !authLocals.session) {
10
10
  throw new Error('Unauthorized');
11
11
  }
12
- if (preview) {
13
- return getEntryVersion({
14
- id: preview,
15
- language: options.language
16
- });
17
- }
18
- return null;
12
+ return getEntryVersion({
13
+ id: preview,
14
+ language: options.language
15
+ });
19
16
  }
@@ -1,5 +1,5 @@
1
1
  import type { ConsentLogData } from '../consent.js';
2
- import type { DbEntry, DbEntryInsert, DbEntryVersion, DbEntryVersionInsert, GetDbEntriesOptions, GetDbEntryVersionsOptions, PaginationOptions } from '../entries.js';
2
+ import type { DbEntry, DbEntryInsert, DbEntryVersion, DbEntryVersionInsert, GetDbEntriesOptions, GetDbEntryVersionsOptions, GetPaginatedEntriesOptions, PaginatedEntryRow, PaginationOptions } from '../entries.js';
3
3
  import type { ImageFieldStyle } from '../fields.js';
4
4
  import type { FormSubmission } from '../forms.js';
5
5
  import type { ImageStyle, MediaFile, MediaTag, UploadedMediaFile } from '../media.js';
@@ -20,6 +20,8 @@ export interface DatabaseAdapter {
20
20
  getFormSubmission: GetFormSubmission;
21
21
  updateFormSubmission: UpdateFormSubmission;
22
22
  deleteFormSubmission: DeleteFormSubmission;
23
+ getPaginatedEntries?: GetPaginatedEntries;
24
+ countPaginatedEntries?: CountPaginatedEntries;
23
25
  createConsentLog: CreateConsentLog;
24
26
  getMediaTags: GetMediaTags;
25
27
  createMediaTag: CreateMediaTag;
@@ -35,6 +37,8 @@ export interface DatabaseAdapter {
35
37
  getAllImageStyles: GetAllImageStyles;
36
38
  deleteAllImageStyles: DeleteAllImageStyles;
37
39
  getMediaFiles: GetMediaFiles;
40
+ countMediaFiles: CountMediaFiles;
41
+ getMediaTagsWithCounts: GetMediaTagsWithCounts;
38
42
  getMediaFile: GetMediaFile;
39
43
  updateMediaFile: UpdateMediaFile;
40
44
  deleteMediaFile: DeleteMediaFile;
@@ -49,6 +53,8 @@ export type UpdateEntry = (data: {
49
53
  export type DeleteEntry = (data: {
50
54
  id: string;
51
55
  }) => Promise<void>;
56
+ export type GetPaginatedEntries = (data: GetPaginatedEntriesOptions) => Promise<PaginatedEntryRow[]>;
57
+ export type CountPaginatedEntries = (data: Omit<GetPaginatedEntriesOptions, 'limit' | 'offset' | 'orderBy'>) => Promise<number>;
52
58
  export type ArchiveEntry = (data: {
53
59
  id: string;
54
60
  archivedAt?: Date;
@@ -80,9 +86,17 @@ export interface GetMediaFilesOptions {
80
86
  ids?: string[];
81
87
  mimeTypes?: string[];
82
88
  search?: string;
89
+ untagged?: boolean;
90
+ limit?: number;
91
+ offset?: number;
83
92
  };
84
93
  }
85
94
  export type GetMediaFiles = (data: GetMediaFilesOptions) => Promise<MediaFile[]>;
95
+ export type CountMediaFiles = (data: GetMediaFilesOptions) => Promise<number>;
96
+ export type GetMediaTagsWithCounts = () => Promise<{
97
+ tag: MediaTag;
98
+ count: number;
99
+ }[]>;
86
100
  export interface GetMediaFileOptions {
87
101
  data: {
88
102
  id?: string;
@@ -5,6 +5,12 @@ export interface FilesAdapter {
5
5
  renameFile: RenameFile;
6
6
  deleteFile: DeleteFile;
7
7
  listFiles: ListFiles;
8
+ uploadPrivateFile?: (file: File) => Promise<{
9
+ filename: string;
10
+ url: string;
11
+ }>;
12
+ downloadPrivateFile?: (filename: string) => Promise<File | null>;
13
+ deletePrivateFile?: (filename: string) => Promise<void>;
8
14
  }
9
15
  export type DownloadFile = (id: string) => Promise<File | null>;
10
16
  export type UploadFile = (file: File) => Promise<UploadedMediaFile>;
@@ -36,6 +36,9 @@ export interface ApiKeyConfig {
36
36
  name?: string;
37
37
  role?: 'admin' | 'editor';
38
38
  }
39
+ export interface TypographyConfig {
40
+ fixOrphans?: boolean;
41
+ }
39
42
  export interface CMSConfig {
40
43
  languages: Language[];
41
44
  collections?: CollectionConfig[];
@@ -49,6 +52,7 @@ export interface CMSConfig {
49
52
  ai?: AIAdapter;
50
53
  media?: MediaConfig;
51
54
  apiKeys?: ApiKeyConfig[];
55
+ typography?: TypographyConfig;
52
56
  }
53
57
  export interface ICMS {
54
58
  collections: Record<string, CollectionConfigWithType>;
@@ -64,4 +68,5 @@ export interface ICMS {
64
68
  aiAdapter: AIAdapter | null;
65
69
  mediaConfig: MediaConfig;
66
70
  apiKeys: ApiKeyConfig[];
71
+ typographyConfig: TypographyConfig;
67
72
  }
@@ -12,44 +12,40 @@ export interface DbEntry {
12
12
  id: string;
13
13
  slug: string;
14
14
  type: EntryType;
15
- availableLocales: string[] | null;
16
15
  createdAt: Date;
17
16
  updatedAt: Date;
18
17
  archivedAt: Date | null;
19
- publishedAt: Date | null;
20
- publishedVersionId: string | null;
21
- publishedBy: string | null;
22
18
  sortOrder: number | null;
23
19
  }
24
20
  export interface DbEntryInsert {
25
21
  slug: string;
26
22
  type: EntryType;
27
- availableLocales?: string[] | null;
28
23
  createdAt?: Date;
29
24
  updatedAt?: Date;
30
25
  archivedAt?: Date | null;
31
- publishedAt?: Date | null;
32
- publishedVersionId?: string | null;
33
- publishedBy?: string | null;
34
26
  sortOrder?: number | null;
35
27
  }
36
28
  export interface RawEntry extends DbEntry {
37
29
  collection: CollectionConfigWithType | SingleConfigWithType;
38
30
  versions: DbEntryVersion[];
39
- publishedVersion: DbEntryVersion | null;
40
- scheduledVersion: DbEntryVersion | null;
41
- draftVersion: DbEntryVersion | null;
42
- }
43
- export interface Entry {
44
- id: string;
45
- data: PopulatedEntryData;
46
- slug: string;
47
- type: EntryType;
48
- publishedAt?: Date | null;
31
+ /** Per-language published versions */
32
+ publishedVersions: Record<string, DbEntryVersion | null>;
33
+ /** Per-language scheduled versions */
34
+ scheduledVersions: Record<string, DbEntryVersion | null>;
35
+ /** Per-language latest draft versions */
36
+ draftVersions: Record<string, DbEntryVersion | null>;
49
37
  }
38
+ export type Entry = {
39
+ _id: string;
40
+ _slug: string;
41
+ _type: EntryType;
42
+ _publishedAt?: Date | null;
43
+ _url?: string;
44
+ } & Record<string, unknown>;
50
45
  export interface DbEntryVersion {
51
46
  id: string;
52
47
  entryId: string;
48
+ lang: string;
53
49
  versionNumber: number;
54
50
  data: EntryData;
55
51
  createdAt: Date;
@@ -59,6 +55,7 @@ export interface DbEntryVersion {
59
55
  }
60
56
  export interface DbEntryVersionInsert {
61
57
  entryId: string;
58
+ lang: string;
62
59
  versionNumber?: number;
63
60
  data: EntryData;
64
61
  createdAt?: Date;
@@ -73,6 +70,10 @@ export interface PaginationOptions {
73
70
  column: 'createdAt' | 'updatedAt' | 'sortOrder';
74
71
  direction: 'asc' | 'desc';
75
72
  };
73
+ dataOrderBy?: {
74
+ field: string;
75
+ direction: 'asc' | 'desc';
76
+ };
76
77
  }
77
78
  export interface GetDbEntriesOptions extends PaginationOptions {
78
79
  ids?: string[];
@@ -103,30 +104,65 @@ export interface GetEntriesOptions {
103
104
  slug?: string;
104
105
  dataValues?: Record<string, unknown>;
105
106
  dataLike?: Record<string, unknown>;
107
+ dataILikeOr?: Record<string, unknown>;
106
108
  language?: string;
107
109
  status?: EntryStatus;
108
110
  orderBy?: {
109
111
  column: 'createdAt' | 'updatedAt' | 'sortOrder';
110
112
  direction: 'asc' | 'desc';
111
113
  };
114
+ dataOrderBy?: {
115
+ field: string;
116
+ direction: 'asc' | 'desc';
117
+ };
118
+ limit?: number;
119
+ offset?: number;
112
120
  }
113
121
  export interface GetEntryOptions {
114
122
  id?: string;
115
123
  slug?: string;
116
124
  dataValues?: Record<string, unknown>;
117
125
  dataLike?: Record<string, unknown>;
126
+ dataILikeOr?: Record<string, unknown>;
118
127
  language?: string;
119
128
  status?: EntryStatus;
120
129
  }
121
130
  export interface GetDbEntryVersionsOptions {
122
131
  ids?: string[];
123
132
  entryIds?: string[];
133
+ lang?: string;
124
134
  dataValues?: Record<string, unknown>;
125
135
  dataLike?: Record<string, unknown>;
136
+ dataILikeOr?: Record<string, unknown>;
126
137
  }
127
138
  export interface GetDbEntryVersionOptions {
128
139
  id?: string;
129
140
  entryId?: string;
141
+ lang?: string;
130
142
  dataValues?: Record<string, unknown>;
131
143
  dataLike?: Record<string, unknown>;
144
+ dataILikeOr?: Record<string, unknown>;
145
+ }
146
+ export interface GetPaginatedEntriesOptions {
147
+ slug?: string;
148
+ ids?: string[];
149
+ language: string;
150
+ status: EntryStatus;
151
+ dataValues?: Record<string, unknown>;
152
+ dataLike?: Record<string, unknown>;
153
+ dataILikeOr?: Record<string, unknown>;
154
+ orderBy?: {
155
+ column: 'createdAt' | 'updatedAt' | 'sortOrder';
156
+ direction: 'asc' | 'desc';
157
+ };
158
+ dataOrderBy?: {
159
+ field: string;
160
+ direction: 'asc' | 'desc';
161
+ };
162
+ limit: number;
163
+ offset: number;
164
+ }
165
+ export interface PaginatedEntryRow {
166
+ entry: DbEntry;
167
+ version: DbEntryVersion;
132
168
  }