feat(graph): navigation & UX — deep-link, depth, PNG, rich panel (PR D)

Final corpus-graph PR. Connects the graph to the chair's workflow and rounds
out the Obsidian-grade interactions.

Backend (web/graph_api.py): neighborhood depth cap 2 → 3 (still bounded by
NODE_CAP_MAX).

Frontend:
- URL deep-link: /graph?focus=cl:<id> is read on mount and written on focus
  change (router.replace, scroll:false). GraphView wrapped in <Suspense> per
  Next 16's useSearchParams requirement.
- "הצג בגרף" button on the precedent detail page → /graph?focus=cl:<id>.
- Depth slider (1–3) in the focused overlay → useNodeNeighborhood(id, depth).
- Export PNG: grabs the rendered <canvas> from the area ref → toDataURL →
  download; failures surface a toast (UI4).
- Rich node panel: precedent nodes fetch headnote/summary via the existing
  usePrecedent hook (Skeleton while pending, error surfaced — UI4).
- Edge-type legend (ציטוט / נושא-תחום / יומון) added under the node legend.

Deferred (noted for a later pass): expand-in-place merge, search→camera-center.

web-ui build + lint pass. Invariants: G2 (depth change is read-only), UI4
(PNG + detail errors surfaced, not swallowed). api:types post-deploy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 04:56:01 +00:00
parent 2c75666d26
commit 5f1b96ccaf
5 changed files with 152 additions and 23 deletions

View File

@@ -1,3 +1,4 @@
import { Suspense } from "react";
import Link from "next/link";
import { AppShell } from "@/components/app-shell";
@@ -22,7 +23,15 @@ export default function GraphPage() {
</p>
</header>
<div className="h-[2px] bg-gradient-to-l from-transparent via-gold to-transparent" />
<GraphView />
<Suspense
fallback={
<div className="grid h-[560px] place-items-center text-sm text-ink-muted">
טוען גרף
</div>
}
>
<GraphView />
</Suspense>
</section>
</AppShell>
);

View File

@@ -2,7 +2,7 @@
import { use, useState } from "react";
import Link from "next/link";
import { Pencil, Check, X } from "lucide-react";
import { Pencil, Check, X, Share2 } from "lucide-react";
import { toast } from "sonner";
import { AppShell } from "@/components/app-shell";
import { Card, CardContent } from "@/components/ui/card";
@@ -88,9 +88,16 @@ export default function PrecedentDetailPage({
{data.case_number}
</div>
</div>
<Button variant="outline" size="sm" onClick={() => setEditing(true)}>
<Pencil className="w-3.5 h-3.5 me-1" /> ערוך פרטים
</Button>
<div className="flex items-center gap-2">
<Button asChild variant="outline" size="sm">
<Link href={`/graph?focus=cl:${id}`}>
<Share2 className="w-3.5 h-3.5 me-1" /> הצג בגרף
</Link>
</Button>
<Button variant="outline" size="sm" onClick={() => setEditing(true)}>
<Pencil className="w-3.5 h-3.5 me-1" /> ערוך פרטים
</Button>
</div>
</div>
{/* Citation per Israeli unified citation rules. The LLM