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.
- package/CHANGELOG.md +36 -0
- package/ROADMAP.md +24 -0
- package/dist/admin/api/handler.js +2 -0
- package/dist/admin/api/replace.js +2 -1
- package/dist/admin/api/rest/routes/upload.js +2 -1
- package/dist/admin/api/upload-limit.d.ts +2 -0
- package/dist/admin/api/upload-limit.js +7 -0
- package/dist/admin/api/upload.js +2 -1
- package/dist/admin/client/collection/collection-entries.svelte +58 -11
- package/dist/admin/client/users/users-page.svelte +5 -6
- package/dist/admin/client/users/users-page.svelte.d.ts +1 -4
- package/dist/admin/components/fields/block-picker-modal.svelte +13 -4
- package/dist/admin/components/fields/blocks-field.svelte +31 -9
- package/dist/admin/components/fields/simple-array-field.svelte +22 -11
- package/dist/admin/components/layout/layout-renderer.svelte +10 -4
- package/dist/admin/components/media/file-preview.svelte +10 -1
- package/dist/admin/components/media/file-upload.svelte +66 -9
- package/dist/admin/components/media/files-list.svelte +12 -3
- package/dist/admin/components/media/media-selector.svelte +11 -5
- package/dist/admin/components/tiptap/FigureNodeView.svelte +15 -10
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +32 -1
- package/dist/admin/components/tiptap/SlashCommandPopup.svelte +8 -3
- package/dist/admin/components/tiptap/editor-toolbar.svelte +28 -23
- package/dist/admin/components/tiptap/image-dialog.svelte +12 -7
- package/dist/admin/components/tiptap/lang.d.ts +77 -0
- package/dist/admin/components/tiptap/lang.js +170 -0
- package/dist/admin/components/tiptap/link-dialog.svelte +22 -18
- package/dist/admin/components/tiptap/slash-command.js +26 -22
- package/dist/admin/components/tiptap/table-dialog.svelte +9 -4
- package/dist/admin/components/tiptap/video-dialog.svelte +6 -1
- package/dist/admin/remote/email.remote.d.ts +1 -0
- package/dist/admin/remote/email.remote.js +5 -0
- package/dist/admin/remote/entry.remote.d.ts +1 -0
- package/dist/admin/remote/entry.remote.js +6 -4
- package/dist/admin/remote/index.d.ts +1 -0
- package/dist/admin/remote/index.js +1 -0
- package/dist/admin/remote/reorder.d.ts +1 -0
- package/dist/admin/remote/reorder.js +33 -0
- package/dist/core/server/entries/operations/get.js +15 -3
- package/dist/core/server/fields/utils/imageStyles.js +7 -3
- package/dist/core/server/generator/fields.js +2 -2
- package/dist/core/server/generator/generator.js +4 -2
- package/dist/core/server/media/styles/operations/createMediaStyle.d.ts +2 -2
- package/dist/core/server/media/styles/operations/createMediaStyle.js +5 -3
- package/dist/core/server/media/styles/operations/generateDefaultStyles.js +33 -6
- package/dist/core/server/media/styles/operations/getImageStyle.d.ts +1 -0
- package/dist/core/server/media/styles/operations/getImageStyle.js +3 -0
- package/dist/core/server/media/styles/sharp/generateImageStyle.d.ts +2 -1
- package/dist/core/server/media/styles/sharp/generateImageStyle.js +5 -3
- package/dist/core/server/media/uploadLimit.d.ts +2 -0
- package/dist/core/server/media/uploadLimit.js +26 -0
- package/dist/types/entries.d.ts +1 -0
- package/dist/types/layout.d.ts +0 -1
- package/dist/updates/0.13.1/index.d.ts +2 -0
- package/dist/updates/0.13.1/index.js +20 -0
- package/dist/updates/0.13.2/index.d.ts +2 -0
- package/dist/updates/0.13.2/index.js +20 -0
- package/dist/updates/index.js +3 -1
- 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>
|
|
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 ||
|
|
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">
|
|
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
|
-
|
|
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
|
-
|
|
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=
|
|
274
|
+
placeholder={t.ariaLabelPlaceholder}
|
|
271
275
|
bind:value={ariaLabel}
|
|
272
276
|
/>
|
|
273
277
|
<p class="text-muted-foreground text-xs">
|
|
274
|
-
|
|
278
|
+
{t.ariaLabelHint}
|
|
275
279
|
</p>
|
|
276
|
-
<Label for="link-title">
|
|
277
|
-
<Input id="link-title" type="text" placeholder=
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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}>
|
|
338
|
+
<Button type="button" variant="destructive" onclick={handleRemove}>{t.removeLink}</Button>
|
|
335
339
|
{/if}
|
|
336
|
-
<Button type="submit" disabled={!url}>
|
|
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:
|
|
11
|
-
description:
|
|
12
|
-
group:
|
|
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:
|
|
19
|
-
description:
|
|
20
|
-
group:
|
|
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:
|
|
27
|
-
description:
|
|
28
|
-
group:
|
|
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:
|
|
35
|
-
description:
|
|
36
|
-
group:
|
|
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:
|
|
43
|
-
description:
|
|
44
|
-
group:
|
|
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:
|
|
51
|
-
description:
|
|
52
|
-
group:
|
|
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:
|
|
59
|
-
description:
|
|
60
|
-
group:
|
|
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:
|
|
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>
|
|
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">
|
|
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">
|
|
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">
|
|
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>
|
|
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>;
|
|
@@ -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
|
-
|
|
183
|
+
orderedIds: z.array(z.string().uuid()),
|
|
184
|
+
collectionSlug: z.string()
|
|
185
|
+
}), async ({ orderedIds, collectionSlug }) => {
|
|
184
186
|
requireAuth();
|
|
185
|
-
await
|
|
187
|
+
await stableSlotReorder(orderedIds, collectionSlug);
|
|
186
188
|
});
|
|
187
189
|
export const getEntryVersion = query(z.object({
|
|
188
190
|
id: z.string().uuid(),
|
|
@@ -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
|
+
}
|