Skip to content

emrg: remove TUI selector widgets from chat on confirm/cancel - #921

Merged
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove
Aug 21, 2026
Merged

emrg: remove TUI selector widgets from chat on confirm/cancel#921
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix TUI selector overlays staying in the chat after selection (host rant 2026-08-21T16:47:44/trigger popup: the task list remains visible in the chat after picking a task).

Root cause

All six interactive selectors (session_sel, delete_sel, project_sel, model_sel, rewind_sel, task_sel) shared the same latent bug: on Esc-cancel and Enter-confirm they only set active = False / widget = None but never called chat.remove(widget), so the selector widget was never removed from the chat rows and stayed on screen. Only the autocomplete widget was being removed.

Changes (emrg/client/app.py, +18 lines)

  1. Every selector deactivation path now removes the widget firstchat.remove(<sel>.widget) immediately before widget = None in:
    • session_sel: Esc-cancel, Enter-confirm, and the direct /resume <name> path
    • delete_sel: Esc-cancel and Enter-confirm
    • project_sel: Esc-cancel and Enter-confirm
    • model_sel: Esc-cancel and Enter-confirm
    • rewind_sel: Esc-cancel and Enter-confirm
    • task_sel: Esc-cancel and Enter-confirm (the /trigger rant target)
  2. Duplicate-activation guard in the tasks_list handler — if a TaskSelector is already in the chat (duplicate /trigger while one is pending), drop it before stacking a new one instead of rendering two stacked lists.

ChatHistory.remove(row) removes by widget identity and silently no-ops on ValueError, so this is safe in every path.

Verification

  • uv run pytest tests/ -v → 994 passed + 1 skipped (995 collected; doc-count guard green, Agent.md stays at 995 on master)
  • cd emrg/gui && npm test → 249 passed + 8 skipped (257)
  • uv run python -c "from emrg.client.app import run_client" → OK
  • uv run python -m emrg --help → OK

All six interactive selectors (session/delete/project/model/rewind/task)
kept their widget in the chat after selection: Esc/Enter only set
active=False and widget=None, but never called chat.remove(), so the
overlay list stayed visible (rant 2026-08-21T16:47:44 for /trigger).
Add chat.remove(<sel>.widget) to every deactivation path (Esc-cancel and
Enter-confirm for each selector, plus the direct /resume name path), and
guard the tasks_list handler against stacking a second TaskSelector on
re-trigger while one is already active.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1214. Fixes rant 2026-08-21T16:47:44 (/trigger task list stays in chat): every selector deactivation path now calls chat.remove(.widget) before widget=None (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus /resume name path), and the tasks_list handler drops a stale TaskSelector before stacking a new one. ChatHistory.remove is identity-based and no-ops on ValueError — safe in all paths. Local: pytest 994 passed + 1 skipped (995, doc-count guard green), GUI 249+8, import + CLI OK. CI test + test-windows PASS.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1215. Independent re-review on head 9161006 (MERGEABLE, CI test + test-windows PASS):

  • All 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the overlay is removed from chat rows instead of lingering.
  • ChatHistory.remove is identity-based and swallows ValueError (widgets.py:683), so calling it with a None/stale widget is a safe no-op — the /resume path's unconditional remove is defensive, not fragile.
  • Duplicate-activation guard in the tasks_list handler drops a stale TaskSelector before stacking a new one on repeated /trigger.
  • Local: pytest 994 passed + 1 skipped, GUI 249+8 (257), import + CLI OK.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle R2165. Independent review on head 9161006 (MERGEABLE, mergeStateStatus CLEAN; CI test + test-windows PASS):

  • Diff audited: all 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the missing widget-removal was the exact root cause of rant 2026-08-21T16:47:44 (/trigger task list stays in chat).
  • Duplicate-activation guard in tasks_list handler correctly drops a stale pending TaskSelector before stacking a new one.
  • ChatHistory.remove no-ops on ValueError, so removing an already-removed widget is safe.
  • Local verification on this branch: pytest 994 passed + 1 skipped (995 collected); GUI 249 pass / 0 fail / 8 skipped; import + CLI OK. Merge-tree vs master: 0 conflict markers.
    3 consecutive ✅ from different cycles — merging.

@argszero
argszero merged commit 95150e7 into masterAug 21, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/tui-trigger-selector-remove branch August 23, 2026 02:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
emrg: remove TUI selector widgets from chat on confirm/cancel by argszero · Pull Request #921 · argszero/emrg · GitHub
Skip to content

