diff --git a/bun.lockb b/bun.lockb
index 6ee513d..408a62e 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 29ec4a1..d036256 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"dependencies": {
"@orama/orama": "^1.2.4",
"@orama/plugin-match-highlight": "^1.2.4",
+ "darkreader": "^4.9.58",
"jotai": "^2.4.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
diff --git a/src/app.tsx b/src/app.tsx
index 009e442..4d251ea 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -4,6 +4,7 @@ import { Item, selectedItemAtom } from './state';
import { FiMenu, FiSearch } from 'react-icons/fi';
import { type OramaWithHighlight } from '@orama/plugin-match-highlight';
import { Search } from './search';
+import { DarkModeToggle } from './dark-mode';
export const App = (props: { title: string; itemTree: Item[]; searchIndex: OramaWithHighlight }) => {
const { title, itemTree, searchIndex } = props;
@@ -22,6 +23,8 @@ export const App = (props: { title: string; itemTree: Item[]; searchIndex: Orama
void setSidebarOpen((state) => !state)} />
+
+
diff --git a/src/dark-mode.tsx b/src/dark-mode.tsx
new file mode 100644
index 0000000..e1b7f8e
--- /dev/null
+++ b/src/dark-mode.tsx
@@ -0,0 +1,35 @@
+import { FiMoon, FiSun } from 'react-icons/fi';
+import { type IconType } from 'react-icons';
+import { enable as enableDarkMode, disable as disableDarkMode } from 'darkreader';
+import { useEffect, useState } from 'react';
+
+const localStorageKey = 'dark-mode';
+
+const localStorageItem = localStorage.getItem(localStorageKey);
+const prefersColorScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
+const initialState = localStorageItem === null ? prefersColorScheme : localStorageItem === 'true';
+
+export const DarkModeToggle = () => {
+ const [darkMode, setDarkMode] = useState(initialState);
+
+ useEffect(() => {
+ if (darkMode) enableDarkMode({});
+ else disableDarkMode();
+ }, [darkMode]);
+
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ const DarkModeIcon: IconType = darkMode ? FiMoon : FiSun;
+
+ return (
+
+ void setDarkMode((state) => {
+ const newState = !state;
+ localStorage.setItem(localStorageKey, JSON.stringify(newState));
+ return newState;
+ })
+ }
+ />
+ );
+};
diff --git a/vite.config.ts b/vite.config.ts
index 6c56d76..00ca8d2 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -23,7 +23,15 @@ export default defineConfig({
react(),
monkey({
entry: 'src/main.tsx',
- build: { metaFileName: true },
+ build: {
+ metaFileName: true,
+ // GM_addStyle breaks Dark Reader for some reason, so insert CSS manually instead
+ cssSideEffects: () => (css: string) => {
+ const style = document.createElement('style');
+ style.textContent = css;
+ document.head.append(style);
+ },
+ },
userscript: {
icon: 'https://nixos.org/favicon.png',
match: nixManualUrls,
diff --git a/yarn.lock b/yarn.lock
index eeab963..bfc23b6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1,6 +1,6 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
-# bun ./bun.lockb --hash: 765D68983BAA40F8-cc15583d5104f4f7-C01A0FB9945A39CE-8eab4a2be698f6ad
+# bun ./bun.lockb --hash: 799E9B82A03E3219-e19cfcaf4c78bfc7-D96348C9F9999CDD-a14326ceb5100d22
"@aashutoshrathi/word-wrap@^1.2.3":
@@ -991,6 +991,11 @@ csstype@^3.0.2:
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
+darkreader@^4.9.58:
+ version "4.9.58"
+ resolved "https://registry.npmjs.org/darkreader/-/darkreader-4.9.58.tgz"
+ integrity sha512-D/JGoJqW3m2AWBLhO+Pev+eThfs+CwRT4bcLb/1zKjql2yVwG0lx8C2XRDdSVGHw4y11n26W7syWoBpUfuhMqQ==
+
debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"