diff --git a/packages/web/src/pages/Workspace.tsx b/packages/web/src/pages/Workspace.tsx
index 3684a146..f4b90a55 100644
--- a/packages/web/src/pages/Workspace.tsx
+++ b/packages/web/src/pages/Workspace.tsx
@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
-import { Plus, Share2, Users, Table, Activity, Network, CreditCard, Columns, CalendarDays, GanttChartSquare, LayoutDashboard, Database, AlertTriangle, Map, X, Minimize2, Edit3, Trash2, FolderPlus, ChevronLeft, ChevronRight, MoreHorizontal, Lock, Unlock } from 'lucide-react';
+import { Plus, Share2, Users, Table, Activity, Network, CreditCard, Columns, CalendarDays, GanttChartSquare, LayoutDashboard, Database, AlertTriangle, Map, X, Minimize2, Edit3, Trash2, FolderPlus, ChevronLeft, ChevronRight, Lock, Unlock } from 'lucide-react';
import { createPortal } from 'react-dom';
import { useQuery } from '@apollo/client';
import { SafeGraphVisualization } from '../components/SafeGraphVisualization';
@@ -17,6 +17,7 @@ import { useAuth } from '../contexts/AuthContext';
import { GET_WORK_ITEMS, GET_EDGES } from '../lib/queries';
import { APP_VERSION } from '../utils/version';
import { useHealthStatus } from '../hooks/useHealthStatus';
+import { useViewMode } from '../contexts/ViewModeContext';
export function Workspace() {
const [showCreateModal, setShowCreateModal] = useState(false);
@@ -25,17 +26,10 @@ export function Workspace() {
const [showDeleteGraphModal, setShowDeleteGraphModal] = useState(false);
const [showGraphSelectionModal, setShowGraphSelectionModal] = useState(false);
const [graphToEdit, setGraphToEdit] = useState
(null);
- const [viewMode, setViewMode] = useState<'graph' | 'dashboard' | 'table' | 'cards' | 'kanban' | 'gantt' | 'calendar' | 'activity'>(() => {
- if (typeof window === 'undefined') return 'graph';
- const saved = window.localStorage.getItem('graphdone:viewMode');
- if (saved) return saved as any;
- // On the go: a phone-sized screen lands on the readable card list, not the graph.
- return window.matchMedia('(max-width: 767px)').matches ? 'cards' : 'graph';
- });
+ const { viewMode, setViewMode } = useViewMode();
const [isMobile, setIsMobile] = useState(() =>
typeof window !== 'undefined' && window.matchMedia('(max-width: 767px)').matches
);
- const [showMoreSheet, setShowMoreSheet] = useState(false);
// Graph touch-interaction lock — on a phone, default to locked so panning the
// canvas never accidentally drags a node or an edge label; unlock to edit.
const [graphLocked, setGraphLocked] = useState(() =>
@@ -48,10 +42,6 @@ export function Workspace() {
const { currentTeam, currentUser } = useAuth();
const { health, loading: healthLoading, error: healthError } = useHealthStatus();
- useEffect(() => {
- try { window.localStorage.setItem('graphdone:viewMode', viewMode); } catch { /* private mode */ }
- }, [viewMode]);
-
useEffect(() => {
const mq = window.matchMedia('(max-width: 767px)');
const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);
@@ -319,7 +309,7 @@ export function Workspace() {
)}
{/* Main Content */}
-
+
{!currentGraph ? (
@@ -459,37 +449,6 @@ export function Workspace() {
)}
- {/* Mobile bottom navigation — the primary way to move between views on a phone.
- List first, then Graph; the rest live in the More sheet. */}
- {currentGraph && (
-
- )}
-
{/* Create FAB (phones) — primary action; hidden for read-only guests and in
graph view (where the on-canvas "+" grow flow creates nodes). */}
{currentGraph && currentUser?.role !== 'GUEST' && viewMode !== 'graph' && (
@@ -504,48 +463,6 @@ export function Workspace() {
)}
- {/* "More" views sheet (phones) */}
- {showMoreSheet && createPortal(
-
setShowMoreSheet(false)}
- >
-
-
e.stopPropagation()}
- >
-
-
More views
-
- {([
- { mode: 'dashboard', label: 'Dashboard', Icon: LayoutDashboard },
- { mode: 'table', label: 'Table', Icon: Table },
- { mode: 'kanban', label: 'Board', Icon: Columns },
- { mode: 'gantt', label: 'Gantt', Icon: GanttChartSquare },
- { mode: 'calendar', label: 'Calendar', Icon: CalendarDays },
- { mode: 'activity', label: 'Activity', Icon: Activity },
- ] as const).map(({ mode, label, Icon }) => (
-
- ))}
-
-
-
,
- document.body
- )}
-
{/* Mini-Map Navigation - Bottom Right Corner (hidden on mobile: it covers too
much of a phone screen, and panning the graph by touch is the primary nav there) */}
{viewMode === 'graph' && currentGraph && showMiniMap && !isMobile && createPortal(
diff --git a/packages/web/tailwind.config.js b/packages/web/tailwind.config.js
index 1d355329..4c639461 100644
--- a/packages/web/tailwind.config.js
+++ b/packages/web/tailwind.config.js
@@ -1,5 +1,9 @@
/** @type {import('tailwindcss').Config} */
export default {
+ // The app is unconditionally dark-themed; force the `dark` class so every
+ // `dark:` text/color variant always applies (otherwise light-OS users get
+ // black text on the dark surfaces). Applied via .
+ darkMode: 'class',
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",