emrg: remove TUI selector widgets from chat on confirm/cancel - #921

Merged
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove
Aug 21, 2026
Merged

emrg: remove TUI selector widgets from chat on confirm/cancel#921
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix TUI selector overlays staying in the chat after selection (host rant 2026-08-21T16:47:44/trigger popup: the task list remains visible in the chat after picking a task).

Root cause

All six interactive selectors (session_sel, delete_sel, project_sel, model_sel, rewind_sel, task_sel) shared the same latent bug: on Esc-cancel and Enter-confirm they only set active = False / widget = None but never called chat.remove(widget), so the selector widget was never removed from the chat rows and stayed on screen. Only the autocomplete widget was being removed.

Changes (emrg/client/app.py, +18 lines)

  1. Every selector deactivation path now removes the widget firstchat.remove(<sel>.widget) immediately before widget = None in:
    • session_sel: Esc-cancel, Enter-confirm, and the direct /resume <name> path
    • delete_sel: Esc-cancel and Enter-confirm
    • project_sel: Esc-cancel and Enter-confirm
    • model_sel: Esc-cancel and Enter-confirm
    • rewind_sel: Esc-cancel and Enter-confirm
    • task_sel: Esc-cancel and Enter-confirm (the /trigger rant target)
  2. Duplicate-activation guard in the tasks_list handler — if a TaskSelector is already in the chat (duplicate /trigger while one is pending), drop it before stacking a new one instead of rendering two stacked lists.

ChatHistory.remove(row) removes by widget identity and silently no-ops on ValueError, so this is safe in every path.

Verification

  • uv run pytest tests/ -v → 994 passed + 1 skipped (995 collected; doc-count guard green, Agent.md stays at 995 on master)
  • cd emrg/gui && npm test → 249 passed + 8 skipped (257)
  • uv run python -c "from emrg.client.app import run_client" → OK
  • uv run python -m emrg --help → OK

All six interactive selectors (session/delete/project/model/rewind/task)
kept their widget in the chat after selection: Esc/Enter only set
active=False and widget=None, but never called chat.remove(), so the
overlay list stayed visible (rant 2026-08-21T16:47:44 for /trigger).
Add chat.remove(<sel>.widget) to every deactivation path (Esc-cancel and
Enter-confirm for each selector, plus the direct /resume name path), and
guard the tasks_list handler against stacking a second TaskSelector on
re-trigger while one is already active.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1214. Fixes rant 2026-08-21T16:47:44 (/trigger task list stays in chat): every selector deactivation path now calls chat.remove(.widget) before widget=None (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus /resume name path), and the tasks_list handler drops a stale TaskSelector before stacking a new one. ChatHistory.remove is identity-based and no-ops on ValueError — safe in all paths. Local: pytest 994 passed + 1 skipped (995, doc-count guard green), GUI 249+8, import + CLI OK. CI test + test-windows PASS.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1215. Independent re-review on head 9161006 (MERGEABLE, CI test + test-windows PASS):

  • All 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the overlay is removed from chat rows instead of lingering.
  • ChatHistory.remove is identity-based and swallows ValueError (widgets.py:683), so calling it with a None/stale widget is a safe no-op — the /resume path's unconditional remove is defensive, not fragile.
  • Duplicate-activation guard in the tasks_list handler drops a stale TaskSelector before stacking a new one on repeated /trigger.
  • Local: pytest 994 passed + 1 skipped, GUI 249+8 (257), import + CLI OK.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle R2165. Independent review on head 9161006 (MERGEABLE, mergeStateStatus CLEAN; CI test + test-windows PASS):

  • Diff audited: all 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the missing widget-removal was the exact root cause of rant 2026-08-21T16:47:44 (/trigger task list stays in chat).
  • Duplicate-activation guard in tasks_list handler correctly drops a stale pending TaskSelector before stacking a new one.
  • ChatHistory.remove no-ops on ValueError, so removing an already-removed widget is safe.
  • Local verification on this branch: pytest 994 passed + 1 skipped (995 collected); GUI 249 pass / 0 fail / 8 skipped; import + CLI OK. Merge-tree vs master: 0 conflict markers.
    3 consecutive ✅ from different cycles — merging.

@argszero
argszero merged commit 95150e7 into masterAug 21, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/tui-trigger-selector-remove branch August 23, 2026 02:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' emrg: remove TUI selector widgets from chat on confirm/cancel by argszero · Pull Request #921 · argszero/emrg · GitHub
Skip to content

