From 609bfe95032ace4d4ba3f4e2333c52500c075238 Mon Sep 17 00:00:00 2001 From: Ethan Lane <1124774683@qq.com> Date: Tue, 17 Feb 2026 20:57:49 +0800 Subject: [PATCH] fix: support multiline input with Shift+Enter in CLI - Add Shift+Enter handler to insert newline in input box - Preserve newlines when pasting text (except in URL/key modes) - Render input as multiline with per-line cursor display - Add Shift+Enter tip to TIPS pool Co-Authored-By: Claude Opus 4.6 --- packages/codeblog/src/tui/commands.ts | 1 + packages/codeblog/src/tui/routes/home.tsx | 29 +++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/codeblog/src/tui/commands.ts b/packages/codeblog/src/tui/commands.ts index 5436978..06be64f 100644 --- a/packages/codeblog/src/tui/commands.ts +++ b/packages/codeblog/src/tui/commands.ts @@ -165,6 +165,7 @@ export const TIPS = [ "Type / to see all available commands with autocomplete", "Just start typing to chat with AI — no command needed!", "Use /clear to reset the conversation", + "Press Shift+Enter to add a new line in the input box", ] export const TIPS_NO_AI = [ diff --git a/packages/codeblog/src/tui/routes/home.tsx b/packages/codeblog/src/tui/routes/home.tsx index 36be6c2..5c543b0 100644 --- a/packages/codeblog/src/tui/routes/home.tsx +++ b/packages/codeblog/src/tui/routes/home.tsx @@ -163,11 +163,18 @@ export function Home(props: { }) usePaste((evt) => { - const text = evt.text.replace(/[\n\r]/g, "").trim() + // For URL/key modes, strip newlines; for normal input, preserve them + if (aiMode() === "url" || aiMode() === "key") { + const text = evt.text.replace(/[\n\r]/g, "").trim() + if (!text) return + evt.preventDefault() + if (aiMode() === "url") { setAiUrl(text); return } + setAiKey(text) + return + } + const text = evt.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n") if (!text) return evt.preventDefault() - if (aiMode() === "url") { setAiUrl(text); return } - if (aiMode() === "key") { setAiKey(text); return } setInput((s) => s + text) }) @@ -444,6 +451,7 @@ export function Home(props: { if (evt.name === "escape" && chatting() && !streaming()) { clearChat(); evt.preventDefault(); return } if (evt.name === "return" && !evt.shift) { handleSubmit(); evt.preventDefault(); return } + if (evt.name === "return" && evt.shift) { setInput((s) => s + "\n"); evt.preventDefault(); return } if (evt.name === "backspace") { setInput((s) => s.slice(0, -1)); setSelectedIdx(0); evt.preventDefault(); return } if (evt.sequence && evt.sequence.length >= 1 && !evt.ctrl && !evt.meta) { const clean = evt.sequence.replace(/[\x00-\x1f\x7f]/g, "") @@ -632,10 +640,17 @@ export function Home(props: { {/* Input line with blinking cursor */} - - {"❯ "} - {input()} - {"█"} + + {(() => { + const lines = input().split("\n") + return lines.map((line, i) => ( + + {i === 0 ? "❯ " : " "} + {line} + {i === lines.length - 1 && {"█"}} + + )) + })()}