Skip to content

feat(publish)!: muted and invisible keep capture open - #7157

Merged
moq-bot[bot] merged 1 commit into
mainfrom
quest/m1/push-to-talk/mute
Oct 30, 2026
Merged

moq-bot[bot] merged 1 commit into
mainfrom
quest/m1/push-to-talk/mute

Conversation

@kixelated

@kixelated kixelated commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Implements quest/m1/push-to-talk/mute.md, part of the push-to-talk epic for moq-dev/moq#7101. The epic issue is not closed here.

Summary

  • <moq-publish muted> and invisible now disable only the audio or video encoder (js/publish/src/element.ts). Source.Camera, Source.Microphone, Source.Screen, Source.File and Audio.Capture follow the element's lifecycle gate (#enabled) and source. #eitherEnabled is gone. Unmuting skips getUserMedia, the new AudioContext and the worklet load, and unhiding skips the camera reopen.
  • Clearing source or detaching the element still stops both tracks, and reattaching reopens them.
  • Docs: the attribute table in doc/lib/js/publish.md and js/publish/README.md, the muted/invisible doc comments, and a MIGRATE.md entry. They say the browser's capture indicators stay lit, and that releasing only one device means building the pipeline with the library and toggling Source.Microphone or Source.Camera enabled.
  • Tests (test/interop/clients/js/publish-ui.browser.ts):
    • The announce test used to mute and unmute to force a reopen. It now checks that muting and hiding keep the same tracks live, that unmuting flips both catalog entries back with zero new getUserMedia calls, and that the announcement holds. The constraint-change reopen check is unchanged.
    • New test: a camera opens both devices even when it starts muted and hidden. Clearing source ends both tracks, detaching ends them, and reattaching reopens them.
    • The publish-ui fixture now nests a <video> preview. Without one, the UI box shrinks to zero height once a camera is live, which pushes the control bar out of the viewport. The first test only passed on main because it clicked before getUserMedia resolved, and opening capture earlier lost that race.

Choices made without asking

  • The announce gate counts a muted or hidden track as absent (js/publish/src/announce.ts). Without this, a camera that starts both muted and hidden would announce an empty catalog, because its sources are now live. With it, camera behavior is the same as before. One screen case changes: a hidden screen share with no audio now retracts its announcement, where before it stayed announced with an empty catalog. Muting and hiding a screen share now keeps the share open, where before it stopped the share.
  • Hiding blanks the local preview. The <video> preview path now checks #videoEnabled, matching the <canvas> renderer, which already did. The UI sizes the box as empty while hidden, so the self-view goes dark like before, even though the camera is open.
  • A camera source opens both devices even when it starts muted or hidden. This follows from "source and Capture independent of muted and invisible": a page that starts with muted now gets a microphone permission prompt. It is noted in MIGRATE.md. The audio-quality mic client (source="camera" invisible) now also opens the fake camera without encoding it, so I updated its comment.

Checks

  • bun test in js/publish: 279 pass.
  • sh/js/workspace.py check on the changed paths (lint, types, format, build, docs): pass.
  • publish-ui.browser.ts in local Chromium: 7 pass, 2 fail. The two failures are the version-list tests, which already fail on main (quest/m0/interop-green/stale-browser-tests.md).

Suggested follow-ups

  • The publish UI's box has nothing to size it once a camera is live without a nested <video>/<canvas>. player--empty keys off the capture source rather than a visible preview, so the box collapses to zero height and the controls leave the viewport. This affects main too.
  • A failed encoder used to recover on an unmute, because the reopened track was a new capture source. Now only a device swap, a settings change, or a new source recovers it. Worth checking with quest/m1/push-to-talk/reenable.md.
  • Video capture keeps processing frames while hidden. If hidden-camera CPU matters, Video.Capture could idle while no encoder or preview reads it.

Review

  • Reviewer: Codex (quest review), gpt-6.1-sol, medium effort.
  • Head: 96c29e9 (merge of main into the branch).
  • Findings: none.

Public API: behavior change to muted and invisible (capture stays open). Wire: none.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

@kixelated kixelated changed the title The publish element's mute and hide keep capture open feat(publish)!: muted and invisible keep capture open Oct 28, 2026
@kixelated
kixelated marked this pull request as ready for review October 28, 2026 23:21
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T14:52:10.405570Z 96c29e9 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)
Reviewed commit: e473a67

No actionable bugs found. Separating the capture-lifetime gate from encoder mute/hide is a straightforward way to avoid device and AudioContext reopen costs. Clearing the source and disconnecting still reach the source cleanup paths, while the announce gate excludes disabled tracks and the video preview is explicitly hidden. The breaking permission/indicator behavior—including opening both devices when initially muted/hidden—is documented rather than silently preserving the old privacy semantics.

The identity/getUserMedia-count and detach/reattach tests target the important lifecycle guarantees. Static review of the changes and surrounding element, announce, capture, source, and audio-encoder code only; I did not run browser tests, measure resume latency, or verify behavior with real devices. Encoder failure recovery remains a separate concern, as noted in the PR.

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Walkthrough

The publish element now keeps selected capture sources enabled while connected, even when muted or invisible. Those controls still gate audio and video encoding. Clearing the source or disconnecting the element releases capture devices. Announcement and preview behavior, documentation, and browser tests were updated to reflect these changes.

Priority: Normal

Merge Risk: Minimal · up to dad32

The selected changes document the intended capture behavior and remove its completed planning item. No material merge risk is established beyond normal checks.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title "feat(publish)!: muted and invisible keep capture open" matches the main change: muted and invisible now stop only the encoder while microphone and camera capture stay open. It uses a conven…
Docstring Coverage Passed No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
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.
Description check Passed The description directly explains the changes to muted and invisible capture behavior, documentation, tests, and validation results.
Full details: Title check