emrg: remove TUI selector widgets from chat on confirm/cancel - #921

Merged
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove
Aug 21, 2026
Merged

emrg: remove TUI selector widgets from chat on confirm/cancel#921
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix TUI selector overlays staying in the chat after selection (host rant 2026-08-21T16:47:44/trigger popup: the task list remains visible in the chat after picking a task).

Root cause

All six interactive selectors (session_sel, delete_sel, project_sel, model_sel, rewind_sel, task_sel) shared the same latent bug: on Esc-cancel and Enter-confirm they only set active = False / widget = None but never called chat.remove(widget), so the selector widget was never removed from the chat rows and stayed on screen. Only the autocomplete widget was being removed.

Changes (emrg/client/app.py, +18 lines)

  1. Every selector deactivation path now removes the widget firstchat.remove(<sel>.widget) immediately before widget = None in:
    • session_sel: Esc-cancel, Enter-confirm, and the direct /resume <name> path
    • delete_sel: Esc-cancel and Enter-confirm
    • project_sel: Esc-cancel and Enter-confirm
    • model_sel: Esc-cancel and Enter-confirm
    • rewind_sel: Esc-cancel and Enter-confirm
    • task_sel: Esc-cancel and Enter-confirm (the /trigger rant target)
  2. Duplicate-activation guard in the tasks_list handler — if a TaskSelector is already in the chat (duplicate /trigger while one is pending), drop it before stacking a new one instead of rendering two stacked lists.

ChatHistory.remove(row) removes by widget identity and silently no-ops on ValueError, so this is safe in every path.

Verification

  • uv run pytest tests/ -v → 994 passed + 1 skipped (995 collected; doc-count guard green, Agent.md stays at 995 on master)
  • cd emrg/gui && npm test → 249 passed + 8 skipped (257)
  • uv run python -c "from emrg.client.app import run_client" → OK
  • uv run python -m emrg --help → OK

All six interactive selectors (session/delete/project/model/rewind/task)
kept their widget in the chat after selection: Esc/Enter only set
active=False and widget=None, but never called chat.remove(), so the
overlay list stayed visible (rant 2026-08-21T16:47:44 for /trigger).
Add chat.remove(<sel>.widget) to every deactivation path (Esc-cancel and
Enter-confirm for each selector, plus the direct /resume name path), and
guard the tasks_list handler against stacking a second TaskSelector on
re-trigger while one is already active.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1214. Fixes rant 2026-08-21T16:47:44 (/trigger task list stays in chat): every selector deactivation path now calls chat.remove(.widget) before widget=None (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus /resume name path), and the tasks_list handler drops a stale TaskSelector before stacking a new one. ChatHistory.remove is identity-based and no-ops on ValueError — safe in all paths. Local: pytest 994 passed + 1 skipped (995, doc-count guard green), GUI 249+8, import + CLI OK. CI test + test-windows PASS.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1215. Independent re-review on head 9161006 (MERGEABLE, CI test + test-windows PASS):

  • All 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the overlay is removed from chat rows instead of lingering.
  • ChatHistory.remove is identity-based and swallows ValueError (widgets.py:683), so calling it with a None/stale widget is a safe no-op — the /resume path's unconditional remove is defensive, not fragile.
  • Duplicate-activation guard in the tasks_list handler drops a stale TaskSelector before stacking a new one on repeated /trigger.
  • Local: pytest 994 passed + 1 skipped, GUI 249+8 (257), import + CLI OK.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle R2165. Independent review on head 9161006 (MERGEABLE, mergeStateStatus CLEAN; CI test + test-windows PASS):

  • Diff audited: all 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the missing widget-removal was the exact root cause of rant 2026-08-21T16:47:44 (/trigger task list stays in chat).
  • Duplicate-activation guard in tasks_list handler correctly drops a stale pending TaskSelector before stacking a new one.
  • ChatHistory.remove no-ops on ValueError, so removing an already-removed widget is safe.
  • Local verification on this branch: pytest 994 passed + 1 skipped (995 collected); GUI 249 pass / 0 fail / 8 skipped; import + CLI OK. Merge-tree vs master: 0 conflict markers.
    3 consecutive ✅ from different cycles — merging.

@argszero
argszero merged commit 95150e7 into masterAug 21, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/tui-trigger-selector-remove branch August 23, 2026 02:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' emrg: remove TUI selector widgets from chat on confirm/cancel by argszero · Pull Request #921 · argszero/emrg · GitHub
Skip to content

