"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { CornerDownLeftIcon } from "lucide-react"; import { GraphView } from "@/components/graph-view"; import { GraphContextControl, effectiveContextTokens } from "@/components/graph-context-control"; import { DictationBar, useDictation } from "@/components/dictation"; import { SidebarToggle } from "@/components/sidebar"; import { StatsBar } from "@/components/stats-bar"; import type { ChatSettings, TurnUsage } from "@/src/core"; /** * Home — the knowledge graph is the main view, full-bleed edge to edge * (owner, graph-view round 1): the canvas owns the whole column, running * under the header and the new-session dock, both of which float over it * with no gradient scrims. Sessions live in the toggleable sidebar. */ export function Home() { const router = useRouter(); const [topic, setTopic] = useState(""); // Voice dictation (STT wave, 2026-09-17): finals append at the end of the // topic draft; the strip lives in the stats bar's middle slot. const dictation = useDictation({ append: (t) => setTopic((v) => (v ? v.replace(/\s+$/, "") + " " + t : t)), }); const [busy, setBusy] = useState(false); // The model+thinking selection made pre-chat — lifted from the StatsBar's // ModelControl, carried to the chat POST then PUT-on-create (the model // picker defers its save when chatId is empty; see components/model-picker). const [settings, setSettings] = useState(undefined); const start = async () => { const t = topic.trim(); if (!t || busy) return; setBusy(true); try { const res = await fetch("/api/chats", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: t }), }); const { id } = (await res.json()) as { id: string }; if (settings) { // Best-effort persist — the selection was made pre-chat. await fetch(`/api/chats/${id}/settings`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(settings), }).catch(() => {}); } router.push(`/chat/${id}?prompt=${encodeURIComponent(t)}`); } catch { setBusy(false); } }; return (
{/* Window-drag strip (merged chrome, owner 2026-09-17): a dedicated mask element, deliberately FIRST in the DOM. Electron's app-region mask resolves by DOM order — a no-drag element subtracts only drag regions that came EARLIER in the tree — not by z-index, so the header's old inline drag region (after
) re-added itself over the graph chrome row's carve-out and the buttons dragged the window. Placed first, every carved control (chrome row, node detail card, the toggle) comes later and subtracts correctly. Pure mask: it never needs to receive events — the mask routes the pixels. */}