import { Check, ChevronDown, ClipboardPaste, Download, Redo2, Sparkles, Undo2, X } from 'lucide-react'; import { useEffect, useRef, useState, type RefObject } from 'react'; import { useStudioStore } from '../../store/studioStore'; import '../../styles/studio-head.css'; import HelpButton from '../Help/HelpButton'; import { Tooltip } from '../shared/Tooltip'; import { PAGE_PRESET_ORDER, PAGE_PRESETS } from './pagePresets'; import { PeerBadges } from './PeerBadges'; import { SaveIndicator } from './SaveIndicator'; import { StudioAssistantToggle } from './StudioAssistantPanel'; import type { Studio } from './StudioShell'; import { StudioWordmark } from './StudioWordmark'; import { TrimField } from './TrimField'; import type { StudioAssistant } from './useStudioAssistant'; /** * The Studio's head band, in the shape every TREK dialog has: a raised tile on * the left, the Studio's mark above the title, the title, a line under it, and * the close button at the far end. * * The tile is the journey's cover and the title its name, so it is always clear * whose book this is. The tools on the right are grouped by what they do: * who else is here and whether the book is saved, what the book is printed as, editing (undo, redo, paste), auto * layout, the assistant, help, and getting the book out. Every piece of * the row is one raised tile of one height, export alone in the accent. How * the page is looked at, the book view, lives with the zoom under the page. */ export function StudioHeader({ s, onExport, assistant, }: { s: Studio; onExport: () => void; assistant: StudioAssistant; }) { return (
{s.coverUrl ? : null}

{s.journey?.title || s.t('journey.title')}

{/* Whether the book is safe, as a tile beside who else is here: both say how the book stands. */} s.loadDoc(s.acceptTheirs(current))} onKeepMine={s.keepMine} onRetry={() => { void s.saveNow(); }} />
{/* The book's assistant, a tile of its own like help; only where it can run for this user. */} {/* Studio has no navbar, so the help center's button rides on its own bar. */}
); } function IconTool({ label, disabled, onClick, children, }: { label: string; disabled?: boolean; onClick: () => void; children: React.ReactNode; }) { return ( ); } /** * Closes a popover on a pointer press outside it and on Escape. Escape is * swallowed, or the shell's own handler would close Studio itself. */ function useDismiss(box: RefObject, open: boolean, setOpen: (open: boolean) => void) { useEffect(() => { if (!open) return; const onDown = (e: PointerEvent) => { if (!box.current?.contains(e.target as Node)) setOpen(false); }; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') { e.stopPropagation(); setOpen(false); } }; document.addEventListener('pointerdown', onDown); document.addEventListener('keydown', onKey, true); return () => { document.removeEventListener('pointerdown', onDown); document.removeEventListener('keydown', onKey, true); }; }, [box, open, setOpen]); } /** * Paste what was copied onto the page on screen (#2316). Beside undo and redo * because it is the same kind of thing, and there so the keyboard shortcut is * not the only way to find it. */ function PasteButton({ s }: { s: Studio }) { const clipboard = useStudioStore((st) => st.clipboard); const paste = useStudioStore((st) => st.paste); const label = s.t('journey.studio.paste'); return ( ); } /** * Lay out again: this spread, or the whole book. * * A split button rather than two: they are the same action at two scopes, and * the spread is the one you want nine times out of ten. Rebuilding the whole * book is behind the chevron, where it cannot be hit by accident, and both are * ordinary undo steps. */ function AutoLayoutButton({ s }: { s: Studio }) { const [open, setOpen] = useState(false); const box = useRef(null); useDismiss(box, open, setOpen); return (
{open && (
)}
); } /** * The format, as a tool of the head row: it says what the book is printed as, * and opening it changes that. * * A native