emrg: remove TUI selector widgets from chat on confirm/cancel - #921

Merged
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove
Aug 21, 2026
Merged

emrg: remove TUI selector widgets from chat on confirm/cancel#921
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix TUI selector overlays staying in the chat after selection (host rant 2026-08-21T16:47:44/trigger popup: the task list remains visible in the chat after picking a task).

Root cause

All six interactive selectors (session_sel, delete_sel, project_sel, model_sel, rewind_sel, task_sel) shared the same latent bug: on Esc-cancel and Enter-confirm they only set active = False / widget = None but never called chat.remove(widget), so the selector widget was never removed from the chat rows and stayed on screen. Only the autocomplete widget was being removed.

Changes (emrg/client/app.py, +18 lines)

  1. Every selector deactivation path now removes the widget firstchat.remove(<sel>.widget) immediately before widget = None in:
    • session_sel: Esc-cancel, Enter-confirm, and the direct /resume <name> path
    • delete_sel: Esc-cancel and Enter-confirm
    • project_sel: Esc-cancel and Enter-confirm
    • model_sel: Esc-cancel and Enter-confirm
    • rewind_sel: Esc-cancel and Enter-confirm
    • task_sel: Esc-cancel and Enter-confirm (the /trigger rant target)
  2. Duplicate-activation guard in the tasks_list handler — if a TaskSelector is already in the chat (duplicate /trigger while one is pending), drop it before stacking a new one instead of rendering two stacked lists.

ChatHistory.remove(row) removes by widget identity and silently no-ops on ValueError, so this is safe in every path.

Verification

  • uv run pytest tests/ -v → 994 passed + 1 skipped (995 collected; doc-count guard green, Agent.md stays at 995 on master)
  • cd emrg/gui && npm test → 249 passed + 8 skipped (257)
  • uv run python -c "from emrg.client.app import run_client" → OK
  • uv run python -m emrg --help → OK

All six interactive selectors (session/delete/project/model/rewind/task)
kept their widget in the chat after selection: Esc/Enter only set
active=False and widget=None, but never called chat.remove(), so the
overlay list stayed visible (rant 2026-08-21T16:47:44 for /trigger).
Add chat.remove(<sel>.widget) to every deactivation path (Esc-cancel and
Enter-confirm for each selector, plus the direct /resume name path), and
guard the tasks_list handler against stacking a second TaskSelector on
re-trigger while one is already active.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1214. Fixes rant 2026-08-21T16:47:44 (/trigger task list stays in chat): every selector deactivation path now calls chat.remove(.widget) before widget=None (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus /resume name path), and the tasks_list handler drops a stale TaskSelector before stacking a new one. ChatHistory.remove is identity-based and no-ops on ValueError — safe in all paths. Local: pytest 994 passed + 1 skipped (995, doc-count guard green), GUI 249+8, import + CLI OK. CI test + test-windows PASS.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1215. Independent re-review on head 9161006 (MERGEABLE, CI test + test-windows PASS):

  • All 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the overlay is removed from chat rows instead of lingering.
  • ChatHistory.remove is identity-based and swallows ValueError (widgets.py:683), so calling it with a None/stale widget is a safe no-op — the /resume path's unconditional remove is defensive, not fragile.
  • Duplicate-activation guard in the tasks_list handler drops a stale TaskSelector before stacking a new one on repeated /trigger.
  • Local: pytest 994 passed + 1 skipped, GUI 249+8 (257), import + CLI OK.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle R2165. Independent review on head 9161006 (MERGEABLE, mergeStateStatus CLEAN; CI test + test-windows PASS):

  • Diff audited: all 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the missing widget-removal was the exact root cause of rant 2026-08-21T16:47:44 (/trigger task list stays in chat).
  • Duplicate-activation guard in tasks_list handler correctly drops a stale pending TaskSelector before stacking a new one.
  • ChatHistory.remove no-ops on ValueError, so removing an already-removed widget is safe.
  • Local verification on this branch: pytest 994 passed + 1 skipped (995 collected); GUI 249 pass / 0 fail / 8 skipped; import + CLI OK. Merge-tree vs master: 0 conflict markers.
    3 consecutive ✅ from different cycles — merging.

@argszero
argszero merged commit 95150e7 into masterAug 21, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/tui-trigger-selector-remove branch August 23, 2026 02:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' emrg: remove TUI selector widgets from chat on confirm/cancel by argszero · Pull Request #921 · argszero/emrg · GitHub
Skip to content

emrg: remove TUI selector widgets from chat on confirm/cancel - #921

