fix: v2.7.5 — the chat shows each AskUserQuestion with the user's answer

- Chat: an AskUserQuestion is its own card (AskCard in MessageList.tsx), not
  part of the collapsed work line — each question with the option picked or
  the text typed, "declined" when refused. The transcript route passes the
  answers from Claude Code's toolUseResult (askAnswers, beside diff).
- Chat: the activity panel keeps the user's open/close per session for the
  tab, and opens by default only once the session has an output.
- Workspace home: the sent prompt's draft is dropped even though opening the
  session unmounts the home.
- Terminal: Ctrl+V with text on the clipboard pastes the text; only an empty
  clipboard still sends ^V.
- AGENTS.md / CLAUDE.md: run only the tests a change touches while iterating,
  the full suite once before committing.
This commit is contained in:
2026-10-13 10:46:56 +07:00
parent 7cb4f64781
commit b0dd5af22f
20 changed files with 320 additions and 31 deletions
+23 -5
View File
@@ -20,8 +20,10 @@
* (`answerRunMenu`).
* It also has Claude Desktop's activity panel (`ChatOutline`: progress,
* outputs, what the session used), open at first in a pane spanning the
* grid's full width or height (`panelOpen`) and collapsed into a file button
* with the output count in a quarter-sized one; it sits beside the
* grid's full width or height (`panelOpen`) once the session has an output,
* else collapsed into a file button with the output count; the user's own
* open/close is kept per session for the tab (sessionStorage), so the chat
* leaving for the terminal and back keeps it; it sits beside the
* conversation when the pane is wide and over it when narrow (a CSS container
* query on the pane, not the window), dragged wider or narrower by its
* splitter there (a fraction of the pane, remembered per browser). A file
@@ -92,6 +94,11 @@ const MODES: [PermissionMode, string][] = [
const isSettable = (m: unknown): m is PermissionMode => MODES.some(([v]) => v === m);
const SIDE_KEY = "agenthub.chat.panelWidth";
const clampSide = (f: number) => Math.round(Math.min(0.7, Math.max(0.2, f)) * 100) / 100;
const outlineKey = (sessionId: string) => `agenthub.chat.panel.${sessionId}`;
const readOutlineChoice = (sessionId: string) => {
const v = sessionStorage.getItem(outlineKey(sessionId));
return v === null ? null : v === "1";
};
const storedSide = () => {
const f = Number(localStorage.getItem(SIDE_KEY));
return f ? clampSide(f) : null;
@@ -328,11 +335,20 @@ export function PaneChat({
const [model, setModel] = useState(run.model ?? "");
const [effort, setEffort] = useState<EffortLevel>(run.effort ?? "");
const pickFiles = useRef<(() => void) | null>(null);
const [outlineOpen, setOutlineOpen] = useState(panelOpen);
// The user's own open/close, per session for this tab: it outlives the chat
// leaving for the terminal (an unmount). Unset, the default rules.
const [outlineChoice, setOutlineChoice] = useState(() => readOutlineChoice(sessionId));
// A split or a close changes the pane's shape: its default comes back.
useEffect(() => setOutlineOpen(panelOpen), [panelOpen]);
const shape = useRef(panelOpen);
useEffect(() => {
if (shape.current === panelOpen) return;
shape.current = panelOpen;
sessionStorage.removeItem(outlineKey(sessionId));
setOutlineChoice(null);
}, [panelOpen, sessionId]);
const openOutline = (open: boolean) => {
setOutlineOpen(open);
sessionStorage.setItem(outlineKey(sessionId), open ? "1" : "0");
setOutlineChoice(open);
setPreview(null);
};
const [preview, setPreview] = useState<string | null>(null);
@@ -355,6 +371,8 @@ export function PaneChat({
}, []);
const [list, setList] = useState<OutlineList | null>(null);
const activity = useSessionOutline(sessionId);
// Open by default only in a spanning pane, and only once there is an output.
const outlineOpen = outlineChoice ?? (panelOpen && activity.outputs.length > 0);
const scope: PreviewScope = { laneId, laneCwd: laneCwd || cwd, cwd, sessionId };
const text = promptWithImages(prompt, images);