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.
- package/CHANGELOG.md +128 -0
- package/ROADMAP.md +54 -2
- package/dist/admin/api/generate-styles.d.ts +2 -0
- package/dist/admin/api/generate-styles.js +32 -0
- package/dist/admin/api/handler.js +33 -0
- package/dist/admin/api/media-gc.js +10 -4
- package/dist/admin/api/rest/handler.js +17 -0
- package/dist/admin/api/rest/routes/collections.js +25 -13
- package/dist/admin/api/rest/routes/entries.d.ts +1 -1
- package/dist/admin/api/rest/routes/entries.js +10 -10
- package/dist/admin/api/rest/routes/media.d.ts +2 -0
- package/dist/admin/api/rest/routes/media.js +9 -0
- package/dist/admin/api/rest/routes/schema.d.ts +5 -0
- package/dist/admin/api/rest/routes/schema.js +152 -0
- package/dist/admin/api/rest/routes/singletons.d.ts +1 -1
- package/dist/admin/api/rest/routes/singletons.js +8 -7
- package/dist/admin/api/rest/routes/upload.d.ts +2 -0
- package/dist/admin/api/rest/routes/upload.js +28 -0
- package/dist/admin/api/upload.js +13 -0
- package/dist/admin/client/collection/collection-entries.svelte +35 -13
- package/dist/admin/client/entry/entry.svelte +21 -23
- package/dist/admin/client/entry/header/a11y-validator.js +2 -2
- package/dist/admin/client/entry/header/publish-panel.svelte +33 -85
- package/dist/admin/client/entry/header/status-badge.svelte +2 -2
- package/dist/admin/client/entry/header/version-history-sheet.svelte +9 -9
- package/dist/admin/client/entry/header/visibility.svelte +16 -10
- package/dist/admin/client/entry/utils.d.ts +3 -0
- package/dist/admin/client/entry/utils.js +22 -4
- package/dist/admin/client/form/form-submission/form-submission-page.svelte +4 -1
- package/dist/admin/client/form/form-submission/submission-field.svelte +10 -0
- package/dist/admin/client/index.d.ts +1 -0
- package/dist/admin/client/index.js +1 -0
- package/dist/admin/client/maintenance/maintenance-page.svelte +146 -2
- 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 +40 -19
- package/dist/admin/components/fields/field-renderer.svelte +4 -8
- package/dist/admin/components/fields/object-field.svelte +7 -12
- package/dist/admin/components/fields/select-field.svelte +8 -2
- package/dist/admin/components/fields/seo-field.svelte +40 -93
- package/dist/admin/components/fields/simple-array-field.svelte +27 -16
- package/dist/admin/components/fields/text-field-wrapper.svelte +52 -197
- package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
- package/dist/admin/components/fields/url-field-wrapper.svelte +15 -25
- package/dist/admin/components/fields/url-field.svelte +61 -72
- 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 +5 -1
- package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
- package/dist/admin/components/media/files-list.svelte +12 -3
- package/dist/admin/components/media/media-library.svelte +109 -37
- package/dist/admin/components/media/media-selector.svelte +90 -16
- package/dist/admin/components/media/tag-sidebar.svelte +10 -6
- package/dist/admin/components/media/tag-sidebar.svelte.d.ts +7 -2
- package/dist/admin/components/tiptap/FigureNodeView.svelte +15 -10
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +53 -94
- 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/inline-block-node.js +6 -5
- 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 +31 -28
- package/dist/admin/components/tiptap/slash-command.js +27 -23
- 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 +2 -5
- package/dist/admin/remote/entry.remote.js +23 -28
- package/dist/admin/remote/index.d.ts +1 -0
- package/dist/admin/remote/index.js +1 -0
- package/dist/admin/remote/media.remote.d.ts +15 -0
- package/dist/admin/remote/media.remote.js +18 -2
- package/dist/admin/remote/preview.remote.js +3 -1
- package/dist/admin/utils/entryLabel.js +9 -6
- package/dist/admin/utils/translationStatus.js +1 -2
- package/dist/cli/scaffold/admin.js +34 -2
- package/dist/cms/runtime/api.d.ts +16 -12
- package/dist/cms/runtime/api.js +7 -6
- package/dist/cms/runtime/remote.js +2 -2
- package/dist/cms/runtime/schemas.d.ts +1 -1
- package/dist/cms/runtime/schemas.js +1 -1
- package/dist/cms/runtime/types.d.ts +118 -112
- package/dist/cms/runtime/types.js +0 -12
- package/dist/core/cms.d.ts +3 -1
- package/dist/core/cms.js +30 -0
- package/dist/core/fields/fieldSchemaToTs.js +9 -15
- package/dist/core/fields/formFieldSchemaToTs.js +7 -0
- package/dist/core/server/entries/operations/create.js +10 -4
- package/dist/core/server/entries/operations/get.d.ts +1 -0
- package/dist/core/server/entries/operations/get.js +186 -191
- package/dist/core/server/entries/operations/update.d.ts +6 -7
- package/dist/core/server/entries/operations/update.js +20 -38
- package/dist/core/server/fields/populateEntry.js +16 -52
- package/dist/core/server/fields/resolveImageFields.js +69 -120
- package/dist/core/server/fields/resolveRelationFields.js +30 -51
- package/dist/core/server/fields/resolveRichtextLinks.js +46 -100
- package/dist/core/server/fields/resolveTypographyOrphans.bench.d.ts +1 -0
- package/dist/core/server/fields/resolveTypographyOrphans.bench.js +87 -0
- package/dist/core/server/fields/resolveTypographyOrphans.d.ts +3 -0
- package/dist/core/server/fields/resolveTypographyOrphans.js +128 -0
- package/dist/core/server/fields/resolveUrlFields.js +47 -56
- package/dist/core/server/fields/utils/fixOrphans.d.ts +5 -0
- package/dist/core/server/fields/utils/fixOrphans.js +12 -0
- package/dist/core/server/fields/utils/imageStyles.d.ts +4 -2
- package/dist/core/server/fields/utils/imageStyles.js +41 -25
- package/dist/core/server/fields/utils/resolveMedia.js +1 -6
- package/dist/core/server/forms/submissions/operations/delete.js +26 -2
- package/dist/core/server/forms/submissions/utils/parseMultipart.d.ts +2 -0
- package/dist/core/server/forms/submissions/utils/parseMultipart.js +75 -0
- package/dist/core/server/generator/fields.d.ts +6 -0
- package/dist/core/server/generator/fields.js +43 -5
- package/dist/core/server/generator/formFieldSchemaToString.js +10 -0
- package/dist/core/server/generator/formFields.js +1 -0
- package/dist/core/server/generator/generator.js +98 -30
- package/dist/core/server/media/operations/getFiles.d.ts +5 -0
- package/dist/core/server/media/operations/getFiles.js +6 -0
- package/dist/core/server/media/operations/uploadPrivateFile.d.ts +4 -0
- package/dist/core/server/media/operations/uploadPrivateFile.js +8 -0
- package/dist/core/server/media/styles/operations/batchGenerateStyles.d.ts +16 -0
- package/dist/core/server/media/styles/operations/batchGenerateStyles.js +144 -0
- package/dist/db-postgres/index.js +303 -37
- package/dist/db-postgres/schema/entry.d.ts +0 -94
- package/dist/db-postgres/schema/entry.js +0 -6
- package/dist/db-postgres/schema/entryVersion.d.ts +17 -0
- package/dist/db-postgres/schema/entryVersion.js +1 -0
- package/dist/entity/index.d.ts +9 -4
- package/dist/entity/index.js +24 -24
- package/dist/files-local/index.js +43 -0
- package/dist/paraglide/messages/_index.d.ts +36 -3
- package/dist/paraglide/messages/_index.js +71 -3
- package/dist/paraglide/messages/en.d.ts +5 -0
- package/dist/paraglide/messages/en.js +14 -0
- package/dist/paraglide/messages/pl.d.ts +5 -0
- package/dist/paraglide/messages/pl.js +14 -0
- package/dist/sveltekit/components/preview.svelte +2 -326
- package/dist/sveltekit/components/preview.svelte.d.ts +5 -16
- package/dist/sveltekit/server/index.d.ts +2 -1
- package/dist/sveltekit/server/index.js +2 -1
- package/dist/sveltekit/server/preview.js +4 -7
- package/dist/types/adapters/db.d.ts +15 -1
- package/dist/types/adapters/files.d.ts +6 -0
- package/dist/types/cms.d.ts +5 -0
- package/dist/types/entries.d.ts +54 -18
- package/dist/types/fields.d.ts +14 -24
- package/dist/types/formFields.d.ts +7 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/layout.d.ts +0 -1
- package/dist/types/structured-content.d.ts +5 -0
- package/dist/updates/0.10.0/index.d.ts +2 -0
- package/dist/updates/0.10.0/index.js +15 -0
- package/dist/updates/0.11.0/index.d.ts +2 -0
- package/dist/updates/0.11.0/index.js +12 -0
- package/dist/updates/0.12.0/index.d.ts +2 -0
- package/dist/updates/0.12.0/index.js +12 -0
- package/dist/updates/0.13.0/index.d.ts +2 -0
- package/dist/updates/0.13.0/index.js +10 -0
- 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.7.3/index.d.ts +2 -0
- package/dist/updates/0.7.3/index.js +10 -0
- package/dist/updates/0.8.0/index.d.ts +2 -0
- package/dist/updates/0.8.0/index.js +18 -0
- package/dist/updates/0.8.0/migrate.d.ts +2 -0
- package/dist/updates/0.8.0/migrate.js +101 -0
- package/dist/updates/0.9.0/index.d.ts +2 -0
- package/dist/updates/0.9.0/index.js +38 -0
- package/dist/updates/index.js +9 -1
- package/package.json +7 -6
- package/dist/admin/components/fields/image-field.svelte +0 -198
- package/dist/admin/components/fields/image-field.svelte.d.ts +0 -8
- package/dist/admin/components/fields/richtext-field.svelte +0 -13
- package/dist/admin/components/fields/richtext-field.svelte.d.ts +0 -8
- package/dist/admin/components/tiptap.svelte +0 -11
- package/dist/admin/components/tiptap.svelte.d.ts +0 -6
- package/dist/core/server/entries/utils/getEntryTranslation.d.ts +0 -1
- package/dist/core/server/entries/utils/getEntryTranslation.js +0 -18
- package/dist/paraglide/messages/hello_world.d.ts +0 -5
- package/dist/paraglide/messages/hello_world.js +0 -33
- package/dist/paraglide/messages/login_hello.d.ts +0 -16
- package/dist/paraglide/messages/login_hello.js +0 -34
- package/dist/paraglide/messages/login_please_login.d.ts +0 -16
- 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=
|
|
95
|
-
title=
|
|
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=
|
|
105
|
-
title=
|
|
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=
|
|
115
|
-
title=
|
|
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=
|
|
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>
|
|
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=
|
|
162
|
+
placeholder={t.captionPlaceholder}
|
|
158
163
|
/>
|
|
159
164
|
{:else}
|
|
160
165
|
<button type="button" class="caption-display" ondblclick={() => (editing = true)}>
|
|
161
|
-
{node.attrs.caption ||
|
|
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'
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
52
|
-
|
|
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 &&
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
item.
|
|
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'
|
|
82
|
-
|
|
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
|
|
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
|
|
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
|
|
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 ?
|
|
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=
|
|
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">
|
|
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
|
-
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
191
|
+
<ToolbarButton label={t.image} active={false} onclick={onImageDialog}>
|
|
187
192
|
<Photo class="h-4 w-4" />
|
|
188
193
|
</ToolbarButton>
|
|
189
|
-
<ToolbarButton label=
|
|
194
|
+
<ToolbarButton label={t.video} active={false} onclick={onVideoDialog}>
|
|
190
195
|
<VideoIcon class="h-4 w-4" />
|
|
191
196
|
</ToolbarButton>
|
|
192
|
-
<ToolbarButton label=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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>
|
|
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>
|
|
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>
|
|
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">
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|
});
|