Merged
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove
Aug 21, 2026
Merged

emrg: remove TUI selector widgets from chat on confirm/cancel#921
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix TUI selector overlays staying in the chat after selection (host rant 2026-08-21T16:47:44/trigger popup: the task list remains visible in the chat after picking a task).

Root cause

All six interactive selectors (session_sel, delete_sel, project_sel, model_sel, rewind_sel, task_sel) shared the same latent bug: on Esc-cancel and Enter-confirm they only set active = False / widget = None but never called chat.remove(widget), so the selector widget was never removed from the chat rows and stayed on screen. Only the autocomplete widget was being removed.

Changes (emrg/client/app.py, +18 lines)

  1. Every selector deactivation path now removes the widget firstchat.remove(<sel>.widget) immediately before widget = None in:
    • session_sel: Esc-cancel, Enter-confirm, and the direct /resume <name> path
    • delete_sel: Esc-cancel and Enter-confirm
    • project_sel: Esc-cancel and Enter-confirm
    • model_sel: Esc-cancel and Enter-confirm
    • rewind_sel: Esc-cancel and Enter-confirm
    • task_sel: Esc-cancel and Enter-confirm (the /trigger rant target)
  2. Duplicate-activation guard in the tasks_list handler — if a TaskSelector is already in the chat (duplicate /trigger while one is pending), drop it before stacking a new one instead of rendering two stacked lists.

ChatHistory.remove(row) removes by widget identity and silently no-ops on ValueError, so this is safe in every path.

Verification

  • uv run pytest tests/ -v → 994 passed + 1 skipped (995 collected; doc-count guard green, Agent.md stays at 995 on master)
  • cd emrg/gui && npm test → 249 passed + 8 skipped (257)
  • uv run python -c "from emrg.client.app import run_client" → OK
  • uv run python -m emrg --help → OK

All six interactive selectors (session/delete/project/model/rewind/task)
kept their widget in the chat after selection: Esc/Enter only set
active=False and widget=None, but never called chat.remove(), so the
overlay list stayed visible (rant 2026-08-21T16:47:44 for /trigger).
Add chat.remove(<sel>.widget) to every deactivation path (Esc-cancel and
Enter-confirm for each selector, plus the direct /resume name path), and
guard the tasks_list handler against stacking a second TaskSelector on
re-trigger while one is already active.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1214. Fixes rant 2026-08-21T16:47:44 (/trigger task list stays in chat): every selector deactivation path now calls chat.remove(.widget) before widget=None (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus /resume name path), and the tasks_list handler drops a stale TaskSelector before stacking a new one. ChatHistory.remove is identity-based and no-ops on ValueError — safe in all paths. Local: pytest 994 passed + 1 skipped (995, doc-count guard green), GUI 249+8, import + CLI OK. CI test + test-windows PASS.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1215. Independent re-review on head 9161006 (MERGEABLE, CI test + test-windows PASS):

  • All 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the overlay is removed from chat rows instead of lingering.
  • ChatHistory.remove is identity-based and swallows ValueError (widgets.py:683), so calling it with a None/stale widget is a safe no-op — the /resume path's unconditional remove is defensive, not fragile.
  • Duplicate-activation guard in the tasks_list handler drops a stale TaskSelector before stacking a new one on repeated /trigger.
  • Local: pytest 994 passed + 1 skipped, GUI 249+8 (257), import + CLI OK.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle R2165. Independent review on head 9161006 (MERGEABLE, mergeStateStatus CLEAN; CI test + test-windows PASS):

  • Diff audited: all 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the missing widget-removal was the exact root cause of rant 2026-08-21T16:47:44 (/trigger task list stays in chat).
  • Duplicate-activation guard in tasks_list handler correctly drops a stale pending TaskSelector before stacking a new one.
  • ChatHistory.remove no-ops on ValueError, so removing an already-removed widget is safe.
  • Local verification on this branch: pytest 994 passed + 1 skipped (995 collected); GUI 249 pass / 0 fail / 8 skipped; import + CLI OK. Merge-tree vs master: 0 conflict markers.
    3 consecutive ✅ from different cycles — merging.

@argszero
argszero merged commit 95150e7 into masterAug 21, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/tui-trigger-selector-remove branch August 23, 2026 02:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' emrg: remove TUI selector widgets from chat on confirm/cancel by argszero · Pull Request #921 · argszero/emrg · GitHub
Skip to content

emrg: remove TUI selector widgets from chat on confirm/cancel - #921

