includio-cms 0.13.0 → 0.13.2

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 (59) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/ROADMAP.md +24 -0
  3. package/dist/admin/api/handler.js +2 -0
  4. package/dist/admin/api/replace.js +2 -1
  5. package/dist/admin/api/rest/routes/upload.js +2 -1
  6. package/dist/admin/api/upload-limit.d.ts +2 -0
  7. package/dist/admin/api/upload-limit.js +7 -0
  8. package/dist/admin/api/upload.js +2 -1
  9. package/dist/admin/client/collection/collection-entries.svelte +58 -11
  10. package/dist/admin/client/users/users-page.svelte +5 -6
  11. package/dist/admin/client/users/users-page.svelte.d.ts +1 -4
  12. package/dist/admin/components/fields/block-picker-modal.svelte +13 -4
  13. package/dist/admin/components/fields/blocks-field.svelte +31 -9
  14. package/dist/admin/components/fields/simple-array-field.svelte +22 -11
  15. package/dist/admin/components/layout/layout-renderer.svelte +10 -4
  16. package/dist/admin/components/media/file-preview.svelte +10 -1
  17. package/dist/admin/components/media/file-upload.svelte +66 -9
  18. package/dist/admin/components/media/files-list.svelte +12 -3
  19. package/dist/admin/components/media/media-selector.svelte +11 -5
  20. package/dist/admin/components/tiptap/FigureNodeView.svelte +15 -10
  21. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +32 -1
  22. package/dist/admin/components/tiptap/SlashCommandPopup.svelte +8 -3
  23. package/dist/admin/components/tiptap/editor-toolbar.svelte +28 -23
  24. package/dist/admin/components/tiptap/image-dialog.svelte +12 -7
  25. package/dist/admin/components/tiptap/lang.d.ts +77 -0
  26. package/dist/admin/components/tiptap/lang.js +170 -0
  27. package/dist/admin/components/tiptap/link-dialog.svelte +22 -18
  28. package/dist/admin/components/tiptap/slash-command.js +26 -22
  29. package/dist/admin/components/tiptap/table-dialog.svelte +9 -4
  30. package/dist/admin/components/tiptap/video-dialog.svelte +6 -1
  31. package/dist/admin/remote/email.remote.d.ts +1 -0
  32. package/dist/admin/remote/email.remote.js +5 -0
  33. package/dist/admin/remote/entry.remote.d.ts +1 -0
  34. package/dist/admin/remote/entry.remote.js +6 -4
  35. package/dist/admin/remote/index.d.ts +1 -0
  36. package/dist/admin/remote/index.js +1 -0
  37. package/dist/admin/remote/reorder.d.ts +1 -0
  38. package/dist/admin/remote/reorder.js +33 -0
  39. package/dist/core/server/entries/operations/get.js +15 -3
  40. package/dist/core/server/fields/utils/imageStyles.js +7 -3
  41. package/dist/core/server/generator/fields.js +2 -2
  42. package/dist/core/server/generator/generator.js +4 -2
  43. package/dist/core/server/media/styles/operations/createMediaStyle.d.ts +2 -2
  44. package/dist/core/server/media/styles/operations/createMediaStyle.js +5 -3
  45. package/dist/core/server/media/styles/operations/generateDefaultStyles.js +33 -6
  46. package/dist/core/server/media/styles/operations/getImageStyle.d.ts +1 -0
  47. package/dist/core/server/media/styles/operations/getImageStyle.js +3 -0
  48. package/dist/core/server/media/styles/sharp/generateImageStyle.d.ts +2 -1
  49. package/dist/core/server/media/styles/sharp/generateImageStyle.js +5 -3
  50. package/dist/core/server/media/uploadLimit.d.ts +2 -0
  51. package/dist/core/server/media/uploadLimit.js +26 -0
  52. package/dist/types/entries.d.ts +1 -0
  53. package/dist/types/layout.d.ts +0 -1
  54. package/dist/updates/0.13.1/index.d.ts +2 -0
  55. package/dist/updates/0.13.1/index.js +20 -0
  56. package/dist/updates/0.13.2/index.d.ts +2 -0
  57. package/dist/updates/0.13.2/index.js +20 -0
  58. package/dist/updates/index.js +3 -1
  59. package/package.json +1 -1
