Skip to content

feat(desktop): move v2 terminal ws reconnect notices to a pane-side log#3888

Merged
Kitenite merged 1 commit into
mainfrom
ws-reconnect-logs-pane
Apr 30, 2026
Merged

feat(desktop): move v2 terminal ws reconnect notices to a pane-side log#3888
Kitenite merged 1 commit into
mainfrom
ws-reconnect-logs-pane

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Apr 30, 2026

Reconnect/close/error chatter no longer streams into xterm scrollback. The transport now buffers up to 200 log entries and exposes them via the runtime registry; a pane-header button reveals a popover with the log and a clear action, and only appears when there's something to show. Removes the move-to-background button from the v2 terminal pane header.

Description

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Additional Notes


Summary by cubic

Moved WebSocket reconnect/close/error notices out of terminal scrollback into a pane-side connection log, keeping the terminal clean and making connection issues easier to spot. Added a header button that shows a popover log with a clear action and only appears when there are events.

  • New Features

    • Transport buffers up to 200 connection log entries (with level and timestamp) and exposes them via the runtime registry.
    • New TerminalLogsButton in the pane header opens a popover with the log and a Clear action; the button only renders when logs exist.
  • Refactors

    • Removed reconnect/close/error messages from xterm output.
    • Removed the “Move to background” button from the v2 terminal pane header.

Written for commit 96102a6. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

Release Notes

  • New Features
    • Added terminal logs viewer accessible from the terminal header
    • Monitor terminal connection events with timestamps and severity levels
    • Error indicators highlight when critical issues occur
    • Clear terminal logs through the dedicated interface button

Reconnect/close/error chatter no longer streams into xterm scrollback.
The transport now buffers up to 200 log entries and exposes them via
the runtime registry; a pane-header button reveals a popover with the
log and a clear action, and only appears when there's something to
show. Removes the move-to-background button from the v2 terminal pane
header.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

A terminal logging system has been added, replacing direct error writes to the terminal buffer with a buffered log mechanism. The transport layer now stores timestamped log entries with severity levels, exposes listener notifications, and the runtime registry provides public APIs to access and manage logs. A new UI component displays these logs via a button in the terminal header.

Changes

Cohort / File(s) Summary
Terminal Log Core Infrastructure
apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts, apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts
Introduces transport-scoped logging with timestamped, level-tagged entries (max 200 buffered), listener notification, and capped history. Registry exposes getLogs, clearLogs, and onLogsChange to retrieve, manage, and subscribe to log changes. Exports TerminalLogEntry and TerminalLogLevel types.
Terminal UI Components
apps/desktop/src/renderer/routes/...TerminalPane/components/TerminalHeaderExtras/TerminalHeaderExtras.tsx, apps/desktop/src/renderer/routes/...TerminalPane/components/TerminalLogsButton/*
Replaces "Move to background" tooltip with new TerminalLogsButton component. Button displays alert icon, shows log count in tooltip, and renders a popover listing all log entries in reverse chronological order with level, timestamp, and message. Includes "Clear" action to purge logs.

Sequence Diagram

sequenceDiagram
    participant Transport as TerminalTransport
    participant Registry as TerminalRuntimeRegistry
    participant Listener as UI Subscriber
    participant UI as TerminalLogsButton

    Transport->>Transport: Connection error/close event
    Transport->>Transport: pushLog(entry)
    Transport->>Transport: Append to logs (max 200)
    Transport->>Listener: Notify logListeners
    
    UI->>Registry: useSyncExternalStore(onLogsChange)
    Listener->>Registry: onLogsChange(callback)
    Registry->>Transport: Call transport.logListeners.add()
    Registry-->>UI: Trigger subscription
    
    UI->>Registry: getLogs(terminalId)
    Registry->>Transport: Return transport.logs[]
    Transport-->>Registry: TerminalLogEntry[]
    Registry-->>UI: Updated logs
    
    UI->>UI: Render button (if logs exist)
    UI->>UI: Display popover with entries
    
    User->>UI: Click Clear
    UI->>Registry: clearLogs(terminalId)
    Registry->>Transport: clearLogs()
    Transport->>Listener: Notify logListeners
    Listener->>UI: Trigger re-render
    UI->>UI: Return null (no logs)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 Hops with glee through terminal logs so fine,
No more lost whispers in the xterm line!
With buttons and buffers, a logging design,
Now errors and warnings? They'll surely align!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete. While an auto-generated summary provides helpful context, the required template sections (Related Issues, Type of Change, Testing, Screenshots, Additional Notes) are either empty or unchecked. Complete the template by selecting the Type of Change (New feature/Refactor), describing testing approach, and filling in any other relevant template sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: moving WebSocket reconnect notices from terminal output to a pane-side log UI component.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ws-reconnect-logs-pane

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 5/8 reviews remaining, refill in 19 minutes and 20 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 30, 2026

Greptile Summary

This PR reroutes WebSocket reconnect/close/error notices away from the xterm scrollback buffer and into a bounded in-memory log (max 200 entries) exposed via the runtime registry. A new TerminalLogsButton in the pane header displays the log in a popover — colour-coded by severity — and only appears when entries exist; it also removes the now-unneeded "move to background" button.

Confidence Score: 5/5

Safe to merge; the one finding is a UX polish suggestion and does not block correct behaviour.

All remaining findings are P2. The transport, registry, and UI layers are well-structured, the useSyncExternalStore subscription pattern is correct, and the EMPTY_LOGS sentinel prevents snapshot thrash. The missing reconnect-success log entry is a quality improvement, not a defect.

apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts — open handler does not emit a reconnect-success log entry.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts Moves WebSocket close/error notices from xterm scrollback to a bounded in-memory log (max 200 entries). The open handler on reconnect does not emit a success entry, so the warning badge persists indefinitely after recovery.
apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts Adds getLogs, clearLogs, and onLogsChange methods. Uses a frozen EMPTY_LOGS sentinel to avoid useSyncExternalStore snapshot thrash on missing entries. Pattern is consistent with existing state/title change subscriptions.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalLogsButton/TerminalLogsButton.tsx New component using useSyncExternalStore for log subscription; renders null when no logs exist. Correct use of Radix Popover + Tooltip composition and stopPropagation for pane-focus prevention.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalHeaderExtras/TerminalHeaderExtras.tsx Replaces the move-to-background button with TerminalLogsButton. Clean removal of dead imports.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalLogsButton/index.ts Simple barrel export for the new TerminalLogsButton component.

Sequence Diagram

sequenceDiagram
    participant WS as WebSocket
    participant Transport as TerminalTransport
    participant Registry as TerminalRuntimeRegistry
    participant UI as TerminalLogsButton

    WS->>Transport: close / error event
    Transport->>Transport: pushLog(level, message)
    Transport->>Registry: notify logListeners
    Registry->>UI: useSyncExternalStore callback
    UI->>Registry: getLogs(terminalId, instanceId)
    UI-->>UI: render badge (warn/error colour)

    UI->>Registry: clearLogs(terminalId, instanceId)
    Registry->>Transport: transport.logs = []
    Transport->>Registry: notify logListeners
    Registry->>UI: useSyncExternalStore callback
    UI-->>UI: return null (hidden)
Loading

Comments Outside Diff (1)

  1. apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts, line 198-211 (link)

    P2 No "reconnected" log entry on successful reconnect

    After a network blip, the close handler pushes a warn entry ("Reconnecting…") so the badge appears. But the open handler only resets _reconnectAttempt — it never pushes an info entry to indicate the connection was restored. As a result the warning badge stays visible with only "Reconnecting…" in the log until the user manually clears it, giving no confirmation that the connection is back up.

    Consider pushing a log entry in the open handler when _reconnectAttempt > 0 (i.e. this is a reconnect, not the initial connect):

    socket.addEventListener("open", () => {
        if (transport.socket !== socket) return;
        if (transport._reconnectAttempt > 0) {
            pushLog(
                transport,
                "info",
                `Reconnected to ${formatWsEndpoint(transport.currentUrl)}.`,
            );
        }
        transport._reconnectAttempt = 0;
        setConnectionState(transport, "open");
        sendResize(transport, terminal.cols, terminal.rows);
        if (options.initialCommand) {
            socket.send(
                JSON.stringify({
                    type: "initialCommand",
                    data: options.initialCommand,
                }),
            );
        }
    });
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts
    Line: 198-211
    
    Comment:
    **No "reconnected" log entry on successful reconnect**
    
    After a network blip, the close handler pushes a `warn` entry ("Reconnecting…") so the badge appears. But the `open` handler only resets `_reconnectAttempt` — it never pushes an `info` entry to indicate the connection was restored. As a result the warning badge stays visible with only "Reconnecting…" in the log until the user manually clears it, giving no confirmation that the connection is back up.
    
    Consider pushing a log entry in the `open` handler when `_reconnectAttempt > 0` (i.e. this is a reconnect, not the initial connect):
    
    ```ts
    socket.addEventListener("open", () => {
        if (transport.socket !== socket) return;
        if (transport._reconnectAttempt > 0) {
            pushLog(
                transport,
                "info",
                `Reconnected to ${formatWsEndpoint(transport.currentUrl)}.`,
            );
        }
        transport._reconnectAttempt = 0;
        setConnectionState(transport, "open");
        sendResize(transport, terminal.cols, terminal.rows);
        if (options.initialCommand) {
            socket.send(
                JSON.stringify({
                    type: "initialCommand",
                    data: options.initialCommand,
                }),
            );
        }
    });
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts:198-211
**No "reconnected" log entry on successful reconnect**

After a network blip, the close handler pushes a `warn` entry ("Reconnecting…") so the badge appears. But the `open` handler only resets `_reconnectAttempt` — it never pushes an `info` entry to indicate the connection was restored. As a result the warning badge stays visible with only "Reconnecting…" in the log until the user manually clears it, giving no confirmation that the connection is back up.

Consider pushing a log entry in the `open` handler when `_reconnectAttempt > 0` (i.e. this is a reconnect, not the initial connect):

```ts
socket.addEventListener("open", () => {
    if (transport.socket !== socket) return;
    if (transport._reconnectAttempt > 0) {
        pushLog(
            transport,
            "info",
            `Reconnected to ${formatWsEndpoint(transport.currentUrl)}.`,
        );
    }
    transport._reconnectAttempt = 0;
    setConnectionState(transport, "open");
    sendResize(transport, terminal.cols, terminal.rows);
    if (options.initialCommand) {
        socket.send(
            JSON.stringify({
                type: "initialCommand",
                data: options.initialCommand,
            }),
        );
    }
});
```

Reviews (1): Last reviewed commit: "feat(desktop): move v2 terminal ws recon..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalLogsButton/TerminalLogsButton.tsx (1)

95-116: ⚡ Quick win

Split LogRow into its own component file.

TerminalLogsButton.tsx currently defines multiple components (TerminalLogsButton + LogRow). Please move LogRow to its own component file to align with repo structure rules.

♻️ Suggested split
-import {
-	type TerminalLogEntry,
-	terminalRuntimeRegistry,
-} from "renderer/lib/terminal/terminal-runtime-registry";
+import { terminalRuntimeRegistry } from "renderer/lib/terminal/terminal-runtime-registry";
+import { LogRow } from "./LogRow";
@@
-function LogRow({ entry }: { entry: TerminalLogEntry }) {
-  ...
-}
// apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalLogsButton/LogRow/LogRow.tsx
import { cn } from "@superset/ui/utils";
import type { TerminalLogEntry } from "renderer/lib/terminal/terminal-runtime-registry";

export function LogRow({ entry }: { entry: TerminalLogEntry }) {
	// existing row body
}
// .../TerminalLogsButton/LogRow/index.ts
export { LogRow } from "./LogRow";

As per coding guidelines: **/*.{tsx,ts}: Do not create multi-component files; use one component per file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalLogsButton/TerminalLogsButton.tsx
around lines 95 - 116, The file defines two React components in one file (LogRow
and TerminalLogsButton); split LogRow into its own module by creating LogRow.tsx
exporting the LogRow component (preserve its props type TerminalLogEntry,
imports like cn and formatTime), add an index.ts that re-exports LogRow, and
update TerminalLogsButton to import { LogRow } from the new folder instead of
the local definition; ensure the implementation and prop types remain unchanged
and any utilities (cn, formatTime) are imported where LogRow now lives.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalLogsButton/TerminalLogsButton.tsx:
- Around line 95-116: The file defines two React components in one file (LogRow
and TerminalLogsButton); split LogRow into its own module by creating LogRow.tsx
exporting the LogRow component (preserve its props type TerminalLogEntry,
imports like cn and formatTime), add an index.ts that re-exports LogRow, and
update TerminalLogsButton to import { LogRow } from the new folder instead of
the local definition; ensure the implementation and prop types remain unchanged
and any utilities (cn, formatTime) are imported where LogRow now lives.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3db32cf4-2664-4707-bb42-989c547f7521

📥 Commits

Reviewing files that changed from the base of the PR and between 0d6225d and 96102a6.

📒 Files selected for processing (5)
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalHeaderExtras/TerminalHeaderExtras.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalLogsButton/TerminalLogsButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalLogsButton/index.ts

@Kitenite Kitenite merged commit 001ccc2 into main Apr 30, 2026
7 checks passed
@Kitenite Kitenite deleted the ws-reconnect-logs-pane branch April 30, 2026 00:44
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch

Thank you for your contribution! 🎉

Kitenite added a commit that referenced this pull request May 12, 2026
…ove-to-background button (#4459)

* feat(desktop): surface background terminal sessions in v2 tab bar; restore move-to-background button

- Add renderTabBarTrailing slot to @superset/panes Workspace/TabBar
- New BackgroundTerminalsButton in v2-workspace tab bar: lists daemon
  terminal sessions with no pane attached; click to re-open as a tab,
  trash icon to kill
- Restore the 'Move terminal to background' archive button in the v2
  terminal pane header (removed in #3888)

* fix(desktop): scope background-session kill button disabled state per row
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