Skip to content

emrg: fix GUI message display bugs — empty-state overlay, typing cursor residue, false from-other-client label - #543

Merged
argszero merged 1 commit into
masterfrom
feature/gui-msg-display-fixes
Aug 7, 2026
Merged

emrg: fix GUI message display bugs — empty-state overlay, typing cursor residue, false from-other-client label#543
argszero merged 1 commit into
masterfrom
feature/gui-msg-display-fixes

Conversation

@argszero

@argszero argszero commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Fixes three GUI message display bugs (host-verified screenshots, rant 14:11) that appear after sending the first message in a session:

Bug A — welcome screen never hides

updateEmptyState() was only evaluated on session switch / new session; the streaming path (Chat.addUserMessage / delta / done) appended nodes without re-evaluating it, so #empty-state stayed overlaid on the message area.
Fix: Chat.append() and Chat.clear() now call App.updateEmptyState?.() — welcome visibility always follows chat content.

Bug B — typing cursor residue + false "from other client" label (same race)

G122 batches message_delta on a 16ms timer, but done bypasses the buffer. When the final deltas were still buffered, done reached the renderer first: groupNodes entry deleted, ownStreamRequestId cleared — then the 16ms flush delivered a stale delta that created an orphan node with isOwn=false (false label) that never receives done (cursor forever).
Fix:

  1. main.js — flushDeltaBuf() runs synchronously before done / error / cancelled events (webContents.send preserves order → deltas can never arrive after the terminal event)
  2. chat.js — doneRids set drops any residual stale delta (defense in depth, UUID requestIds never reused, capped at 500)
  3. app.js — cancelled / stream-error clear the typing class on in-flight nodes (mid-round cancel emits no done frame, only the session-level cancelled event)

Verification

  • +3 renderer smoke regression tests (welcome hides on first message / stale delta dropped / cancelled clears typing)
  • npm test 91/91 green (was 88 + 3 new); Python 508 passed (incl. doc-count guard); docs synced

…or residue, false from-other-client label

Rant 14:11 (host-verified screenshots) reported three GUI display bugs after
the first message in a session:

