import { useState } from "react"; import { Bookmark, Plus, X } from "lucide-react"; import clsx from "clsx"; import Box from "../Box.tsx"; import CardHeader from "../CardHeader.tsx"; import PrimaryButton from "../buttons/PrimaryButton.tsx"; import type { Marker } from "../../utils/types.ts"; import { formatLocalTime } from "../../utils/utils.ts"; const inputClass = "border border-hairline bg-fields rounded-md w-full p-2 text-sm focus:outline-none focus:ring-2 focus:ring-muted transition-colors"; type RailSegment = "none" | "plain" | "lit"; const railClass = (segment: RailSegment) => clsx( "w-px", segment === "lit" && "bg-terracotta", segment === "plain" && "bg-inactive-strong/60", segment === "none" && "bg-transparent" ); type MarkerRowProps = { marker: Marker; role: "start" | "end" | "between" | null; /** Rail segments above and below the dot — "lit" inside the chosen section. */ railAbove: RailSegment; railBelow: RailSegment; onSelect: () => void; onDelete: () => void; }; const MarkerRow = ({ marker, role, railAbove, railBelow, onSelect, onDelete }: MarkerRowProps) => (
  • {/* Timeline rail: a dot per marker, with the segment between the chosen start and end lit so the selected section reads as one run. */}
    {/* The rail spans the full row, so the breathing room goes on the button — the line stays continuous while neighbouring rings no longer touch. */}
  • ); type Props = { /** Newest first — the list renders them in the order given. */ markers: Marker[]; startMarkerId: string; endMarkerId: string; error: string | null; /** Resolves true once the marker exists, so the input only clears on success. */ onAdd: (message: string) => Promise; onSelect: (id: number) => void; onDelete: (id: number) => void; className?: string; }; const MarkerList = ({ markers, startMarkerId, endMarkerId, error, onAdd, onSelect, onDelete, className }: Props) => { const [message, setMessage] = useState(""); const [busy, setBusy] = useState(false); const handleAdd = async () => { if (!message.trim() || busy) return; setBusy(true); if (await onAdd(message.trim())) setMessage(""); setBusy(false); }; // Either of the two may have been picked first, so the lit run is bounded by // whichever indices come first/last rather than by start/end. const startIndex = markers.findIndex((m) => String(m.id) === startMarkerId); const endIndex = markers.findIndex((m) => String(m.id) === endMarkerId); const hasRange = startIndex !== -1 && endIndex !== -1; const rangeFrom = hasRange ? Math.min(startIndex, endIndex) : -1; const rangeTo = hasRange ? Math.max(startIndex, endIndex) : -1; // The rail segment sitting between marker `i` and the one after it. const railSegment = (i: number): RailSegment => { if (i < 0 || i >= markers.length - 1) return "none"; return hasRange && i >= rangeFrom && i < rangeTo ? "lit" : "plain"; }; return (
    {markers.length > 0 && ( {markers.length} )}
    setMessage(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleAdd()} placeholder="What just happened?" className={inputClass} /> Add
    {error &&

    {error}

    }
    {markers.length === 0 ? (

    Nothing marked yet. Markers stack up here, newest first — pick two to save the section between them.

    ) : ( /* py-0.5 keeps the scroll container from clipping the selection ring on the first and last rows. */ )}
    ); }; export default MarkerList;