Skip to content

feat(portal): live WebSocket connection badge in portal header#449

Open
jaeko44 wants to merge 5 commits intomainfrom
task/b3364eceda27-feat-portal-live-websocket-connection-badge-in-p
Open

feat(portal): live WebSocket connection badge in portal header#449
jaeko44 wants to merge 5 commits intomainfrom
task/b3364eceda27-feat-portal-live-websocket-connection-badge-in-p

Conversation

@jaeko44
Copy link
Copy Markdown
Member

@jaeko44 jaeko44 commented Mar 26, 2026

Task-ID: b3364ece-da27-43ce-b950-6e15d057a33d\n\nAutomated PR for task b3364ece-da27-43ce-b950-6e15d057a33d\n\n---\n\nBosun-Origin: created

@jaeko44 jaeko44 added the bosun-attached Bosun PR attachment marker label Mar 26, 2026
Copilot AI review requested due to automatic review settings March 26, 2026 03:08
@github-actions github-actions bot added the bosun-pr-public PR observed by Bosun but not trusted for high-risk automation label Mar 26, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

Bosun CI signal: Bosun-created PR currently has failing checks.

* Header
* ═══════════════════════════════════════════════ */
function ConnectionBadge() {
const status = wsStatus.value;
function ConnectionBadge() {
const status = wsStatus.value;
const reconnectCount = wsReconnectCount.value;
const lastReconnectAt = wsLastReconnectAt.value;
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a live WebSocket connection status badge to the portal header, including styling and basic regression tests, with corresponding UI changes mirrored into site/ui/.

Changes:

  • Introduces WebSocket status signals (wsStatus, wsLastReconnectAt) and updates them during WS lifecycle events.
  • Renders a new ConnectionBadge component in the header showing WS state + reconnect metadata.
  • Adds badge CSS (colors + pulse animation) and a Vitest regression test.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
ui/styles.css Adds connection badge styling, status colors, and pulse keyframes.
ui/modules/api.js Adds reactive WS status signals and updates them on open/close/error/disconnect.
ui/app.js Imports new WS signals and renders ConnectionBadge in the header.
tests/ui-connection-badge.test.mjs Adds regression tests asserting badge wiring and CSS presence.
site/ui/styles.css Mirrors the connection badge styling for the site UI.
site/ui/app.js Mirrors header badge rendering/imports for the site UI.
Comments suppressed due to low confidence (1)

ui/modules/api.js:393

  • disconnectWebSocket() is documented as canceling reconnects, but calling ws.close() will still trigger the close handler which unconditionally sets wsStatus to "reconnecting" and schedules a reconnect timer. Add a guard (e.g., a manualClose/shouldReconnect flag set by disconnectWebSocket) so manual disconnects don’t flip the badge to reconnecting or restart the connection.
  socket.addEventListener("close", () => {
    wsConnected.value = false;
    wsLatency.value = null;
    wsStatus.value = "reconnecting";
    ws = null;
    stopPing();
    wsReconnectCount.value += 1;
    // Auto-reconnect with exponential backoff (max 15 s)
    if (reconnectTimer) clearTimeout(reconnectTimer);
    startCountdown(retryMs);
    reconnectTimer = setTimeout(() => {
      reconnectTimer = null;
      connectWebSocket();
    }, retryMs);
    retryMs = Math.min(15000, retryMs * 2);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +352 to +353
wsStatus,
wsLastReconnectAt,
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

site/ui/modules/api.js currently does not export wsStatus or wsLastReconnectAt, so this import will throw at module-evaluation time and break the site build. Mirror the new signals + connectWebSocket/disconnectWebSocket status updates into site/ui/modules/api.js (or stop importing these names here if the site bundle shouldn’t have the badge).

Suggested change
wsStatus,
wsLastReconnectAt,

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +13
expect(apiSource).toContain("'connected'");
expect(apiSource).toContain("'reconnecting'");
expect(apiSource).toContain("'offline'");
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

These assertions won’t match the implementation: the source sets statuses with double quotes (e.g. "connected"), but the test looks for single-quoted strings ('connected', etc.), so it will fail. Update the expectations to match the actual quoting style or use a more resilient regex/assertion.

Suggested change
expect(apiSource).toContain("'connected'");
expect(apiSource).toContain("'reconnecting'");
expect(apiSource).toContain("'offline'");
expect(apiSource).toContain("\"connected\"");
expect(apiSource).toContain("\"reconnecting\"");
expect(apiSource).toContain("\"offline\"");

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +10
it("tracks explicit websocket badge signals and reconnect metadata", () => {
const apiSource = readFileSync(resolve(process.cwd(), "ui/modules/api.js"), "utf8");

expect(apiSource).toContain("wsStatus = signal");
expect(apiSource).toContain("wsLastReconnectAt = signal");
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

This test only inspects ui/modules/api.js, but this PR also updates site/ui/app.js to use the new signals. Add coverage that site/ui/modules/api.js exports/updates the same badge signals so regressions like missing exports in the site bundle are caught.

Copilot uses AI. Check for mistakes.
50% { transform: scale(1.35); opacity: 0.55; }
100% { transform: scale(1); opacity: 1; }
}

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The new pulsing animation should respect prefers-reduced-motion. There’s already a reduced-motion section in ui/styles/layout.css disabling other connection pulses; add a similar rule for .connection-badge-pulse to avoid continuous animation for users who prefer reduced motion.

Suggested change
@media (prefers-reduced-motion: reduce) {
.connection-badge-pulse {
animation: none;
}
}

Copilot uses AI. Check for mistakes.
50% { transform: scale(1.35); opacity: 0.55; }
100% { transform: scale(1); opacity: 1; }
}

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The new pulsing animation should respect prefers-reduced-motion. There’s already a reduced-motion section in site/ui/styles/layout.css disabling other connection pulses; add a similar rule for .connection-badge-pulse to avoid continuous animation for users who prefer reduced motion.

Suggested change
@media (prefers-reduced-motion: reduce) {
.connection-badge-pulse {
animation: none;
}
}

Copilot uses AI. Check for mistakes.
export const wsReconnectCount = signal(0);
/** Reactive signal: WebSocket badge status for portal header */
export const wsStatus = signal("offline");
/** Reactive signal: timestamp of the last successful reconnect */
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The comment says wsLastReconnectAt is the “timestamp of the last successful reconnect”, but it’s set on every open (including the initial connection). Either adjust the wording (e.g. “last successful (re)connect”) or only set this timestamp when a reconnect actually occurred.

Suggested change
/** Reactive signal: timestamp of the last successful reconnect */
/** Reactive signal: timestamp of the last successful (re)connect */

Copilot uses AI. Check for mistakes.
@jaeko44 jaeko44 added bosun-needs-fix Attached PR with failing CI that Bosun should pick up for repair bosun-pr-bosun-created PR created by Bosun and eligible for Bosun automation and removed bosun-pr-public PR observed by Bosun but not trusted for high-risk automation labels Mar 26, 2026
@github-actions
Copy link
Copy Markdown

Bosun PR classification: Bosun-created.
This PR is tracked by Bosun attachment automation.

  • PR class label: bosun-pr-bosun-created
  • Attach label: bosun-attached (yes, because Bosun-created PRs remain attached regardless of human PR attach mode)
  • Attach mode policy: all
  • Trusted author: no
  • Bosun-created label present: yes
  • Automation scope: Eligible for Bosun repair and merge automation (Bosun-created PR).
  • CI failure signal label: bosun-needs-fix
  • CI failure marker:
  • Trigger: pull_request_target / synchronize

jaeko44 added 2 commits March 26, 2026 23:44
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
@jaeko44 jaeko44 added bosun-needs-fix Attached PR with failing CI that Bosun should pick up for repair and removed bosun-needs-fix Attached PR with failing CI that Bosun should pick up for repair labels Mar 26, 2026
@jaeko44 jaeko44 added bosun-needs-fix Attached PR with failing CI that Bosun should pick up for repair and removed bosun-needs-fix Attached PR with failing CI that Bosun should pick up for repair labels Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bosun-attached Bosun PR attachment marker bosun-needs-fix Attached PR with failing CI that Bosun should pick up for repair bosun-pr-bosun-created PR created by Bosun and eligible for Bosun automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants