{"repo":"NousResearch/hermes-agent","public_id":"19084","display_label":"Dashboard embedded chat wheel scrolling and scrollback handling","summary":"Most members concern mouse wheel scrolling and transcript scrollback in the dashboard's embedded chat (/chat), including one issue report and several pull requests with similar titles. Some also mention related areas such as chat resume stability and terminal clipboard handling, but the shared theme is scroll behavior.","subsystem":"dashboard","counts":{"members":5,"open_issues":0,"open_issues_distinct":0,"open_prs":1,"closed_issues":1,"merged_prs":1,"attempt_prs":3},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":28584,"merged_at":"2026-05-19T07:07:33+00:00"},"resolution":{"state":"resolved","by":"pr-28584"},"freshness":{"state":"verified","last_verified_at":"2026-05-19T07:07:33+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.4},"fix_edges":[{"issue_number":19084,"pr_number":28584,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-05-19T07:07:33+00:00"}],"members":[{"kind":"issue","number":19084,"title":"dashboard /chat: wheel scrolling does not reliably scroll transcript history in embedded chat","url":null,"state":"closed"},{"kind":"pr","number":19088,"title":"fix(dashboard): stabilize embedded chat resume and scrollback","url":null,"state":"closed","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":28029,"title":"fix(dashboard): use browser scrollback for chat wheel","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"afffb8d9a56b850f98ba5d696de030c5c46b8030","number":0},{"kind":"pr","value":"#28584","number":28584}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":28584,"title":"fix(dashboard): use browser scrollback for chat wheel (#28029)","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/web_server.py","tests/hermes_cli/test_web_server.py","web/src/pages/ChatPage.tsx"],"fix_hunks":[{"file":"hermes_cli/web_server.py","hunks":["@@ -3350,6 +3350,7 @@ def _resolve_chat_argv(\n     # build unchanged for native CLI usage; only disable mouse tracking for\n     # the dashboard PTY path.\n     env.setdefault(\"HERMES_TUI_DISABLE_MOUSE\", \"1\")\n+    env.setdefault(\"HERMES_TUI_INLINE\", \"1\")\n \n     if resume:\n         latest_resume, _latest_path = _session_latest_descendant(resume)"]},{"file":"tests/hermes_cli/test_web_server.py","hunks":["@@ -2092,6 +2092,21 @@ def _url(self, token: str | None = None, **params: str) -> str:\n         q = {\"token\": tok, **params}\n         return f\"/api/pty?{urlencode(q)}\"\n \n+    def test_resolve_chat_argv_uses_dashboard_scroll_env(self, monkeypatch):\n+        \"\"\"Dashboard chat runs the TUI in browser-scrollback mode.\"\"\"\n+        import hermes_cli.main as main_mod\n+\n+        monkeypatch.setattr(\n+            main_mod,\n+            \"_make_tui_argv\",\n+            lambda project_root, tui_dev=False: ([\"node\", \"dist/entry.js\"], \"/tmp/ui-tui\"),\n+        )\n+\n+        _argv, _cwd, env = self.ws_module._resolve_chat_argv()\n+\n+        assert env[\"HERMES_TUI_INLINE\"] == \"1\"\n+        assert env[\"HERMES_TUI_DISABLE_MOUSE\"] == \"1\"\n+\n     def test_rejects_when_embedded_chat_disabled(self, monkeypatch):\n         monkeypatch.setattr(self.ws_module, \"_DASHBOARD_EMBEDDED_CHAT_ENABLED\", False)\n         from starlette.websockets import WebSocketDisconnect"]},{"file":"web/src/pages/ChatPage.tsx","hunks":["@@ -298,10 +298,9 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) {\n       // is false; enabling it gives users a single-action selection\n       // path on top of the modifier-based bypass above.\n       rightClickSelectsWord: true,\n-      // Single-scroll-system experiment:\n-      // let the inner Hermes TUI own transcript history/scroll behavior.\n-      // The outer browser xterm should act as a display/input bridge only.\n-      scrollback: 0,\n+      // Browser-embedded chat runs the TUI in inline mode. Keep transcript\n+      // history in xterm.js so the browser wheel can scroll it directly.\n+      scrollback: 5000,\n       theme: TERMINAL_THEME,\n     });\n     termRef.current = term;","@@ -345,7 +344,7 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) {\n           // original keydown event's activation. Log to aid debugging.\n           console.warn(\"[dashboard clipboard] OSC 52 write failed:\", err.message);\n         });\n-      } catch (e) {\n+      } catch {\n         console.warn(\"[dashboard clipboard] malformed OSC 52 payload\");\n       }\n       return true;","@@ -404,34 +403,16 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) {\n     fitRef.current = fit;\n     term.loadAddon(fit);\n \n-    // Single-scroll-system experiment:\n-    // keep browser xterm as a display/input bridge only, and let the inner\n-    // Hermes TUI own transcript scrolling.\n-    //\n-    // In practice, the most reliable path here is NOT terminal mouse-wheel\n-    // protocol emulation — that can vary by terminal mode and parser path.\n-    // The inner TUI already handles keyboard-driven transcript scrolling\n-    // correctly (`Shift+Up` / `Shift+Down`, `PageUp` / `PageDown`), so we\n-    // translate browser wheel gestures into those known-good key sequences.\n+    // Dashboard chat should scroll the browser-side transcript, not send\n+    // mouse-wheel protocol bytes through the PTY.\n     term.attachCustomWheelEventHandler((ev) => {\n-      if (wsRef.current?.readyState !== WebSocket.OPEN) {\n-        return false;\n-      }\n-\n       const delta = ev.deltaY;\n       if (!delta) {\n         return false;\n       }\n \n-      // Shift+Up / Shift+Down: the TUI maps these to line-by-line\n-      // transcript scrolling, which feels much closer to wheel behavior\n-      // than PageUp/PageDown's half-page jumps.\n       const step = Math.max(1, Math.round(Math.abs(delta) / 50));\n-      const seq = delta > 0 ? \"\\x1b[1;2B\" : \"\\x1b[1;2A\";\n-\n-      for (let i = 0; i < step; i++) {\n-        wsRef.current.send(seq);\n-      }\n+      term.scrollLines(delta > 0 ? step : -step);\n \n       ev.preventDefault();\n       ev.stopPropagation();\n"]}]},{"kind":"pr","number":29975,"title":"fix: improve dashboard terminal clipboard and wheel handling","url":null,"state":"open","close_reason":{"refs":[{"kind":"pr","value":"#24860","number":24860},{"kind":"pr","value":"#30007","number":30007},{"kind":"pr","value":"#30008","number":30008},{"kind":"pr","value":"#19084","number":19084},{"kind":"pr","value":"#29989","number":29989}],"category":"uncategorized","implication":"needs_llm"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}