feat(ux): unify Ask-Claude-to-Edit on a split-below multi-line webview

Operator feedback iteration on the Ask-Claude input. Replaces the inline
comment-thread box (and the top-center QuickInput before it) with ONE input for
both scopes: a multi-line webview in a split pane below the document.

- New `editInstructionInput.ts` (`promptEditInstruction`): a tall, resizable
  textarea + Send, opened via `newGroupBelow` so it sits under the document, not
  over it. Auto-focused; ⌘↵/Ctrl↵ sends; Esc cancels. Cancel/Esc confirms ONLY
  when there's text to lose (a webview can read its own textarea); closing the tab
  is an explicit dismiss. On submit/cancel the split collapses AND focus is handed
  back to the document, so the bottom panel (Output/Debug Console) no longer pops.
- Selection edits use the same box; the document above keeps the selection
  highlighted (inactive-selection style) so the user sees what Claude will edit —
  the selection is captured before the prompt, never touched.
- Removes the whole inline-comment mechanism: deletes `inlineAsk.ts`
  (InlineAskController), the `cowriting.askClaude.submit`/`.cancel` commands, their
  comment-thread menus, and the Escape keybinding — net deletion. One overridable
  seam `TrackChangesPreviewController.askEditInstruction` (used by editSelection
  and the preview), stubbed by the host E2E.

Sealed webview (INV-8/35): collects text only, no SDK/secret surface, no network.
242 unit + typecheck (src + e2e) + build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-26 06:49:49 -07:00
parent 56cc9eb6ad
commit 687f644ee5
7 changed files with 180 additions and 147 deletions
+7 -7
View File
@@ -76,9 +76,9 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
await vscode.window.showTextDocument(a.doc);
await settle();
// Stub the instruction prompt (sealed input box can't run in CI) + the LLM turn.
const origPrompt = api.inlineAsk.prompt;
(api.inlineAsk as any).prompt = async () => "rewrite it";
// Stub the document instruction prompt (the webview can't run in CI) + the LLM turn.
const origPrompt = ctl.askEditInstruction;
ctl.askEditInstruction = async () => "rewrite it";
ctl.setEditTurnForTest(async () => ({
replacement: "# Tab\n\nThe REWRITTEN tab paragraph.\n",
model: "sonnet",
@@ -88,7 +88,7 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
await vscode.commands.executeCommand("cowriting.editDocument", b.doc.uri);
await settle();
} finally {
(api.inlineAsk as any).prompt = origPrompt;
ctl.askEditInstruction = origPrompt;
}
// The proposal(s) landed on the TAB doc (B), and the ACTIVE doc (A) has none.
@@ -110,8 +110,8 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
await vscode.window.showTextDocument(a.doc);
await settle();
const origPrompt = api.inlineAsk.prompt;
(api.inlineAsk as any).prompt = async () => "rewrite it";
const origPrompt = ctl.askEditInstruction;
ctl.askEditInstruction = async () => "rewrite it";
ctl.setEditTurnForTest(async () => ({
replacement: "# No arg\n\nThe REWRITTEN active doc paragraph.\n",
model: "sonnet",
@@ -121,7 +121,7 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
await vscode.commands.executeCommand("cowriting.editDocument");
await settle();
} finally {
(api.inlineAsk as any).prompt = origPrompt;
ctl.askEditInstruction = origPrompt;
}
assert.ok(api.proposalController.listProposals(a.doc).length >= 1, "active doc received the proposal(s) on no-arg");
});