Skip to content

Commit 1980ffb

Browse files
committed
add back the light/dark toggle button
1 parent 6ef2486 commit 1980ffb

3 files changed

Lines changed: 134 additions & 41 deletions

File tree

api-docs/cppdocs/binaryninja-darkmode.js

Lines changed: 96 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,67 @@
11
/**
2-
* Binary Ninja Dark Mode - System Preference with Cookie Persistence
2+
* Binary Ninja Dark Mode - System Preference with localStorage Persistence
33
* Follows system color scheme automatically via CSS
4-
* Provides console toggle for testing with cookie-based persistence
4+
* Provides console toggle for testing with localStorage-based persistence
55
*/
66

77
(function() {
88
'use strict';
99

10-
const COOKIE_NAME = 'bn-docs-theme';
11-
const COOKIE_DAYS = 365;
10+
const STORAGE_KEY = 'bn-docs-theme';
11+
const STORAGE_TIMESTAMP_KEY = 'bn-docs-theme-timestamp';
12+
const EXPIRY_HOURS = 24;
1213

1314
/**
14-
* Get cookie value by name
15+
* Get theme from localStorage if not expired
1516
*/
16-
function getCookie(name) {
17-
const value = `; ${document.cookie}`;
18-
const parts = value.split(`; ${name}=`);
19-
if (parts.length === 2) return parts.pop().split(';').shift();
20-
return null;
17+
function getTheme() {
18+
try {
19+
const theme = localStorage.getItem(STORAGE_KEY);
20+
const timestamp = localStorage.getItem(STORAGE_TIMESTAMP_KEY);
21+
22+
if (!theme || !timestamp) {
23+
return null;
24+
}
25+
26+
// Check if expired (24 hours)
27+
const now = Date.now();
28+
const age = now - parseInt(timestamp, 10);
29+
const maxAge = EXPIRY_HOURS * 60 * 60 * 1000; // 24 hours in ms
30+
31+
if (age > maxAge) {
32+
// Expired, remove and return null
33+
removeTheme();
34+
return null;
35+
}
36+
37+
return theme;
38+
} catch (e) {
39+
return null;
40+
}
2141
}
2242

2343
/**
24-
* Set cookie value
44+
* Set theme in localStorage with timestamp
2545
*/
26-
function setCookie(name, value, days) {
27-
const date = new Date();
28-
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
29-
const expires = `expires=${date.toUTCString()}`;
30-
document.cookie = `${name}=${value};${expires};path=/`;
46+
function setTheme(value) {
47+
try {
48+
localStorage.setItem(STORAGE_KEY, value);
49+
localStorage.setItem(STORAGE_TIMESTAMP_KEY, Date.now().toString());
50+
} catch (e) {
51+
// Silently fail if localStorage is unavailable
52+
}
3153
}
3254

3355
/**
34-
* Delete cookie
56+
* Remove theme from localStorage
3557
*/
36-
function deleteCookie(name) {
37-
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`;
58+
function removeTheme() {
59+
try {
60+
localStorage.removeItem(STORAGE_KEY);
61+
localStorage.removeItem(STORAGE_TIMESTAMP_KEY);
62+
} catch (e) {
63+
// Silently fail if localStorage is unavailable
64+
}
3865
}
3966

4067
/**
@@ -57,42 +84,78 @@
5784
}
5885

5986
/**
60-
* Console-accessible toggle function with cookie persistence
87+
* Console-accessible toggle function with localStorage persistence
6188
* Usage: bnToggleDarkMode('dark'), bnToggleDarkMode('light'), or bnToggleDarkMode('auto')
6289
*/
6390
window.bnToggleDarkMode = function(mode) {
6491
if (mode === 'dark') {
6592
applyTheme('dark');
66-
setCookie(COOKIE_NAME, 'dark', COOKIE_DAYS);
67-
console.log('Dark mode: FORCED ON (saved to cookie, will persist across reloads)');
93+
setTheme('dark');
6894
} else if (mode === 'light') {
6995
applyTheme('light');
70-
setCookie(COOKIE_NAME, 'light', COOKIE_DAYS);
71-
console.log('Dark mode: FORCED OFF (saved to cookie, will persist across reloads)');
96+
setTheme('light');
7297
} else if (mode === 'auto') {
7398
applyTheme('auto');
74-
deleteCookie(COOKIE_NAME);
75-
console.log('Dark mode: AUTO (following system preference, cookie cleared)');
99+
removeTheme();
100+
}
101+
};
102+
103+
/**
104+
* Update button icon based on current theme
105+
*/
106+
function updateButtonIcon() {
107+
const button = document.getElementById('bn-darkmode-toggle');
108+
if (!button) return;
109+
110+
const savedTheme = getTheme();
111+
112+
if (savedTheme === 'light') {
113+
button.textContent = '☀';
114+
button.title = 'Light mode (click for Dark)';
115+
} else if (savedTheme === 'dark') {
116+
button.textContent = '☾';
117+
button.title = 'Dark mode (click for Light)';
118+
} else {
119+
// No saved theme - show system preference
120+
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
121+
if (isDarkMode) {
122+
button.textContent = '☾';
123+
button.title = 'System: Dark (click for Light)';
124+
} else {
125+
button.textContent = '☀';
126+
button.title = 'System: Light (click for Dark)';
127+
}
128+
}
129+
}
130+
131+
/**
132+
* Cycle between light and dark mode
133+
*/
134+
window.cycleDarkMode = function() {
135+
const savedTheme = getTheme();
136+
137+
if (savedTheme === 'light') {
138+
window.bnToggleDarkMode('dark');
76139
} else {
77-
console.log('Usage: bnToggleDarkMode("dark"), bnToggleDarkMode("light"), or bnToggleDarkMode("auto")');
78-
console.log('Current setting:', getCookie(COOKIE_NAME) || 'auto (system preference)');
140+
// From dark or system -> go to light
141+
window.bnToggleDarkMode('light');
79142
}
143+
144+
updateButtonIcon();
80145
};
81146

82147
/**
83148
* Initialize theme on page load
84149
*/
85150
function init() {
86-
const savedTheme = getCookie(COOKIE_NAME);
151+
const savedTheme = getTheme();
87152

88153
if (savedTheme) {
89154
applyTheme(savedTheme);
90-
console.log(`Binary Ninja Docs: Theme loaded from cookie: ${savedTheme}`);
91-
} else {
92-
console.log('Binary Ninja Docs: Following system preference (no cookie set)');
93155
}
94156

95-
console.log('Use bnToggleDarkMode("dark"), bnToggleDarkMode("light"), or bnToggleDarkMode("auto") to change theme');
157+
// Update button icon after a short delay to ensure DOM is ready
158+
setTimeout(updateButtonIcon, 100);
96159
}
97160

98161
// Initialize on page load

api-docs/cppdocs/binaryninja-docs.css

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -397,11 +397,13 @@ tr#projectrow {
397397
}
398398

399399
#MSearchBox {
400-
width: calc(var(--sidebar-width) - calc(2 * var(--spacing-medium)) - 1px);
400+
width: calc(var(--sidebar-width) - calc(2 * var(--spacing-medium)) - 50px);
401+
display: inline-block;
402+
vertical-align: top;
401403
}
402404

403405
#MSearchField {
404-
width: calc(var(--sidebar-width) - calc(2 * var(--spacing-medium)) - 66px);
406+
width: calc(var(--sidebar-width) - calc(2 * var(--spacing-medium)) - 113px);
405407
padding-left: 10px;
406408
top: -1px;
407409
}
@@ -513,7 +515,7 @@ tr#projectrow {
513515
overflow: hidden;
514516
position: relative;
515517
box-shadow: none;
516-
display: block;
518+
display: inline-block;
517519
margin-top: 0;
518520
}
519521

@@ -1304,10 +1306,35 @@ pre.fragment {
13041306
}
13051307

13061308
/* ============================================================================
1307-
* DARK MODE TOGGLE (Hidden - CSS-only system preference)
1309+
* DARK MODE TOGGLE
13081310
* ============================================================================ */
1309-
#bn-darkmode-toggle {
1310-
display: none;
1311+
.bn-darkmode-toggle {
1312+
display: inline-block;
1313+
width: 33px;
1314+
height: var(--searchbar-height);
1315+
border: 1px solid var(--border-color);
1316+
background-color: var(--page-bg);
1317+
color: var(--page-text);
1318+
cursor: pointer;
1319+
font-size: 16px;
1320+
line-height: var(--searchbar-height);
1321+
border-radius: var(--searchbar-border-radius);
1322+
vertical-align: top;
1323+
margin-left: 5px;
1324+
margin-right: 3px;
1325+
text-align: center;
1326+
padding: 0;
1327+
}
1328+
1329+
.bn-darkmode-toggle:hover {
1330+
background-color: var(--table-header-bg);
1331+
border-color: var(--bn-red);
1332+
}
1333+
1334+
@media screen and (max-width: 1024px) {
1335+
.bn-darkmode-toggle {
1336+
display: none;
1337+
}
13111338
}
13121339

13131340
/* ============================================================================
@@ -1355,6 +1382,9 @@ html.dark-mode .darkmode_inverted_image object[type="image/svg+xml"] {
13551382

13561383
/* Collapsed sidebar state */
13571384
#side-nav.collapsed {
1385+
width: 0 !important;
1386+
min-width: 0 !important;
1387+
max-width: 0 !important;
13581388
overflow: visible !important;
13591389
}
13601390

api-docs/cppdocs/header.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,14 @@
8080
<!--BEGIN DISABLE_INDEX-->
8181
<!--BEGIN SEARCHENGINE-->
8282
<!--BEGIN !FULL_SIDEBAR-->
83-
<td>$searchbox</td>
83+
<td>$searchbox<button id="bn-darkmode-toggle" class="bn-darkmode-toggle" onclick="cycleDarkMode()" title="Toggle theme"></button></td>
8484
<!--END !FULL_SIDEBAR-->
8585
<!--END SEARCHENGINE-->
8686
<!--END DISABLE_INDEX-->
8787
</tr>
8888
<!--BEGIN SEARCHENGINE-->
8989
<!--BEGIN FULL_SIDEBAR-->
90-
<tr><td colspan="2">$searchbox</td></tr>
90+
<tr><td colspan="2">$searchbox<button id="bn-darkmode-toggle" class="bn-darkmode-toggle" onclick="cycleDarkMode()" title="Toggle theme"></button></td></tr>
9191
<!--END FULL_SIDEBAR-->
9292
<!--END SEARCHENGINE-->
9393
</tbody>

0 commit comments

Comments
 (0)