Release v0.51.283 — Release IY (stage-w2 — composer queue hint during auto-compaction #3512) (#3690)
* feat(composer): surface that messages queue during auto-compaction (#3512, #3079) Co-authored-by: Rod Boev <rod.boev@gmail.com> * docs(changelog): v0.51.283 — Release IY (stage-w2) * fix(composer): restore placeholder on ALL compaction-exit paths, not just clearCompressionUi (#3512) Codex+Opus both caught: setCompressionUi(done) and the live-anchored SSE window._compressionUi=null paths bypassed clearCompressionUi, leaving the 'will queue' placeholder stuck after compaction. Factor restore into _restoreCompressionPlaceholder() + call from every compaction-exit path. --------- Co-authored-by: nesquena-hermes <[email protected]> Co-authored-by: Rod Boev <rod.boev@gmail.com>
This commit is contained in:
parent
fd45b54c17
commit
a7241f75fc
5 changed files with 67 additions and 4 deletions
|
|
@ -3,6 +3,11 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.51.283] — 2026-06-05 — Release IY (stage-w2 — composer queue hint during auto-compaction)
|
||||
|
||||
### Fixed
|
||||
- **The composer now tells you a message will queue during auto-compaction instead of looking dead.** While automatic compression runs, the send button previously went `disabled` with only a "Waiting for compression to finish" tooltip. It now shows a `queue` action with the placeholder + tooltip "Type a message — it will queue and send after compression", so you can type and have it sent automatically when compaction completes. (#3512, @rodboev; closes #3079)
|
||||
|
||||
## [v0.51.282] — 2026-06-05 — Release IX (stage-3544 — surface memory/skill saves in Activity summary)
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -1300,6 +1300,7 @@ const LOCALES = {
|
|||
composer_stop: 'Stop generation',
|
||||
composer_disabled_clarify: 'Respond to the clarification request',
|
||||
composer_disabled_compression: 'Waiting for compression to finish',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: 'Type a message to send',
|
||||
composer_mobile_workspace: 'Workspace',
|
||||
composer_mobile_model: 'Model',
|
||||
|
|
@ -2629,6 +2630,7 @@ const LOCALES = {
|
|||
composer_stop: 'Ferma generazione',
|
||||
composer_disabled_clarify: 'Rispondi alla richiesta di chiarimento',
|
||||
composer_disabled_compression: 'In attesa del completamento della compressione',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: 'Scrivi un messaggio da inviare',
|
||||
composer_mobile_workspace: 'Workspace',
|
||||
composer_mobile_model: 'Modello',
|
||||
|
|
@ -3963,6 +3965,7 @@ const LOCALES = {
|
|||
composer_stop: '生成を停止',
|
||||
composer_disabled_clarify: '確認要求に応答してください',
|
||||
composer_disabled_compression: '圧縮の完了待ち',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: '送信するメッセージを入力してください',
|
||||
composer_mobile_workspace: 'ワークスペース',
|
||||
composer_mobile_model: 'モデル',
|
||||
|
|
@ -5167,6 +5170,7 @@ const LOCALES = {
|
|||
composer_stop: 'Остановить генерацию',
|
||||
composer_disabled_clarify: 'Ответьте на запрос о разъяснении',
|
||||
composer_disabled_compression: 'Ожидание завершения сжатия',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: 'Введите сообщение для отправки',
|
||||
composer_mobile_workspace: 'Рабочее пространство',
|
||||
composer_mobile_model: 'Модель',
|
||||
|
|
@ -6421,6 +6425,7 @@ const LOCALES = {
|
|||
composer_stop: 'Detener generación',
|
||||
composer_disabled_clarify: 'Responde a la solicitud de aclaración',
|
||||
composer_disabled_compression: 'Esperando a que finalice la compresión',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: 'Escribe un mensaje para enviar',
|
||||
composer_mobile_workspace: 'Espacio de trabajo',
|
||||
composer_mobile_model: 'Modelo',
|
||||
|
|
@ -7391,6 +7396,7 @@ const LOCALES = {
|
|||
composer_stop: 'Generierung stoppen',
|
||||
composer_disabled_clarify: 'Auf die Klärungsanfrage antworten',
|
||||
composer_disabled_compression: 'Warte auf Abschluss der Komprimierung',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: 'Nachricht eingeben zum Senden',
|
||||
composer_mobile_workspace: 'Arbeitsbereich',
|
||||
composer_mobile_model: 'Modell',
|
||||
|
|
@ -8951,6 +8957,7 @@ const LOCALES = {
|
|||
composer_stop: '停止生成',
|
||||
composer_disabled_clarify: '请回复上方的澄清请求',
|
||||
composer_disabled_compression: '等待压缩完成',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: '请输入消息后发送',
|
||||
composer_mobile_workspace: '工作区',
|
||||
composer_mobile_model: '模型',
|
||||
|
|
@ -10360,6 +10367,7 @@ const LOCALES = {
|
|||
composer_stop: '停止產生',
|
||||
composer_disabled_clarify: '請回覆上方的澄清要求',
|
||||
composer_disabled_compression: '等待壓縮完成',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: '請輸入訊息後傳送',
|
||||
composer_mobile_workspace: '工作區',
|
||||
composer_mobile_model: '模型',
|
||||
|
|
@ -12748,6 +12756,7 @@ const LOCALES = {
|
|||
composer_stop: '생성 중지',
|
||||
composer_disabled_clarify: '위의 명확화 요청에 응답하세요',
|
||||
composer_disabled_compression: '압축 완료 대기 중',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: '메시지를 입력하세요',
|
||||
composer_mobile_workspace: '워크스페이스',
|
||||
composer_mobile_model: '모델',
|
||||
|
|
@ -13994,6 +14003,7 @@ const LOCALES = {
|
|||
composer_stop: 'Arrêter la génération',
|
||||
composer_disabled_clarify: 'Répondre à la demande de précisions',
|
||||
composer_disabled_compression: 'En attendant la fin de la compression',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: 'Tapez un message à envoyer',
|
||||
composer_mobile_workspace: 'Espace de travail',
|
||||
composer_mobile_model: 'Modèle',
|
||||
|
|
@ -15341,6 +15351,7 @@ const LOCALES = {
|
|||
composer_stop: 'Üretimi durdur',
|
||||
composer_disabled_clarify: 'Açıklama isteğine yanıt verin',
|
||||
composer_disabled_compression: 'Sıkıştırmanın bitmesi bekleniyor',
|
||||
composer_compression_will_queue: 'Type a message — it will queue and send after compression',
|
||||
composer_disabled_empty: 'Göndermek için bir mesaj yazın',
|
||||
composer_mobile_workspace: 'Çalışma alanı',
|
||||
composer_mobile_model: 'Modeli',
|
||||
|
|
|
|||
|
|
@ -2388,6 +2388,7 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
|||
// state in global transient UI makes later renderMessages() calls insert
|
||||
// a duplicate Automatic Compression card.
|
||||
window._compressionUi=null;
|
||||
if(typeof _restoreCompressionPlaceholder==='function') _restoreCompressionPlaceholder();
|
||||
snapshotLiveTurn();
|
||||
return;
|
||||
}
|
||||
|
|
@ -2435,6 +2436,7 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
|||
// completion state as global transient UI, otherwise every subsequent
|
||||
// render projects the same Auto Compression card again.
|
||||
window._compressionUi=null;
|
||||
if(typeof _restoreCompressionPlaceholder==='function') _restoreCompressionPlaceholder();
|
||||
snapshotLiveTurn();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
33
static/ui.js
33
static/ui.js
|
|
@ -3834,6 +3834,7 @@ function setComposerStatus(t){
|
|||
}
|
||||
|
||||
let _composerLockState=null;
|
||||
let _compressionPlaceholderSaved=null;
|
||||
|
||||
function lockComposerForClarify(placeholderText){
|
||||
const input=$('msg');
|
||||
|
|
@ -3905,6 +3906,7 @@ function getComposerPrimaryAction(){
|
|||
if(!isBusy) return hasContent?'send':'disabled';
|
||||
if(!hasContent){
|
||||
if(S.activeStreamId&&typeof cancelStream==='function') return 'stop';
|
||||
if(compressionRunning) return 'queue';
|
||||
return 'disabled';
|
||||
}
|
||||
const explicitAction=_getExplicitBusyCommandAction(msg&&msg.value);
|
||||
|
|
@ -3949,10 +3951,10 @@ function updateSendBtn(){
|
|||
let _btnTitle;
|
||||
if(action==='disabled'){
|
||||
const _dmsg=$('msg');
|
||||
const _dcompr=typeof isCompressionUiRunning==='function'&&isCompressionUiRunning();
|
||||
if(_dmsg&&_dmsg.disabled) _btnTitle=_tt('composer_disabled_clarify','Respond to the clarification request');
|
||||
else if(_dcompr) _btnTitle=_tt('composer_disabled_compression','Waiting for compression to finish');
|
||||
else _btnTitle=_tt('composer_disabled_empty','Type a message to send');
|
||||
}else if(action==='queue'&&typeof isCompressionUiRunning==='function'&&isCompressionUiRunning()){
|
||||
_btnTitle=_tt('composer_compression_will_queue','Type a message — it will queue and send after compression');
|
||||
}else{
|
||||
const _tmap={send:'Send message',queue:'Queue message',interrupt:'Interrupt and send',steer:'Steer current response',stop:'Stop generation'};
|
||||
_btnTitle=_tt('composer_'+action,_tmap[action]||'Send message');
|
||||
|
|
@ -6182,10 +6184,22 @@ function isCompressionUiRunning(){
|
|||
const lock=_compressionSessionLock();
|
||||
return !!((state&&state.phase==='running') || (lock && S.session && lock===S.session.session_id));
|
||||
}
|
||||
// Restore the composer placeholder saved when auto-compaction started. Safe to
|
||||
// call whenever compression leaves the running state, from any path (clear,
|
||||
// non-running setCompressionUi, or a direct window._compressionUi=null in the
|
||||
// SSE handler) — it no-ops when nothing was saved. (#3512)
|
||||
function _restoreCompressionPlaceholder(){
|
||||
const _input=$('msg');
|
||||
if(_input&&typeof _compressionPlaceholderSaved==='string'){
|
||||
_input.placeholder=_compressionPlaceholderSaved;
|
||||
}
|
||||
_compressionPlaceholderSaved=null;
|
||||
}
|
||||
function clearCompressionUi(){
|
||||
window._compressionUi=null;
|
||||
_clearCompressionElapsedTimer();
|
||||
_setCompressionSessionLock(null);
|
||||
_restoreCompressionPlaceholder();
|
||||
renderCompressionUi();
|
||||
}
|
||||
function setCompressionUi(state){
|
||||
|
|
@ -6199,8 +6213,19 @@ function setCompressionUi(state){
|
|||
}
|
||||
window._compressionUi=nextState;
|
||||
if(nextState.sessionId) _setCompressionSessionLock(nextState.sessionId);
|
||||
if(nextState.automatic&&nextState.phase==='running') _startCompressionElapsedTimer();
|
||||
else _clearCompressionElapsedTimer();
|
||||
if(nextState.automatic&&nextState.phase==='running'){
|
||||
_startCompressionElapsedTimer();
|
||||
const _input=$('msg');
|
||||
if(_input&&_compressionPlaceholderSaved===null){
|
||||
_compressionPlaceholderSaved=_input.placeholder;
|
||||
_input.placeholder=typeof t==='function'?t('composer_compression_will_queue')||'Type a message — it will queue and send after compression':'Type a message — it will queue and send after compression';
|
||||
}
|
||||
} else {
|
||||
_clearCompressionElapsedTimer();
|
||||
// Leaving the running state (e.g. setCompressionUi(done)) must restore the
|
||||
// placeholder too — not only clearCompressionUi(). (#3512 leak fix)
|
||||
_restoreCompressionPlaceholder();
|
||||
}
|
||||
renderCompressionUi();
|
||||
}
|
||||
function _compressionCardsHtml(state){
|
||||
|
|
|
|||
|
|
@ -276,3 +276,23 @@ def test_messaging_source_badge_in_sidebar_not_gated_on_is_cli_session():
|
|||
assert '.session-source-chip[data-source-key="telegram"]' in style_css
|
||||
assert '.session-source-chip[data-source-key="discord"]' in style_css
|
||||
assert '.session-item.cli-session[data-source-key="telegram"]' in style_css
|
||||
|
||||
|
||||
def test_compression_queue_discoverability_ux():
|
||||
ui_js = (REPO_ROOT / "static" / "ui.js").read_text(encoding="utf-8")
|
||||
i18n_js = (REPO_ROOT / "static" / "i18n.js").read_text(encoding="utf-8")
|
||||
|
||||
# Old misleading tooltip key must be gone from the compression branch
|
||||
assert "composer_disabled_compression','Waiting for compression to finish'" not in ui_js
|
||||
|
||||
# New will-queue key must be present in ui.js (tooltip and placeholder)
|
||||
assert "composer_compression_will_queue" in ui_js
|
||||
|
||||
# getComposerPrimaryAction must return 'queue' for compressionRunning+no-content
|
||||
assert "if(compressionRunning) return 'queue';" in ui_js
|
||||
|
||||
# clearCompressionUi must restore the placeholder
|
||||
assert "_compressionPlaceholderSaved" in ui_js
|
||||
|
||||
# New i18n key must appear in all 11 locales (count occurrences)
|
||||
assert i18n_js.count("composer_compression_will_queue") >= 11
|
||||
|
|
|
|||
Loading…
Reference in a new issue