diff --git a/config/current.ver b/config/current.ver
index d66ddbd..c3635d3 100644
--- a/config/current.ver
+++ b/config/current.ver
@@ -1 +1 @@
-1.1.54
\ No newline at end of file
+1.1.55
\ No newline at end of file
diff --git a/public/assets/js/bridge/rte-editor.js b/public/assets/js/bridge/rte-editor.js
index 543d69d..f116717 100644
--- a/public/assets/js/bridge/rte-editor.js
+++ b/public/assets/js/bridge/rte-editor.js
@@ -502,6 +502,87 @@
saveSelection();
} catch {}
};
+ const selectionHasStyle = (range, styleProp, tags, styleMatch) => {
+ if (!range) return false;
+ try {
+ const fragment = range.cloneContents();
+ const walker = (node) => {
+ if (!node) return false;
+ if (node.nodeType === 1) {
+ const tag = node.tagName ? node.tagName.toUpperCase() : '';
+ if (tags && tags.includes(tag)) return true;
+ if (styleProp && node.style) {
+ if (styleMatch && styleMatch(node.style[styleProp] || '', node)) return true;
+ }
+ for (const child of Array.from(node.childNodes)) {
+ if (walker(child)) return true;
+ }
+ }
+ return false;
+ };
+ for (const child of Array.from(fragment.childNodes)) {
+ if (walker(child)) return true;
+ }
+ } catch {}
+ return false;
+ };
+ const removeInlineStyle = (styleProp, tags, clearFn) => {
+ try {
+ content.focus();
+ restoreSelection();
+ const docRef = content.ownerDocument || document;
+ const range = getSelectionRange();
+ if (!range || range.collapsed) return;
+ const fragment = range.extractContents();
+ const unwrap = (node) => {
+ const parent = node.parentNode;
+ if (!parent) return;
+ while (node.firstChild) parent.insertBefore(node.firstChild, node);
+ parent.removeChild(node);
+ };
+ const strip = (node) => {
+ if (!node) return;
+ if (node.nodeType === 1) {
+ const tag = node.tagName ? node.tagName.toUpperCase() : '';
+ if (tags && tags.includes(tag)) {
+ const children = Array.from(node.childNodes);
+ children.forEach(strip);
+ unwrap(node);
+ return;
+ }
+ if (styleProp && node.style) {
+ if (typeof clearFn === 'function') {
+ clearFn(node.style);
+ } else {
+ node.style[styleProp] = '';
+ }
+ const styleAttr = node.getAttribute('style');
+ if (!styleAttr || !String(styleAttr).trim()) {
+ const children = Array.from(node.childNodes);
+ children.forEach(strip);
+ unwrap(node);
+ return;
+ }
+ }
+ Array.from(node.childNodes).forEach(strip);
+ }
+ };
+ Array.from(fragment.childNodes).forEach(strip);
+ range.insertNode(fragment);
+ saveSelection();
+ } catch {}
+ };
+ const toggleInlineStyle = (cmd, styleProp, value, tags, styleMatch, clearFn) => {
+ const range = getSelectionRange();
+ if (!range || range.collapsed) return;
+ if (selectionHasStyle(range, styleProp, tags, styleMatch)) {
+ if (cmd) exec(cmd);
+ removeInlineStyle(styleProp, tags, clearFn);
+ return;
+ }
+ if (cmd) exec(cmd);
+ if (styleProp) applyInlineStyle(styleProp, value);
+ };
const removeInlineFormatting = () => {
try {
content.focus();
@@ -669,25 +750,52 @@
if (!ui.overrideToolbar) {
const icon = (path) => ``;
addButton(
- icon('M6 4h5a3 3 0 0 1 0 6H6V4zm0 8h6a3 3 0 0 1 0 6H6v-6z'),
+ 'B',
'Fett',
'bold',
null,
- () => applyInlineStyle('fontWeight', '700')
+ () => toggleInlineStyle(
+ 'bold',
+ 'fontWeight',
+ '700',
+ ['B', 'STRONG'],
+ (val) => {
+ const num = parseInt(String(val || '').replace(/[^0-9]/g, ''), 10);
+ return Number.isFinite(num) ? num >= 600 : /bold/i.test(String(val || ''));
+ },
+ (style) => { style.fontWeight = ''; }
+ )
);
addButton(
- icon('M10 4h8v2h-3l-4 12h3v2H6v-2h3l4-12h-3V4z'),
+ 'I',
'Kursiv',
'italic',
null,
- () => applyInlineStyle('fontStyle', 'italic')
+ () => toggleInlineStyle(
+ 'italic',
+ 'fontStyle',
+ 'italic',
+ ['I', 'EM'],
+ (val) => String(val || '').toLowerCase() === 'italic',
+ (style) => { style.fontStyle = ''; }
+ )
);
addButton(
- icon('M5 4h14v2h-6v3h4a4 4 0 0 1 0 8H7v-2h10a2 2 0 0 0 0-4h-4V6H5V4z'),
+ 'U',
'Unterstrichen',
'underline',
null,
- () => applyInlineStyle('textDecoration', 'underline')
+ () => toggleInlineStyle(
+ 'underline',
+ 'textDecoration',
+ 'underline',
+ ['U'],
+ (val) => /underline/i.test(String(val || '')),
+ (style) => {
+ style.textDecoration = '';
+ style.textDecorationLine = '';
+ }
+ )
);
addButton(icon('M4 7h10v2H4zM4 11h16v2H4zM4 15h10v2H4z'), 'Liste (ungeordnet)', 'insertUnorderedList');
addButton(icon('M4 6h4v2H4V6zm0 4h4v2H4v-2zm0 4h4v2H4v-2zm6-8h10v2H10V6zm0 4h10v2H10v-2zm0 4h10v2H10v-2z'), 'Liste (geordnet)', 'insertOrderedList');
@@ -702,7 +810,7 @@
addButton(icon('M7 4h4v4H7V4zm6 12h4v4h-4v-4zM7 10h10v2H7v-2z'), 'Einzug', 'indent');
addButton(icon('M13 4h4v4h-4V4zM7 16h4v4H7v-4zM7 10h10v2H7v-2z'), 'Ausruecken', 'outdent');
addButton(
- icon('M5 5h14v2H5zM5 9h10v2H5zM5 13h14v2H5zM5 17h10v2H5z'),
+ 'Tx',
'Formatierung entfernen',
null,
null,
@@ -776,7 +884,21 @@
editorCss = editor && typeof editor.getCss === 'function' ? String(editor.getCss() || '') : '';
} catch {}
const frameCss = this.collectFrameCss(editor);
- injectedStyle.textContent = `${fontCss}\n${editorCss}\n${frameCss}`.trim();
+ const rteUiCss = `
+ .bridge-rte-toolbar { gap: 8px; }
+ .bridge-rte-btn {
+ font-size: 14px;
+ line-height: 1;
+ min-height: 30px;
+ min-width: 30px;
+ color: #0f172a;
+ background: #f1f5f9;
+ }
+ .bridge-rte-btn:hover { background: #e2e8f0; }
+ .bridge-rte-btn:active { transform: translateY(1px); }
+ .bridge-rte-actions .bridge-rte-btn { min-width: 88px; }
+ `.trim();
+ injectedStyle.textContent = [fontCss, editorCss, frameCss, rteUiCss].filter(Boolean).join('\n');
if (injectedStyle.textContent) {
container.appendChild(injectedStyle);
}