From eafeec2b2cb9b84538c2dedc4de5811be72aa56a Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Sat, 2 Mar 2024 09:52:59 +0000 Subject: [PATCH 01/11] theme config option - light/dark mode --- action/action.php | 1 + conf/default.php | 1 + conf/metadata.php | 1 + lang/en/settings.php | 4 ++++ script/DiagramsEditor.js | 2 +- 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/action/action.php b/action/action.php index 39e808c..5e91d54 100644 --- a/action/action.php +++ b/action/action.php @@ -40,6 +40,7 @@ public function addJsinfo(Doku_Event $event) $JSINFO['sectok'] = getSecurityToken(); $JSINFO['plugins']['diagrams'] = [ 'service_url' => $this->getConf('service_url'), + 'theme' => $this->getConf('theme'), 'mode' => $this->getConf('mode'), ]; } diff --git a/conf/default.php b/conf/default.php index 3f88c01..f548dc0 100644 --- a/conf/default.php +++ b/conf/default.php @@ -3,5 +3,6 @@ * Diagrams plugin, default configuration settings */ $conf['service_url'] = 'https://embed.diagrams.net/?embed=1&proto=json&spin=1&svg-warning=0'; +$conf['theme'] = 'light'; $conf['mode'] = 1; $conf['pngcache'] = 0; diff --git a/conf/metadata.php b/conf/metadata.php index 997c5f2..b1c2494 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -3,5 +3,6 @@ * Diagrams plugin, configuration metadata */ $meta['service_url'] = array('string'); +$meta['theme'] = array('multichoice', '_choices' => array('light', 'dark')); $meta['mode'] = array('multichoice', '_choices' => array(1, 2, 3)); $meta['pngcache'] = array('onoff'); diff --git a/lang/en/settings.php b/lang/en/settings.php index 3884723..092a82d 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -6,9 +6,13 @@ */ $lang['service_url'] = 'Defines which diagrams.net editor url is used.'; +$lang['theme'] = 'Which theme should diagrams use?'; $lang['mode'] = 'How should diagrams be stored?'; $lang['pngcache'] = 'Should diagrams also be cached as PNG? (recommended when using the dw2pdf plugin)'; +$lang['theme_o_light'] = 'Light'; +$lang['theme_o_dark'] = 'Dark'; + $lang['mode_o_1'] = 'Media Files'; $lang['mode_o_2'] = 'Embedded in the page'; $lang['mode_o_3'] = 'Allow both'; diff --git a/script/DiagramsEditor.js b/script/DiagramsEditor.js index 8f44776..e8198a0 100644 --- a/script/DiagramsEditor.js +++ b/script/DiagramsEditor.js @@ -192,7 +192,7 @@ class DiagramsEditor { #createEditor() { this.#diagramsEditor = document.createElement('iframe'); this.#diagramsEditor.id = 'plugin__diagrams-editor'; - this.#diagramsEditor.src = JSINFO['plugins']['diagrams']['service_url']; + this.#diagramsEditor.src = JSINFO['plugins']['diagrams']['service_url'] + '&ui=' + JSINFO['plugins']['diagrams']['theme']; document.body.appendChild(this.#diagramsEditor); this.#listener = this.#handleMessage.bind(this); From fc11b14a63a8458f09f2773b7a87f6e7fb6d1d03 Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Sun, 3 Mar 2024 15:47:54 +0000 Subject: [PATCH 02/11] dark mode styling for svg diagram --- all.less | 6 +++++- syntax/mediafile.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/all.less b/all.less index ab55a5e..16473b3 100644 --- a/all.less +++ b/all.less @@ -41,7 +41,11 @@ a.plugin_diagrams_create { object { // fix for sometimes missing edges - max-width: 99%; + max-width: 99%; + + &.dark { + filter: invert(93%) hue-rotate(180deg); + } } } diff --git a/syntax/mediafile.php b/syntax/mediafile.php index 474ea5e..4eec7f8 100644 --- a/syntax/mediafile.php +++ b/syntax/mediafile.php @@ -125,7 +125,7 @@ public function render($format, Doku_Renderer $renderer, $data) $wrapperAttributes['class'] = 'media diagrams-svg-wrapper media' . $data['align']; $imageAttributes = []; - $imageAttributes['class'] = 'diagrams-svg'; + $imageAttributes['class'] = "diagrams-svg " . $this->getConf('theme'); $imageAttributes['data'] = $data['url']; $imageAttributes['data-id'] = cleanID($data['src'] ?? ''); $imageAttributes['type'] = 'image/svg+xml'; From 03023c5e012958b458a175231a93ad8067304714 Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Mon, 4 Mar 2024 20:38:14 +0000 Subject: [PATCH 03/11] dark mode applied at svg level --- action/embed.php | 23 +++++++++++++++++++++++ all.less | 6 +----- syntax/mediafile.php | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/action/embed.php b/action/embed.php index 2c05c56..42645af 100644 --- a/action/embed.php +++ b/action/embed.php @@ -112,6 +112,10 @@ public function handleSave(Doku_Event $event) return; } + if ($this->getConf('theme') === 'dark') { + $svg = $this->addDarkModeStyle($svg); + } + $original = rawWiki($id); $new = substr($original, 0, $pos) . $svg . substr($original, $pos + $len); saveWikiText($id, $new, $this->getLang('embedSaveSummary')); @@ -119,5 +123,24 @@ public function handleSave(Doku_Event $event) echo 'OK'; } + /** + * Adds style node to render svg in dark theme. + * + * @param string $svg + */ + private function addDarkModeStyle(string $svg) + { + $svgAsXML = simplexml_load_string($svg); + $svgAsXML->addAttribute('class', 'ge-export-svg-dark'); + + $defs = $svgAsXML->defs; + + $style = $defs->addChild('style'); + $style->addAttribute('type', 'text/css'); + $style[0] = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; + + $output = $svgAsXML->saveXML(); + return $output; + } } diff --git a/all.less b/all.less index 16473b3..ab55a5e 100644 --- a/all.less +++ b/all.less @@ -41,11 +41,7 @@ a.plugin_diagrams_create { object { // fix for sometimes missing edges - max-width: 99%; - - &.dark { - filter: invert(93%) hue-rotate(180deg); - } + max-width: 99%; } } diff --git a/syntax/mediafile.php b/syntax/mediafile.php index 4eec7f8..a836b49 100644 --- a/syntax/mediafile.php +++ b/syntax/mediafile.php @@ -125,7 +125,7 @@ public function render($format, Doku_Renderer $renderer, $data) $wrapperAttributes['class'] = 'media diagrams-svg-wrapper media' . $data['align']; $imageAttributes = []; - $imageAttributes['class'] = "diagrams-svg " . $this->getConf('theme'); + $imageAttributes['class'] = "diagrams-svg"; $imageAttributes['data'] = $data['url']; $imageAttributes['data-id'] = cleanID($data['src'] ?? ''); $imageAttributes['type'] = 'image/svg+xml'; From 229e33efa693850609a99a836cd167d545b9e9c7 Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Mon, 4 Mar 2024 21:31:22 +0000 Subject: [PATCH 04/11] moved helper into Diagrams class --- Diagrams.php | 20 ++++++++++++++++++++ action/embed.php | 22 +--------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Diagrams.php b/Diagrams.php index 2e37e4f..341cbe6 100644 --- a/Diagrams.php +++ b/Diagrams.php @@ -23,4 +23,24 @@ class Diagrams ]; const CACHE_EXT = '.diagrams.png'; + + /** + * Adds style node to render svg in dark theme + * + * @param string $svg + */ + public static function addDarkModeStyle(string $svg) + { + $svgAsXML = simplexml_load_string($svg); + $svgAsXML->addAttribute('class', 'ge-export-svg-dark'); + + $defs = $svgAsXML->defs; + + $style = $defs->addChild('style'); + $style->addAttribute('type', 'text/css'); + $style[0] = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; + + $output = $svgAsXML->saveXML(); + return $output; + } } diff --git a/action/embed.php b/action/embed.php index 42645af..d164835 100644 --- a/action/embed.php +++ b/action/embed.php @@ -113,7 +113,7 @@ public function handleSave(Doku_Event $event) } if ($this->getConf('theme') === 'dark') { - $svg = $this->addDarkModeStyle($svg); + $svg = Diagrams::addDarkModeStyle($svg); } $original = rawWiki($id); @@ -122,25 +122,5 @@ public function handleSave(Doku_Event $event) unlock($id); echo 'OK'; } - - /** - * Adds style node to render svg in dark theme. - * - * @param string $svg - */ - private function addDarkModeStyle(string $svg) - { - $svgAsXML = simplexml_load_string($svg); - $svgAsXML->addAttribute('class', 'ge-export-svg-dark'); - - $defs = $svgAsXML->defs; - - $style = $defs->addChild('style'); - $style->addAttribute('type', 'text/css'); - $style[0] = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; - - $output = $svgAsXML->saveXML(); - return $output; - } } From 119d80dcf43df29d33ab98e6b47b19f0436e8556 Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Mon, 4 Mar 2024 21:37:34 +0000 Subject: [PATCH 05/11] moving dark mode helper to correct place --- Diagrams.php | 20 -------------------- action/embed.php | 2 +- helper.php | 21 +++++++++++++++++++++ 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/Diagrams.php b/Diagrams.php index 341cbe6..2e37e4f 100644 --- a/Diagrams.php +++ b/Diagrams.php @@ -23,24 +23,4 @@ class Diagrams ]; const CACHE_EXT = '.diagrams.png'; - - /** - * Adds style node to render svg in dark theme - * - * @param string $svg - */ - public static function addDarkModeStyle(string $svg) - { - $svgAsXML = simplexml_load_string($svg); - $svgAsXML->addAttribute('class', 'ge-export-svg-dark'); - - $defs = $svgAsXML->defs; - - $style = $defs->addChild('style'); - $style->addAttribute('type', 'text/css'); - $style[0] = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; - - $output = $svgAsXML->saveXML(); - return $output; - } } diff --git a/action/embed.php b/action/embed.php index d164835..848c68d 100644 --- a/action/embed.php +++ b/action/embed.php @@ -113,7 +113,7 @@ public function handleSave(Doku_Event $event) } if ($this->getConf('theme') === 'dark') { - $svg = Diagrams::addDarkModeStyle($svg); + $svg = $this->helper->addDarkModeStyle($svg); } $original = rawWiki($id); diff --git a/helper.php b/helper.php index 7bd2d3a..b4523f1 100644 --- a/helper.php +++ b/helper.php @@ -41,4 +41,25 @@ public function isDiagram($svg) $serviceHost = parse_url($confServiceUrl, PHP_URL_HOST); // Host-Portion of the Url, e.g. "diagrams.xyz.org" return strpos($svg, 'embed.diagrams.net') || strpos($svg, 'draw.io') || strpos($svg, $serviceHost); } + + /** + * Adds style node to render svg in dark theme + * + * @param string $svg The raw SVG data + * @return string + */ + public static function addDarkModeStyle(string $svg) + { + $svgAsXML = simplexml_load_string($svg); + $svgAsXML->addAttribute('class', 'ge-export-svg-dark'); + + $defs = $svgAsXML->defs; + + $style = $defs->addChild('style'); + $style->addAttribute('type', 'text/css'); + $style[0] = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; + + $output = $svgAsXML->saveXML(); + return $output; + } } From 3fa21099d144fcef212759642f2e8e2ff9447a8e Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Tue, 5 Mar 2024 16:01:18 +0000 Subject: [PATCH 06/11] moving dark style to js --- action/embed.php | 4 ---- helper.php | 21 --------------------- script/DiagramsEditor.js | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/action/embed.php b/action/embed.php index 848c68d..a3b13a0 100644 --- a/action/embed.php +++ b/action/embed.php @@ -112,10 +112,6 @@ public function handleSave(Doku_Event $event) return; } - if ($this->getConf('theme') === 'dark') { - $svg = $this->helper->addDarkModeStyle($svg); - } - $original = rawWiki($id); $new = substr($original, 0, $pos) . $svg . substr($original, $pos + $len); saveWikiText($id, $new, $this->getLang('embedSaveSummary')); diff --git a/helper.php b/helper.php index b4523f1..7bd2d3a 100644 --- a/helper.php +++ b/helper.php @@ -41,25 +41,4 @@ public function isDiagram($svg) $serviceHost = parse_url($confServiceUrl, PHP_URL_HOST); // Host-Portion of the Url, e.g. "diagrams.xyz.org" return strpos($svg, 'embed.diagrams.net') || strpos($svg, 'draw.io') || strpos($svg, $serviceHost); } - - /** - * Adds style node to render svg in dark theme - * - * @param string $svg The raw SVG data - * @return string - */ - public static function addDarkModeStyle(string $svg) - { - $svgAsXML = simplexml_load_string($svg); - $svgAsXML->addAttribute('class', 'ge-export-svg-dark'); - - $defs = $svgAsXML->defs; - - $style = $defs->addChild('style'); - $style->addAttribute('type', 'text/css'); - $style[0] = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; - - $output = $svgAsXML->saveXML(); - return $output; - } } diff --git a/script/DiagramsEditor.js b/script/DiagramsEditor.js index e8198a0..1e6b44b 100644 --- a/script/DiagramsEditor.js +++ b/script/DiagramsEditor.js @@ -126,7 +126,9 @@ class DiagramsEditor { const response = await fetch(uploadUrl, { method: 'POST', cache: 'no-cache', - body: svg, + body: JSINFO['plugins']['diagrams']['theme'] === 'dark' + ? this.#addDarkModeStyle(svg) + : svg }); return response.ok; @@ -151,7 +153,12 @@ class DiagramsEditor { '§ok=' + JSINFO['sectok']; const body = new FormData(); - body.set('svg', svg); + body.set( + 'svg', + JSINFO['plugins']['diagrams']['theme'] === 'dark' + ? this.#addDarkModeStyle(svg) + : svg + ); const response = await fetch(uploadUrl, { method: 'POST', @@ -227,6 +234,29 @@ class DiagramsEditor { ); } + /** + * Adds style node to render svg in dark theme + * + * @param {string} svg The raw SVG data + * @return {string} + */ + #addDarkModeStyle(svg) + { + const parser = new DOMParser(); + const xml = parser.parseFromString(svg, "image/svg+xml"); + + xml.documentElement.setAttribute('class', 'ge-export-svg-dark'); + + const darkStyle = xml.createElementNS(xml.documentElement.namespaceURI, 'style'); + darkStyle.setAttribute('type', 'text/css'); + darkStyle.textContent = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; + + const defs = xml.getElementsByTagName('defs')[0]; + defs.appendChild(darkStyle); + + return new XMLSerializer().serializeToString(xml); + } + /** * Handle messages from diagramming service * From a0d4921561e5f2e239bf4146f022ee01815c077d Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Tue, 5 Mar 2024 18:03:14 +0000 Subject: [PATCH 07/11] add button handles dark mode --- script/DiagramsEditor.js | 19 +++++++------------ script/embed-toolbar.js | 2 ++ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/script/DiagramsEditor.js b/script/DiagramsEditor.js index 1e6b44b..2333b4b 100644 --- a/script/DiagramsEditor.js +++ b/script/DiagramsEditor.js @@ -126,9 +126,7 @@ class DiagramsEditor { const response = await fetch(uploadUrl, { method: 'POST', cache: 'no-cache', - body: JSINFO['plugins']['diagrams']['theme'] === 'dark' - ? this.#addDarkModeStyle(svg) - : svg + body: DiagramsEditor.svgThemeProcessing(svg), }); return response.ok; @@ -153,12 +151,7 @@ class DiagramsEditor { '§ok=' + JSINFO['sectok']; const body = new FormData(); - body.set( - 'svg', - JSINFO['plugins']['diagrams']['theme'] === 'dark' - ? this.#addDarkModeStyle(svg) - : svg - ); + body.set('svg', DiagramsEditor.svgThemeProcessing(svg)); const response = await fetch(uploadUrl, { method: 'POST', @@ -181,7 +174,7 @@ class DiagramsEditor { '§ok=' + JSINFO['sectok']; const body = new FormData(); - body.set('svg', svg); + body.set('svg', DiagramsEditor.svgThemeProcessing(svg)); body.set('png', png); const response = await fetch(uploadUrl, { @@ -235,13 +228,15 @@ class DiagramsEditor { } /** - * Adds style node to render svg in dark theme + * Add additional theme properties to svg data * * @param {string} svg The raw SVG data * @return {string} */ - #addDarkModeStyle(svg) + static svgThemeProcessing(svg) { + if (JSINFO['plugins']['diagrams']['theme'] !== 'dark') return svg; + const parser = new DOMParser(); const xml = parser.parseFromString(svg, "image/svg+xml"); diff --git a/script/embed-toolbar.js b/script/embed-toolbar.js index 8e62965..5443106 100644 --- a/script/embed-toolbar.js +++ b/script/embed-toolbar.js @@ -52,6 +52,8 @@ if (typeof window.toolbar !== 'undefined') { const origSvg = area.value.substring(selection.start, selection.end); diagramsEditor.editMemory(origSvg, svg => { + svg = DiagramsEditor.svgThemeProcessing(svg); + if (!origSvg) { // if this is a new diagram, wrap it in a tag svg = '' + svg + ''; From a2dbe344dcb0510faf1b280e201c61e62f03b6d5 Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Tue, 5 Mar 2024 19:33:25 +0000 Subject: [PATCH 08/11] handle svg theme on export in editor --- script/DiagramsEditor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script/DiagramsEditor.js b/script/DiagramsEditor.js index 2333b4b..b47e858 100644 --- a/script/DiagramsEditor.js +++ b/script/DiagramsEditor.js @@ -281,6 +281,7 @@ class DiagramsEditor { case 'export': if (msg.format === 'svg') { this.#svg = this.#decodeDataUri(msg.data); + this.#svg = DiagramsEditor.svgThemeProcessing(this.#svg); // export again as PNG this.#diagramsEditor.contentWindow.postMessage( From 36ca60e6f058ca0e0be9d455fcf1ecb9569a740a Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Tue, 5 Mar 2024 21:37:35 +0000 Subject: [PATCH 09/11] postsavecallback replace bug fix --- script/embed-editbutton.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/embed-editbutton.js b/script/embed-editbutton.js index 9e61443..019fc9c 100644 --- a/script/embed-editbutton.js +++ b/script/embed-editbutton.js @@ -18,7 +18,8 @@ document.addEventListener('DOMContentLoaded', () => { event.preventDefault(); const diagramsEditor = new DiagramsEditor(() => { // replace instead of reload to avoid accidentally re-submitting forms - window.location.replace(window.location.href); + // hash breaks replace, must be ignored + window.location.replace(window.location.pathname + window.location.search); }); diagramsEditor.editEmbed( JSINFO.id, From b3d3b9fd74d35922c18aee142f8b5f8e48d940a6 Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Thu, 7 Mar 2024 21:07:23 +0000 Subject: [PATCH 10/11] removing unicode characters to fix invert of foreign objects --- script/DiagramsEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/DiagramsEditor.js b/script/DiagramsEditor.js index b47e858..ea7d1ee 100644 --- a/script/DiagramsEditor.js +++ b/script/DiagramsEditor.js @@ -244,7 +244,7 @@ class DiagramsEditor { const darkStyle = xml.createElementNS(xml.documentElement.namespaceURI, 'style'); darkStyle.setAttribute('type', 'text/css'); - darkStyle.textContent = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; + darkStyle.textContent = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; const defs = xml.getElementsByTagName('defs')[0]; defs.appendChild(darkStyle); From 734f986a30b4e9de35a8a80f67bc05188141ffd2 Mon Sep 17 00:00:00 2001 From: sungreenpepper Date: Thu, 7 Mar 2024 21:11:31 +0000 Subject: [PATCH 11/11] replaceChildren to avoid same style tag appended multiple times --- script/DiagramsEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/DiagramsEditor.js b/script/DiagramsEditor.js index ea7d1ee..e6f9767 100644 --- a/script/DiagramsEditor.js +++ b/script/DiagramsEditor.js @@ -247,7 +247,7 @@ class DiagramsEditor { darkStyle.textContent = 'svg.ge-export-svg-dark { filter: invert(100%) hue-rotate(180deg); } svg.ge-export-svg-dark foreignObject img, svg.ge-export-svg-dark image:not(svg.ge-export-svg-dark switch image), svg.ge-export-svg-dark svg { filter: invert(100%) hue-rotate(180deg) }'; const defs = xml.getElementsByTagName('defs')[0]; - defs.appendChild(darkStyle); + defs.replaceChildren(darkStyle); return new XMLSerializer().serializeToString(xml); }