@@ -0,0 +1,77 @@
1
+ import type { InterfaceLanguage } from '../../../types/languages.js';
2
+ export declare const tiptapLang: Record<InterfaceLanguage, {
3
+ heading2: string;
4
+ heading3: string;
5
+ paragraph: string;
6
+ bold: string;
7
+ italic: string;
8
+ underline: string;
9
+ strikethrough: string;
10
+ highlight: string;
11
+ alignLeft: string;
12
+ alignCenter: string;
13
+ alignRight: string;
14
+ alignJustify: string;
15
+ bulletList: string;
16
+ orderedList: string;
17
+ blockquote: string;
18
+ link: string;
19
+ image: string;
20
+ video: string;
21
+ table: string;
22
+ inlineCode: string;
23
+ codeBlock: string;
24
+ insertBlock: string;
25
+ htmlView: string;
26
+ linkTitle: string;
27
+ untitled: string;
28
+ openInNewTab: string;
29
+ linkTextWarning: (text: string) => string;
30
+ accessibility: string;
31
+ ariaLabelPlaceholder: string;
32
+ ariaLabelHint: string;
33
+ titleTooltip: string;
34
+ titlePlaceholder: string;
35
+ titleHint: string;
36
+ indexing: string;
37
+ nofollowLabel: string;
38
+ nofollowHint: string;
39
+ sponsoredLabel: string;
40
+ sponsoredHint: string;
41
+ ugcLabel: string;
42
+ ugcHint: string;
43
+ removeLink: string;
44
+ save: string;
45
+ insertImage: string;
46
+ imageDescription: string;
47
+ addAltBanner: string;
48
+ altLabel: string;
49
+ altPlaceholder: string;
50
+ addAndInsert: string;
51
+ skip: string;
52
+ insertVideo: string;
53
+ insertTable: string;
54
+ rows: string;
55
+ columns: string;
56
+ insert: string;
57
+ commandsLabel: string;
58
+ noResults: string;
59
+ more: string;
60
+ alignLeftLabel: string;
61
+ alignCenterLabel: string;
62
+ alignRightLabel: string;
63
+ imageAltPlaceholder: string;
64
+ addAltMessage: string;
65
+ captionPlaceholder: string;
66
+ captionDefault: string;
67
+ heading2Desc: string;
68
+ heading3Desc: string;
69
+ bulletListDesc: string;
70
+ orderedListDesc: string;
71
+ blockquoteDesc: string;
72
+ codeBlockDesc: string;
73
+ horizontalRule: string;
74
+ horizontalRuleDesc: string;
75
+ formattingGroup: string;
76
+ blocksGroup: string;
77
+ }>;
@@ -0,0 +1,170 @@
1
+ export const tiptapLang = {
2
+ pl: {
3
+ // editor-toolbar
4
+ heading2: 'Nagłówek 2',
5
+ heading3: 'Nagłówek 3',
6
+ paragraph: 'Paragraf',
7
+ bold: 'Pogrubienie',
8
+ italic: 'Kursywa',
9
+ underline: 'Podkreślenie',
10
+ strikethrough: 'Przekreślenie',
11
+ highlight: 'Wyróżnienie',
12
+ alignLeft: 'Do lewej',
13
+ alignCenter: 'Wyśrodkuj',
14
+ alignRight: 'Do prawej',
15
+ alignJustify: 'Wyjustuj',
16
+ bulletList: 'Lista punktowa',
17
+ orderedList: 'Lista numerowana',
18
+ blockquote: 'Cytat',
19
+ link: 'Link',
20
+ image: 'Obrazek',
21
+ video: 'Wideo',
22
+ table: 'Tabela',
23
+ inlineCode: 'Kod inline',
24
+ codeBlock: 'Blok kodu',
25
+ insertBlock: 'Wstaw blok',
26
+ htmlView: 'Widok HTML',
27
+ // link-dialog
28
+ linkTitle: 'Link',
29
+ untitled: 'Bez tytułu',
30
+ openInNewTab: 'Otwórz w nowej karcie',
31
+ linkTextWarning: (text) => `Tekst linku „${text}" nie opisuje celu. Dodaj`,
32
+ accessibility: 'Dostępność',
33
+ ariaLabelPlaceholder: 'Opis celu linku',
34
+ ariaLabelHint: 'Ustaw gdy tekst linku nie opisuje celu (np. „kliknij tutaj").',
35
+ titleTooltip: 'Tytuł (tooltip)',
36
+ titlePlaceholder: 'Tytuł linku',
37
+ titleHint: 'Tekst wyświetlany po najechaniu kursorem na link.',
38
+ indexing: 'Indeksowanie',
39
+ nofollowLabel: 'nofollow',
40
+ nofollowHint: 'Nie przekazuj autorytetu SEO. Zaznacz dla linków, którym nie chcesz ufać.',
41
+ sponsoredLabel: 'sponsored',
42
+ sponsoredHint: 'Link reklamowy lub sponsorowany.',
43
+ ugcLabel: 'ugc',
44
+ ugcHint: 'Link dodany przez użytkownika (np. w komentarzu).',
45
+ removeLink: 'Usuń link',
46
+ save: 'Zapisz',
47
+ // image-dialog
48
+ insertImage: 'Wstaw obrazek',
49
+ imageDescription: 'Opis zdjęcia',
50
+ addAltBanner: 'Dodaj opis zdjęcia, żeby każdy mógł je zrozumieć',
51
+ altLabel: 'Opis alternatywny (alt text)',
52
+ altPlaceholder: 'Np. Zdjęcie zespołu na konferencji...',
53
+ addAndInsert: 'Dodaj i wstaw',
54
+ skip: 'Pomiń',
55
+ // video-dialog
56
+ insertVideo: 'Wstaw wideo',
57
+ // table-dialog
58
+ insertTable: 'Wstaw tabelę',
59
+ rows: 'Wiersze',
60
+ columns: 'Kolumny',
61
+ insert: 'Wstaw',
62
+ // SlashCommandPopup
63
+ commandsLabel: 'Polecenia',
64
+ noResults: 'Brak wyników',
65
+ more: 'Więcej...',
66
+ // FigureNodeView
67
+ alignLeftLabel: 'Wyrównaj do lewej',
68
+ alignCenterLabel: 'Wyśrodkuj',
69
+ alignRightLabel: 'Wyrównaj do prawej',
70
+ imageAltPlaceholder: 'Opis zdjęcia...',
71
+ addAltMessage: 'Dodaj opis zdjęcia, żeby każdy mógł je zrozumieć',
72
+ captionPlaceholder: 'Dodaj podpis...',
73
+ captionDefault: 'Kliknij dwukrotnie, by dodać podpis',
74
+ // slash-command
75
+ heading2Desc: 'Główna sekcja',
76
+ heading3Desc: 'Podsekcja',
77
+ bulletListDesc: 'Lista bez numeracji',
78
+ orderedListDesc: 'Lista numerowana',
79
+ blockquoteDesc: 'Cytat blokowy',
80
+ codeBlockDesc: 'Blok kodu',
81
+ horizontalRule: 'Linia pozioma',
82
+ horizontalRuleDesc: 'Linia oddzielająca',
83
+ formattingGroup: 'Formatowanie',
84
+ blocksGroup: 'Bloki'
85
+ },
86
+ en: {
87
+ // editor-toolbar
88
+ heading2: 'Heading 2',
89
+ heading3: 'Heading 3',
90
+ paragraph: 'Paragraph',
91
+ bold: 'Bold',
92
+ italic: 'Italic',
93
+ underline: 'Underline',
94
+ strikethrough: 'Strikethrough',
95
+ highlight: 'Highlight',
96
+ alignLeft: 'Align left',
97
+ alignCenter: 'Center',
98
+ alignRight: 'Align right',
99
+ alignJustify: 'Justify',
100
+ bulletList: 'Bullet list',
101
+ orderedList: 'Ordered list',
102
+ blockquote: 'Blockquote',
103
+ link: 'Link',
104
+ image: 'Image',
105
+ video: 'Video',
106
+ table: 'Table',
107
+ inlineCode: 'Inline code',
108
+ codeBlock: 'Code block',
109
+ insertBlock: 'Insert block',
110
+ htmlView: 'HTML view',
111
+ // link-dialog
112
+ linkTitle: 'Link',
113
+ untitled: 'Untitled',
114
+ openInNewTab: 'Open in new tab',
115
+ linkTextWarning: (text) => `Link text "${text}" does not describe the destination. Add`,
116
+ accessibility: 'Accessibility',
117
+ ariaLabelPlaceholder: 'Describe the link destination',
118
+ ariaLabelHint: 'Set when link text does not describe the destination (e.g. "click here").',
119
+ titleTooltip: 'Title (tooltip)',
120
+ titlePlaceholder: 'Link title',
121
+ titleHint: 'Text displayed when hovering over the link.',
122
+ indexing: 'Indexing',
123
+ nofollowLabel: 'nofollow',
124
+ nofollowHint: 'Do not pass SEO authority. Use for untrusted links.',
125
+ sponsoredLabel: 'sponsored',
126
+ sponsoredHint: 'Advertising or sponsored link.',
127
+ ugcLabel: 'ugc',
128
+ ugcHint: 'User-generated link (e.g. in a comment).',
129
+ removeLink: 'Remove link',
130
+ save: 'Save',
131
+ // image-dialog
132
+ insertImage: 'Insert image',
133
+ imageDescription: 'Image description',
134
+ addAltBanner: 'Add an image description so everyone can understand it',
135
+ altLabel: 'Alternative description (alt text)',
136
+ altPlaceholder: 'E.g. Team photo at a conference...',
137
+ addAndInsert: 'Add and insert',
138
+ skip: 'Skip',
139
+ // video-dialog
140
+ insertVideo: 'Insert video',
141
+ // table-dialog
142
+ insertTable: 'Insert table',
143
+ rows: 'Rows',
144
+ columns: 'Columns',
145
+ insert: 'Insert',
146
+ // SlashCommandPopup
147
+ commandsLabel: 'Commands',
148
+ noResults: 'No results',
149
+ more: 'More...',
150
+ // FigureNodeView
151
+ alignLeftLabel: 'Align left',
152
+ alignCenterLabel: 'Center',
153
+ alignRightLabel: 'Align right',
154
+ imageAltPlaceholder: 'Image description...',
155
+ addAltMessage: 'Add an image description so everyone can understand it',
156
+ captionPlaceholder: 'Add caption...',
157
+ captionDefault: 'Double-click to add caption',
158
+ // slash-command
159
+ heading2Desc: 'Main section',
160
+ heading3Desc: 'Subsection',
161
+ bulletListDesc: 'Unordered list',
162
+ orderedListDesc: 'Numbered list',
163
+ blockquoteDesc: 'Block quote',
164
+ codeBlockDesc: 'Code block',
165
+ horizontalRule: 'Horizontal rule',
166
+ horizontalRuleDesc: 'Divider line',
167
+ formattingGroup: 'Formatting',
168
+ blocksGroup: 'Blocks'
169
+ }
170
+ };
@@ -15,6 +15,11 @@
15
15
  import AlertTriangle from '@tabler/icons-svelte/icons/alert-triangle';
