Skip to content

fix(ui): restore rail-era app titlebar state (v0.50.226)#1163

Merged
nesquena-hermes merged 3 commits intomasterfrom
integrate/pr1141
Apr 27, 2026
Merged

fix(ui): restore rail-era app titlebar state (v0.50.226)#1163
nesquena-hermes merged 3 commits intomasterfrom
integrate/pr1141

Conversation

@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Attribution: Original work by @aronprins (PR #1141). Integrated and shipped by the Hermes agent team.
Original PR: #1141

What this PR does

Restores the app titlebar to its rail-era state:

  • Removes the TPS chip from the top bar (static/index.html) — it was global shell chrome that accumulated transient runtime noise
  • Centers the titlebarjustify-content: center on both .app-titlebar and .app-titlebar-inner (desktop), with the correct justify-content: space-between override on mobile where the hamburger needs room
  • Restores message count in the subtitle slotsyncAppTitlebar() in panels.js now computes vis.length and passes it to t('n_messages', …), bringing back the behavior from the original rail branch
  • Removes _syncQueueTitlebar — the function that was hijacking the subtitle slot to show queue state has been deleted from ui.js. Queue affordances belong somewhere less dominant. The queue pill and flyout card are untouched.

Review notes

The metering source.addEventListener('metering', ...) handler in messages.js already has an early if (!el) return guard, so removing the DOM element is safe — no JS errors occur when the element is absent.

Tests: 4 new tests in tests/test_app_titlebar_restore.py. Full suite: 2595 passing.

Browser QA: Tested at 1440×900 desktop and iPhone 14 mobile. Both modes pass all checks.

Changelog

See CHANGELOG.md[v0.50.226]

This PR removes the titlebar queue badge (_syncQueueTitlebar), but the
hide button on the queue card still tells the user to "click the
titlebar badge to show again" — which no longer exists in the titlebar
slot (subtitle slot now shows message count).

Update the tooltip and adjacent comment to reference the queue pill,
which is the actual affordance that re-shows a hidden queue card via
_updateQueuePill().

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Copy link
Copy Markdown
Owner

@nesquena nesquena left a comment

Choose a reason for hiding this comment

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

Review — end-to-end ✅ (approved after small tooltip fix pushed)

What this ships

v0.50.226 — restores the rail-era app titlebar by absorbing @aronprins' PR #1141:

  • Removes the tps-chip element (id="tpsStat") from the global app titlebar (static/index.html)
  • Centers the titlebar (desktop): .app-titlebar and .app-titlebar-inner use justify-content:center; mobile media query overrides back to space-between so the hamburger keeps its slot (static/style.css:210-211, static/style.css:844-846)
  • Restores n_messages count in the subtitle slot via syncAppTitlebar (static/panels.js:36-37)
  • Deletes the _syncQueueTitlebar helper that was hijacking the subtitle slot for queue state, plus its single call site in updateQueueBadge and syncTopbar (static/ui.js)

Traced against upstream hermes-agent

Pulled fresh nousresearch/hermes-agent tarball. grep -rn "tpsStat\|app-titlebar\|tps-chip" returns nothing — pure WebUI-frontend chrome change with zero agent coupling.

End-to-end trace

Removing #tpsStat is safe

The streaming metering SSE listener at static/messages.js:827-839 is the only consumer of the chip. It has a defensive guard:

source.addEventListener('metering',e=>{
  try{
    const d=JSON.parse(e.data||'{}');
    const el=$('tpsStat');
    if(!el) return;          // ← guard at line 833
    ...
    el.textContent=`${tps} t/s · ${high}${low?' · '+low:''}`;
  }catch(_){}
});

With the chip gone, $('tpsStat') returns null, the guard returns immediately, and the wrapping try/catch swallows nothing because no error is thrown. ✅ The PR description's claim is accurate.

_syncQueueTitlebar removal — orphan check

grep -rn "tpsStat\|tps-chip\|_syncQueueTitlebar" static/ api/
# static/messages.js:832    ← guarded, fine
# (no other refs)

Both updateQueueBadge (the line that was calling it) and syncTopbar had their _syncQueueTitlebar invocations removed in the diff. The queue is still surfaced through the existing queuePill element via _updateQueuePill at static/ui.js:1317-1347 — count>0 + flyout-not-visible → pill shows; click pill → re-opens card. The user-facing affordance for queue state is unchanged; only the titlebar duplicate is gone.

syncAppTitlebar subtitle update path

syncAppTitlebar is called from many places that matter:

  • syncTopbar() calls it (chat panel updates, message stream completion, send/queue events)
  • i18n.js:4199 after locale switch
  • panels.js:81-100, 177, 737, 1719, 1755, 1783, 2076, 2082, 2770 (panel changes, rename, switch session)
  • commands.js:336 after slash-command runs

That covers every path where S.messages would change in chat panel context. The subtitle stays current.

Mobile layout

static/style.css:843-846 override:

.app-titlebar{justify-content:space-between;}
.app-titlebar-hamburger,.app-titlebar-spacer{display:flex;}
.app-titlebar-inner{flex:1 1 auto;}

Inside the @media (max-width:768px) block — mobile gets space-between (so the hamburger pushes to one side and content fills) while desktop centers. Verified the override comes after the desktop rule so cascade is correct.

What I caught — stale tooltip on queue hide button

static/ui.js:1206 had:

hideBtn.title='Hide queue (click the titlebar badge to show again)';

But this PR removes the titlebar queue badge entirely — there is no longer anything in the titlebar to "click to show again". The actual affordance is the queue pill (rendered by _updateQueuePill after the card is hidden). User following the tooltip would look in the wrong place.

What I pushed — 1d11646

hideBtn.title='Hide queue (click the queue pill to show again)';

Plus updated the adjacent code comment. Two-line cosmetic fix; no behaviour change.

Other audit — confirmed correct

  • JS syntax: node --check passes on panels.js, ui.js, messages.js.
  • i18n coverage: n_messages key exists in all 7 locales (en, ru, es, de, zh, zh-Hant, ko).
  • PR's tests: 4/4 pass — verifies (a) id="tpsStat" gone from index.html, (b) centered desktop CSS present, (c) subtitle line in panels.js, (d) _syncQueueTitlebar symbol gone from ui.js.
  • Race / state: no shared state mutated; pure DOM chrome.
  • CI on PR: ✅ test (3.11), ✅ test (3.12), ✅ test (3.13).

Edge-case trace

Scenario Expected Actual
Chat panel + active session, 5 messages subtitle "5 messages" ✅ syncAppTitlebar sets t('n_messages',5)
Empty fresh session subtitle "0 messages" ✅ (consistent with rail-era behaviour)
Non-chat panel (settings/files) subtitle hidden ✅ subText stays ''subEl.hidden=true
Queue grows to N>0 pill shows "N queued" _updateQueuePill unchanged path
Hide queue card via X button pill re-appears, tooltip says "click queue pill" ✅ after my fix
TPS metering events arrive after chip removal guard if(!el) return ✅ messages.js:833
Mobile (≤768px) titlebar uses space-between, hamburger fits @media override at 843-846
Locale switch i18n.js calls syncAppTitlebar ✅ line 4199
Inline rename of session title syncAppTitlebar guarded by _renamingAppTitlebar ✅ panels.js:46 (unchanged)

Tests

  • PR's tests (test_app_titlebar_restore.py): 4/4 pass.
  • Local full suite: 2547 passed, 47 skipped, 1 PR-unrelated failure (test_sprint3.py::test_workspace_add_rejects_system_paths — verified pre-existing on master with the same file; macOS-specific quirk unrelated to titlebar).
  • CI on PR: green on 3.11/3.12/3.13.

Minor observations (non-blocking)

  • The "0 messages" subtitle on a fresh session is technically rendered (since the conditional is if (typeof t === 'function') not if (vis.length > 0)). This matches the rail-era behaviour the PR explicitly restores, so it's intended — just noting for future polish.
  • The tps-chip CSS rule deletion also removes its styling block; if anyone re-introduces a metering chip elsewhere they'll need to redefine the styles.

Recommendation

Approved. Clean restoration of the rail-era titlebar — TPS chip removal is safe (guarded listener), _syncQueueTitlebar removal is complete (no orphan refs), subtitle correctly switches to message count via syncAppTitlebar, mobile override preserves hamburger layout. One stale tooltip on the queue hide button caught and fixed in 1d11646. CI green; PR's 4 tests + full suite (2547 passed, only an unrelated pre-existing failure) confirm no regression. Parked at approval — ready for the release agent's merge/tag pipeline.

@nesquena-hermes nesquena-hermes merged commit dca8624 into master Apr 27, 2026
3 checks passed
@nesquena-hermes
Copy link
Copy Markdown
Collaborator Author

Merged as v0.50.226.

Integration branch absorbed @aronprins's original PR #1141 with one reviewer fix from @nesquena (1d11646: queue hide tooltip updated to reference the queue pill, not the removed titlebar badge).

Full gate results:

  • 2595 tests passing ✅
  • Browser QA 21/21 (desktop 1440×900 + mobile iPhone 14) ✅
  • Independent review: APPROVED by @nesquena

Thank you @aronprins for the clean PR — the titlebar is properly restored.

@nesquena-hermes nesquena-hermes deleted the integrate/pr1141 branch April 27, 2026 18:44
JKJameson pushed a commit to JKJameson/hermes-webui that referenced this pull request Apr 29, 2026
Merged as v0.50.226.

Integration branch absorbed @aronprins's original PR nesquena#1141 with one reviewer fix from @nesquena (`1d11646`: queue hide tooltip updated to reference the queue pill, not the removed titlebar badge).

**Full gate results:**
- 2595 tests passing ✅
- Browser QA 21/21 (desktop 1440×900 + mobile iPhone 14) ✅
- Independent review: APPROVED by @nesquena ✅

Thank you @aronprins for the clean PR — the titlebar is properly restored.
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.

3 participants