diff --git a/components/AIMode/DocumentPane.tsx b/components/AIMode/DocumentPane.tsx index 4ae7858ec..1847f8aa0 100644 --- a/components/AIMode/DocumentPane.tsx +++ b/components/AIMode/DocumentPane.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { Editor } from '@tiptap/react'; +import { ExternalLink } from 'lucide-react'; import { BlockEditorClientWrapper } from '@/components/Editor/components/BlockEditor/components/BlockEditorClientWrapper'; import { NoteReviewBanner } from '@/components/Notebook/NoteReview/NoteReviewBanner'; import { NotebookTabs, type NotebookTab } from '@/components/Notebook/NotebookTabs'; @@ -142,10 +143,26 @@ export function DocumentPane({ // mid-edit would make its next edit_note stale and the review jumpy. const locked = writing || editorLostContent; + // The assistant is writing the first version: nothing to show in the + // editor yet, so the whole pane becomes the progress screen. + const startingDocument = + status === 'working' && !document.hasWrittenVersion && review.review == null; + return (
-
+
+ {document.notebookHref && ( + + + )}
{/* The document is the pane: no gutter, no card, just the page. The @@ -165,6 +182,8 @@ export function DocumentPane({
+ ) : startingDocument ? ( + ) : (
{editorLostContent && ( @@ -173,12 +192,7 @@ export function DocumentPane({ nothing has been changed.
)} - {status === 'empty' && review.review == null && ( - - )} - {status === 'working' && !document.hasWrittenVersion && ( - - )} + {status === 'empty' && review.review == null && } {/* Mounted once per note: the editor's content prop is only read on creation, and later versions arrive through the review. */} @@ -225,29 +239,30 @@ export function DocumentPane({ } /** - * The note exists but has no version yet. Spins only while a turn is - * running; a settled conversation that never wrote anything says so plainly. + * The assistant is writing the first version. Fills the pane: there is no + * document to sit beside yet, so the progress state is the page. */ -function EmptyDocument({ - label, - active, -}: { - readonly label: string | null; - readonly active: boolean; -}) { +function StartingDocument({ label }: { readonly label: string | null }) { + return ( +
+ +

Starting the document…

+ {label &&

{label}

} +
+ ); +} + +/** The note exists, the turn has settled, and nothing was ever written. */ +function EmptyDocument() { return (
- {active ? ( - <> - -

Starting the document…

- {label &&

{label}

} - - ) : ( -

- Nothing has been written here yet. You can start typing, or ask the assistant. -

- )} +

+ Nothing has been written here yet. You can start typing, or ask the assistant. +

); } diff --git a/components/AgentChat/ModelControls.tsx b/components/AgentChat/ModelControls.tsx index 5e85fe5db..545c8e309 100644 --- a/components/AgentChat/ModelControls.tsx +++ b/components/AgentChat/ModelControls.tsx @@ -51,9 +51,9 @@ const PANEL_WIDTH = 'w-[360px] max-w-[calc(100vw-1rem)]'; /** * The composer's two controls: which model answers, and how hard it works. * - * Model and effort lock after the first turn. Once effort is locked the - * panel only says so: thinking and temperature depend on the effort the chat - * runs at, and offering them under a lock reads as a control that half works. + * Model and effort lock after the first turn: both triggers go disabled and + * show a lock, the same way. Thinking and temperature depend on the effort + * the chat runs at, so they lock with it rather than half working. * * The model picker is a menu (`BaseMenu`): one choice, closes on pick. The * effort panel is a popover: it holds all three controls, temperature @@ -91,10 +91,9 @@ export function ModelControls({ const hasEffort = model.capabilities.effort.length > 0 || options.effort != null; const hasEffortMenu = hasEffort || thinkingModes.length > 0 || showTemperature; const effortLocked = effortPinned && hasEffort; - const lockedEffortLabel = options.effort ? EFFORT_LABELS[options.effort] : 'Locked effort'; const lockedEffortDescription = options.effort - ? `${EFFORT_LABELS[options.effort]} effort is locked for this chat. Start a new chat to change it.` - : 'Effort is locked for this chat. Start a new chat to change it.'; + ? `${EFFORT_LABELS[options.effort]} effort — locked for this chat. Start a new chat to change it.` + : 'Effort — locked for this chat. Start a new chat to change it.'; const allowedModels = models.filter((option) => option.allowed); return ( @@ -145,7 +144,7 @@ export function ModelControls({ - {effortLocked ? lockedEffortLabel : effortButtonLabel(options)} + {effortLocked ? 'Effort' : effortButtonLabel(options)} - {effortLocked && ( -

{lockedEffortDescription}

- )} - {!effortLocked && effortLevels.length > 0 && (