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 (
-
+
{/* 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 && (