diff --git a/package-lock.json b/package-lock.json
index d7cbf93..43a3384 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -72,7 +72,6 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1378,7 +1377,6 @@
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1420,7 +1418,6 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1643,7 +1640,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782",
@@ -1966,7 +1962,6 @@
"integrity": "sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.11.0",
@@ -2530,7 +2525,6 @@
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -2923,7 +2917,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -2971,7 +2964,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -3161,7 +3153,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
"integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3642,7 +3633,6 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
diff --git a/src/App.jsx b/src/App.jsx
index b98bf6c..730d0ec 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,7 +1,9 @@
-import React, { useState, useMemo } from 'react';
+import React, { useState, useMemo, useEffect } from 'react';
+import { Menu, Layers } from 'lucide-react';
import Sidebar from './components/Sidebar';
import EndpointViewer from './components/EndpointViewer';
import Introduction from './components/Introduction';
+import ThemeToggle from './components/ThemeToggle';
import { graphqlTopics, GRAPHQL_BASE_URL } from './data/apiData';
import { restTopics, REST_BASE_URL } from './data/restTopics';
@@ -27,31 +29,79 @@ function filterTopics(topics, q) {
function App() {
const [activeTopic, setActiveTopic] = useState(null);
const [searchQuery, setSearchQuery] = useState('');
+ const [sidebarOpen, setSidebarOpen] = useState(false);
+ const [dark, setDark] = useState(() => {
+ if (typeof window === 'undefined') return false;
+ const saved = localStorage.getItem('theme');
+ if (saved) return saved === 'dark';
+ return window.matchMedia('(prefers-color-scheme: dark)').matches;
+ });
+
+ useEffect(() => {
+ const root = document.documentElement;
+ root.classList.toggle('dark', dark);
+ localStorage.setItem('theme', dark ? 'dark' : 'light');
+ }, [dark]);
const q = searchQuery.trim().toLowerCase();
const visibleGraphql = useMemo(() => filterTopics(allGraphql, q), [q]);
const visibleRest = useMemo(() => filterTopics(allRest, q), [q]);
+ // On mobile, picking a topic should close the drawer
+ const selectTopic = (topic) => {
+ setActiveTopic(topic);
+ setSidebarOpen(false);
+ };
+
return (
-
+
{/* Ambient background glows */}
-
-
-
+
+
+
+
+ {/* Mobile top bar */}
+
+
+
+
+
+ {/* Mobile drawer backdrop */}
+ {sidebarOpen && (
+
setSidebarOpen(false)}
+ className="lg:hidden fixed inset-0 z-30 bg-slate-900/40 backdrop-blur-sm"
+ />
+ )}
setSidebarOpen(false)}
/>
-
+
+
+
{activeTopic ? (
-
+
) : (
@@ -59,7 +109,7 @@ function App() {
)}
diff --git a/src/components/EndpointViewer.jsx b/src/components/EndpointViewer.jsx
index 825035c..0753197 100644
--- a/src/components/EndpointViewer.jsx
+++ b/src/components/EndpointViewer.jsx
@@ -132,8 +132,8 @@ function EndpointRow({ endpoint, topic }) {
const urlPath = endpoint.url.split('?')[0];
return (
-
-
+
+
{/* Left Column: Description & Parameters */}
@@ -142,36 +142,36 @@ function EndpointRow({ endpoint, topic }) {
{endpoint.method}
-
+
{endpoint.name}
-
{endpoint.description}
+
{endpoint.description}
-
+
-
{topic.baseUrl}
-
{urlPath}
+
{topic.baseUrl}
+
{urlPath}
{/* Query params (GET endpoints) */}
{endpoint.method === 'GET' && Object.keys(params).length > 0 && (
-
- Query Parameters
+
+ Query Parameters
{Object.entries(params).map(([key, val]) => (
-