import { useMemo, useState } from "react"; import { useNotes } from "../../hooks/useNotes"; import { useNoteDraft } from "./useNoteDraft"; import NoteEditor from "./NoteEditor"; import Button from "../ui/Button"; import SaveIndicator from "../ui/SaveIndicator"; interface Props { projectId: string; } const UNTITLED = "Untitled note"; /** * The notes surface itself, shared by the Project Home tab and the dock so the * two cannot drift into different behaviour. * * Master/detail: titles beside the editor when there is room, stacked above it * when there is not. That is a **container** query, not a viewport one, because * the two surfaces differ in width while sharing a viewport — the dock opens at * 352px and the tab is the width of the main area. A `md:` breakpoint would * read the window and give both the same answer, which is the wrong answer for * one of them. * * The threshold is arithmetic, not taste: side by side needs the 192px list, * plus an editor wide enough for its own action row (~280px), plus the divider. * Below ~473px the editor is narrower than its buttons, so `@lg` (512px) is the * first stop that clears it. * * The editor holds draft text locally and commits on blur, which is how every * other editable field in the app behaves (`ClaudeInstructionsEditor`, the * Config tab). */ export default function NotesPanel({ projectId }: Props) { const { notes, loading, saveState, createNote, saveNote, deleteNote } = useNotes(projectId); const [selectedId, setSelectedId] = useState(null); const selected = useMemo( () => notes.find((n) => n.id === selectedId) ?? notes[0] ?? null, [notes, selectedId], ); const { title, body, setTitle, setBody, commit } = useNoteDraft( selected, saveNote, ); const onCreate = async () => { const note = await createNote(); if (note) setSelectedId(note.id); }; if (loading) { return (

Loading notes…

); } return (
{notes.length === 0 ? (

No notes yet. Keep reminders here, and send any of them straight to a running Claude session.

) : (
{/* Stacked: a capped strip of titles above the editor, so the note being written keeps most of the height. Side by side: a full-height column of the fixed width the editor's arithmetic assumes. */}
    {notes.map((n) => (
  • ))}
{selected && ( void deleteNote(selected.id)} /> )}
)}
); }