Merged
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove
Aug 21, 2026
Merged

emrg: remove TUI selector widgets from chat on confirm/cancel#921
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix TUI selector overlays staying in the chat after selection (host rant 2026-08-21T16:47:44/trigger popup: the task list remains visible in the chat after picking a task).

Root cause

All six interactive selectors (session_sel, delete_sel, project_sel, model_sel, rewind_sel, task_sel) shared the same latent bug: on Esc-cancel and Enter-confirm they only set active = False / widget = None but never called chat.remove(widget), so the selector widget was never removed from the chat rows and stayed on screen. Only the autocomplete widget was being removed.

Changes (emrg/client/app.py, +18 lines)

  1. Every selector deactivation path now removes the widget firstchat.remove(<sel>.widget) immediately before widget = None in:
    • session_sel: Esc-cancel, Enter-confirm, and the direct /resume <name> path
    • delete_sel: Esc-cancel and Enter-confirm
    • project_sel: Esc-cancel and Enter-confirm
    • model_sel: Esc-cancel and Enter-confirm
    • rewind_sel: Esc-cancel and Enter-confirm
    • task_sel: Esc-cancel and Enter-confirm (the /trigger rant target)
  2. Duplicate-activation guard in the tasks_list handler — if a TaskSelector is already in the chat (duplicate /trigger while one is pending), drop it before stacking a new one instead of rendering two stacked lists.

ChatHistory.remove(row) removes by widget identity and silently no-ops on ValueError, so this is safe in every path.

Verification

  • uv run pytest tests/ -v → 994 passed + 1 skipped (995 collected; doc-count guard green, Agent.md stays at 995 on master)
  • cd emrg/gui && npm test → 249 passed + 8 skipped (257)
  • uv run python -c "from emrg.client.app import run_client" → OK
  • uv run python -m emrg --help → OK

All six interactive selectors (session/delete/project/model/rewind/task)
kept their widget in the chat after selection: Esc/Enter only set
active=False and widget=None, but never called chat.remove(), so the
overlay list stayed visible (rant 2026-08-21T16:47:44 for /trigger).
Add chat.remove(<sel>.widget) to every deactivation path (Esc-cancel and
Enter-confirm for each selector, plus the direct /resume name path), and
guard the tasks_list handler against stacking a second TaskSelector on
re-trigger while one is already active.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1214. Fixes rant 2026-08-21T16:47:44 (/trigger task list stays in chat): every selector deactivation path now calls chat.remove(.widget) before widget=None (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus /resume name path), and the tasks_list handler drops a stale TaskSelector before stacking a new one. ChatHistory.remove is identity-based and no-ops on ValueError — safe in all paths. Local: pytest 994 passed + 1 skipped (995, doc-count guard green), GUI 249+8, import + CLI OK. CI test + test-windows PASS.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1215. Independent re-review on head 9161006 (MERGEABLE, CI test + test-windows PASS):

  • All 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the overlay is removed from chat rows instead of lingering.
  • ChatHistory.remove is identity-based and swallows ValueError (widgets.py:683), so calling it with a None/stale widget is a safe no-op — the /resume path's unconditional remove is defensive, not fragile.
  • Duplicate-activation guard in the tasks_list handler drops a stale TaskSelector before stacking a new one on repeated /trigger.
  • Local: pytest 994 passed + 1 skipped, GUI 249+8 (257), import + CLI OK.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle R2165. Independent review on head 9161006 (MERGEABLE, mergeStateStatus CLEAN; CI test + test-windows PASS):

  • Diff audited: all 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the missing widget-removal was the exact root cause of rant 2026-08-21T16:47:44 (/trigger task list stays in chat).
  • Duplicate-activation guard in tasks_list handler correctly drops a stale pending TaskSelector before stacking a new one.
  • ChatHistory.remove no-ops on ValueError, so removing an already-removed widget is safe.
  • Local verification on this branch: pytest 994 passed + 1 skipped (995 collected); GUI 249 pass / 0 fail / 8 skipped; import + CLI OK. Merge-tree vs master: 0 conflict markers.
    3 consecutive ✅ from different cycles — merging.

@argszero
argszero merged commit 95150e7 into masterAug 21, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/tui-trigger-selector-remove branch August 23, 2026 02:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' emrg: remove TUI selector widgets from chat on confirm/cancel by argszero · Pull Request #921 · argszero/emrg · GitHub
Skip to content

emrg: remove TUI selector widgets from chat on confirm/cancel - #921

Merged
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove
Aug 21, 2026
Merged

emrg: remove TUI selector widgets from chat on confirm/cancel#921
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix TUI selector overlays staying in the chat after selection (host rant 2026-08-21T16:47:44/trigger popup: the task list remains visible in the chat after picking a task).

Root cause

All six interactive selectors (session_sel, delete_sel, project_sel, model_sel, rewind_sel, task_sel) shared the same latent bug: on Esc-cancel and Enter-confirm they only set active = False / widget = None but never called chat.remove(widget), so the selector widget was never removed from the chat rows and stayed on screen. Only the autocomplete widget was being removed.

Changes (emrg/client/app.py, +18 lines)

  1. Every selector deactivation path now removes the widget firstchat.remove(<sel>.widget) immediately before widget = None in:
    • session_sel: Esc-cancel, Enter-confirm, and the direct /resume <name> path
    • delete_sel: Esc-cancel and Enter-confirm
    • project_sel: Esc-cancel and Enter-confirm
    • model_sel: Esc-cancel and Enter-confirm
    • rewind_sel: Esc-cancel and Enter-confirm
    • task_sel: Esc-cancel and Enter-confirm (the /trigger rant target)
  2. Duplicate-activation guard in the tasks_list handler — if a TaskSelector is already in the chat (duplicate /trigger while one is pending), drop it before stacking a new one instead of rendering two stacked lists.

ChatHistory.remove(row) removes by widget identity and silently no-ops on ValueError, so this is safe in every path.

Verification

  • uv run pytest tests/ -v → 994 passed + 1 skipped (995 collected; doc-count guard green, Agent.md stays at 995 on master)
  • cd emrg/gui && npm test → 249 passed + 8 skipped (257)
  • uv run python -c "from emrg.client.app import run_client" → OK
  • uv run python -m emrg --help → OK

All six interactive selectors (session/delete/project/model/rewind/task)
kept their widget in the chat after selection: Esc/Enter only set
active=False and widget=None, but never called chat.remove(), so the
overlay list stayed visible (rant 2026-08-21T16:47:44 for /trigger).
Add chat.remove(<sel>.widget) to every deactivation path (Esc-cancel and
Enter-confirm for each selector, plus the direct /resume name path), and
guard the tasks_list handler against stacking a second TaskSelector on
re-trigger while one is already active.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1214. Fixes rant 2026-08-21T16:47:44 (/trigger task list stays in chat): every selector deactivation path now calls chat.remove(.widget) before widget=None (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus /resume name path), and the tasks_list handler drops a stale TaskSelector before stacking a new one. ChatHistory.remove is identity-based and no-ops on ValueError — safe in all paths. Local: pytest 994 passed + 1 skipped (995, doc-count guard green), GUI 249+8, import + CLI OK. CI test + test-windows PASS.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1215. Independent re-review on head 9161006 (MERGEABLE, CI test + test-windows PASS):

  • All 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the overlay is removed from chat rows instead of lingering.
  • ChatHistory.remove is identity-based and swallows ValueError (widgets.py:683), so calling it with a None/stale widget is a safe no-op — the /resume path's unconditional remove is defensive, not fragile.
  • Duplicate-activation guard in the tasks_list handler drops a stale TaskSelector before stacking a new one on repeated /trigger.
  • Local: pytest 994 passed + 1 skipped, GUI 249+8 (257), import + CLI OK.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle R2165. Independent review on head 9161006 (MERGEABLE, mergeStateStatus CLEAN; CI test + test-windows PASS):

  • Diff audited: all 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the missing widget-removal was the exact root cause of rant 2026-08-21T16:47:44 (/trigger task list stays in chat).
  • Duplicate-activation guard in tasks_list handler correctly drops a stale pending TaskSelector before stacking a new one.
  • ChatHistory.remove no-ops on ValueError, so removing an already-removed widget is safe.
  • Local verification on this branch: pytest 994 passed + 1 skipped (995 collected); GUI 249 pass / 0 fail / 8 skipped; import + CLI OK. Merge-tree vs master: 0 conflict markers.
    3 consecutive ✅ from different cycles — merging.

@argszero
argszero merged commit 95150e7 into masterAug 21, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/tui-trigger-selector-remove branch August 23, 2026 02:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); emrg: remove TUI selector widgets from chat on confirm/cancel by argszero · Pull Request #921 · argszero/emrg · GitHub
Skip to content

emrg: remove TUI selector widgets from chat on confirm/cancel - #921

Merged
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove
Aug 21, 2026
Merged

