diff --git a/i18n/en.json b/i18n/en.json index 2297129..9dc5771 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -42,6 +42,7 @@ "Response stopped": "Response stopped", "Cosmo is responding": "Cosmo is responding", "Response complete": "Response complete", + "Wait for Cosmo to finish responding before typing.": "Wait for Cosmo to finish responding before typing.", "Your message": "Your message", "Cosmo's reply": "Cosmo's reply", "Regenerate response": "Regenerate response", diff --git a/i18n/es.json b/i18n/es.json index 99b9ce2..5e5861b 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -42,6 +42,7 @@ "Response stopped": "Respuesta detenida", "Cosmo is responding": "Cosmo está respondiendo", "Response complete": "Respuesta completa", + "Wait for Cosmo to finish responding before typing.": "Espera a que Cosmo termine de responder antes de escribir.", "Your message": "Tu mensaje", "Cosmo's reply": "Respuesta de Cosmo", "Regenerate response": "Regenerar respuesta", diff --git a/public/app.js b/public/app.js index 28acdc1..709334e 100644 --- a/public/app.js +++ b/public/app.js @@ -1146,11 +1146,40 @@ function renderMessages(liveMessages, status) { }); } - const streaming = status === 'streaming'; - promptInput.disabled = streaming; + syncComposerAvailability(status === 'streaming'); updateSendBtn(); } +/** + * Keep the composer focusable while a reply streams (A3). + * Prefer readOnly over disabled so focus is not dropped to , and + * explain the wait via aria-describedby. Restore focus on completion if + * it fell to the document body. + */ +function syncComposerAvailability(streaming) { + // Never leave the field disabled — that removes it from the tab order. + promptInput.disabled = false; + promptInput.readOnly = streaming; + + const hint = document.getElementById('promptInputBusyHint'); + if (hint) { + if (streaming) { + hint.textContent = t('Wait for Cosmo to finish responding before typing.'); + promptInput.setAttribute('aria-describedby', hint.id); + } else { + hint.textContent = ''; + promptInput.removeAttribute('aria-describedby'); + } + } + + if (!streaming) { + const activeEl = document.activeElement; + if (activeEl === document.body || activeEl === document.documentElement) { + promptInput.focus({ preventScroll: true }); + } + } +} + /** * Split fingerprints so streaming token ticks can patch prose without * recreating the article/heading chrome (keeps VoiceOver's place). diff --git a/public/index.html b/public/index.html index c22de6b..cffe3d7 100644 --- a/public/index.html +++ b/public/index.html @@ -106,6 +106,8 @@

What's on your mind?

rows="1" aria-label="Prompt input" > + +