autumnnote 1.0.7 → 1.0.9
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/README.md +332 -638
- package/dist/autumnnote.css +40 -4
- package/dist/autumnnote.es.js +3375 -262
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +3374 -261
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/Context.js +4 -0
- package/src/js/editing/Style.js +195 -44
- package/src/js/editing/Typing.js +2 -2
- package/src/js/i18n/de.js +320 -0
- package/src/js/i18n/en.js +328 -0
- package/src/js/i18n/es.js +320 -0
- package/src/js/i18n/fr.js +321 -0
- package/src/js/i18n/index.js +59 -0
- package/src/js/i18n/ja.js +321 -0
- package/src/js/i18n/ko.js +320 -0
- package/src/js/i18n/vi.js +321 -0
- package/src/js/i18n/zh.js +321 -0
- package/src/js/index.js +2 -1
- package/src/js/module/CodeTooltip.js +12 -9
- package/src/js/module/ContextMenu.js +13 -11
- package/src/js/module/Editor.js +14 -3
- package/src/js/module/EmojiDialog.js +19 -7
- package/src/js/module/FindReplace.js +14 -13
- package/src/js/module/IconDialog.js +25 -13
- package/src/js/module/ImageDialog.js +25 -20
- package/src/js/module/ImageTooltip.js +13 -12
- package/src/js/module/LinkDialog.js +10 -9
- package/src/js/module/LinkTooltip.js +6 -5
- package/src/js/module/Placeholder.js +6 -1
- package/src/js/module/ShortcutsDialog.js +5 -4
- package/src/js/module/Statusbar.js +6 -5
- package/src/js/module/TableTooltip.js +412 -102
- package/src/js/module/Toolbar.js +21 -15
- package/src/js/module/VideoDialog.js +10 -9
- package/src/js/module/VideoTooltip.js +12 -11
- package/src/js/settings.js +4 -0
- package/src/styles/autumnnote.scss +50 -5
- package/types/index.d.ts +58 -1
package/package.json
CHANGED
package/src/js/Context.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { mergeDeep } from './core/func.js';
|
|
8
8
|
import { defaultOptions } from './settings.js';
|
|
9
|
+
import { resolveLocale } from './i18n/index.js';
|
|
9
10
|
import { renderLayout } from './renderer.js';
|
|
10
11
|
import { on } from './core/dom.js';
|
|
11
12
|
|
|
@@ -46,6 +47,9 @@ export class Context {
|
|
|
46
47
|
this.targetEl = targetEl;
|
|
47
48
|
this.options = mergeDeep(defaultOptions, userOptions);
|
|
48
49
|
|
|
50
|
+
/** @type {import('./i18n/index.js').AsnLocale} */
|
|
51
|
+
this.locale = resolveLocale(this.options.lang);
|
|
52
|
+
|
|
49
53
|
/** @type {{ container: HTMLElement, editable: HTMLElement, toolbar?: HTMLElement, statusbar?: HTMLElement }} */
|
|
50
54
|
this.layoutInfo = {};
|
|
51
55
|
|
package/src/js/editing/Style.js
CHANGED
|
@@ -60,8 +60,29 @@ export function underline() {
|
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Strikethrough / removes strikethrough.
|
|
63
|
+
* Falls back to manual DOM manipulation inside nested formats where
|
|
64
|
+
* execCommand's state detection is unreliable (mirrors underline() logic).
|
|
63
65
|
*/
|
|
64
|
-
export
|
|
66
|
+
export function strikethrough() {
|
|
67
|
+
const sel = window.getSelection();
|
|
68
|
+
if (!sel || !sel.rangeCount) return;
|
|
69
|
+
// Use startContainer for consistent detection across collapsed and range
|
|
70
|
+
// selections — commonAncestorContainer can miss ancestor <s>/<strike> tags
|
|
71
|
+
// when the selection spans across nested inline elements.
|
|
72
|
+
let sc = sel.getRangeAt(0).startContainer;
|
|
73
|
+
if (sc.nodeType === 3) sc = sc.parentElement;
|
|
74
|
+
const sEl = sc && sc.closest && (sc.closest('s') || sc.closest('strike'));
|
|
75
|
+
const nativeState = document.queryCommandState('strikeThrough');
|
|
76
|
+
if (sEl && !nativeState) {
|
|
77
|
+
// Browser doesn’t recognise the strikethrough state (e.g. inside <code>
|
|
78
|
+
// or deeply nested inline formats). Manually unwrap the <s>/<strike>.
|
|
79
|
+
const parent = sEl.parentNode;
|
|
80
|
+
while (sEl.firstChild) parent.insertBefore(sEl.firstChild, sEl);
|
|
81
|
+
parent.removeChild(sEl);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
execCommand('strikeThrough');
|
|
85
|
+
}
|
|
65
86
|
|
|
66
87
|
/**
|
|
67
88
|
* Superscript toggle.
|
|
@@ -101,8 +122,35 @@ export function fontSize(size, editable = document) {
|
|
|
101
122
|
const sel = window.getSelection();
|
|
102
123
|
const wasCollapsed = !sel || !sel.rangeCount || sel.getRangeAt(0).collapsed;
|
|
103
124
|
|
|
104
|
-
|
|
105
|
-
//
|
|
125
|
+
// B-I-3/4: For a collapsed (caret) selection the browser's execCommand
|
|
126
|
+
// 'fontSize' leaves an internal "pending" state of size-7 (=48px) instead of
|
|
127
|
+
// creating a <font> element, so the very next typed character comes out at
|
|
128
|
+
// 48px. Fix: bypass execCommand entirely for collapsed selections and directly
|
|
129
|
+
// insert a span with the requested size, placing the cursor inside it.
|
|
130
|
+
// Only applies when there IS an active selection (sel.rangeCount > 0); when
|
|
131
|
+
// there is no selection at all (e.g. jsdom unit tests) fall through to the
|
|
132
|
+
// execCommand path so the font-replacement logic still runs.
|
|
133
|
+
if (wasCollapsed && sel && sel.rangeCount > 0) {
|
|
134
|
+
try {
|
|
135
|
+
const range = sel.getRangeAt(0);
|
|
136
|
+
const span = document.createElement('span');
|
|
137
|
+
span.style.fontSize = size;
|
|
138
|
+
const zwsNode = document.createTextNode('\u200B');
|
|
139
|
+
span.appendChild(zwsNode);
|
|
140
|
+
range.insertNode(span);
|
|
141
|
+
const nr = document.createRange();
|
|
142
|
+
nr.setStart(zwsNode, zwsNode.textContent.length);
|
|
143
|
+
nr.collapse(true);
|
|
144
|
+
sel.removeAllRanges();
|
|
145
|
+
sel.addRange(nr);
|
|
146
|
+
} catch (_) { /* ignore range errors on unusual DOM structures */ }
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Non-collapsed selection (or no selection — handles jsdom test setup where
|
|
151
|
+
// <font size="7"> elements are injected directly without a live selection):
|
|
152
|
+
// use execCommand placeholder approach then replace <font> with <span>.
|
|
153
|
+
execCommand('fontSize', '7');
|
|
106
154
|
const scope = editable instanceof HTMLElement ? editable : document;
|
|
107
155
|
const newSpans = [];
|
|
108
156
|
scope.querySelectorAll('font[size="7"]').forEach((el) => {
|
|
@@ -114,36 +162,20 @@ export function fontSize(size, editable = document) {
|
|
|
114
162
|
newSpans.push(span);
|
|
115
163
|
});
|
|
116
164
|
|
|
117
|
-
//
|
|
118
|
-
// 1
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
// would produce size 7 = 48 px instead of the requested value).
|
|
122
|
-
if (sel && newSpans.length > 0) {
|
|
165
|
+
// Re-select all replaced content so toolbar getValue() reads the new size
|
|
166
|
+
// (B-I-1/2: without this re-selection the toolbar dropdown stays on the old
|
|
167
|
+
// value until the next selectionchange event).
|
|
168
|
+
if (!wasCollapsed && sel && newSpans.length > 0) {
|
|
123
169
|
const first = newSpans[0];
|
|
124
170
|
const last = newSpans[newSpans.length - 1];
|
|
125
171
|
try {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
nr.setStart(anchor, anchor.textContent.length);
|
|
134
|
-
nr.collapse(true);
|
|
135
|
-
sel.removeAllRanges();
|
|
136
|
-
sel.addRange(nr);
|
|
137
|
-
} else {
|
|
138
|
-
// Re-select all replaced content so toolbar refresh reads the new size.
|
|
139
|
-
const nr = document.createRange();
|
|
140
|
-
const startNode = first.firstChild || first;
|
|
141
|
-
const endNode = last.lastChild || last;
|
|
142
|
-
nr.setStart(startNode, 0);
|
|
143
|
-
nr.setEnd(endNode, endNode.nodeType === Node.TEXT_NODE ? endNode.textContent.length : endNode.childNodes.length);
|
|
144
|
-
sel.removeAllRanges();
|
|
145
|
-
sel.addRange(nr);
|
|
146
|
-
}
|
|
172
|
+
const nr = document.createRange();
|
|
173
|
+
const startNode = first.firstChild || first;
|
|
174
|
+
const endNode = last.lastChild || last;
|
|
175
|
+
nr.setStart(startNode, 0);
|
|
176
|
+
nr.setEnd(endNode, endNode.nodeType === Node.TEXT_NODE ? endNode.textContent.length : endNode.childNodes.length);
|
|
177
|
+
sel.removeAllRanges();
|
|
178
|
+
sel.addRange(nr);
|
|
147
179
|
} catch (_) { /* ignore range errors on unusual DOM structures */ }
|
|
148
180
|
}
|
|
149
181
|
}
|
|
@@ -185,8 +217,69 @@ export const indent = () => execCommand('indent');
|
|
|
185
217
|
|
|
186
218
|
/**
|
|
187
219
|
* Outdents the list or block.
|
|
220
|
+
* G.5: When cursor is inside a checklist item, "outdent" means converting
|
|
221
|
+
* that item back to a regular <p> element rather than calling execCommand
|
|
222
|
+
* (which would destroy the ul > li checklist structure).
|
|
223
|
+
*/
|
|
224
|
+
export function outdent() {
|
|
225
|
+
const sel = window.getSelection();
|
|
226
|
+
if (sel && sel.rangeCount) {
|
|
227
|
+
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
228
|
+
if (container.nodeType === 3) container = container.parentElement;
|
|
229
|
+
const checkLi = container && container.closest && container.closest('.an-checklist li');
|
|
230
|
+
if (checkLi) {
|
|
231
|
+
_checklistItemToP(checkLi);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
execCommand('outdent');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* G.5 helper: splits a checklist at checkLi, converts it to a <p>,
|
|
240
|
+
* and keeps items before/after as separate checklists.
|
|
241
|
+
* @param {HTMLElement} checkLi
|
|
188
242
|
*/
|
|
189
|
-
|
|
243
|
+
function _checklistItemToP(checkLi) {
|
|
244
|
+
const checkUl = checkLi.closest('.an-checklist');
|
|
245
|
+
if (!checkUl) return;
|
|
246
|
+
|
|
247
|
+
const allLis = Array.from(checkUl.children);
|
|
248
|
+
const liIndex = allLis.indexOf(checkLi);
|
|
249
|
+
const afterLis = allLis.slice(liIndex + 1);
|
|
250
|
+
|
|
251
|
+
// Build <p> from the item's text (skip the checkbox INPUT)
|
|
252
|
+
const p = document.createElement('p');
|
|
253
|
+
const text = Array.from(checkLi.childNodes)
|
|
254
|
+
.filter(n => !(n.nodeType === 1 && n.tagName === 'INPUT'))
|
|
255
|
+
.map(n => n.textContent).join('').replace(/\u200B/g, '').trim();
|
|
256
|
+
p.textContent = text || '\u00a0';
|
|
257
|
+
|
|
258
|
+
// Move items after the current li into a new checklist
|
|
259
|
+
if (afterLis.length > 0) {
|
|
260
|
+
const newUl = document.createElement('ul');
|
|
261
|
+
newUl.className = 'an-checklist';
|
|
262
|
+
afterLis.forEach(li => newUl.appendChild(li));
|
|
263
|
+
checkUl.parentNode.insertBefore(newUl, checkUl.nextSibling);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Insert <p> after checkUl (before any newUl)
|
|
267
|
+
checkUl.parentNode.insertBefore(p, checkUl.nextSibling);
|
|
268
|
+
|
|
269
|
+
// Remove current li from checkUl; delete checkUl if now empty
|
|
270
|
+
checkUl.removeChild(checkLi);
|
|
271
|
+
if (checkUl.children.length === 0) checkUl.parentNode.removeChild(checkUl);
|
|
272
|
+
|
|
273
|
+
// Place caret at start of the new <p>
|
|
274
|
+
try {
|
|
275
|
+
const nr = document.createRange();
|
|
276
|
+
const firstChild = p.firstChild;
|
|
277
|
+
nr.setStart(firstChild && firstChild.nodeType === 3 ? firstChild : p, 0);
|
|
278
|
+
nr.collapse(true);
|
|
279
|
+
const s = window.getSelection();
|
|
280
|
+
if (s) { s.removeAllRanges(); s.addRange(nr); }
|
|
281
|
+
} catch {}
|
|
282
|
+
}
|
|
190
283
|
|
|
191
284
|
/**
|
|
192
285
|
* Inserts an unordered list or converts selection.
|
|
@@ -471,20 +564,78 @@ export function toggleChecklist() {
|
|
|
471
564
|
return;
|
|
472
565
|
}
|
|
473
566
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
567
|
+
// G-4: Non-collapsed, non-checklist selection — convert each intersected
|
|
568
|
+
// block element into a checklist item using direct DOM manipulation.
|
|
569
|
+
// execCommand('insertHTML') is avoided here because in modern browsers it
|
|
570
|
+
// deletes the selection but may silently fail to insert when the selection
|
|
571
|
+
// spans multiple block elements, causing text to disappear.
|
|
572
|
+
|
|
573
|
+
// Guard: if the raw selection is entirely whitespace, do nothing (mirrors
|
|
574
|
+
// the old line-filter behaviour that prevented empty checklist creation).
|
|
575
|
+
const rawSelText = sel.toString().replace(/[\u00a0\u200B]/g, ' ').trim();
|
|
576
|
+
if (!rawSelText) return;
|
|
577
|
+
|
|
578
|
+
const BLOCK_TAGS_MULTI = new Set(['P', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE', 'PRE', 'LI']);
|
|
579
|
+
|
|
580
|
+
// Collect block-level ancestors of every node in the selection, in order.
|
|
581
|
+
const blocks = [];
|
|
582
|
+
const seenBlocks = new Set();
|
|
583
|
+
const commonAncestor = range.commonAncestorContainer;
|
|
584
|
+
const iter = document.createNodeIterator(
|
|
585
|
+
commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor,
|
|
586
|
+
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,
|
|
587
|
+
null,
|
|
487
588
|
);
|
|
589
|
+
let node;
|
|
590
|
+
while ((node = iter.nextNode())) {
|
|
591
|
+
if (!range.intersectsNode(node)) continue;
|
|
592
|
+
let block = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
593
|
+
while (block && !BLOCK_TAGS_MULTI.has(block.tagName)) {
|
|
594
|
+
block = block.parentElement;
|
|
595
|
+
}
|
|
596
|
+
if (block && !seenBlocks.has(block)) {
|
|
597
|
+
seenBlocks.add(block);
|
|
598
|
+
blocks.push(block);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (blocks.length === 0) return;
|
|
603
|
+
|
|
604
|
+
// Build checklist and replace collected blocks.
|
|
605
|
+
const newUl = document.createElement('ul');
|
|
606
|
+
newUl.className = 'an-checklist';
|
|
607
|
+
let lastTextNode = null;
|
|
608
|
+
blocks.forEach((block) => {
|
|
609
|
+
const li = document.createElement('li');
|
|
610
|
+
const cb = document.createElement('input');
|
|
611
|
+
cb.type = 'checkbox';
|
|
612
|
+
cb.setAttribute('contenteditable', 'false');
|
|
613
|
+
li.appendChild(cb);
|
|
614
|
+
// Preserve plain text content; ZWS/NBSP are stripped for display.
|
|
615
|
+
const blockText = Array.from(block.childNodes)
|
|
616
|
+
.map((n) => n.textContent)
|
|
617
|
+
.join('')
|
|
618
|
+
.replace(/[\u00a0\u200B]/g, ' ')
|
|
619
|
+
.trim();
|
|
620
|
+
const tn = document.createTextNode(blockText || '\u200B');
|
|
621
|
+
li.appendChild(tn);
|
|
622
|
+
newUl.appendChild(li);
|
|
623
|
+
lastTextNode = tn;
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// Insert the new list before the first block, then remove all source blocks.
|
|
627
|
+
const firstBlock = blocks[0];
|
|
628
|
+
firstBlock.parentNode.insertBefore(newUl, firstBlock);
|
|
629
|
+
blocks.forEach((block) => block.parentNode && block.parentNode.removeChild(block));
|
|
630
|
+
|
|
631
|
+
// Move caret to end of the last checklist item.
|
|
632
|
+
if (lastTextNode) {
|
|
633
|
+
const nr = document.createRange();
|
|
634
|
+
nr.setStart(lastTextNode, lastTextNode.textContent.length);
|
|
635
|
+
nr.collapse(true);
|
|
636
|
+
sel.removeAllRanges();
|
|
637
|
+
sel.addRange(nr);
|
|
638
|
+
}
|
|
488
639
|
}
|
|
489
640
|
|
|
490
641
|
/**
|
package/src/js/editing/Typing.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { key, isKey } from '../core/key.js';
|
|
7
7
|
import { closestPara, isLi } from '../core/dom.js';
|
|
8
|
-
import { execCommand } from './Style.js';
|
|
8
|
+
import { execCommand, outdent } from './Style.js';
|
|
9
9
|
import { currentRange } from '../core/range.js';
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
@@ -199,7 +199,7 @@ export function handleKeydown(event, editable, options = {}) {
|
|
|
199
199
|
if (para && isLi(para)) {
|
|
200
200
|
event.preventDefault();
|
|
201
201
|
if (event.shiftKey) {
|
|
202
|
-
|
|
202
|
+
outdent();
|
|
203
203
|
} else {
|
|
204
204
|
execCommand('indent');
|
|
205
205
|
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* de.js - German locale
|
|
3
|
+
* Deep-merged over en.js; only keys that differ from English are listed.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** @type {Partial<import('../../../types/index.js').AsnLocale>} */
|
|
7
|
+
export const de = {
|
|
8
|
+
toolbar: {
|
|
9
|
+
bold: 'Fett (Ctrl+B)',
|
|
10
|
+
italic: 'Kursiv (Ctrl+I)',
|
|
11
|
+
underline: 'Unterstrichen (Ctrl+U)',
|
|
12
|
+
strikethrough: 'Durchgestrichen',
|
|
13
|
+
superscript: 'Hochgestellt',
|
|
14
|
+
subscript: 'Tiefgestellt',
|
|
15
|
+
alignLeft: 'Linksbündig',
|
|
16
|
+
alignCenter: 'Zentriert',
|
|
17
|
+
alignRight: 'Rechtsbündig',
|
|
18
|
+
alignJustify: 'Blocksatz',
|
|
19
|
+
ul: 'Ungeordnete Liste',
|
|
20
|
+
ol: 'Geordnete Liste',
|
|
21
|
+
checklist: 'Checkliste',
|
|
22
|
+
indent: 'Einzug vergrößern',
|
|
23
|
+
outdent: 'Einzug verkleinern',
|
|
24
|
+
undo: 'Rückgängig (Ctrl+Z)',
|
|
25
|
+
redo: 'Wiederholen (Ctrl+Y)',
|
|
26
|
+
hr: 'Horizontale Linie',
|
|
27
|
+
link: 'Link einfügen',
|
|
28
|
+
image: 'Bild einfügen',
|
|
29
|
+
video: 'Video einfügen',
|
|
30
|
+
emoji: 'Emoji einfügen',
|
|
31
|
+
icon: 'FA-Symbol einfügen',
|
|
32
|
+
table: 'Tabelle einfügen',
|
|
33
|
+
fontSize: 'Schriftgröße',
|
|
34
|
+
fontSizePlaceholder: 'Größe',
|
|
35
|
+
removeFormat: 'Formatierung entfernen',
|
|
36
|
+
direction: 'Textrichtung umschalten (LTR / RTL)',
|
|
37
|
+
fontFamily: 'Schriftart',
|
|
38
|
+
paragraphStyle: 'Absatzstil',
|
|
39
|
+
paragraphStylePlaceholder: 'Stil',
|
|
40
|
+
lineHeight: 'Zeilenhöhe',
|
|
41
|
+
lineHeightPlaceholder: '↕ Zeile',
|
|
42
|
+
codeview: 'HTML-Code-Ansicht',
|
|
43
|
+
fullscreen: 'Vollbild',
|
|
44
|
+
shortcuts: 'Tastenkürzel (Ctrl+Shift+/)',
|
|
45
|
+
find: 'Suchen (Ctrl+F)',
|
|
46
|
+
findReplace: 'Suchen & Ersetzen (Ctrl+H)',
|
|
47
|
+
inlineCode: 'Inline-Code (Ctrl+`)',
|
|
48
|
+
print: 'Drucken',
|
|
49
|
+
foreColor: 'Textfarbe',
|
|
50
|
+
backColor: 'Hervorhebungsfarbe',
|
|
51
|
+
chooseTextColor: 'Textfarbe auswählen',
|
|
52
|
+
chooseHighlightColor: 'Hervorhebungsfarbe auswählen',
|
|
53
|
+
customColor: 'Benutzerdefinierte Farbe',
|
|
54
|
+
insertTableLabel: 'Tabelle einfügen',
|
|
55
|
+
paragraphItems: {
|
|
56
|
+
p: 'Normal',
|
|
57
|
+
blockquote: 'Zitat',
|
|
58
|
+
pre: 'Code',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
linkDialog: {
|
|
63
|
+
ariaLabel: 'Link einfügen',
|
|
64
|
+
title: 'Link einfügen',
|
|
65
|
+
url: 'URL',
|
|
66
|
+
urlPlaceholder: 'https://',
|
|
67
|
+
displayText: 'Anzeigetext',
|
|
68
|
+
textPlaceholder: 'Linktext',
|
|
69
|
+
openInNewTab: 'In neuem Tab öffnen',
|
|
70
|
+
insertBtn: 'Einfügen',
|
|
71
|
+
cancelBtn: 'Abbrechen',
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
imageDialog: {
|
|
75
|
+
ariaLabel: 'Bild einfügen',
|
|
76
|
+
title: 'Bild einfügen',
|
|
77
|
+
imageUrl: 'Bild-URL',
|
|
78
|
+
urlPlaceholder: 'https://example.com/image.png',
|
|
79
|
+
altText: 'Alt-Text',
|
|
80
|
+
altPlaceholder: 'Bild beschreiben',
|
|
81
|
+
alignment: 'Ausrichtung',
|
|
82
|
+
alignNone: 'Keine',
|
|
83
|
+
alignLeft: 'Links',
|
|
84
|
+
alignCenter: 'Mitte',
|
|
85
|
+
alignRight: 'Rechts',
|
|
86
|
+
uploadLabel: 'Oder Datei hochladen',
|
|
87
|
+
insertBtn: 'Einfügen',
|
|
88
|
+
cancelBtn: 'Abbrechen',
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
videoDialog: {
|
|
92
|
+
ariaLabel: 'Video einfügen',
|
|
93
|
+
title: 'Video einfügen',
|
|
94
|
+
videoUrl: 'Video-URL',
|
|
95
|
+
urlPlaceholder: 'YouTube, Vimeo oder direkte .mp4-URL',
|
|
96
|
+
widthLabel: 'Breite (px)',
|
|
97
|
+
widthPlaceholder: '560',
|
|
98
|
+
insertBtn: 'Einfügen',
|
|
99
|
+
cancelBtn: 'Abbrechen',
|
|
100
|
+
detected: (type) => `Erkannt: ${type}`,
|
|
101
|
+
unknownFormat: 'Unbekanntes Format — direktes Video-Einbetten wird versucht',
|
|
102
|
+
invalidUrl: 'Ungültige URL — bitte geben Sie einen gültigen Videolink ein.',
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
emojiDialog: {
|
|
106
|
+
ariaLabel: 'Emoji einfügen',
|
|
107
|
+
title: 'Emoji einfügen',
|
|
108
|
+
searchPlaceholder: 'Emojis suchen…',
|
|
109
|
+
all: 'Alle',
|
|
110
|
+
cancelBtn: 'Abbrechen',
|
|
111
|
+
close: 'Schließen',
|
|
112
|
+
categories: {
|
|
113
|
+
smileys: 'Smileys',
|
|
114
|
+
people: 'Menschen',
|
|
115
|
+
animals: 'Tiere',
|
|
116
|
+
food: 'Essen',
|
|
117
|
+
travel: 'Reisen',
|
|
118
|
+
objects: 'Objekte',
|
|
119
|
+
symbols: 'Symbole',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
iconDialog: {
|
|
124
|
+
ariaLabel: 'FA-Symbol einfügen',
|
|
125
|
+
title: 'FA-Symbol einfügen',
|
|
126
|
+
searchPlaceholder: 'Symbole suchen…',
|
|
127
|
+
all: 'Alle',
|
|
128
|
+
style: 'Stil',
|
|
129
|
+
size: 'Größe',
|
|
130
|
+
color: 'Farbe',
|
|
131
|
+
useColor: ' Farbe verwenden',
|
|
132
|
+
selectHint: 'Symbol auswählen',
|
|
133
|
+
insertBtn: 'FA-Symbol einfügen',
|
|
134
|
+
cancelBtn: 'Abbrechen',
|
|
135
|
+
close: 'Schließen',
|
|
136
|
+
categories: {
|
|
137
|
+
popular: 'Beliebt',
|
|
138
|
+
interface: 'Benutzeroberfläche',
|
|
139
|
+
navigation: 'Navigation',
|
|
140
|
+
media: 'Medien',
|
|
141
|
+
communication: 'Kommunikation',
|
|
142
|
+
files: 'Dateien',
|
|
143
|
+
people: 'Menschen',
|
|
144
|
+
objects: 'Objekte',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
findReplace: {
|
|
149
|
+
findTitle: 'Suchen',
|
|
150
|
+
findReplaceTitle: 'Suchen & Ersetzen',
|
|
151
|
+
findPlaceholder: 'Suchen…',
|
|
152
|
+
searchAriaLabel: 'Suchtext',
|
|
153
|
+
caseSensitive: '\u00a0Groß-/Kleinschreibung',
|
|
154
|
+
prevBtn: '← Zurück',
|
|
155
|
+
nextBtn: 'Weiter →',
|
|
156
|
+
replacePlaceholder: 'Ersetzen durch…',
|
|
157
|
+
replaceAriaLabel: 'Ersetzen durch',
|
|
158
|
+
replaceBtn: 'Ersetzen',
|
|
159
|
+
replaceAllBtn: 'Alle ersetzen',
|
|
160
|
+
close: '×',
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
shortcutsDialog: {
|
|
164
|
+
title: 'Tastenkürzel',
|
|
165
|
+
ariaLabel: 'Tastenkürzel',
|
|
166
|
+
close: 'Schließen',
|
|
167
|
+
shortcuts: [
|
|
168
|
+
{
|
|
169
|
+
category: 'Textformatierung',
|
|
170
|
+
items: [
|
|
171
|
+
{ keys: 'Ctrl + B', action: 'Fett' },
|
|
172
|
+
{ keys: 'Ctrl + I', action: 'Kursiv' },
|
|
173
|
+
{ keys: 'Ctrl + U', action: 'Unterstrichen' },
|
|
174
|
+
{ keys: 'Ctrl + K', action: 'Link einfügen / bearbeiten' },
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
category: 'Verlauf',
|
|
179
|
+
items: [
|
|
180
|
+
{ keys: 'Ctrl + Z', action: 'Rückgängig' },
|
|
181
|
+
{ keys: 'Ctrl + Y / Ctrl + Shift + Z', action: 'Wiederholen' },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
category: 'Auswahl & Navigation',
|
|
186
|
+
items: [
|
|
187
|
+
{ keys: 'Ctrl + A', action: 'Alles auswählen' },
|
|
188
|
+
{ keys: 'Tab', action: 'Listenebene erhöhen / Leerzeichen einfügen' },
|
|
189
|
+
{ keys: 'Shift + Tab', action: 'Listenebene verringern' },
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
category: 'Zwischenablage',
|
|
194
|
+
items: [
|
|
195
|
+
{ keys: 'Ctrl + Shift + V', action: 'Als einfachen Text einfügen' },
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
category: 'Suchen & Ersetzen',
|
|
200
|
+
items: [
|
|
201
|
+
{ keys: 'Ctrl + F', action: 'Im Dokument suchen' },
|
|
202
|
+
{ keys: 'Ctrl + H', action: 'Suchen & Ersetzen' },
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
category: 'Editor',
|
|
207
|
+
items: [
|
|
208
|
+
{ keys: 'Ctrl + Shift + /', action: 'Diesen Tastenkürzel-Dialog anzeigen' },
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
contextMenu: {
|
|
215
|
+
cut: 'Ausschneiden',
|
|
216
|
+
copy: 'Kopieren',
|
|
217
|
+
paste: 'Einfügen',
|
|
218
|
+
bold: 'Fett',
|
|
219
|
+
italic: 'Kursiv',
|
|
220
|
+
underline: 'Unterstrichen',
|
|
221
|
+
textColor: 'Textfarbe',
|
|
222
|
+
highlightColor: 'Hervorhebungsfarbe',
|
|
223
|
+
copyFormat: 'Format kopieren',
|
|
224
|
+
pasteFormat: 'Format einfügen',
|
|
225
|
+
removeFormat: 'Formatierung entfernen',
|
|
226
|
+
link: 'Link einfügen',
|
|
227
|
+
image: 'Bild einfügen',
|
|
228
|
+
video: 'Video einfügen',
|
|
229
|
+
table: 'Tabelle einfügen',
|
|
230
|
+
back: 'Zurück',
|
|
231
|
+
noHighlight: 'Keine Hervorhebung',
|
|
232
|
+
customColor: 'Benutzerdefinierte Farbe',
|
|
233
|
+
customColorLabel: 'Benutzerdefiniert…',
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
statusbar: {
|
|
237
|
+
resizeHandle: 'Editor-Größe ändern',
|
|
238
|
+
words: (n) => `Wörter: ${n}`,
|
|
239
|
+
wordsLimit: (n, max) => `Wörter: ${n}/${max}`,
|
|
240
|
+
chars: (n) => `Zeichen: ${n}`,
|
|
241
|
+
charsLimit: (n, max) => `Zeichen: ${n}/${max}`,
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
tooltips: {
|
|
245
|
+
link: {
|
|
246
|
+
ariaLabel: 'Link-Aktionen',
|
|
247
|
+
openLink: 'Link öffnen',
|
|
248
|
+
copyUrl: 'URL kopieren',
|
|
249
|
+
editLink: 'Link bearbeiten',
|
|
250
|
+
removeLink: 'Link entfernen',
|
|
251
|
+
},
|
|
252
|
+
image: {
|
|
253
|
+
ariaLabel: 'Bild-Aktionen',
|
|
254
|
+
label: 'Bild',
|
|
255
|
+
floatLeft: 'Links umfließen',
|
|
256
|
+
noFloat: 'Kein Umfluss',
|
|
257
|
+
alignCenter: 'Zentriert',
|
|
258
|
+
floatRight: 'Rechts umfließen',
|
|
259
|
+
originalSize: 'Originalgröße',
|
|
260
|
+
rotateLeft: 'Links drehen',
|
|
261
|
+
rotateRight: 'Rechts drehen',
|
|
262
|
+
cropImage: 'Bild zuschneiden',
|
|
263
|
+
addCaption: 'Beschriftung hinzufügen / bearbeiten',
|
|
264
|
+
deleteImage: 'Bild löschen',
|
|
265
|
+
},
|
|
266
|
+
code: {
|
|
267
|
+
ariaLabel: 'Codeblock-Aktionen',
|
|
268
|
+
label: 'Code',
|
|
269
|
+
syntaxLanguage: 'Syntaxsprache',
|
|
270
|
+
syntaxAriaLabel: 'Syntaxsprache',
|
|
271
|
+
copyCode: 'Code kopieren',
|
|
272
|
+
toggleWordWrap: 'Zeilenumbruch umschalten',
|
|
273
|
+
enableWordWrap: 'Zeilenumbruch aktivieren',
|
|
274
|
+
disableWordWrap: 'Zeilenumbruch deaktivieren',
|
|
275
|
+
convertToParagraph: 'In Absatz umwandeln',
|
|
276
|
+
deleteCodeBlock: 'Codeblock löschen',
|
|
277
|
+
},
|
|
278
|
+
table: {
|
|
279
|
+
ariaLabel: 'Tabellen-Aktionen',
|
|
280
|
+
label: 'Tabelle',
|
|
281
|
+
selectCells: 'Zellen auswählen',
|
|
282
|
+
addRowAbove: 'Zeile oben hinzufügen',
|
|
283
|
+
addRowBelow: 'Zeile unten hinzufügen',
|
|
284
|
+
deleteRow: 'Zeile löschen',
|
|
285
|
+
addColumnLeft: 'Spalte links hinzufügen',
|
|
286
|
+
addColumnRight: 'Spalte rechts hinzufügen',
|
|
287
|
+
deleteColumn: 'Spalte löschen',
|
|
288
|
+
mergeCells: 'Zellen zusammenführen',
|
|
289
|
+
unmergeCells: 'Zellen trennen',
|
|
290
|
+
columnWidth: 'Spaltenbreite',
|
|
291
|
+
rowHeight: 'Zeilenhöhe',
|
|
292
|
+
tableBorderWidth: 'Tabellenrahmenbreite',
|
|
293
|
+
deleteTable: 'Tabelle löschen',
|
|
294
|
+
columnWidthPx: 'Spaltenbreite (px)',
|
|
295
|
+
rowHeightPx: 'Zeilenhöhe (px)',
|
|
296
|
+
tableBorderWidthPx: 'Tabellenrahmenbreite (px)',
|
|
297
|
+
cancelBtn: 'Abbrechen',
|
|
298
|
+
applyBtn: 'Anwenden',
|
|
299
|
+
},
|
|
300
|
+
video: {
|
|
301
|
+
ariaLabel: 'Video-Aktionen',
|
|
302
|
+
label: 'Video',
|
|
303
|
+
floatLeft: 'Links umfließen',
|
|
304
|
+
noFloat: 'Kein Umfluss',
|
|
305
|
+
alignCenter: 'Zentriert',
|
|
306
|
+
floatRight: 'Rechts umfließen',
|
|
307
|
+
originalSize: 'Originalgröße',
|
|
308
|
+
previewVideo: 'Video-Vorschau',
|
|
309
|
+
exitPreview: 'Vorschau beenden',
|
|
310
|
+
deleteVideo: 'Video löschen',
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
errors: {
|
|
315
|
+
imageFormat: (type) =>
|
|
316
|
+
`Das Format „${type}" wird in Webbrowsern nicht unterstützt. Bitte konvertieren Sie es zuerst in JPEG, PNG oder WebP.`,
|
|
317
|
+
imageSize: (maxSize) =>
|
|
318
|
+
`Die Bilddatei ist zu groß. Die maximal zulässige Größe beträgt ${maxSize} MB.`,
|
|
319
|
+
},
|
|
320
|
+
};
|