emrg: remove TUI selector widgets from chat on confirm/cancel#921
argszero merged 1 commit into
masterfrom
feature/tui-trigger-selector-remove

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix TUI selector overlays staying in the chat after selection (host rant 2026-08-21T16:47:44/trigger popup: the task list remains visible in the chat after picking a task).

Root cause

All six interactive selectors (session_sel, delete_sel, project_sel, model_sel, rewind_sel, task_sel) shared the same latent bug: on Esc-cancel and Enter-confirm they only set active = False / widget = None but never called chat.remove(widget), so the selector widget was never removed from the chat rows and stayed on screen. Only the autocomplete widget was being removed.

Changes (emrg/client/app.py, +18 lines)

  1. Every selector deactivation path now removes the widget firstchat.remove(<sel>.widget) immediately before widget = None in:
    • session_sel: Esc-cancel, Enter-confirm, and the direct /resume <name> path
    • delete_sel: Esc-cancel and Enter-confirm
    • project_sel: Esc-cancel and Enter-confirm
    • model_sel: Esc-cancel and Enter-confirm
    • rewind_sel: Esc-cancel and Enter-confirm
    • task_sel: Esc-cancel and Enter-confirm (the /trigger rant target)
  2. Duplicate-activation guard in the tasks_list handler — if a TaskSelector is already in the chat (duplicate /trigger while one is pending), drop it before stacking a new one instead of rendering two stacked lists.

ChatHistory.remove(row) removes by widget identity and silently no-ops on ValueError, so this is safe in every path.

Verification

  • uv run pytest tests/ -v → 994 passed + 1 skipped (995 collected; doc-count guard green, Agent.md stays at 995 on master)
  • cd emrg/gui && npm test → 249 passed + 8 skipped (257)
  • uv run python -c "from emrg.client.app import run_client" → OK
  • uv run python -m emrg --help → OK

All six interactive selectors (session/delete/project/model/rewind/task)
kept their widget in the chat after selection: Esc/Enter only set
active=False and widget=None, but never called chat.remove(), so the
overlay list stayed visible (rant 2026-08-21T16:47:44 for /trigger).
Add chat.remove(<sel>.widget) to every deactivation path (Esc-cancel and
Enter-confirm for each selector, plus the direct /resume name path), and
guard the tasks_list handler against stacking a second TaskSelector on
re-trigger while one is already active.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1214. Fixes rant 2026-08-21T16:47:44 (/trigger task list stays in chat): every selector deactivation path now calls chat.remove(.widget) before widget=None (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus /resume name path), and the tasks_list handler drops a stale TaskSelector before stacking a new one. ChatHistory.remove is identity-based and no-ops on ValueError — safe in all paths. Local: pytest 994 passed + 1 skipped (995, doc-count guard green), GUI 249+8, import + CLI OK. CI test + test-windows PASS.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 1215. Independent re-review on head 9161006 (MERGEABLE, CI test + test-windows PASS):

  • All 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the overlay is removed from chat rows instead of lingering.
  • ChatHistory.remove is identity-based and swallows ValueError (widgets.py:683), so calling it with a None/stale widget is a safe no-op — the /resume path's unconditional remove is defensive, not fragile.
  • Duplicate-activation guard in the tasks_list handler drops a stale TaskSelector before stacking a new one on repeated /trigger.
  • Local: pytest 994 passed + 1 skipped, GUI 249+8 (257), import + CLI OK.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle R2165. Independent review on head 9161006 (MERGEABLE, mergeStateStatus CLEAN; CI test + test-windows PASS):

  • Diff audited: all 13 selector deactivation paths (session/delete/project/model/rewind/task × Esc-cancel + Enter-confirm, plus the /resume direct path) now call chat.remove(.widget) before widget=None — the missing widget-removal was the exact root cause of rant 2026-08-21T16:47:44 (/trigger task list stays in chat).
  • Duplicate-activation guard in tasks_list handler correctly drops a stale pending TaskSelector before stacking a new one.
  • ChatHistory.remove no-ops on ValueError, so removing an already-removed widget is safe.
  • Local verification on this branch: pytest 994 passed + 1 skipped (995 collected); GUI 249 pass / 0 fail / 8 skipped; import + CLI OK. Merge-tree vs master: 0 conflict markers.
    3 consecutive ✅ from different cycles — merging.

@argszero
argszero merged commit 95150e7 into masterAug 21, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/tui-trigger-selector-remove branch August 23, 2026 02:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero