Make the action input auto-grow up to ~4 lines

Swap the single-line action <input> for a <textarea> that grows with its
content (capped at ~4 lines, then scrolls) and shrinks back after send.
Enter still sends; Shift+Enter inserts a newline. Buttons stay bottom-aligned
as it grows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEpWMjnfPqzZ13nPMoGhs5
This commit is contained in:
parththakkar106
2026-07-21 01:41:00 +05:30
co-authored by Claude Opus 4.8
parent 8a1650c0d4
commit 92044a53c4
2 changed files with 27 additions and 4 deletions
+10 -1
View File
@@ -392,8 +392,17 @@ button:disabled { opacity: 0.45; cursor: default; transform: none; box-shadow: n
.turn-buttons button { font-size: 0.78rem; padding: 4px 12px; color: var(--text-dim); } .turn-buttons button { font-size: 0.78rem; padding: 4px 12px; color: var(--text-dim); }
.turn-buttons button:hover:not(:disabled) { color: var(--text); } .turn-buttons button:hover:not(:disabled) { color: var(--text); }
.input-bar { display: flex; gap: 8px; align-items: stretch; } .input-bar { display: flex; gap: 8px; align-items: flex-end; }
.input-bar input { flex: 1; } .input-bar input { flex: 1; }
/* Auto-growing action box: starts at one line, grows to ~4, then scrolls. */
.input-bar textarea {
flex: 1;
resize: none;
min-height: 0;
max-height: calc(6em + 20px); /* ~4 lines (1.5em each) + padding/border */
overflow-y: auto;
line-height: 1.5;
}
.mode-select { display: flex; } .mode-select { display: flex; }
.mode-select button { .mode-select button {
+17 -3
View File
@@ -612,6 +612,16 @@ export default function Play() {
const storyEndRef = useRef(null) const storyEndRef = useRef(null)
const abortRef = useRef(null) const abortRef = useRef(null)
const pinnedRef = useRef(true) // autoscroll only while the reader is at the bottom const pinnedRef = useRef(true) // autoscroll only while the reader is at the bottom
const inputRef = useRef(null)
// Grow the action box with its content (CSS caps it at ~4 lines, then
// scrolls); shrinks back after send() clears the text.
useEffect(() => {
const el = inputRef.current
if (!el) return
el.style.height = 'auto'
el.style.height = `${el.scrollHeight}px`
}, [input])
useEffect(() => { useEffect(() => {
api.getAdventure(id) api.getAdventure(id)
@@ -863,8 +873,9 @@ export default function Play() {
</button> </button>
))} ))}
</div> </div>
<input <textarea
type="text" ref={inputRef}
rows={1}
value={input} value={input}
disabled={busy} disabled={busy}
placeholder={ placeholder={
@@ -873,7 +884,10 @@ export default function Play() {
: 'What happens next?' : 'What happens next?'
} }
onChange={(e) => setInput(e.target.value)} onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter' && !busy) send() }} // Enter sends; Shift+Enter inserts a newline.
onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey && !busy) { e.preventDefault(); send() }
}}
/> />
{busy ? ( {busy ? (
<button className="danger" onClick={stopGeneration}>■ Stop</button> <button className="danger" onClick={stopGeneration}>■ Stop</button>