1. Welcome screen (#empty-state) stayed visible on top of the message area
   — updateEmptyState() was only evaluated on session switch/new, never after
   append/clear. Fix: Chat.append()/clear() call App.updateEmptyState?.() so
   welcome visibility always follows chat content.

2. Typing cursor (▍) never disappeared + own replies mislabeled
   "from other client" — G122 16ms delta batching: done/error/cancelled
   bypass the buffer, so buffered deltas flushed after done reached the
   renderer, which then created an orphan node with ownStreamRequestId
   already null (false label) and no done ever arriving (cursor forever).
   Fix (main.js): flush deltaBuf synchronously before terminal events
   (webContents.send preserves order). Defense (chat.js): doneRids set drops
   stale deltas; cancelled/error clear typing on in-flight nodes.

+3 renderer smoke regression tests; docs synced (npm test 88 -> 91).

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

✅ LGTM — cycle (independent verification on ba8b59a)

Checked out the branch and verified each fix against the rant's root-cause analysis:

  1. main.js flush ordering: flushDeltaBuf() runs synchronously before done/error/cancelled are forwarded; the pending 16ms timer is cleared first, and webContents.send preserves order on the channel — buffered deltas can no longer arrive after the terminal event. The extracted named function is also reused by the timer path (no behavior change there).
  2. chat.js doneRids defense: stale deltas for finished streams are dropped before node creation (no orphan nodes, no false from-other-client label). UUID requestIds are never reused, and the 500-entry cap bounds long-running growth. clear() resets the set on session switch.
  3. chat.js welcome screen: append()/clear() now call App.updateEmptyState?.(), so welcome visibility follows chat content on every add/remove — not just session switch.
  4. app.js cancelled/error: clearTyping() on in-flight nodes is correct — the daemon's session-level cancelled event carries no request_id, and mid-round cancellation emits no done frame, so clearing all in-flight typing cursors is the only sound option.

Verification: npm test 91/91 on the branch (incl. the 3 new regression tests), Python 508 passed (doc-count guard satisfied, README/Agent.md/README.cn.md synced 88→91), CI run 31153709355 green.

Minor non-blocking observation: the disconnected path doesn't flush deltaBuf — a daemon death mid-stream could still deliver one buffered batch post-reconnect. Pre-existing edge, partially mitigated by the doneRids/groupNodes guards; can be addressed separately if it ever surfaces.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

✅ LGTM — cycle (independent verification on ba8b59a)

Checked out the branch and verified each fix against the rant root-cause analysis:

  1. main.js flush ordering: flushDeltaBuf() runs synchronously before done/error/cancelled are forwarded; the pending 16ms timer is cleared first, and webContents.send preserves order on the channel — buffered deltas can no longer arrive after the terminal event. The extracted named function is reused by the timer path with no behavior change.
  2. chat.js doneRids defense: stale deltas for finished streams are dropped before node creation (no orphan nodes, no false from-other-client label). UUID requestIds are never reused; the 500-entry cap bounds long-running growth; clear() resets the set on session switch.
  3. chat.js welcome screen: append()/clear() now call App.updateEmptyState?.(), so welcome visibility follows chat content on every add/remove — not just session switch.
  4. app.js cancelled/error: clearTyping() on in-flight nodes is correct — the daemon session-level cancelled event carries no request_id, and mid-round cancellation emits no done frame, so clearing all in-flight typing cursors is the only sound option.

Verification: npm test 91/91 on the branch (incl. the 3 new regression tests), Python 508 passed (doc-count guard satisfied, README/Agent.md/README.cn.md synced 88→91), CI run 31153709355 green.

Minor non-blocking observation: the disconnected path does not flush deltaBuf — a daemon death mid-stream could still deliver one buffered batch post-reconnect. Pre-existing edge, partially mitigated by the doneRids/groupNodes guards; can be addressed separately if it ever surfaces.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

✅ LGTM — cycle 2 (independent re-verification, different angle: test quality + edge cases)

Branch unchanged since last review (ba8b59a, +96/-13). This pass scrutinized the three new regression tests rather than the fix code:

  1. Welcome-screen test asserts hidden=true after addUserMessage — valid: only the new append() hook can produce this state. (Mock limitation noted: the sandbox classList.toggle ignores the force argument, so an inverted force usage would not be caught here — consistent with the existing suite's mock fidelity, non-blocking.)
  2. Stale-delta test calls Chat.handleDone directly then replays a delta — correctly isolates the renderer-side doneRids guard independent of the main.js flush; without doneRids the replayed delta would create an orphan node and fail the count assertion.
  3. Cancelled test drives the real App.handleEvent path (not Chat.clearTyping directly) — verifies the full event wiring, and the querySelector-null mock fallback keeps the assertion meaningful.

Re-ran on the branch: npm test 91/91 (all 3 rant-14:11 tests green), Python 508 passed. No new commits since the previous cycle's review; CI 31153709355 remains green.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

✅ LGTM — cycle 3 (independent re-verification, third angle: regression-surface audit)

Branch unchanged (ba8b59a). This pass audited every call site of the modified functions for regressions:

  1. append() has 4 callers (user message / assistant node / system message / tool row) — all are chat content, so hiding the welcome screen on any of them is correct.
  2. Chat.clear() has 4 callers (boot / rewind / switchSession / newSession); switchSession and newSession already called updateEmptyState() explicitly — the new hook makes those double calls, which is idempotent and harmless.
  3. clearTyping() is wired exactly at cancelled and stream-error; the session-busy error branch correctly does NOT call it (no in-flight nodes exist on that path).
  4. doneRids lifecycle is sound: add on done, check on delta, clear on session switch, 500-entry cap for long-running sessions.

Final verification on the branch: npm test 91/91, Python 508 passed, import OK, CI 31153709355 green. Three independent cycle reviews complete (fix correctness / test discriminating power / regression surface) — merge gate satisfied.

@argszero
argszero merged commit a06af27 into master Aug 7, 2026
1 check passed
@argszero
argszero deleted the feature/gui-msg-display-fixes branch August 7, 2026 06:43
argszero added a commit that referenced this pull request Aug 7, 2026
… entry (#544)

Co-authored-by: EMRG Evolution <emrg@argszero.dev>
argszero added a commit that referenced this pull request Aug 7, 2026
…p tolerance (#552)

Version bump 0.2.10 → 0.2.11 across all 6 version sources
(pyproject.toml / emrg/__init__.py / gui/package.json / uv.lock /
make-installer.sh / build-runtime.sh). Release for Windows verification:

- #541 LLM gzip body tolerance
- #543 GUI message display fixes (#544 quick-ref)
- #545 Windows GCM silent-fail Stage 1
- #546 Windows TUI CJK input + legacy arrow keys + /rant visibility
- #548/#549/#550 GitHub auth in GUI (PAT + device flow + banner, Stage 2)
- #551 quick-ref

All 548 tests green.

Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for free to 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