Explanation

The title "feat(publish)!: muted and invisible keep capture open" matches the main change: muted and invisible now stop only the encoder while microphone and camera capture stay open. It uses a conventional commit scope and the breaking-change marker, which fits a public API behavior change. The title is specific, short, and a teammate scanning history can identify the primary change.

Full details: Docstring Coverage

Explanation

No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 6 files. (1 skipped: 1 unsupported.)

Finishing Touches
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merge summary:

  • Merged main into the branch (0dbe9a6). The only conflict was the link list in quest/m1/push-to-talk/README.md, which the quest merge driver resolved. main had removed the reenable.md entry and this PR removes the mute.md entry, so the list now holds just hold-js and hold-rs. publish-ui.browser.ts and the js/publish encoder changes from main merged without conflict.
  • quest check --base origin/main --branch quest/m1/push-to-talk/mute passes (661 documents ok). CI passes on the new head.
  • The reviews on e473a67 found nothing actionable, and no review threads are open.
  • The suggested follow-ups (controls collapse, encoder recovery) are already planned in quest: plan follow-ups from the 2026-10-28 m0/m1 spawn #7195.

(Written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merge summary (re-land after merge_conflict dequeue):

  • Merged main into the branch again (b90d34a). The merge was conflict-free, both locally and under a plain git merge-tree with no merge driver. The PR's diff against main is unchanged: the js/publish element, docs, interop test, and removal of the mute.md quest.
  • quest check --base origin/main --branch quest/m1/push-to-talk/mute passes (652 documents ok).
  • I didn't run a local typecheck because the host was too loaded for quest gate to start one. The merge queue runs Check on the combined head.
  • Under CONTRIBUTING, a conflict-free base merge needs no new review. Codex's clean review of the previous head 0dbe9a6 still covers this one, and no review threads are open.

(Written by Claude Opus 5.5)

@kixelated
kixelated enabled auto-merge October 29, 2026 16:09
@kixelated

Copy link
Copy Markdown
Collaborator Author

Follow-up: main moved again (de13e6d) while the PR was being re-landed. I merged it in, and the new head is feef914. GitHub flagged a conflict in the link list in quest/m1/push-to-talk/README.md, and the quest merge driver resolved it. That list now holds hold-js, hold-rs, and main's new audio-support-cache entry, without mute.md. This is the same kind of resolution as the earlier 0dbe9a6 merge, and no other files conflicted. quest check passes (662 documents ok).

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 29, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 29, 2026
@kixelated
kixelated added this pull request to the merge queue Oct 29, 2026
@kixelated

Copy link
Copy Markdown
Collaborator Author

Re-landing after the merge queue dropped this PR on merge_conflict. I merged main into the branch (dad324b). The only conflict was the Required list in quest/m1/push-to-talk/README.md, and quest's merge driver resolved it on its own: main had already removed hold-rs.md, and this PR's prose stays as written. No code was edited by hand. quest check passes against origin/main, and the PR is queued again.

(Written by Claude Opus 5.5)

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 30, 2026
@kixelated

Copy link
Copy Markdown
Collaborator Author

Re-landing after the old merge queue dropped this PR on merge_conflict (2026-10-30T01:23:09Z), now through quest land.

  • Merged main into the branch (96c29e9). The only overlap was the Required list in quest/m1/push-to-talk/README.md, which quest's merge driver resolved: it now holds just audio-support-cache.md. No code was edited by hand.
  • quest check --base origin/main passes (665 documents ok), sh/js/workspace.py check on the changed paths passes, and bun test in js/publish passes (289).
  • The body had no ## Review record, so I ran quest review (Codex, gpt-6.1-sol, medium) on the new head: no findings. The record is now in the body.
  • No unresolved review threads; Codex's online review of the previous head found nothing.

(Written by Claude Opus 5.5)

@kixelated
kixelated enabled auto-merge (squash) October 30, 2026 14:52
* chore(quest): claim quest/m1/push-to-talk/mute.md

Quest-Claim: e1709a982a08d378

* feat(publish)!: muted and invisible keep capture open

`<moq-publish muted>` and `invisible` now disable only the audio or video
encoder. The camera, microphone, screen, or file source and the audio
capture follow the element's connection gate and `source`, so unmuting
skips getUserMedia, AudioContext setup, and the worklet load, and
unhiding skips a camera reopen. Clearing `source` or detaching the
element still releases both devices.

The announce gate counts a muted or hidden track as absent, so a camera
that starts both muted and hidden still waits before announcing. A hidden
camera also blanks the `<video>` preview and sizes the UI box as empty.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@moq-bot
moq-bot Bot force-pushed the quest/m1/push-to-talk/mute branch from 96c29e9 to 2b61981 Compare October 30, 2026 18:56
@moq-bot
moq-bot Bot merged commit 2b61981 into main Oct 30, 2026
7 checks passed
@moq-bot
moq-bot Bot deleted the quest/m1/push-to-talk/mute branch October 30, 2026 18:56
kixelated added a commit that referenced this pull request Oct 31, 2026
Resolve js/publish/src/ui/element.ts against #7157: keep sizing the box by
the slotted content's rendered size, and keep main's rule that a hidden
camera sizes it as empty. Drop the fixture's <video> workaround so the
no-preview path is what the browser tests cover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was previously deployed

1 inactive deployment
cache-write — 2b619817 Deployed Oct 30, 2026 by moq-bot[bot] via Warm (macos-latest) #1776
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant