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
@@ -5,6 +5,11 @@
5
5
  import AlignCenter from '@tabler/icons-svelte/icons/align-center';
6
6
  import AlignRight from '@tabler/icons-svelte/icons/align-right';
7
7
  import AlertTriangle from '@tabler/icons-svelte/icons/alert-triangle';
8
+ import { tiptapLang } from './lang.js';
9
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
10
+
11
+ const interfaceLanguage = useInterfaceLanguage();
12
+ const t = $derived(tiptapLang[interfaceLanguage.current]);
8
13
 
9
14
  let { node, updateAttributes, selected }: NodeViewProps = $props();
10
15
 
@@ -91,8 +96,8 @@
91
96
  class="toolbar-btn"
92
97
  class:active={node.attrs.align === 'left'}
93
98
  onclick={() => setAlign('left')}
94
- aria-label="Wyrównaj do lewej"
95
- title="Wyrównaj do lewej"
99
+ aria-label={t.alignLeftLabel}
100
+ title={t.alignLeftLabel}
96
101
  >
97
102
  <AlignLeft size={16} />
98
103
  </button>
@@ -101,8 +106,8 @@
101
106
  class="toolbar-btn"
102
107
  class:active={node.attrs.align === 'center'}
103
108
  onclick={() => setAlign('center')}
104
- aria-label="Wyśrodkuj"
105
- title="Wyśrodkuj"
109
+ aria-label={t.alignCenterLabel}
110
+ title={t.alignCenterLabel}
106
111
  >
107
112
  <AlignCenter size={16} />
108
113
  </button>
@@ -111,8 +116,8 @@
111
116
  class="toolbar-btn"
112
117
  class:active={node.attrs.align === 'right'}
113
118
  onclick={() => setAlign('right')}
114
- aria-label="Wyrównaj do prawej"
115
- title="Wyrównaj do prawej"
119
+ aria-label={t.alignRightLabel}
120
+ title={t.alignRightLabel}
116
121
  >
117
122
  <AlignRight size={16} />
118
123
  </button>
@@ -131,7 +136,7 @@
131
136
  bind:value={altInput}
132
137
  onblur={saveAlt}
133
138
  onkeydown={handleAltKeydown}
134
- placeholder="Opis zdjęcia..."
139
+ placeholder={t.imageAltPlaceholder}
135
140
  />
136
141
  {:else if hasAlt}
137
142
  <button type="button" class="figure-alt-display" onclick={startEditAlt}>
@@ -140,7 +145,7 @@
140
145
  {:else}
141
146
  <button type="button" class="figure-alt-display figure-alt-missing" onclick={startEditAlt}>
142
147
  <AlertTriangle size={14} />
143
- <span>Dodaj opis zdjęcia, żeby każdy mógł je zrozumieć</span>
148
+ <span>{t.addAltMessage}</span>
144
149
  </button>
145
150
  {/if}
146
151
  </div>
@@ -154,11 +159,11 @@
154
159
  bind:value={captionInput}
155
160
  onblur={saveCaption}
156
161
  onkeydown={handleKeydown}
157
- placeholder="Dodaj podpis..."
162
+ placeholder={t.captionPlaceholder}
158
163
  />
159
164
  {:else}
160
165
  <button type="button" class="caption-display" ondblclick={() => (editing = true)}>
161
- {node.attrs.caption || 'Kliknij dwukrotnie, by dodać podpis'}
166
+ {node.attrs.caption || t.captionDefault}
162
167
  </button>
163
168
  {/if}
164
169
  </div>
@@ -26,114 +26,44 @@
26
26
 
27
27
  const SKIP_TYPES: Set<FieldType> = new Set(['slug', 'seo']);
28
28
 
29
+ /** Ensure blockData has default values for all fields (no per-lang wrapping — data is flat) */
29
30
  function normalizeBlockData(
30
31
  data: Record<string, unknown>,
31
- fields: Field[],
32
- langs: string[]
32
+ fields: Field[]
33
33
  ): Record<string, unknown> {
34
34
  const result = { ...data };
35
35
  for (const f of fields) {
36
36
  const key = f.slug;
37
- if (['text', 'richtext', 'content'].includes(f.type)) {
38
- const val = result[key];
39
- const needsWrap =
40
- val == null ||
41
- typeof val !== 'object' ||
42
- (f.type === 'content' && 'type' in (val as Record<string, unknown>));
43
- if (needsWrap) {
44
- const fallback = val ?? (f.type === 'content' ? null : '');
45
- result[key] = Object.fromEntries(langs.map((l) => [l, fallback]));
46
- }
37
+ if (['text', 'richtext'].includes(f.type)) {
38
+ if (result[key] == null) result[key] = '';
39
+ } else if (f.type === 'content') {
40
+ // Leave as-is (null or doc)
47
41
  } else if (f.type === 'url') {
48
42
  const v = result[key] as Record<string, unknown> | undefined;
49
43
  if (!v || typeof v !== 'object') {
50
- result[key] = { id: '', url: {} };
51
- } else if (!('url' in v) || v.url == null) {
52
- result[key] = { ...v, url: {} };
53
- } else if (typeof v.url === 'string') {
54
- const urlStr = v.url as string;
55
- const textStr = typeof v.text === 'string' ? v.text : '';
56
- result[key] = {
57
- ...v,
58
- url: Object.fromEntries(langs.map((l) => [l, urlStr])),
59
- text: Object.fromEntries(langs.map((l) => [l, textStr]))
60
- };
44
+ result[key] = { id: '', url: '' };
45
+ } else {
46
+ if (typeof v.url !== 'string') v.url = '';
61
47
  }
62
48
  const urlField = f as import('../../../types/fields.js').UrlField;
63
49
  const d = result[key] as Record<string, unknown>;
64
- if (urlField.text && !d.text) d.text = {};
50
+ if (urlField.text && d.text == null) d.text = '';
65
51
  if (urlField.newTab && d.newTab === undefined) d.newTab = false;
66
52
  if (urlField.rel && d.rel === undefined) d.rel = '';
67
53
  } else if (f.type === 'blocks') {
68
54
  const bf = f as BlocksField;
69
55
  if (Array.isArray(result[key])) {
70
- result[key] = (result[key] as any[]).map((item) => ({
71
- ...item,
72
- data: normalizeBlockData(
73
- item.data ?? {},
74
- bf.of.find((d: ObjectField) => d.slug === item.slug)?.fields ?? [],
75
- langs
56
+ result[key] = (result[key] as any[]).map((item) =>
57
+ normalizeBlockData(
58
+ item,
59
+ bf.of.find((d: ObjectField) => d.slug === item._slug)?.fields ?? []
76
60
  )
77
- }));
61
+ );
78
62
  }
79
63
  } else if (f.type === 'object') {
80
64
  const of_ = f as ObjectField;
81
- if (result[key] && typeof result[key] === 'object' && 'data' in (result[key] as object)) {
82
- const obj = result[key] as Record<string, unknown>;
83
- result[key] = { ...obj, data: normalizeBlockData(obj.data as Record<string, unknown>, of_.fields, langs) };
84
- }
85
- }
86
- }
87
- return result;
88
- }
89
-
90
- function denormalizeBlockData(
91
- data: Record<string, unknown>,
92
- fields: Field[],
93
- currentLang: string
94
- ): Record<string, unknown> {
95
- const result = { ...data };
96
- for (const f of fields) {
97
- const key = f.slug;
98
- const val = result[key];
99
- if (['text', 'richtext', 'content'].includes(f.type)) {
100
- if (val && typeof val === 'object' && !Array.isArray(val)) {
101
- result[key] = (val as Record<string, unknown>)[currentLang] ?? (f.type === 'content' ? null : '');
102
- }
103
- } else if (f.type === 'url' && val && typeof val === 'object') {
104
- const v = val as Record<string, unknown>;
105
- const flat: Record<string, unknown> = {};
106
- if (v.id !== undefined) flat.id = v.id;
107
- flat.url =
108
- v.url && typeof v.url === 'object'
109
- ? (v.url as Record<string, string>)[currentLang] ?? ''
110
- : v.url ?? '';
111
- if ('text' in v) {
112
- flat.text =
113
- v.text && typeof v.text === 'object'
114
- ? (v.text as Record<string, string>)[currentLang] ?? ''
115
- : v.text ?? '';
116
- }
117
- if (v.newTab !== undefined) flat.newTab = v.newTab;
118
- if (v.rel !== undefined) flat.rel = v.rel;
119
- result[key] = flat;
120
- } else if (f.type === 'blocks') {
121
- const bf = f as BlocksField;
122
- if (Array.isArray(val)) {
123
- result[key] = (val as any[]).map((item) => ({
124
- ...item,
125
- data: denormalizeBlockData(
126
- item.data ?? {},
127
- bf.of.find((d: ObjectField) => d.slug === item.slug)?.fields ?? [],
128
- currentLang
129
- )
130
- }));
131
- }
132
- } else if (f.type === 'object') {
133
- const of_ = f as ObjectField;
134
- if (val && typeof val === 'object' && 'data' in (val as object)) {
135
- const obj = val as Record<string, unknown>;
136
- result[key] = { ...obj, data: denormalizeBlockData(obj.data as Record<string, unknown>, of_.fields, currentLang) };
65
+ if (result[key] && typeof result[key] === 'object') {
66
+ result[key] = normalizeBlockData(result[key] as Record<string, unknown>, of_.fields);
137
67
  }
138
68
  }
139
69
  }
@@ -145,9 +75,27 @@
145
75
  );
146
76
 
147
77
  const blockDef = $derived(inlineBlocks.find((b) => b.slug === node.attrs.blockType));
148
- const blockLabel = $derived(blockDef?.label ? (Object.values(blockDef.label)[0] ?? blockDef.slug) : node.attrs.blockType);
78
+ const blockLabel = $derived(blockDef?.label ? (typeof blockDef.label === 'string' ? blockDef.label : Object.values(blockDef.label)[0] ?? blockDef.slug) : node.attrs.blockType);
149
79
  const supportedFields = $derived(blockDef?.fields.filter((f) => !SKIP_TYPES.has(f.type)) ?? []);
150
80
 
81
+ const accordionLabel = $derived.by(() => {
82
+ const field = blockDef?.accordionLabelField;
83
+ if (!field || typeof field !== 'string' || !field.trim()) return '';
84
+ const val = $formStore[field] as string | Record<string, string> | undefined;
85
+ if (!val) return '';
86
+ if (typeof val === 'string') return val.trim();
87
+ if (typeof val === 'object') {
88
+ if ('url' in val) {
89
+ const urlData = val as { url: string | Record<string, string>; text?: string | Record<string, string> };
90
+ const display = urlData.text || urlData.url;
91
+ if (typeof display === 'string') return display.trim();
92
+ if (typeof display === 'object' && display !== null) return display[contentLanguage.current] ?? '';
93
+ }
94
+ return (val as Record<string, string>)[contentLanguage.current] ?? '';
95
+ }
96
+ return '';
97
+ });
98
+
151
99
  function parseBlockData(raw: unknown): Record<string, unknown> {
152
100
  if (typeof raw === 'string') {
153
101
  try { return JSON.parse(raw); } catch { return {}; }
@@ -156,9 +104,8 @@
156
104
  return {};
157
105
  }
158
106
 
159
- const langs = contentLanguage.all;
160
107
  const allFields = blockDef?.fields ?? [];
161
- const standaloneForm = createStandaloneForm(normalizeBlockData(parseBlockData(node.attrs.blockData), allFields, langs));
108
+ const standaloneForm = createStandaloneForm(normalizeBlockData(parseBlockData(node.attrs.blockData), allFields));
162
109
  const formStore = standaloneForm.form;
163
110
 
164
111
  // Track last JSON we wrote to PM to avoid re-parsing our own writes
@@ -169,8 +116,7 @@
169
116
  const unsubscribe = formStore.subscribe((data) => {
170
117
  if (debounceTimer) clearTimeout(debounceTimer);
171
118
  debounceTimer = setTimeout(() => {
172
- const denormalized = denormalizeBlockData(data, allFields, contentLanguage.current);
173
- const json = JSON.stringify(denormalized);
119
+ const json = JSON.stringify(data);
174
120
  if (json !== lastWrittenJson) {
175
121
  lastWrittenJson = json;
176
122
  updateAttributes({ blockData: json });
@@ -188,7 +134,7 @@
188
134
  const raw = node.attrs.blockData;
189
135
  const rawJson = typeof raw === 'string' ? raw : JSON.stringify(raw);
190
136
  if (rawJson !== lastWrittenJson) {
191
- formStore.set(normalizeBlockData(parseBlockData(raw), allFields, langs));
137
+ formStore.set(normalizeBlockData(parseBlockData(raw), allFields));
192
138
  }
193
139
  });
194
140
 
@@ -264,9 +210,12 @@
264
210
  class="inline-block-collapse-toggle"
265
211
  onclick={() => (collapsed = !collapsed)}
266
212
  aria-expanded={!collapsed}
267
- aria-label={collapsed ? 'Rozwiń blok' : 'Zwiń blok'}
213
+ aria-label={collapsed ? `Rozwiń blok ${blockLabel}${accordionLabel ? ` — ${accordionLabel}` : ''}` : `Zwiń blok ${blockLabel}${accordionLabel ? ` — ${accordionLabel}` : ''}`}
268
214
  >
269
215
  <span class="inline-block-label">{blockLabel}</span>
216
+ {#if accordionLabel}
217
+ <span class="inline-block-accordion-label">— {accordionLabel}</span>
218
+ {/if}
270
219
  <span class="collapse-chevron" class:collapsed>
271
220
  <ChevronDown size={14} />
272
221
  </span>
@@ -370,6 +319,16 @@
370
319
  color: var(--foreground);
371
320
  }
372
321
 
322
+ .inline-block-accordion-label {
323
+ font-size: 0.8125rem;
324
+ font-weight: 400;
325
+ color: var(--muted-foreground);
326
+ overflow: hidden;
327
+ text-overflow: ellipsis;
328
+ white-space: nowrap;
329
+ max-width: 200px;
330
+ }
331
+
373
332
  .collapse-chevron {
374
333
  display: flex;
375
334
  align-items: center;
@@ -1,6 +1,11 @@
1
1
  <script lang="ts">
2
2
  import type { SlashCommandItem } from './slash-command.js';
3
3
  import { onMount } from 'svelte';
4
+ import { tiptapLang } from './lang.js';
5
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
6
+
7
+ const interfaceLanguage = useInterfaceLanguage();
8
+ const t = $derived(tiptapLang[interfaceLanguage.current]);
4
9
 
5
10
  type Props = {
6
11
  items: SlashCommandItem[];
@@ -79,9 +84,9 @@
79
84
  });
80
85
  </script>
81
86
 
82
- <div class="slash-popup" bind:this={listEl} data-slash-popup role="listbox" aria-label="Polecenia">
87
+ <div class="slash-popup" bind:this={listEl} data-slash-popup role="listbox" aria-label={t.commandsLabel}>
83
88
  {#if items.length === 0}
84
- <div class="slash-empty">Brak wyników</div>
89
+ <div class="slash-empty">{t.noResults}</div>
85
90
  {:else}
86
91
  {@const groups = grouped()}
87
92
  {#each [...groups.entries()] as [group, groupItems]}
@@ -114,7 +119,7 @@
114
119
  class="slash-more-btn"
115
120
  onclick={() => (showTier2 = true)}
116
121
  >
117
- Więcej...
122
+ {t.more}
118
123
  </button>
119
124
  {/if}
120
125
  {/if}
@@ -3,6 +3,11 @@
3
3
  import ToolbarButton from './toolbar-button.svelte';
4
4
  import * as Tooltip from '../../../components/ui/tooltip/index.js';
5
5
  import Separator from '../../../components/ui/separator/separator.svelte';
6
+ import { tiptapLang } from './lang.js';
7
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
8
+
9
+ const interfaceLanguage = useInterfaceLanguage();
10
+ const t = $derived(tiptapLang[interfaceLanguage.current]);
6
11
 
7
12
  // Icons
8
13
  import H_2 from '@tabler/icons-svelte/icons/h-2';
@@ -60,21 +65,21 @@
60
65
  <div class="flex flex-wrap items-center gap-0.5 border-b p-1 sticky top-0 z-10 bg-muted/50">
61
66
  <!-- Headings -->
62
67
  <ToolbarButton
63
- label="Nagłówek 2"
68
+ label={t.heading2}
64
69
  active={ed.isActive('heading', { level: 2 })}
65
70
  onclick={() => ed.chain().focus().toggleHeading({ level: 2 }).run()}
66
71
  >
67
72
  <H_2 class="h-4 w-4" />
68
73
  </ToolbarButton>
69
74
  <ToolbarButton
70
- label="Nagłówek 3"
75
+ label={t.heading3}
71
76
  active={ed.isActive('heading', { level: 3 })}
72
77
  onclick={() => ed.chain().focus().toggleHeading({ level: 3 }).run()}
73
78
  >
74
79
  <H_3 class="h-4 w-4" />
75
80
  </ToolbarButton>
76
81
  <ToolbarButton
77
- label="Paragraf"
82
+ label={t.paragraph}
78
83
  active={ed.isActive('paragraph') && !ed.isActive('heading')}
79
84
  onclick={() => ed.chain().focus().setParagraph().run()}
80
85
  >
@@ -85,35 +90,35 @@
85
90
 
86
91
  <!-- Text formatting -->
87
92
  <ToolbarButton
88
- label="Pogrubienie"
93
+ label={t.bold}
89
94
  active={ed.isActive('bold')}
90
95
  onclick={() => ed.chain().focus().toggleBold().run()}
91
96
  >
92
97
  <Bold class="h-4 w-4" />
93
98
  </ToolbarButton>
94
99
  <ToolbarButton
95
- label="Kursywa"
100
+ label={t.italic}
96
101
  active={ed.isActive('italic')}
97
102
  onclick={() => ed.chain().focus().toggleItalic().run()}
98
103
  >
99
104
  <Italic class="h-4 w-4" />
100
105
  </ToolbarButton>
101
106
  <ToolbarButton
102
- label="Podkreślenie"
107
+ label={t.underline}
103
108
  active={ed.isActive('underline')}
104
109
  onclick={() => ed.chain().focus().toggleUnderline().run()}
105
110
  >
106
111
  <Underline class="h-4 w-4" />
107
112
  </ToolbarButton>
108
113
  <ToolbarButton
109
- label="Przekreślenie"
114
+ label={t.strikethrough}
110
115
  active={ed.isActive('strike')}
111
116
  onclick={() => ed.chain().focus().toggleStrike().run()}
112
117
  >
113
118
  <Strikethrough class="h-4 w-4" />
114
119
  </ToolbarButton>
115
120
  <ToolbarButton
116
- label="Wyróżnienie"
121
+ label={t.highlight}
117
122
  active={ed.isActive('highlight')}
118
123
  onclick={() => ed.chain().focus().toggleHighlight().run()}
119
124
  >
@@ -124,28 +129,28 @@
124
129
 
125
130
  <!-- Alignment -->
126
131
  <ToolbarButton
127
- label="Do lewej"
132
+ label={t.alignLeft}
128
133
  active={ed.isActive({ textAlign: 'left' })}
129
134
  onclick={() => ed.chain().focus().setTextAlign('left').run()}
130
135
  >
131
136
  <AlignLeft class="h-4 w-4" />
132
137
  </ToolbarButton>
133
138
  <ToolbarButton
134
- label="Wyśrodkuj"
139
+ label={t.alignCenter}
135
140
  active={ed.isActive({ textAlign: 'center' })}
136
141
  onclick={() => ed.chain().focus().setTextAlign('center').run()}
137
142
  >
138
143
  <AlignCenter class="h-4 w-4" />
139
144
  </ToolbarButton>
140
145
  <ToolbarButton
141
- label="Do prawej"
146
+ label={t.alignRight}
142
147
  active={ed.isActive({ textAlign: 'right' })}
143
148
  onclick={() => ed.chain().focus().setTextAlign('right').run()}
144
149
  >
145
150
  <AlignRight class="h-4 w-4" />
146
151
  </ToolbarButton>
147
152
  <ToolbarButton
148
- label="Wyjustuj"
153
+ label={t.alignJustify}
149
154
  active={ed.isActive({ textAlign: 'justify' })}
150
155
  onclick={() => ed.chain().focus().setTextAlign('justify').run()}
151
156
  >
@@ -156,21 +161,21 @@
156
161
 
157
162
  <!-- Lists & Quote -->
158
163
  <ToolbarButton
159
- label="Lista punktowa"
164
+ label={t.bulletList}
160
165
  active={ed.isActive('bulletList')}
161
166
  onclick={() => ed.chain().focus().toggleBulletList().run()}
162
167
  >
163
168
  <List class="h-4 w-4" />
164
169
  </ToolbarButton>
165
170
  <ToolbarButton
166
- label="Lista numerowana"
171
+ label={t.orderedList}
167
172
  active={ed.isActive('orderedList')}
168
173
  onclick={() => ed.chain().focus().toggleOrderedList().run()}
169
174
  >
170
175
  <ListNumbers class="h-4 w-4" />
171
176
  </ToolbarButton>
172
177
  <ToolbarButton
173
- label="Cytat"
178
+ label={t.blockquote}
174
179
  active={ed.isActive('blockquote')}
175
180
  onclick={() => ed.chain().focus().toggleBlockquote().run()}
176
181
  >
@@ -180,16 +185,16 @@
180
185
  <Separator orientation="vertical" class="mx-1 h-6" />
181
186
 
182
187
  <!-- Link, Image, Table -->
183
- <ToolbarButton label="Link" active={ed.isActive('link')} onclick={onLinkDialog}>
188
+ <ToolbarButton label={t.link} active={ed.isActive('link')} onclick={onLinkDialog}>
184
189
  <LinkIcon class="h-4 w-4" />
185
190
  </ToolbarButton>
186
- <ToolbarButton label="Obrazek" active={false} onclick={onImageDialog}>
191
+ <ToolbarButton label={t.image} active={false} onclick={onImageDialog}>
187
192
  <Photo class="h-4 w-4" />
188
193
  </ToolbarButton>
189
- <ToolbarButton label="Wideo" active={false} onclick={onVideoDialog}>
194
+ <ToolbarButton label={t.video} active={false} onclick={onVideoDialog}>
190
195
  <VideoIcon class="h-4 w-4" />
191
196
  </ToolbarButton>
192
- <ToolbarButton label="Tabela" active={false} onclick={onTableDialog}>
197
+ <ToolbarButton label={t.table} active={false} onclick={onTableDialog}>
193
198
  <Table class="h-4 w-4" />
194
199
  </ToolbarButton>
195
200
 
@@ -197,14 +202,14 @@
197
202
 
198
203
  <!-- Code -->
199
204
  <ToolbarButton
200
- label="Kod inline"
205
+ label={t.inlineCode}
201
206
  active={ed.isActive('code')}
202
207
  onclick={() => ed.chain().focus().toggleCode().run()}
203
208
  >
204
209
  <Code class="h-4 w-4" />
205
210
  </ToolbarButton>
206
211
  <ToolbarButton
207
- label="Blok kodu"
212
+ label={t.codeBlock}
208
213
  active={ed.isActive('codeBlock')}
209
214
  onclick={() => ed.chain().focus().toggleCodeBlock().run()}
210
215
  >
@@ -213,7 +218,7 @@
213
218
 
214
219
  {#if showInsertBlock && onInsertBlock}
215
220
  <Separator orientation="vertical" class="mx-1 h-6" />
216
- <ToolbarButton label="Wstaw blok" active={false} onclick={onInsertBlock}>
221
+ <ToolbarButton label={t.insertBlock} active={false} onclick={onInsertBlock}>
217
222
  <Plus class="h-4 w-4" />
218
223
  </ToolbarButton>
219
224
  {/if}
@@ -222,7 +227,7 @@
222
227
  <Separator orientation="vertical" class="mx-1 h-6" />
223
228
 
224
229
  <!-- HTML View -->
225
- <ToolbarButton label="Widok HTML" active={isCodeViewActive} onclick={onToggleCodeView}>
230
+ <ToolbarButton label={t.htmlView} active={isCodeViewActive} onclick={onToggleCodeView}>
226
231
  <SourceCode class="h-4 w-4" />
227
232
  </ToolbarButton>
228
233
  {/if}
@@ -4,6 +4,11 @@
4
4
  import { getRemotes } from '../../context/remotes.js';
5
5
  import type { Editor } from '@tiptap/core';
6
6
  import AlertTriangle from '@tabler/icons-svelte/icons/alert-triangle';
7
+ import { tiptapLang } from './lang.js';
8
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
9
+
10
+ const interfaceLanguage = useInterfaceLanguage();
11
+ const t = $derived(tiptapLang[interfaceLanguage.current]);
7
12
 
8
13
  type Props = {
9
14
  open: boolean;
@@ -78,7 +83,7 @@
78
83
  <Dialog.Root bind:open {onOpenChange}>
79
84
  <Dialog.Content class="max-w-5xl! sm:max-w-5xl!">
80
85
  <Dialog.Header>
81
- <Dialog.Title>Wstaw obrazek</Dialog.Title>
86
+ <Dialog.Title>{t.insertImage}</Dialog.Title>
82
87
  </Dialog.Header>
83
88
  <MediaSelector bind:selected accept="image/*" onConfirm={handleConfirm} />
84
89
  </Dialog.Content>
@@ -88,26 +93,26 @@
88
93
  <Dialog.Root bind:open={altOpen}>
89
94
  <Dialog.Content class="max-w-lg! sm:max-w-lg!">
90
95
  <Dialog.Header>
91
- <Dialog.Title>Opis zdjęcia</Dialog.Title>
96
+ <Dialog.Title>{t.imageDescription}</Dialog.Title>
92
97
  </Dialog.Header>
93
98
  <div class="image-alt-prompt">
94
99
  {#if !altText.trim()}
95
100
  <div class="image-alt-banner">
96
101
  <AlertTriangle class="size-4 shrink-0" />
97
- <span>Dodaj opis zdjęcia, żeby każdy mógł je zrozumieć</span>
102
+ <span>{t.addAltBanner}</span>
98
103
  </div>
99
104
  {/if}
100
105
  <div class="image-alt-preview">
101
106
  <img src={fileUrl} alt="" class="image-alt-thumb" />
102
107
  </div>
103
- <label class="image-alt-label" for="image-alt-input">Opis alternatywny (alt text)</label>
108
+ <label class="image-alt-label" for="image-alt-input">{t.altLabel}</label>
104
109
  <input
105
110
  id="image-alt-input"
106
111
  type="text"
107
112
  class="image-alt-input"
108
113
  bind:value={altText}
109
114
  onkeydown={handleAltKeydown}
110
- placeholder="Np. Zdjęcie zespołu na konferencji..."
115
+ placeholder={t.altPlaceholder}
111
116
  />
112
117
  <div class="image-alt-actions">
113
118
  <button
@@ -115,14 +120,14 @@
115
120
  class="image-alt-btn-primary"
116
121
  onclick={() => insertImage(altText.trim())}
117
122
  >
118
- Dodaj i wstaw
123
+ {t.addAndInsert}
119
124
  </button>
120
125
  <button
121
126
  type="button"
122
127
  class="image-alt-btn-ghost"
123
128
  onclick={handleAltCancel}
124
129
  >
125
- Pomiń
130
+ {t.skip}
126
131
  </button>
127
132
  </div>
128
133
  </div>
@@ -25,7 +25,6 @@ function getFieldDefault(field) {
25
25
  return field.defaultValue;
26
26
  switch (field.type) {
27
27
  case 'text':
28
- case 'richtext':
29
28
  case 'date':
30
29
  case 'datetime':
31
30
  return '';
@@ -40,7 +39,6 @@ function getFieldDefault(field) {
40
39
  return [];
41
40
  case 'url':
42
41
  return { url: '' };
43
- case 'image':
44
42
  case 'file':
45
43
  case 'media':
46
44
  case 'relation':
@@ -119,8 +117,11 @@ export const InlineBlockNode = Node.create({
119
117
  };
120
118
  },
121
119
  addNodeView() {
122
- return SvelteNodeViewRenderer(InlineBlockNodeView, {
123
- context: this.options.context
124
- });
120
+ const Component = InlineBlockNodeView;
121
+ const baseContext = this.options.context;
122
+ return (props) => {
123
+ const context = baseContext ? new Map(baseContext) : undefined;
124
+ return SvelteNodeViewRenderer(Component, { context })(props);
125
+ };
125
126
  }
126
127
  });