import { useShallow } from "zustand/react/shallow"; import { useAppState, isHomeTab, isTerminalTab, tabKeyId, NOTES_DOCK_MIN_WIDTH, NOTES_DOCK_MAX_WIDTH, } from "../../store/appState"; import NotesDockPanel from "../notes/NotesDockPanel"; import Button from "../ui/Button"; /** * Notes beside whatever is on screen. * * Project Home and Terminal are sibling top-level tabs, so notes living only * in a sub-tab would be hidden exactly when the agent is running — which is * when a note is worth sending. The dock is the answer to that. * * **It takes space from inside the window and never resizes it.** Growing the * OS window was tried and rejected on evidence: honoured under XWayland, * silently corrupting under native Wayland, where `outer_position()` returns a * confident `Ok(0,0)` for a window that is somewhere else. See the design doc, * §6.1. Narrowing the terminal instead costs nothing — `TerminalView`'s * ResizeObserver already reflows xterm and resizes the container PTY. */ export default function NotesDock() { const { notesDockOpen, setNotesDockOpen, notesDockWidth, setNotesDockWidth, activeTabKey, sessions, } = useAppState( useShallow((s) => ({ notesDockOpen: s.notesDockOpen, setNotesDockOpen: s.setNotesDockOpen, notesDockWidth: s.notesDockWidth, setNotesDockWidth: s.setNotesDockWidth, activeTabKey: s.activeTabKey, sessions: s.sessions, })), ); // Dragging the separator. Pointer capture rather than window listeners, so // the drag survives the pointer crossing the terminal — which swallows // events — and ends correctly if the button is released outside the window. const onPointerDown = (e: React.PointerEvent) => { e.preventDefault(); const handle = e.currentTarget; handle.setPointerCapture(e.pointerId); const startX = e.clientX; const startWidth = notesDockWidth; // The dock is on the right, so dragging left widens it. const onMove = (move: PointerEvent) => setNotesDockWidth(startWidth + (startX - move.clientX)); const onUp = () => { handle.releasePointerCapture(e.pointerId); handle.removeEventListener("pointermove", onMove); handle.removeEventListener("pointerup", onUp); }; handle.addEventListener("pointermove", onMove); handle.addEventListener("pointerup", onUp); }; const onHandleKeyDown = (e: React.KeyboardEvent) => { const step = e.shiftKey ? 64 : 16; if (e.key === "ArrowLeft") { e.preventDefault(); setNotesDockWidth(notesDockWidth + step); } else if (e.key === "ArrowRight") { e.preventDefault(); setNotesDockWidth(notesDockWidth - step); } }; if (!notesDockOpen) return null; // Follow whatever is in front: a home tab is its own project, a terminal tab // is the project it belongs to. let projectId: string | null = null; if (activeTabKey && isHomeTab(activeTabKey)) { projectId = tabKeyId(activeTabKey); } else if (activeTabKey && isTerminalTab(activeTabKey)) { projectId = sessions.find((s) => s.id === tabKeyId(activeTabKey))?.projectId ?? null; } return ( ); }