Skip to content

Commit 7c04ea9

Browse files
committed
Fix code-tabs JS and MathJax rendering
- Critical fix: tabs JS detached <pre> blocks before replaceChild, throwing a DOM error that REMOVED every code block from the page. Now the container is inserted first, then blocks are moved in; each block is wrapped in try/catch so a failure can never blank the page. - Tabs now show ALL languages by default (stacked); the tab bar filters. Zero-JS fallback keeps all 5 languages visible. - Fix math: mdBook 0.5.4 never emits MathJax tex2jax config, so $...$ was rendered as literal text. Added theme/head.hbs with MathJax.Hub.Config and a forced PreProcess+Typeset pass in book.js after page load. - Remove invalid mathjax-src key (mdBook 0.5.4 does not support it; its presence silently disabled the whole [output.html] section, dropping the nightowl css, book.js and mathjax). - Verified with jsdom: all 5 language blocks survive JS execution, tab filtering works, All-view restores.
1 parent ecee3fd commit 7c04ea9

4 files changed

Lines changed: 296 additions & 25 deletions

File tree

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/* ==========================================================================
2+
Coding Interview Fight Club — Night Owl theme for mdBook
3+
Palette: the VS Code "Night Owl" color scheme (Sarah Drasner)
4+
bg #011627 · fg #d6deeb · blue #82aaff · cyan #7fdbca · purple #c792ea
5+
orange #f78c6c · yellow #ecc48d · green #addb67 · red #ff5874 · comment #637777
6+
========================================================================== */
7+
8+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap');
9+
10+
/* ---- Force every built-in mdBook theme to be Night Owl (dark only) ---- */
11+
html.light, html.rust, html.coal, html.navy, html.ayu {
12+
--bg: #011627;
13+
--fg: #d6deeb;
14+
--sidebar-bg: #01111d;
15+
--sidebar-fg: #7e93a8;
16+
--sidebar-active: #e0e9f2;
17+
--sidebar-spacer: #1d3b53;
18+
--scrollbar: #1d3b53;
19+
--icons: #5f7e97;
20+
--icons-hover: #82aaff;
21+
--link: #82aaff;
22+
--link-active: #7fdbca;
23+
--inline-code-color: #7fdbca;
24+
--theme-popup-bg: #01111d;
25+
--theme-popup-border: #1d3b53;
26+
--theme-hover: #0b2942;
27+
--quote-bg: #0b2942;
28+
--quote-border: #7fdbca;
29+
--table-border-color: #1d3b53;
30+
--table-header-bg: #0b2942;
31+
--table-alternate-bg: #081f33;
32+
--searchbar-border-color: #1d3b53;
33+
--searchbar-bg: #01111d;
34+
--searchbar-fg: #d6deeb;
35+
--searchbar-shadow-color: transparent;
36+
--searchbar-border-radius: 8px;
37+
--button-hover-bg: #0b2942;
38+
--button-hover-border: #1d3b53;
39+
--footer-fg: #5f7e97;
40+
--code-bg: #0b1d2e;
41+
--code-fg: #d6deeb;
42+
--toc-bg: #01111d;
43+
--toc-border: #1d3b53;
44+
}
45+
46+
/* ---- Typography ---- */
47+
body, .sidebar, .page-wrapper {
48+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
49+
}
50+
pre, code, .hljs {
51+
font-family: 'JetBrains Mono', "SF Mono", Menlo, Consolas, monospace;
52+
}
53+
h1, h2, h3, h4 {
54+
font-weight: 700;
55+
letter-spacing: -0.01em;
56+
}
57+
h1 { color: #7fdbca; }
58+
h2 { color: #82aaff; border-bottom: 1px solid #1d3b53; padding-bottom: .3em; }
59+
h3 { color: #c792ea; }
60+
h4 { color: #ecc48d; }
61+
a { color: var(--link); }
62+
a:hover { color: var(--link-active); }
63+
64+
/* ---- Code blocks: Night Owl panel ---- */
65+
pre, .hljs {
66+
background: #0b1d2e !important;
67+
color: #d6deeb;
68+
border: 1px solid #16324b;
69+
border-radius: 8px;
70+
font-size: 0.82em;
71+
line-height: 1.55;
72+
}
73+
pre code {
74+
background: transparent !important;
75+
}
76+
code {
77+
background: #0b2942;
78+
border-radius: 4px;
79+
padding: 0.12em 0.35em;
80+
font-size: 0.88em;
81+
}
82+
83+
/* ---- Night Owl syntax highlighting (highlight.js classes) ---- */
84+
.hljs-comment, .hljs-quote { color: #637777; font-style: italic; }
85+
.hljs-keyword, .hljs-selector-tag, .hljs-literal, .hljs-doctag, .hljs-title { color: #c792ea; }
86+
.hljs-string, .hljs-regexp, .hljs-addition, .hljs-meta .hljs-string { color: #ecc48d; }
87+
.hljs-number, .hljs-symbol, .hljs-bullet, .hljs-link { color: #f78c6c; }
88+
.hljs-built_in, .hljs-builtin-name, .hljs-type, .hljs-class .hljs-title { color: #ffcb8b; }
89+
.hljs-function .hljs-title, .hljs-title.function_ { color: #82aaff; }
90+
.hljs-attr, .hljs-attribute, .hljs-variable, .hljs-template-variable, .hljs-selector-attr, .hljs-selector-class, .hljs-selector-id, .hljs-params { color: #d6deeb; }
91+
.hljs-name, .hljs-tag, .hljs-selector-pseudo, .hljs-section, .hljs-meta { color: #7fdbca; }
92+
.hljs-emphasis { font-style: italic; }
93+
.hljs-strong { font-weight: 600; color: #addb67; }
94+
.hljs-deletion { color: #ff5874; }
95+
96+
/* ---- Language tabs ---- */
97+
.code-tabs {
98+
margin: 1.2em 0;
99+
border: 1px solid #16324b;
100+
border-radius: 8px;
101+
overflow: hidden;
102+
background: #0b1d2e;
103+
}
104+
.code-tabs-nav {
105+
display: flex;
106+
flex-wrap: wrap;
107+
gap: 2px;
108+
padding: 6px 8px 0;
109+
background: #01111d;
110+
border-bottom: 1px solid #16324b;
111+
}
112+
.code-tab-btn {
113+
background: transparent;
114+
color: #5f7e97;
115+
border: 1px solid transparent;
116+
border-bottom: none;
117+
border-radius: 6px 6px 0 0;
118+
padding: 5px 12px;
119+
font-size: 0.78em;
120+
font-weight: 600;
121+
cursor: pointer;
122+
font-family: 'JetBrains Mono', monospace;
123+
}
124+
.code-tab-btn:hover { color: #82aaff; background: #0b2942; }
125+
.code-tab-btn.active {
126+
color: #7fdbca;
127+
background: #0b1d2e;
128+
border-color: #16324b;
129+
}
130+
.code-tabs-body pre {
131+
border: none !important;
132+
border-radius: 0 !important;
133+
margin: 0 !important;
134+
}
135+
136+
/* ---- Copy button ---- */
137+
.copy-btn {
138+
position: absolute;
139+
top: 8px;
140+
right: 8px;
141+
z-index: 5;
142+
background: #0b2942;
143+
color: #7fdbca;
144+
border: 1px solid #1d3b53;
145+
border-radius: 6px;
146+
padding: 3px 10px;
147+
font-size: 0.72em;
148+
font-weight: 600;
149+
cursor: pointer;
150+
opacity: 0;
151+
transition: opacity .15s ease;
152+
font-family: 'JetBrains Mono', monospace;
153+
}
154+
.code-block-wrap:hover .copy-btn, .code-tabs:hover .copy-btn { opacity: 1; }
155+
.copy-btn:hover { background: #12395c; color: #ecc48d; }
156+
.code-block-wrap { position: relative; }
157+
.code-tabs-body { position: relative; }
158+
159+
/* ---- Tables ---- */
160+
table {
161+
border-collapse: collapse;
162+
margin: 1em 0;
163+
}
164+
th { background: var(--table-header-bg); color: #82aaff; font-weight: 600; }
165+
th, td { padding: 8px 14px; border: 1px solid var(--table-border-color); }
166+
tbody tr:nth-child(2n) { background: var(--table-alternate-bg); }
167+
168+
/* ---- Blockquotes: the "coach" voice ---- */
169+
blockquote {
170+
background: #0b2942;
171+
border-left: 4px solid #7fdbca;
172+
border-radius: 0 8px 8px 0;
173+
padding: 0.6em 1.2em;
174+
color: #a8c1d9;
175+
}
176+
177+
/* ---- Sidebar polish ---- */
178+
.sidebar .sidebar-scrollbox { padding: 1em 0.6em; }
179+
.sidebar li.chapter-item { line-height: 1.6; }
180+
.sidebar li a { font-size: 0.95em; }
181+
.sidebar li a.active { color: #7fdbca; font-weight: 600; }
182+
183+
/* ---- Content area ---- */
184+
.content main { max-width: 860px; }
185+
.content .header-line { color: #5f7e97; }
186+
187+
/* ---- Callout-ish emphasis for dry runs ---- */
188+
.dryrun {
189+
background: #081525;
190+
border: 1px dashed #1d3b53;
191+
border-radius: 8px;
192+
padding: 0.8em 1.2em;
193+
font-family: 'JetBrains Mono', monospace;
194+
font-size: 0.82em;
195+
white-space: pre;
196+
overflow-x: auto;
197+
color: #d6deeb;
198+
line-height: 1.6;
199+
margin: 1em 0;
200+
}

‎CodingInterviewFightClub/book.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ preferred-dark-theme = "navy"
1414
mathjax-support = true
1515
git-repository-url = "https://github.com/arpanpathak/AdvancedAlgorithmPatterns"
1616
edit-url-template = "https://github.com/arpanpathak/AdvancedAlgorithmPatterns/edit/main/src/{path}"
17-
additional-css = ["theme/css/nightowl.css"]
17+
additional-css = ["../CodingInterviewFightClub/theme/css/nightowl.css"]
1818
additional-js = ["theme/js/book.js"]
1919
no-section-label = true
2020

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!--
2+
MathJax configuration: mdBook 0.5 does not emit the tex2jax config by default,
3+
so $...$ / $$...$$ delimiters would never activate. This file is merged into
4+
<head> (mdBook keeps its own MathJax script; book.js additionally forces a
5+
PreProcess + Typeset pass after load so rendering never races the loader).
6+
-->
7+
<script type="text/x-mathjax-config">
8+
MathJax.Hub.Config({
9+
tex2jax: {
10+
inlineMath: [['$', '$'], ['\\(', '\\)']],
11+
displayMath: [['$$', '$$'], ['\\[', '\\]']],
12+
processEscapes: true
13+
}
14+
});
15+
</script>

‎CodingInterviewFightClub/theme/js/book.js‎

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
Coding Interview Fight Club — interactive book JS
33
1. Groups adjacent code blocks into language tabs (Kotlin/Java/C++/Python/Rust)
44
2. Adds a copy-to-clipboard button to every code block
5+
6+
IMPORTANT: never detach a <pre> from the document before the container is
7+
in place — a thrown DOM error would blank the page's code entirely.
58
========================================================================== */
69
(function () {
710
'use strict';
@@ -33,27 +36,48 @@
3336
return el && el.tagName === 'PRE' && el.querySelector('code');
3437
}
3538

36-
/* Build tabs from a run of adjacent <pre> siblings. */
39+
/* Build tabs from a run of adjacent <pre> siblings.
40+
All languages are VISIBLE by default (stacked); the tab buttons filter.
41+
This way even a JS failure never hides code. */
3742
function buildTabs(run) {
3843
var parent = run[0].parentNode;
3944
var container = document.createElement('div');
4045
container.className = 'code-tabs';
4146

47+
var langs = run.map(langOf);
48+
4249
var nav = document.createElement('div');
4350
nav.className = 'code-tabs-nav';
44-
run.forEach(function (pre, idx) {
51+
52+
var allBtn = document.createElement('button');
53+
allBtn.type = 'button';
54+
allBtn.className = 'code-tab-btn active';
55+
allBtn.textContent = 'All';
56+
nav.appendChild(allBtn);
57+
58+
var uniqueLangs = langs.filter(function (l, i) { return langs.indexOf(l) === i; });
59+
uniqueLangs.forEach(function (l) {
4560
var btn = document.createElement('button');
4661
btn.type = 'button';
47-
btn.className = 'code-tab-btn' + (idx === 0 ? ' active' : '');
48-
btn.textContent = langOf(pre);
62+
btn.className = 'code-tab-btn';
63+
btn.textContent = l;
4964
btn.addEventListener('click', function () {
50-
var buttons = nav.querySelectorAll('.code-tab-btn');
51-
buttons.forEach(function (b) { b.classList.remove('active'); });
52-
btn.classList.add('active');
53-
run.forEach(function (p, k) { p.style.display = k === idx ? 'block' : 'none'; });
65+
setActive(btn);
66+
run.forEach(function (p, k) { p.style.display = langs[k] === l ? 'block' : 'none'; });
5467
});
5568
nav.appendChild(btn);
5669
});
70+
71+
function setActive(btn) {
72+
nav.querySelectorAll('.code-tab-btn').forEach(function (b) { b.classList.remove('active'); });
73+
btn.classList.add('active');
74+
}
75+
76+
allBtn.addEventListener('click', function () {
77+
setActive(allBtn);
78+
run.forEach(function (p) { p.style.display = 'block'; });
79+
});
80+
5781
container.appendChild(nav);
5882

5983
var body = document.createElement('div');
@@ -68,13 +92,14 @@
6892
});
6993
body.appendChild(copy);
7094

71-
run.forEach(function (pre, idx) {
72-
pre.style.display = idx === 0 ? 'block' : 'none';
95+
/* Order matters: put the container in the DOM FIRST (while run[0] is still
96+
attached to parent), then move the pre blocks into it. */
97+
parent.replaceChild(container, run[0]);
98+
run.forEach(function (pre) {
99+
pre.style.display = 'block'; // ALL visible by default
73100
body.appendChild(pre);
74101
});
75102
container.appendChild(body);
76-
77-
parent.replaceChild(container, run[0]);
78103
}
79104

80105
/* Wrap a lone code block with a copy button. */
@@ -106,19 +131,24 @@
106131
var pres = Array.prototype.slice.call(document.querySelectorAll('pre'));
107132
var i = 0;
108133
while (i < pres.length) {
109-
if (!isCodePre(pres[i])) { i++; continue; }
110-
var run = [pres[i]];
111-
var next = pres[i].nextElementSibling;
112-
while (isCodePre(next)) {
113-
run.push(next);
114-
next = next.nextElementSibling;
115-
}
116-
if (run.length > 1) {
117-
buildTabs(run);
118-
} else {
119-
wrapLone(run[0]);
134+
try {
135+
if (!isCodePre(pres[i])) { i++; continue; }
136+
var run = [pres[i]];
137+
var next = pres[i].nextElementSibling;
138+
while (isCodePre(next)) {
139+
run.push(next);
140+
next = next.nextElementSibling;
141+
}
142+
if (run.length > 1) {
143+
buildTabs(run);
144+
} else {
145+
wrapLone(run[0]);
146+
}
147+
i += run.length;
148+
} catch (e) {
149+
/* Never let one bad block blank the page: leave remaining blocks as-is. */
150+
i++;
120151
}
121-
i += run.length;
122152
}
123153
}
124154

@@ -128,3 +158,29 @@
128158
initTabs();
129159
}
130160
})();
161+
162+
/* ---- MathJax guarantee ----
163+
mdBook injects MathJax async; the config script in head.hbs can race it.
164+
After the page settles, force a fresh PreProcess + Typeset pass so every
165+
$...$ / $$...$$ equation renders. tex2jax skips pre/code by default, so
166+
code blocks are never touched. */
167+
(function () {
168+
function forceMathJax() {
169+
if (window.MathJax && MathJax.Hub) {
170+
MathJax.Hub.Config({
171+
tex2jax: {
172+
inlineMath: [['$', '$'], ['\\(', '\\)']],
173+
displayMath: [['$$', '$$'], ['\\[', '\\]']],
174+
processEscapes: true
175+
}
176+
});
177+
MathJax.Hub.Queue(['PreProcess', MathJax.Hub]);
178+
MathJax.Hub.Queue(['Typeset', MathJax.Hub]);
179+
}
180+
}
181+
if (document.readyState === 'complete') {
182+
forceMathJax();
183+
} else {
184+
window.addEventListener('load', forceMathJax);
185+
}
186+
})();

0 commit comments

Comments
 (0)