feat(chat): add Send button to agent composers (mobile fix)

Soft keyboards have no reliable Enter-to-send, so chat was unsendable on
mobile browsers. Add an optional themed Send button wired through
wireAgentChat (Enter-to-send kept for desktop), applied to the Companion
rail, Yerin, and Little Blue composers. Blackflame-styled, flex-row layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-05 08:25:05 +10:00
parent 3c028fed5a
commit c2569cad76
5 changed files with 31 additions and 11 deletions

View File

@@ -38,9 +38,10 @@ function draftCardEl(d, onResolve) {
* @param {boolean} [o.showDrafts] render propose_change draft cards (Dross only)
* @param {object} [o.toolLabels] tool-name → display string
* @param {(text:string)=>object} [o.turnBody] POST body builder
* @returns {{ load: () => Promise<void> }}
* @param {HTMLElement} [o.sendBtnEl] optional Send button (needed on touch/mobile where there's no Enter key)
* @returns {{ load: () => Promise<void>, send: () => Promise<void> }}
*/
export function wireAgentChat({ logEl, inputEl, historyUrl, turnUrl, agentName, showDrafts = false, toolLabels = {}, turnBody = (text) => ({ text }) }) {
export function wireAgentChat({ logEl, inputEl, historyUrl, turnUrl, agentName, showDrafts = false, toolLabels = {}, turnBody = (text) => ({ text }), sendBtnEl = null }) {
async function resolveDraft(id, status, cardNode) {
try {
await api.post(`/api/pending-changes/${id}/${status === 'approved' ? 'approve' : 'reject'}`);
@@ -93,10 +94,19 @@ export function wireAgentChat({ logEl, inputEl, historyUrl, turnUrl, agentName,
}
}
// Desktop: Enter sends (Shift+Enter = newline). Mobile soft keyboards have no
// reliable Enter-to-send, so callers also pass a tappable Send button.
if (inputEl._sendHandler) inputEl.removeEventListener('keydown', inputEl._sendHandler);
const handler = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } };
inputEl._sendHandler = handler;
inputEl.addEventListener('keydown', handler);
return { load };
if (sendBtnEl) {
if (sendBtnEl._sendHandler) sendBtnEl.removeEventListener('click', sendBtnEl._sendHandler);
const click = () => { send(); inputEl.focus(); };
sendBtnEl._sendHandler = click;
sendBtnEl.addEventListener('click', click);
}
return { load, send };
}