Skip to content

fix(watch): hide buffering overlay while offline#1410

Open
YogiSotho wants to merge 6 commits into
moq-dev:mainfrom
YogiSotho:fix/watch-offline-buffering
Open

fix(watch): hide buffering overlay while offline#1410
YogiSotho wants to merge 6 commits into
moq-dev:mainfrom
YogiSotho:fix/watch-offline-buffering

Conversation

@YogiSotho
Copy link
Copy Markdown

Summary

Fix the watch UI buffering overlay so it does not cover the player while the broadcast is offline.

Changes

  • hide the buffering overlay when watchStatus() is offline
  • keep the spinner centered by adding explicit flex centering to the buffering container

Why

Issue #737 reports that the buffering UI covers the element and controls even when nothing is loading because the broadcast is offline. In that state, showing a spinner is misleading and blocks the offline state from being communicated clearly.

Testing

  • verified the change is limited to the watch UI buffering indicator and styles
  • attempted bun run check in js/watch, but local workspace dependencies were not fully installed, so tsc was unavailable in this environment

Closes #737

@YogiSotho
Copy link
Copy Markdown
Author

@YogiSotho YogiSotho marked this pull request as ready for review May 15, 2026 15:38
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4805b60b-124c-4e7d-8784-e06cdb3cc902

📥 Commits

Reviewing files that changed from the base of the PR and between ee061b5 and 0993bc0.

📒 Files selected for processing (1)
  • js/watch/src/backend.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • js/watch/src/backend.ts

Walkthrough

This change threads an "offline" Signal from the MoqWatch component into MultiBackend and into Video.Renderer. Renderer now skips normal frame drawing and fills the canvas black when offline. The UI adds an OfflineIndicator component, integrates it into the video container, and updates BufferingIndicator to hide while offline. CSS was added/updated for the offline badge and to center the buffering overlay.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: hiding the buffering overlay when the broadcast is offline.
Description check ✅ Passed The description provides relevant context explaining why the change was made and what was modified to address the buffering overlay issue when offline.
Linked Issues check ✅ Passed The PR addresses issue #737 by hiding the buffering overlay when offline, clearing the canvas to black to avoid frozen frames, and adding an OFFLINE badge to communicate the offline state clearly.
Out of Scope Changes check ✅ Passed All changes are scoped to addressing issue #737: buffering overlay visibility, canvas rendering when offline, and the offline indicator UI. No unrelated modifications were introduced.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

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

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)
js/watch/src/ui/styles/buffering-indicator.css (1)

3-5: ⚡ Quick win

Remove redundant flex--center class from component.

The CSS now explicitly handles flexbox centering for .watch-ui__buffering, but the component still applies the flex--center utility class (line 10 of BufferingIndicator.tsx). This creates redundancy where two sources control the same layout behavior.

Since you've added explicit centering to the component's CSS, remove the flex--center class from the component:

♻️ Suggested cleanup

In js/watch/src/ui/components/BufferingIndicator.tsx:

-			<div class="watch-ui__buffering flex--center">
+			<div class="watch-ui__buffering">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/watch/src/ui/styles/buffering-indicator.css` around lines 3 - 5, The
component is applying the redundant utility class "flex--center" while the CSS
selector ".watch-ui__buffering" already provides flex centering; open the
BufferingIndicator component (BufferingIndicator.tsx) and remove the
"flex--center" class from the element that renders the buffering UI so only the
".watch-ui__buffering" styling controls layout, then run a quick UI check to
ensure centering remains correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@js/watch/src/ui/styles/buffering-indicator.css`:
- Around line 3-5: The component is applying the redundant utility class
"flex--center" while the CSS selector ".watch-ui__buffering" already provides
flex centering; open the BufferingIndicator component (BufferingIndicator.tsx)
and remove the "flex--center" class from the element that renders the buffering
UI so only the ".watch-ui__buffering" styling controls layout, then run a quick
UI check to ensure centering remains correct.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ae3da5d3-b927-49da-bab5-8258ca9cf3c4

📥 Commits

Reviewing files that changed from the base of the PR and between 6e7d918 and e7082e0.

📒 Files selected for processing (2)
  • js/watch/src/ui/components/BufferingIndicator.tsx
  • js/watch/src/ui/styles/buffering-indicator.css

@kixelated
Copy link
Copy Markdown
Collaborator

Hmm I definitely think we hide the buffering icon, but we should also stop rendering the most recent frame right? A black screen is more obvious than a frozen BBB image.

YogiSotho and others added 3 commits May 15, 2026 22:46
When the watch broadcast goes offline, stop rendering the most recent frame and display a black screen instead. This provides a clearer visual indicator that the connection is lost, rather than showing a frozen frame.

Changes:
- Add offline signal to Renderer to clear canvas when offline
- Pass offline status from MultiBackend to Renderer
- Track offline status in MoqWatch element based on broadcast status
Display a small black badge with white OFFLINE text in the bottom right corner when the broadcast goes offline. This provides a clear and prominent visual indicator of the offline state.

- New OfflineIndicator component that shows when watchStatus is offline
- Styling with responsive sizing for different screen sizes
- Positioned as a floating badge in the bottom right corner
@YogiSotho
Copy link
Copy Markdown
Author

Added two commits to this PR:

9ca2c7f — clears the canvas to black when offline instead of showing the last frozen frame
cb98207 — adds a small OFFLINE badge in the bottom right corner

https://youtu.be/mG6nsVV2jwg

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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@js/watch/src/ui/styles/offline-indicator.css`:
- Around line 42-43: The .watch-ui__offline-text rule uses font-size: 0.65rem
which is likely too small on mobile; update the CSS for the
.watch-ui__offline-text selector to increase the minimum font-size to 0.75rem
(or add a mobile media query that sets .watch-ui__offline-text to 0.75rem for
small viewports) so the offline status text is more readable on mobile devices.
- Around line 1-13: The offline indicator's stacking context is too low; update
the .watch-ui__offline-indicator rule to increase its z-index from 1 to at least
4 so it sits above buffer control elements (which use z-index 2 and 3). Locate
the .watch-ui__offline-indicator CSS selector and change the z-index value to 4
(or higher if needed) ensuring the alert remains visible above other UI
controls.

In `@js/watch/src/video/renderer.ts`:
- Around line 117-124: When detecting offline in the renderer (the block where
effect.get(this.offline) is true and you draw the black canvas), also clear the
cached frame state so stale values don't prevent decoding after reconnect: reset
this.frame and this.timestamp (the fields used by `#runEnabled`()) before
returning from that offline branch so reconnecting while paused won't keep
decoding disabled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 07f4e56c-0eff-4355-9825-8dddd12ed1ce

📥 Commits

Reviewing files that changed from the base of the PR and between e7082e0 and ee061b5.

📒 Files selected for processing (7)
  • js/watch/src/backend.ts
  • js/watch/src/element.ts
  • js/watch/src/ui/components/OfflineIndicator.tsx
  • js/watch/src/ui/element.tsx
  • js/watch/src/ui/styles/index.css
  • js/watch/src/ui/styles/offline-indicator.css
  • js/watch/src/video/renderer.ts
✅ Files skipped from review due to trivial changes (3)
  • js/watch/src/ui/components/OfflineIndicator.tsx
  • js/watch/src/ui/styles/index.css
  • js/watch/src/ui/element.tsx

Comment on lines +1 to +13
.watch-ui__offline-indicator {
position: absolute;
bottom: 1rem;
right: 1rem;
padding: 0.5rem 1rem;
display: flex;
align-items: center;
justify-content: center;
background-color: #000000;
z-index: 1;
pointer-events: auto;
border-radius: 0.25rem;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check z-index values in watch UI stylesheets to ensure offline indicator is visible above other elements.

# Search for z-index declarations in watch UI styles
rg -n 'z-index:' --glob 'js/watch/src/ui/styles/*.css' -C 2

Repository: moq-dev/moq

Length of output: 2072


Increase z-index of the offline indicator above buffer control elements.

The z-index: 1 is too low. Buffer control elements have z-index: 2 and z-index: 3, which means they would appear above the offline indicator. Since this indicator communicates critical status information (broadcast offline), it should be visible above all UI controls. Consider setting z-index to at least 4 to ensure it's not obscured.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/watch/src/ui/styles/offline-indicator.css` around lines 1 - 13, The
offline indicator's stacking context is too low; update the
.watch-ui__offline-indicator rule to increase its z-index from 1 to at least 4
so it sits above buffer control elements (which use z-index 2 and 3). Locate the
.watch-ui__offline-indicator CSS selector and change the z-index value to 4 (or
higher if needed) ensuring the alert remains visible above other UI controls.

Comment on lines +42 to +43
.watch-ui__offline-text {
font-size: 0.65rem;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consider increasing minimum font size for better mobile readability.

The font-size: 0.65rem (approximately 10.4px) may be too small for comfortable reading on mobile devices, especially for important status information like "offline." Consider using a minimum of 0.75rem to improve readability.

📱 Proposed fix for better mobile readability
 	.watch-ui__offline-text {
-		font-size: 0.65rem;
+		font-size: 0.75rem;
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.watch-ui__offline-text {
font-size: 0.65rem;
.watch-ui__offline-text {
font-size: 0.75rem;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/watch/src/ui/styles/offline-indicator.css` around lines 42 - 43, The
.watch-ui__offline-text rule uses font-size: 0.65rem which is likely too small
on mobile; update the CSS for the .watch-ui__offline-text selector to increase
the minimum font-size to 0.75rem (or add a mobile media query that sets
.watch-ui__offline-text to 0.75rem for small viewports) so the offline status
text is more readable on mobile devices.

Comment on lines +117 to +124
const offline = effect.get(this.offline);

// Clear canvas when offline.
if (offline) {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clear cached frame state when entering offline mode.

Line 120 returns after painting black but leaves this.frame and this.timestamp intact. Because #runEnabled() checks this.frame when paused (Line 108), reconnecting while paused can keep decoding disabled and preserve stale pre-offline state.

Proposed fix
 		// Clear canvas when offline.
 		if (offline) {
+			this.frame.update((old) => {
+				old?.close();
+				return undefined;
+			});
+			this.timestamp.set(undefined);
 			ctx.fillStyle = "`#000`";
 			ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 			return;
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const offline = effect.get(this.offline);
// Clear canvas when offline.
if (offline) {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
return;
}
const offline = effect.get(this.offline);
// Clear canvas when offline.
if (offline) {
this.frame.update((old) => {
old?.close();
return undefined;
});
this.timestamp.set(undefined);
ctx.fillStyle = "`#000`";
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/watch/src/video/renderer.ts` around lines 117 - 124, When detecting
offline in the renderer (the block where effect.get(this.offline) is true and
you draw the black canvas), also clear the cached frame state so stale values
don't prevent decoding after reconnect: reset this.frame and this.timestamp (the
fields used by `#runEnabled`()) before returning from that offline branch so
reconnecting while paused won't keep decoding disabled.

Copy link
Copy Markdown
Collaborator

@kixelated kixelated left a comment

Choose a reason for hiding this comment

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

I think you're very close.

Can you look into Decoder and figure out what it does when a broadcast goes offline? Maybe it's just not unsetting the current frame. I think that's a better fix than a signal that toggles a black screen.

paused: Signal<boolean>;

// Whether the watch is offline.
offline: Signal<boolean>;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe flip it and use enabled instead? To match other components.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Actually, maybe the decoder should unset the frame (on offline) instead of telling the renderer to draw black? IDK.

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.

Offline notification

2 participants