16
16
  import LinkIcon from '@tabler/icons-svelte/icons/link';
17
17
  import X from '@tabler/icons-svelte/icons/x';
18
+ import { tiptapLang } from './lang.js';
19
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
20
+
21
+ const interfaceLanguage = useInterfaceLanguage();
22
+ const t = $derived(tiptapLang[interfaceLanguage.current]);
18
23
 
19
24
  type Props = {
20
25
  open: boolean;
@@ -173,7 +178,7 @@
173
178
  <Dialog.Root bind:open onOpenChange={(v) => { if (!v) close(); }}>
174
179
  <Dialog.Content class="sm:max-w-md">
175
180
  <Dialog.Header>
176
- <Dialog.Title>Link</Dialog.Title>
181
+ <Dialog.Title>{t.linkTitle}</Dialog.Title>
177
182
  </Dialog.Header>
178
183
  <form onsubmit={handleSubmit} class="space-y-4">
179
184
  {#if linkedEntry}
@@ -209,7 +214,7 @@
209
214
  >
210
215
  <Item.Content>
211
216
  <Item.Title>
212
- {suggestion.seo?.title || 'Bez tytułu'} ({suggestion.seo?.slug})
217
+ {suggestion.seo?.title || t.untitled} ({suggestion.seo?.slug})
213
218
  </Item.Title>
214
219
  </Item.Content>
215
220
  </Item.Root>
@@ -221,7 +226,7 @@
221
226
 
222
227
  <div class="flex items-center gap-2">
223
228
  <Checkbox id="link-target" bind:checked={openInNewTab} />
224
- <Label for="link-target" class="cursor-pointer font-normal">Otwórz w nowej karcie</Label>
229
+ <Label for="link-target" class="cursor-pointer font-normal">{t.openInNewTab}</Label>
225
230
  </div>
226
231
 
227
232
  {#if showGenericWarning}
@@ -231,7 +236,7 @@
231
236
  >
232
237
  <AlertTriangle class="mt-0.5 h-4 w-4 shrink-0" />
233
238
  <span>
234
- Tekst linku „{selectedText.trim()}" nie opisuje celu. Dodaj
239
+ {t.linkTextWarning(selectedText.trim())}
235
240
  <button
236
241
  type="button"
237
242
  class="underline"
@@ -241,8 +246,7 @@
241
246
  }}
242
247
  >
243
248
  aria-label
244
- </button>
245
- z opisem.
249
+ </button>.
246
250
  </span>
247
251
  </div>
248
252
  {/if}
@@ -259,7 +263,7 @@
259
263
  {:else}
260
264
  <ChevronRight class="h-4 w-4" />
261
265
  {/if}
262
- Dostępność
266
+ {t.accessibility}
263
267
  </button>
264
268
  {#if a11yOpen}
265
269
  <div class="mt-3 space-y-2">
@@ -267,16 +271,16 @@
267
271
  <Input
268
272
  id="link-aria-label"
269
273
  type="text"
270
- placeholder="Opis celu linku"
274
+ placeholder={t.ariaLabelPlaceholder}
271
275
  bind:value={ariaLabel}
272
276
  />
273
277
  <p class="text-muted-foreground text-xs">
274
- Ustaw gdy tekst linku nie opisuje celu (np. „kliknij tutaj").
278
+ {t.ariaLabelHint}
275
279
  </p>
276
- <Label for="link-title">Tytuł (tooltip)</Label>
277
- <Input id="link-title" type="text" placeholder="Tytuł linku" bind:value={title} />
280
+ <Label for="link-title">{t.titleTooltip}</Label>
281
+ <Input id="link-title" type="text" placeholder={t.titlePlaceholder} bind:value={title} />
278
282
  <p class="text-muted-foreground text-xs">
279
- Tekst wyświetlany po najechaniu kursorem na link.
283
+ {t.titleHint}
280
284
  </p>
281
285
  </div>
282
286
  {/if}
@@ -294,7 +298,7 @@
294
298
  {:else}
295
299
  <ChevronRight class="h-4 w-4" />
296
300
  {/if}
297
- Indeksowanie
301
+ {t.indexing}
298
302
  </button>
299
303
  {#if relOpen}
300
304
  <div class="mt-3 space-y-3">
@@ -304,7 +308,7 @@
304
308
  <Label for="link-rel-nofollow" class="cursor-pointer font-normal">nofollow</Label>
305
309
  </div>
306
310
  <p class="text-muted-foreground mt-1 ml-6 text-xs">
307
- Nie przekazuj autorytetu SEO. Zaznacz dla linków, którym nie chcesz ufać.
311
+ {t.nofollowHint}
308
312
  </p>
309
313
  </div>
310
314
  <div>
@@ -313,7 +317,7 @@
313
317
  <Label for="link-rel-sponsored" class="cursor-pointer font-normal">sponsored</Label>
314
318
  </div>
315
319
  <p class="text-muted-foreground mt-1 ml-6 text-xs">
316
- Link reklamowy lub sponsorowany.
320
+ {t.sponsoredHint}
317
321
  </p>
318
322
  </div>
319
323
  <div>
@@ -322,7 +326,7 @@
322
326
  <Label for="link-rel-ugc" class="cursor-pointer font-normal">ugc</Label>
323
327
  </div>
324
328
  <p class="text-muted-foreground mt-1 ml-6 text-xs">
325
- Link dodany przez użytkownika (np. w komentarzu).
329
+ {t.ugcHint}
326
330
  </p>
327
331
  </div>
328
332
  </div>
@@ -331,9 +335,9 @@
331
335
 
332
336
  <Dialog.Footer class="gap-2">
333
337
  {#if editor?.isActive('link')}
334
- <Button type="button" variant="destructive" onclick={handleRemove}>Usuń link</Button>
338
+ <Button type="button" variant="destructive" onclick={handleRemove}>{t.removeLink}</Button>
335
339
  {/if}
336
- <Button type="submit" disabled={!url}>Zapisz</Button>
340
+ <Button type="submit" disabled={!url}>{t.save}</Button>
337
341
  </Dialog.Footer>
338
342
  </form>
339
343
  </Dialog.Content>
@@ -3,71 +3,75 @@ import Suggestion, {} from '@tiptap/suggestion';
3
3
  import tippy, {} from 'tippy.js';
4
4
  import { mount, unmount } from 'svelte';
5
5
  import SlashCommandPopup from './SlashCommandPopup.svelte';
6
+ import { tiptapLang } from './lang.js';
7
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
6
8
  function getStandardItems(editor) {
9
+ const t = tiptapLang[useInterfaceLanguage().current];
7
10
  return [
8
11
  {
9
12
  id: 'heading2',
10
- label: 'Nagłówek 2',
11
- description: 'Główna sekcja',
12
- group: 'Formatowanie',
13
+ label: t.heading2,
14
+ description: t.heading2Desc,
15
+ group: t.formattingGroup,
13
16
  tier: 1,
14
17
  action: () => editor.chain().focus().toggleHeading({ level: 2 }).run()
15
18
  },
16
19
  {
17
20
  id: 'heading3',
18
- label: 'Nagłówek 3',
19
- description: 'Podsekcja',
20
- group: 'Formatowanie',
21
+ label: t.heading3,
22
+ description: t.heading3Desc,
23
+ group: t.formattingGroup,
21
24
  tier: 1,
22
25
  action: () => editor.chain().focus().toggleHeading({ level: 3 }).run()
23
26
  },
24
27
  {
25
28
  id: 'bulletList',
26
- label: 'Lista punktowa',
27
- description: 'Lista bez numeracji',
28
- group: 'Formatowanie',
29
+ label: t.bulletList,
30
+ description: t.bulletListDesc,
31
+ group: t.formattingGroup,
29
32
  tier: 1,
30
33
  action: () => editor.chain().focus().toggleBulletList().run()
31
34
  },
32
35
  {
33
36
  id: 'orderedList',
34
- label: 'Lista numerowana',
35
- description: 'Lista numerowana',
36
- group: 'Formatowanie',
37
+ label: t.orderedList,
38
+ description: t.orderedListDesc,
39
+ group: t.formattingGroup,
37
40
  tier: 1,
38
41
  action: () => editor.chain().focus().toggleOrderedList().run()
39
42
  },
40
43
  {
41
44
  id: 'blockquote',
42
- label: 'Cytat',
43
- description: 'Cytat blokowy',
44
- group: 'Formatowanie',
45
+ label: t.blockquote,
46
+ description: t.blockquoteDesc,
47
+ group: t.formattingGroup,
45
48
  tier: 2,
46
49
  action: () => editor.chain().focus().toggleBlockquote().run()
47
50
  },
48
51
  {
49
52
  id: 'codeBlock',
50
- label: 'Blok kodu',
51
- description: 'Blok kodu',
52
- group: 'Formatowanie',
53
+ label: t.codeBlock,
54
+ description: t.codeBlockDesc,
55
+ group: t.formattingGroup,
53
56
  tier: 2,
54
57
  action: () => editor.chain().focus().toggleCodeBlock().run()
55
58
  },
56
59
  {
57
60
  id: 'horizontalRule',
58
- label: 'Linia pozioma',
59
- description: 'Linia oddzielająca',
60
- group: 'Formatowanie',
61
+ label: t.horizontalRule,
62
+ description: t.horizontalRuleDesc,
63
+ group: t.formattingGroup,
61
64
  tier: 2,
62
65
  action: () => editor.chain().focus().setHorizontalRule().run()
63
66
  }
64
67
  ];
65
68
  }
66
69
  function getInlineBlockItems(inlineBlocks, editor) {
70
+ const t = tiptapLang[useInterfaceLanguage().current];
67
71
  return inlineBlocks.map((block) => ({
68
72
  id: `block-${block.slug}`,
69
73
  label: block.label ? (typeof block.label === 'string' ? block.label : Object.values(block.label)[0]) : block.slug,
70
- group: 'Bloki',
74
+ group: t.blocksGroup,
71
75
  action: () => {
72
76
  editor.commands.insertInlineBlock({ blockType: block.slug });
73
77
  }
@@ -4,6 +4,11 @@
4
4
  import Input from '../../../components/ui/input/input.svelte';
5
5
  import Label from '../../../components/ui/label/label.svelte';
6
6
  import type { Editor } from '@tiptap/core';
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;
@@ -34,21 +39,21 @@
34
39
  <Dialog.Root bind:open {onOpenChange}>
35
40
  <Dialog.Content class="sm:max-w-sm">
36
41
  <Dialog.Header>
37
- <Dialog.Title>Wstaw tabelę</Dialog.Title>
42
+ <Dialog.Title>{t.insertTable}</Dialog.Title>
38
43
  </Dialog.Header>
39
44
  <form onsubmit={handleSubmit} class="space-y-4">
40
45
  <div class="grid grid-cols-2 gap-4">
41
46
  <div class="space-y-2">
42
- <Label for="rows">Wiersze</Label>
47
+ <Label for="rows">{t.rows}</Label>
43
48
  <Input id="rows" type="number" min="1" max="20" bind:value={rows} />
44
49
  </div>
45
50
  <div class="space-y-2">
46
- <Label for="cols">Kolumny</Label>
51
+ <Label for="cols">{t.columns}</Label>
47
52
  <Input id="cols" type="number" min="1" max="10" bind:value={cols} />
48
53
  </div>
49
54
  </div>
50
55
  <Dialog.Footer>
51
- <Button type="submit">Wstaw</Button>
56
+ <Button type="submit">{t.insert}</Button>
52
57
  </Dialog.Footer>
53
58
  </form>
54
59
  </Dialog.Content>
@@ -3,6 +3,11 @@
3
3
  import MediaSelector from '../media/media-selector.svelte';
4
4
  import { getRemotes } from '../../context/remotes.js';
5
5
  import type { Editor } from '@tiptap/core';
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
  type Props = {
8
13
  open: boolean;
@@ -60,7 +65,7 @@
60
65
  <Dialog.Root bind:open {onOpenChange}>
61
66
  <Dialog.Content class="max-w-5xl! sm:max-w-5xl!">
62
67
  <Dialog.Header>
63
- <Dialog.Title>Wstaw wideo</Dialog.Title>
68
+ <Dialog.Title>{t.insertVideo}</Dialog.Title>
64
69
  </Dialog.Header>
65
70
  <MediaSelector bind:selected accept="video/*" />
66
71
  </Dialog.Content>
@@ -0,0 +1 @@
1
+ export declare const getEmailConfigured: import("@sveltejs/kit").RemoteQueryFunction<void, boolean>;
@@ -0,0 +1,5 @@
1
+ import { query } from '$app/server';
2
+ import { getCMS } from '../../core/cms.js';
3
+ export const getEmailConfigured = query(async () => {
4
+ return !!getCMS().emailAdapter;
5
+ });
@@ -73,6 +73,7 @@ export declare const unarchiveEntryCommand: import("@sveltejs/kit").RemoteComman
73
73
  export declare const deleteEntryCommand: import("@sveltejs/kit").RemoteCommand<string, Promise<void>>;
74
74
  export declare const reorderEntriesCommand: import("@sveltejs/kit").RemoteCommand<{
75
75
  orderedIds: string[];
76
+ collectionSlug: string;
76
77
  }, Promise<void>>;
77
78
  export declare const getEntryVersion: import("@sveltejs/kit").RemoteQueryFunction<{
78
79
  id: string;
@@ -5,6 +5,7 @@ import { getCMS } from '../../core/cms.js';
5
5
  import { pruneOldDraftVersions, unpublishEntryLang, upsertDraftVersion, updateEntry, updateEntrySchema, updateEntryVersionCommandTypes } from '../../core/server/entries/operations/update.js';
6
6
  import z from 'zod';
7
7
  import { requireAuth } from './middleware/auth.js';
8
+ import { stableSlotReorder } from './reorder.js';
8
9
  import { entryStatuses } from '../../types/entries.js';
9
10
  export const getRawEntries = query(z.object({
10
11
  ids: z.array(z.string().uuid()).optional(),
@@ -44,7 +45,7 @@ export const getEntries = query(z.object({
44
45
  });
45
46
  export const getEntryLabels = query(z.object({
46
47
  slug: z.string(),
47
- ids: z.array(z.string()).optional(),
48
+ ids: z.array(z.string().uuid()).optional(),
48
49
  search: z.string().optional(),
49
50
  limit: z.number().int().positive().optional(),
50
51
  status: z.enum(['published', 'draft', 'all']).optional()
@@ -179,10 +180,11 @@ export const deleteEntryCommand = command(z.string().uuid(), async (id) => {
179
180
  return getCMS().databaseAdapter.deleteEntry({ id });
180
181
  });
181
182
  export const reorderEntriesCommand = command(z.object({
182
- orderedIds: z.array(z.string().uuid())
183
- }), async ({ orderedIds }) => {
183
+ orderedIds: z.array(z.string().uuid()),
184
+ collectionSlug: z.string()
185
+ }), async ({ orderedIds, collectionSlug }) => {
184
186
  requireAuth();
185
- await Promise.all(orderedIds.map((id, index) => updateEntry(id, { sortOrder: index })));
187
+ await stableSlotReorder(orderedIds, collectionSlug);
186
188
  });
187
189
  export const getEntryVersion = query(z.object({
188
190
  id: z.string().uuid(),
@@ -7,3 +7,4 @@ export * from './languages.remote.js';
7
7
  export * from './form.remote.js';
8
8
  export * from './ai.remote.js';
9
9
  export * from './preview.remote.js';
10
+ export * from './email.remote.js';
@@ -7,3 +7,4 @@ export * from './languages.remote.js';
7
7
  export * from './form.remote.js';
8
8
  export * from './ai.remote.js';
9
9
  export * from './preview.remote.js';
10
+ export * from './email.remote.js';
@@ -0,0 +1 @@
1
+ export declare function stableSlotReorder(orderedIds: string[], collectionSlug: string): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import { getDbEntries } from '../../core/server/entries/operations/get.js';
2
+ import { updateEntry } from '../../core/server/entries/operations/update.js';
3
+ export async function stableSlotReorder(orderedIds, collectionSlug) {
4
+ if (orderedIds.length === 0)
5
+ return;
6
+ const allEntries = await getDbEntries({
7
+ slug: collectionSlug,
8
+ orderBy: { column: 'sortOrder', direction: 'asc' }
9
+ });
10
+ const allIds = allEntries.map((e) => e.id);
11
+ const orderedSet = new Set(orderedIds);
12
+ // Find slot positions occupied by orderedIds in the full list
13
+ const slotIndices = [];
14
+ for (let i = 0; i < allIds.length; i++) {
15
+ if (orderedSet.has(allIds[i])) {
16
+ slotIndices.push(i);
17
+ }
18
+ }
19
+ // Fill slots with new order
20
+ const result = [...allIds];
21
+ for (let i = 0; i < orderedIds.length; i++) {
22
+ result[slotIndices[i]] = orderedIds[i];
23
+ }
24
+ // Update only entries whose sortOrder changed
25
+ const updates = [];
26
+ for (let i = 0; i < result.length; i++) {
27
+ const entry = allEntries.find((e) => e.id === result[i]);
28
+ if (!entry || entry.sortOrder !== i) {
29
+ updates.push(updateEntry(result[i], { sortOrder: i }));
30
+ }
31
+ }
32
+ await Promise.all(updates);
33
+ }