diff --git a/package.json b/package.json
index 2d68ed7..77ab71e 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,8 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "colortheme":"esbuild static/colortheme/colortheme.ts --bundle --minify --sourcemap --outdir=static/colortheme/"
},
"dependencies": {
"@codemirror/autocomplete": "^6.1.0",
diff --git a/static/colortheme/README.md b/static/colortheme/README.md
new file mode 100644
index 0000000..88b09c6
--- /dev/null
+++ b/static/colortheme/README.md
@@ -0,0 +1,5 @@
+It would be nice if colortheme.ts could be a part of the regular bundle.
+But it needs to load as early as possible and blocking to avoid having white flashes in the dark theme.
+Even using it in a seperate module that is loaded in the header doesn't work as modules are always deferred.
+
+That's why it is built to a separate JS file by esbuild and hardcoded into the header.
diff --git a/static/colortheme/colortheme.js b/static/colortheme/colortheme.js
new file mode 100644
index 0000000..93ed036
--- /dev/null
+++ b/static/colortheme/colortheme.js
@@ -0,0 +1,7 @@
+"use strict";(()=>{var o="(prefers-color-scheme: dark)",s=()=>localStorage.getItem("theme"),m=e=>localStorage.setItem("theme",e),i=()=>{let e=s();return e||(window.matchMedia(o).matches?"dark":"light")},n=e=>{e==="auto"&&window.matchMedia(o).matches?document.documentElement.setAttribute("data-bs-theme","dark"):document.documentElement.setAttribute("data-bs-theme",e)};n(i());var c=(e,a=!1)=>{let t=document.getElementById("navbar-main-dropdown");if(!t)return;let r=document.querySelector(`[data-bs-theme-value="${e}"]`);r&&(console.log(r),document.querySelectorAll("[data-bs-theme-value]").forEach(d=>{d.classList.remove("active"),d.setAttribute("aria-pressed","false")}),r.classList.add("active"),r.setAttribute("aria-pressed","true"),a&&t.focus())};window.matchMedia(o).addEventListener("change",()=>{let e=s();e!=="light"&&e!=="dark"&&n(i())});window.addEventListener("DOMContentLoaded",()=>{let e=s();e||(e="auto"),c(e),document.querySelectorAll("[data-bs-theme-value]").forEach(a=>{a.addEventListener("click",()=>{let t=a.getAttribute("data-bs-theme-value");m(t),n(t),c(t,!0)})})});})();
+/*!
+ * Color mode toggler based on Bootstrap's docs (https://getbootstrap.com/)
+ * Copyright 2011-2023 The Bootstrap Authors
+ * Licensed under the Creative Commons Attribution 3.0 Unported License.
+ */
+//# sourceMappingURL=colortheme.js.map
diff --git a/static/colortheme/colortheme.js.map b/static/colortheme/colortheme.js.map
new file mode 100644
index 0000000..98b37bf
--- /dev/null
+++ b/static/colortheme/colortheme.js.map
@@ -0,0 +1,7 @@
+{
+ "version": 3,
+ "sources": ["colortheme.ts"],
+ "sourcesContent": ["/*!\n * Color mode toggler based on Bootstrap's docs (https://getbootstrap.com/)\n * Copyright 2011-2023 The Bootstrap Authors\n * Licensed under the Creative Commons Attribution 3.0 Unported License.\n */\n\nconst darkQuery='(prefers-color-scheme: dark)'\n\nconst getStoredTheme = () => localStorage.getItem('theme')\nconst setStoredTheme = (theme: string) => localStorage.setItem('theme', theme)\n\nconst getPreferredTheme = () => {\n const storedTheme = getStoredTheme()\n if (storedTheme) {\n return storedTheme\n }\n\n return window.matchMedia(darkQuery).matches ? 'dark' : 'light'\n}\n\nconst setTheme = (theme: string) => {\n if (theme === 'auto' && window.matchMedia(darkQuery).matches) {\n document.documentElement.setAttribute('data-bs-theme', 'dark')\n } else {\n document.documentElement.setAttribute('data-bs-theme', theme)\n }\n}\n\nsetTheme(getPreferredTheme())\n\nconst showActiveTheme = (theme: string, focus: boolean = false) => {\n const themeSwitcher = document.getElementById(\"navbar-main-dropdown\")\n if (!themeSwitcher) {\n return\n }\n\n const btnToActive = document.querySelector(`[data-bs-theme-value=\"${theme}\"]`)\n if (!btnToActive) {\n return\n }\n console.log(btnToActive)\n\n document.querySelectorAll('[data-bs-theme-value]').forEach(element => {\n element.classList.remove('active')\n element.setAttribute('aria-pressed', 'false')\n })\n\n btnToActive.classList.add('active')\n btnToActive.setAttribute('aria-pressed', 'true')\n\n if (focus) {\n themeSwitcher.focus()\n }\n}\n\nwindow.matchMedia(darkQuery).addEventListener('change', () => {\n const storedTheme = getStoredTheme()\n if (storedTheme !== 'light' && storedTheme !== 'dark') {\n setTheme(getPreferredTheme())\n }\n})\n\nwindow.addEventListener('DOMContentLoaded', () => {\n let showTheme = getStoredTheme()\n if (!showTheme) {\n showTheme = \"auto\"\n }\n showActiveTheme(showTheme)\n\n document.querySelectorAll('[data-bs-theme-value]')\n .forEach(toggle => {\n toggle.addEventListener('click', () => {\n const theme = toggle.getAttribute('data-bs-theme-value')!\n setStoredTheme(theme)\n setTheme(theme)\n showActiveTheme(theme, true)\n })\n })\n})\n"],
+ "mappings": "mBAMA,IAAMA,EAAU,+BAEVC,EAAiB,IAAM,aAAa,QAAQ,OAAO,EACnDC,EAAkBC,GAAkB,aAAa,QAAQ,QAASA,CAAK,EAEvEC,EAAoB,IAAM,CAC5B,IAAMC,EAAcJ,EAAe,EACnC,OAAII,IAIG,OAAO,WAAWL,CAAS,EAAE,QAAU,OAAS,QAC3D,EAEMM,EAAYH,GAAkB,CAC5BA,IAAU,QAAU,OAAO,WAAWH,CAAS,EAAE,QACjD,SAAS,gBAAgB,aAAa,gBAAiB,MAAM,EAE7D,SAAS,gBAAgB,aAAa,gBAAiBG,CAAK,CAEpE,EAEAG,EAASF,EAAkB,CAAC,EAE5B,IAAMG,EAAkB,CAACJ,EAAeK,EAAiB,KAAU,CAC/D,IAAMC,EAAgB,SAAS,eAAe,sBAAsB,EACpE,GAAI,CAACA,EACD,OAGJ,IAAMC,EAAc,SAAS,cAAc,yBAAyBP,CAAK,IAAI,EACxEO,IAGL,QAAQ,IAAIA,CAAW,EAEvB,SAAS,iBAAiB,uBAAuB,EAAE,QAAQC,GAAW,CAClEA,EAAQ,UAAU,OAAO,QAAQ,EACjCA,EAAQ,aAAa,eAAgB,OAAO,CAChD,CAAC,EAEDD,EAAY,UAAU,IAAI,QAAQ,EAClCA,EAAY,aAAa,eAAgB,MAAM,EAE3CF,GACAC,EAAc,MAAM,EAE5B,EAEA,OAAO,WAAWT,CAAS,EAAE,iBAAiB,SAAU,IAAM,CAC1D,IAAMK,EAAcJ,EAAe,EAC/BI,IAAgB,SAAWA,IAAgB,QAC3CC,EAASF,EAAkB,CAAC,CAEpC,CAAC,EAED,OAAO,iBAAiB,mBAAoB,IAAM,CAC9C,IAAIQ,EAAYX,EAAe,EAC1BW,IACDA,EAAY,QAEhBL,EAAgBK,CAAS,EAEzB,SAAS,iBAAiB,uBAAuB,EAC5C,QAAQC,GAAU,CACfA,EAAO,iBAAiB,QAAS,IAAM,CACnC,IAAMV,EAAQU,EAAO,aAAa,qBAAqB,EACvDX,EAAeC,CAAK,EACpBG,EAASH,CAAK,EACdI,EAAgBJ,EAAO,EAAI,CAC/B,CAAC,CACL,CAAC,CACT,CAAC",
+ "names": ["darkQuery", "getStoredTheme", "setStoredTheme", "theme", "getPreferredTheme", "storedTheme", "setTheme", "showActiveTheme", "focus", "themeSwitcher", "btnToActive", "element", "showTheme", "toggle"]
+}
diff --git a/static/js/colortheme.ts b/static/colortheme/colortheme.ts
similarity index 100%
rename from static/js/colortheme.ts
rename to static/colortheme/colortheme.ts
diff --git a/templates/base.jinja b/templates/base.jinja
index 7646919..3227387 100644
--- a/templates/base.jinja
+++ b/templates/base.jinja
@@ -15,7 +15,7 @@
{% endfor %}
{% endif %}
-
+
@@ -50,7 +50,7 @@
{% else %}
-
+
{% endif %}
{% if sentry_event_id %}
diff --git a/vite.config.js b/vite.config.js
index 530dd88..140ffea 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -14,7 +14,7 @@ export default defineConfig({
// overwrite default .html entry
input: {
"main": 'static/main.ts',
- "colortheme": 'static/js/colortheme.ts',
+ // "colortheme": 'static/js/colortheme.ts',
// "tenantbase": 'static/tenantbase.js',
// "editor": 'static/editor.js'
},