feat: add full-screen toggle to knowledge graph view
Admin graph view was cramped inside the sidebar-constrained content area, making it hard to inspect and edit larger graphs. Adds a full-screen overlay toggle (button + Esc to exit) that expands the graph/table view and edit panels to fill the viewport. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import * as d3 from 'd3';
|
|||||||
import * as db from '../../lib/db';
|
import * as db from '../../lib/db';
|
||||||
import { callLLM } from '../../lib/llm';
|
import { callLLM } from '../../lib/llm';
|
||||||
import { EMIT_GRAPH_ACTIONS_TOOL } from '../../lib/llmTools';
|
import { EMIT_GRAPH_ACTIONS_TOOL } from '../../lib/llmTools';
|
||||||
import { Network, Table2 } from 'lucide-react';
|
import { Network, Table2, Maximize2, Minimize2 } from 'lucide-react';
|
||||||
import { useGraphData } from '../../hooks/useGraphData';
|
import { useGraphData } from '../../hooks/useGraphData';
|
||||||
import { filterAiActions } from '../../lib/graphGuard';
|
import { filterAiActions } from '../../lib/graphGuard';
|
||||||
import GraphControls from './graph/GraphControls';
|
import GraphControls from './graph/GraphControls';
|
||||||
@@ -87,6 +87,7 @@ const KnowledgeGraph = () => {
|
|||||||
const [analyzeNotice, setAnalyzeNotice] = useState(null); // info-level message from last analyze
|
const [analyzeNotice, setAnalyzeNotice] = useState(null); // info-level message from last analyze
|
||||||
const [isRestoring, setIsRestoring] = useState(false);
|
const [isRestoring, setIsRestoring] = useState(false);
|
||||||
const [viewMode, setViewMode] = useState('graph'); // 'graph' | 'table'
|
const [viewMode, setViewMode] = useState('graph'); // 'graph' | 'table'
|
||||||
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
topics, relations, snapshotMeta,
|
topics, relations, snapshotMeta,
|
||||||
@@ -96,15 +97,29 @@ const KnowledgeGraph = () => {
|
|||||||
// ── Canvas sizing ────────────────────────────────────────────────────────────
|
// ── Canvas sizing ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!wrapperRef.current) return;
|
|
||||||
const measure = () => {
|
const measure = () => {
|
||||||
|
if (!wrapperRef.current) return;
|
||||||
const { width, height } = wrapperRef.current.getBoundingClientRect();
|
const { width, height } = wrapperRef.current.getBoundingClientRect();
|
||||||
setDimensions({ width, height });
|
if (width > 0 && height > 0) setDimensions({ width, height });
|
||||||
};
|
};
|
||||||
measure();
|
// rAF: toggling view mode / fullscreen resizes the wrapper without firing a
|
||||||
|
// window resize event, so re-measure once the DOM has reflowed.
|
||||||
|
const raf = requestAnimationFrame(measure);
|
||||||
window.addEventListener('resize', measure);
|
window.addEventListener('resize', measure);
|
||||||
return () => window.removeEventListener('resize', measure);
|
return () => {
|
||||||
}, []);
|
cancelAnimationFrame(raf);
|
||||||
|
window.removeEventListener('resize', measure);
|
||||||
|
};
|
||||||
|
}, [viewMode, isFullscreen]);
|
||||||
|
|
||||||
|
// ── Esc exits fullscreen ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isFullscreen) return;
|
||||||
|
const onKey = (e) => { if (e.key === 'Escape') setIsFullscreen(false); };
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
return () => window.removeEventListener('keydown', onKey);
|
||||||
|
}, [isFullscreen]);
|
||||||
|
|
||||||
// ── D3 force graph ───────────────────────────────────────────────────────────
|
// ── D3 force graph ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -433,7 +448,7 @@ const KnowledgeGraph = () => {
|
|||||||
// ── Render ───────────────────────────────────────────────────────────────────
|
// ── Render ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full flex flex-col md:flex-row">
|
<div className={`${isFullscreen ? 'fixed inset-0 z-50 bg-paper' : 'relative w-full h-full'} flex flex-col md:flex-row`}>
|
||||||
{/* Main view: graph canvas or table */}
|
{/* Main view: graph canvas or table */}
|
||||||
<div className="flex-1 flex flex-col border-r border-bg-warm min-w-0">
|
<div className="flex-1 flex flex-col border-r border-bg-warm min-w-0">
|
||||||
{/* View-mode toggle */}
|
{/* View-mode toggle */}
|
||||||
@@ -456,6 +471,13 @@ const KnowledgeGraph = () => {
|
|||||||
>
|
>
|
||||||
<Table2 size={13} /> Table
|
<Table2 size={13} /> Table
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsFullscreen(f => !f)}
|
||||||
|
className="ml-auto flex items-center gap-1.5 px-3 py-1 text-xs rounded-[var(--r-sm)] text-fg-muted hover:bg-bg-warm/50 transition-colors"
|
||||||
|
title={isFullscreen ? 'Exit full screen (Esc)' : 'Full screen'}
|
||||||
|
>
|
||||||
|
{isFullscreen ? <><Minimize2 size={13} /> Exit</> : <><Maximize2 size={13} /> Full screen</>}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{viewMode === 'graph' ? (
|
{viewMode === 'graph' ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user