diff --git a/static/index.html b/static/index.html
index 29c39fe..9f02734 100644
--- a/static/index.html
+++ b/static/index.html
@@ -5,9 +5,24 @@
Claude Code Chat Browser
-
-
-
+
+
+
+
diff --git a/static/js/app.js b/static/js/app.js
index 262e80e..c3edcd8 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -1,5 +1,33 @@
// Claude Code Chat Browser — Main JS
+// Highlight.js theme stylesheets, keyed by theme name. Both `href` and
+// `integrity` MUST be assigned together when swapping at runtime —
+// changing `href` while leaving a stale `integrity` would make the
+// browser refuse the new stylesheet and break the UI (issue #19).
+// Hashes verified against cdnjs's SRI API. The corresponding static
+// tag in static/index.html carries crossorigin="anonymous" which
+// persists across runtime href swaps.
+const HLJS_THEME_SHEETS = {
+ dark: {
+ href: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css',
+ integrity: 'sha512-mtXspRdOWHCYp+f4c7CkWGYPPRAhq9X+xCvJMUBVAb6pqA4U8pxhT3RWT3LP3bKbiolYL2CkL1bSKZZO4eeTew==',
+ },
+ light: {
+ href: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css',
+ integrity: 'sha512-0aPQyyeZrWj9sCA46UlmWgKOP0mUipLQ6OZXu8l4IcAmD2u31EPEy9VcIMvl7SoAaKe8bLXZhYoMaE/in+gcgA==',
+ },
+};
+
+function applyHljsTheme(themeName) {
+ const link = document.getElementById('hljs-theme');
+ if (!link) return;
+ const sheet = HLJS_THEME_SHEETS[themeName] || HLJS_THEME_SHEETS.dark;
+ // Set integrity FIRST, then href — the browser reads the current
+ // integrity at fetch time, and href change is what triggers the fetch.
+ link.integrity = sheet.integrity;
+ link.href = sheet.href;
+}
+
function showToast(message, type = 'info') {
const icons = { success: '\u2713', error: '\u2717', info: '\u2139' };
const toast = document.createElement('div');
@@ -122,14 +150,8 @@ function setHamburgerVisible(visible) {
function setWorkspaceMode(active) {
// No container class change needed — workspace lives inside the standard container
document.body.classList.toggle('workspace-mode', active);
- // Switch highlight.js theme
- const hljsLink = document.getElementById('hljs-theme');
- if (hljsLink) {
- const theme = localStorage.getItem('theme') || 'dark';
- hljsLink.href = theme === 'dark'
- ? 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css'
- : 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css';
- }
+ // Switch highlight.js theme — helper updates href + integrity together (issue #19).
+ applyHljsTheme(localStorage.getItem('theme') || 'dark');
}
let _navInProgress = false;
@@ -836,12 +858,7 @@ function applyTheme(theme) {
moon.style.display = theme === 'dark' ? 'block' : 'none';
sun.style.display = theme === 'light' ? 'block' : 'none';
}
- const hljsLink = document.getElementById('hljs-theme');
- if (hljsLink) {
- hljsLink.href = theme === 'dark'
- ? 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css'
- : 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css';
- }
+ applyHljsTheme(theme); // href + integrity swapped together (issue #19)
}
function toggleTheme() {