agent terminal: multi-line textarea input
swap the single-line <input> for an auto-growing <textarea>. enter submits, shift+enter newlines, ime composition respected (skip submit while isComposing). height caps at ~12em then scrolls. submit-hint updates to '↵ send · ⇧↵ newline'. async-form handler now also clears textareas on success.
This commit is contained in:
parent
fd39226883
commit
85e1f1a8f4
2 changed files with 36 additions and 12 deletions
|
|
@ -156,18 +156,21 @@ pre.diff {
|
|||
.term-input { padding: 0.4em 1em 0.8em; }
|
||||
.term-input .sendform-term {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
gap: 0.5em;
|
||||
border-top: 1px dashed var(--purple-dim);
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
.term-input .prompt, .term-input .submit-hint {
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
.term-input .prompt {
|
||||
color: var(--green);
|
||||
text-shadow: 0 0 6px rgba(166, 227, 161, 0.6);
|
||||
user-select: none;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.term-input input {
|
||||
.term-input textarea {
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
|
|
@ -177,11 +180,15 @@ pre.diff {
|
|||
font-size: 1em;
|
||||
padding: 0.2em 0;
|
||||
caret-color: var(--green);
|
||||
resize: none;
|
||||
overflow-y: auto;
|
||||
line-height: 1.4;
|
||||
min-height: 1.4em;
|
||||
}
|
||||
.term-input input::placeholder { color: var(--muted); }
|
||||
.term-input textarea::placeholder { color: var(--muted); }
|
||||
.term-input .submit-hint { color: var(--muted); font-size: 0.8em; flex: 0 0 auto; }
|
||||
.term-input.disabled .prompt { color: var(--muted); text-shadow: none; }
|
||||
.term-input.disabled input { color: var(--muted); }
|
||||
.term-input.disabled textarea { color: var(--muted); }
|
||||
.live {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid var(--purple-dim);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
return;
|
||||
}
|
||||
// Clear text inputs the operator typed into (the form value was sent).
|
||||
f.querySelectorAll('input[type="text"], input:not([type])').forEach((i) => { i.value = ''; });
|
||||
f.querySelectorAll('input[type="text"], input:not([type]), textarea').forEach((i) => { i.value = ''; });
|
||||
// Re-enable the button — refreshState() often skips re-rendering the
|
||||
// form (status unchanged), so without this the spinner sticks and
|
||||
// the operator can't submit again.
|
||||
|
|
@ -165,20 +165,37 @@
|
|||
action: '/send', method: 'POST',
|
||||
class: 'sendform-term', 'data-async': '',
|
||||
});
|
||||
const ta = el('textarea', {
|
||||
name: 'body', placeholder: 'message ' + label + '…',
|
||||
required: '', autocomplete: 'off', rows: '1',
|
||||
});
|
||||
// Enter submits, Shift+Enter inserts a newline. Auto-grow up to
|
||||
// ~8 rows of content, then scroll inside the textarea.
|
||||
const MAX_PX = 12 * 16; // ~8 lines @ 1.5 line-height, 1em base
|
||||
const grow = () => {
|
||||
ta.style.height = 'auto';
|
||||
ta.style.height = Math.min(ta.scrollHeight, MAX_PX) + 'px';
|
||||
};
|
||||
ta.addEventListener('input', grow);
|
||||
ta.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) {
|
||||
e.preventDefault();
|
||||
if (ta.value.trim()) form.requestSubmit();
|
||||
}
|
||||
});
|
||||
// Reset height after async submit clears the value.
|
||||
form.addEventListener('submit', () => setTimeout(grow, 0));
|
||||
form.append(
|
||||
el('span', { class: 'prompt' }, 'operator@' + label + ' ▸'),
|
||||
el('input', {
|
||||
name: 'body', placeholder: 'message ' + label + '…',
|
||||
required: '', autocomplete: 'off',
|
||||
}),
|
||||
el('span', { class: 'submit-hint' }, 'enter ↵'),
|
||||
ta,
|
||||
el('span', { class: 'submit-hint' }, '↵ send · ⇧↵ newline'),
|
||||
);
|
||||
slot.append(form);
|
||||
termInputRendered = true;
|
||||
}
|
||||
slot.classList.toggle('disabled', !online);
|
||||
const input = slot.querySelector('input');
|
||||
if (input) input.disabled = !online;
|
||||
const ta = slot.querySelector('textarea');
|
||||
if (ta) ta.disabled = !online;
|
||||
}
|
||||
|
||||
// Track banner activity by reference-counting in-flight turns. A turn
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue