✨(frontend) add Picture-in-Picture recorder - closes #96#104
✨(frontend) add Picture-in-Picture recorder - closes #96#104prince0xdev wants to merge 2 commits into
Conversation
|
Warning Review limit reached
More reviews will be available in 43 minutes and 37 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more credits in the billing tab to continue. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (11)
📝 WalkthroughWalkthroughThis PR adds tab audio capture and Document Picture-in-Picture (PiP) recording controls to the dictaphone app. It introduces ChangesTab Audio and Picture-in-Picture Recording Enhancements
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
src/frontend/src/locales/fr-FR/record.json (1)
33-33:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winVerify usage of
pip.tabAudiotranslation key.Same concern as the English locale: the
pip.tabAudiokey is defined but usage is not visible in the provided context. Verify whether this key is actually referenced in the PiP panel UI.🤖 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 `@src/frontend/src/locales/fr-FR/record.json` at line 33, The translation key pip.tabAudio is present in the French locale but may be unused; search the codebase for references to "pip.tabAudio" (e.g., in PiP panel components, templates, or i18n lookup calls) and either update the PiP panel UI to use this key where the audio tab label is rendered (e.g., in the component that renders PiP tabs) or remove the unused key from the locale to avoid dead translations; if you add usage, ensure the i18n lookup matches the exact key name and fallback behavior is handled.
🤖 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 @.gitignore:
- Around line 50-53: Remove the entries that ignore dependency lock and
workspace files from .gitignore so they remain versioned: delete the lines
listing package-lock.json, yarn.lock, pnpm-lock.yaml, and pnpm-workspace.yaml
(the ignore patterns shown in the diff) so those lockfiles are tracked and
deterministic dependency resolution and supply-chain auditing are preserved.
In
`@src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.scss`:
- Line 2: The font-family declaration "font-family: 'Marianne', sans-serif;"
violates the font-family-name-quotes rule; update it to use double quotes
instead of single quotes (i.e. "font-family: \"Marianne\", sans-serif;") so the
declaration for Marianne conforms to the stylelint rule while preserving the
fallback sans-serif.
In
`@src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.tsx`:
- Around line 88-95: The current render in RecordingPictureInPicturePanel hides
the tab-audio action when tabAudioLabel is falsy; change the conditional so the
action button is shown whenever tabAudioSupported is true (regardless of
tabAudioLabel) — keep showing the <strong> label only when tabAudioLabel exists,
but always render the Button using tabAudioButtonLabel and onTabAudioAction;
update the conditional that currently uses "tabAudioSupported && tabAudioLabel"
to use "tabAudioSupported" and adjust JSX accordingly.
- Around line 73-77: The label element in RecordingPictureInPicturePanel has no
visible text so the select (source selector) lacks a proper accessible name;
update the JSX in RecordingPictureInPicturePanel.tsx by moving the accessible
name onto the select (e.g., add aria-label or aria-labelledby to the select
using the existing sourceLabel) or add visible/visually-hidden label text inside
the <label> (a span with a screen-reader-only class) and ensure the select is
associated via htmlFor/id; target the <label> and <select> around
selectedAudioInputId/onSelectAudioInput to implement this change.
---
Duplicate comments:
In `@src/frontend/src/locales/fr-FR/record.json`:
- Line 33: The translation key pip.tabAudio is present in the French locale but
may be unused; search the codebase for references to "pip.tabAudio" (e.g., in
PiP panel components, templates, or i18n lookup calls) and either update the PiP
panel UI to use this key where the audio tab label is rendered (e.g., in the
component that renders PiP tabs) or remove the unused key from the locale to
avoid dead translations; if you add usage, ensure the i18n lookup matches the
exact key name and fallback behavior is handled.
🪄 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: ASSERTIVE
Plan: Pro
Run ID: d2dc0168-0d6f-410a-8fd1-2227deeeb235
📒 Files selected for processing (13)
.gitignoresrc/backend/dictaphone/settings.pysrc/frontend/src/features/recordings/components/RecordComponent.scsssrc/frontend/src/features/recordings/components/RecordComponent.tsxsrc/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.scsssrc/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.tsxsrc/frontend/src/features/recordings/hooks/useDocumentPictureInPicture.tsxsrc/frontend/src/features/recordings/hooks/useRecordingController.tssrc/frontend/src/features/recordings/recorder/RecorderManager.tssrc/frontend/src/features/recordings/recorder/TabAudioInputManager.tssrc/frontend/src/locales/en-US/record.jsonsrc/frontend/src/locales/fr-FR/record.jsonsrc/frontend/src/main.tsx
| package-lock.json | ||
| yarn.lock | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml |
There was a problem hiding this comment.
Keep dependency lock/workspace files tracked.
Lines 50-53 ignore all JS lock/workspace manifests, which weakens deterministic dependency resolution and supply-chain auditability. These files should remain versioned.
Suggested fix
# npm / yarn / pnpm
node_modules
-package-lock.json
-yarn.lock
-pnpm-lock.yaml
-pnpm-workspace.yaml🤖 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 @.gitignore around lines 50 - 53, Remove the entries that ignore dependency
lock and workspace files from .gitignore so they remain versioned: delete the
lines listing package-lock.json, yarn.lock, pnpm-lock.yaml, and
pnpm-workspace.yaml (the ignore patterns shown in the diff) so those lockfiles
are tracked and deterministic dependency resolution and supply-chain auditing
are preserved.
| @@ -0,0 +1,67 @@ | |||
| .record-pip-panel { | |||
| font-family: 'Marianne', sans-serif; | |||
There was a problem hiding this comment.
Fix stylelint violation for font-family.
Line 2 violates font-family-name-quotes and may fail style checks.
Proposed fix
- font-family: 'Marianne', sans-serif;
+ font-family: Marianne, sans-serif;📝 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.
| font-family: 'Marianne', sans-serif; | |
| font-family: Marianne, sans-serif; |
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 2-2: Expected no quotes around "Marianne" (font-family-name-quotes)
(font-family-name-quotes)
🤖 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
`@src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.scss`
at line 2, The font-family declaration "font-family: 'Marianne', sans-serif;"
violates the font-family-name-quotes rule; update it to use double quotes
instead of single quotes (i.e. "font-family: \"Marianne\", sans-serif;") so the
declaration for Marianne conforms to the stylelint rule while preserving the
fallback sans-serif.
Source: Linters/SAST tools
| <label className="record-pip-panel__field" aria-label={sourceLabel}> | ||
| <select | ||
| value={selectedAudioInputId} | ||
| onChange={(event) => onSelectAudioInput(event.target.value)} | ||
| disabled={audioInputs.length === 0} |
There was a problem hiding this comment.
Programmatically label the source selector.
On Line 73 and Line 74, the <label> has no text content, so the <select> can be announced without a proper name. Put the accessible label directly on the <select> (or add visible/visually-hidden label text).
Proposed fix
- <label className="record-pip-panel__field" aria-label={sourceLabel}>
+ <label className="record-pip-panel__field">
<select
+ aria-label={sourceLabel}
value={selectedAudioInputId}
onChange={(event) => onSelectAudioInput(event.target.value)}
disabled={audioInputs.length === 0}
>📝 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.
| <label className="record-pip-panel__field" aria-label={sourceLabel}> | |
| <select | |
| value={selectedAudioInputId} | |
| onChange={(event) => onSelectAudioInput(event.target.value)} | |
| disabled={audioInputs.length === 0} | |
| <label className="record-pip-panel__field"> | |
| <select | |
| aria-label={sourceLabel} | |
| value={selectedAudioInputId} | |
| onChange={(event) => onSelectAudioInput(event.target.value)} | |
| disabled={audioInputs.length === 0} | |
| > |
🤖 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
`@src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.tsx`
around lines 73 - 77, The label element in RecordingPictureInPicturePanel has no
visible text so the select (source selector) lacks a proper accessible name;
update the JSX in RecordingPictureInPicturePanel.tsx by moving the accessible
name onto the select (e.g., add aria-label or aria-labelledby to the select
using the existing sourceLabel) or add visible/visually-hidden label text inside
the <label> (a span with a screen-reader-only class) and ensure the select is
associated via htmlFor/id; target the <label> and <select> around
selectedAudioInputId/onSelectAudioInput to implement this change.
|
Hello @prince0xdev, thanks for your PR, to ease with reviewing, could you please split the changes in separate commits and avoid unecessary changes. My branch was heavily based on LLM generated / Codex. I feel yours is a bit too much, but open to move forward or hear otherwise :) |
ce4680b to
c91ebea
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.tsx (1)
88-95:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRender the tab-audio action whenever support exists.
Line 88 currently requires
tabAudioLabel, which hides the PiP tab-audio action when tab audio is supported but not yet active. That prevents enabling tab audio directly from the PiP panel.Proposed fix
- {tabAudioSupported && tabAudioLabel && ( + {tabAudioSupported && ( <div className="record-pip-panel__tab-audio"> - <strong title={tabAudioLabel}>{tabAudioLabel}</strong> + {tabAudioLabel && <strong title={tabAudioLabel}>{tabAudioLabel}</strong>} <Button size="nano" color="neutral" onClick={onTabAudioAction}> {tabAudioButtonLabel} </Button> </div> )}🤖 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 `@src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.tsx` around lines 88 - 95, The PiP tab-audio action is gated on tabAudioLabel which hides the control when tab audio is supported but not active; update the rendering in RecordingPictureInPicturePanel to base visibility only on tabAudioSupported (remove the tabAudioLabel check) so the action/button always appears when supported, but keep using tabAudioLabel optionally for the <strong> text and its title (rendering a fallback or empty title if label is falsy); ensure onTabAudioAction and tabAudioButtonLabel remain used as-is.src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.scss (1)
2-2:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix
font-family-name-quotesviolation.Line 2 uses quotes around
Marianne, which violates the configured stylelint rule.Proposed fix
- font-family: 'Marianne', sans-serif; + font-family: Marianne, sans-serif;🤖 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 `@src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.scss` at line 2, The stylelint complaint is about quoted font-family names in RecordingPictureInPicturePanel.scss; update the font-family declaration used in that stylesheet (the rule setting 'Marianne') to remove the quotes so it reads an unquoted font family followed by the fallback (i.e., use Marianne, sans-serif instead of 'Marianne', sans-serif) to satisfy the font-family-name-quotes rule.Source: Linters/SAST tools
🤖 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 `@src/frontend/src/features/recordings/recorder/RecorderManager.ts`:
- Around line 387-389: When stopping recording, ensure the tab capture is
released in the mediaRecorder stop-completion path by invoking the same cleanup
used in start/dispose: call this.detachSource('tab', true) and
this.tabAudioInputManager.stopActiveStream() when mediaRecorder.stop() finishes
(e.g. in the mediaRecorder.onstop handler or immediately after awaiting the stop
promise). Add these calls alongside the existing stop-completion logic so
sourceStreams no longer retains 'tab' and TabAudioInputManager.activeStream is
cleared.
In `@src/frontend/src/features/recordings/recorder/TabAudioInputManager.ts`:
- Around line 13-15: isSupported currently only checks for
navigator.mediaDevices?.getDisplayMedia but that can return streams without
audio; change the support gating to reflect actual audio-track availability by
(a) making TabAudioInputManager.isSupported() return a promise/async boolean
that attempts a silent getDisplayMedia({audio:true,video:true}) or otherwise
queries requestStream() and resolves true only if the returned MediaStream has
stream.getAudioTracks().length > 0, (b) update
RecorderManager.supportsTabAudioCapture() to await the new async isSupported or
use a cached flag, and (c) on requestStream() failure with "No tab audio source
was selected." downgrade the cached support flag so subsequent UI
(useRecordingController/RecordComponent) hides/disables the tab-audio control
until a successful capture occurs; reference TabAudioInputManager.isSupported,
TabAudioInputManager.requestStream, and RecorderManager.supportsTabAudioCapture
when implementing this behavior.
In `@src/frontend/src/locales/fr-FR/record.json`:
- Line 29: The translation string keyed by "activeLabel" is terse; update its
value from "Audio onglet : {{label}}" to a more natural French phrasing such as
"Audio de l'onglet : {{label}}". Locate the "activeLabel" entry in
src/frontend/src/locales/fr-FR/record.json and replace the string while
preserving the {{label}} interpolation token exactly.
---
Duplicate comments:
In
`@src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.scss`:
- Line 2: The stylelint complaint is about quoted font-family names in
RecordingPictureInPicturePanel.scss; update the font-family declaration used in
that stylesheet (the rule setting 'Marianne') to remove the quotes so it reads
an unquoted font family followed by the fallback (i.e., use Marianne, sans-serif
instead of 'Marianne', sans-serif) to satisfy the font-family-name-quotes rule.
In
`@src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.tsx`:
- Around line 88-95: The PiP tab-audio action is gated on tabAudioLabel which
hides the control when tab audio is supported but not active; update the
rendering in RecordingPictureInPicturePanel to base visibility only on
tabAudioSupported (remove the tabAudioLabel check) so the action/button always
appears when supported, but keep using tabAudioLabel optionally for the <strong>
text and its title (rendering a fallback or empty title if label is falsy);
ensure onTabAudioAction and tabAudioButtonLabel remain used as-is.
🪄 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: ASSERTIVE
Plan: Pro
Run ID: 516db41c-3537-4314-b8c6-32b35bf6ec6d
📒 Files selected for processing (11)
.gitignoresrc/frontend/src/features/recordings/components/RecordComponent.scsssrc/frontend/src/features/recordings/components/RecordComponent.tsxsrc/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.scsssrc/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.tsxsrc/frontend/src/features/recordings/hooks/useDocumentPictureInPicture.tsxsrc/frontend/src/features/recordings/hooks/useRecordingController.tssrc/frontend/src/features/recordings/recorder/RecorderManager.tssrc/frontend/src/features/recordings/recorder/TabAudioInputManager.tssrc/frontend/src/locales/en-US/record.jsonsrc/frontend/src/locales/fr-FR/record.json
| public isSupported() { | ||
| return !!navigator.mediaDevices?.getDisplayMedia | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Is checking navigator.mediaDevices.getDisplayMedia alone a reliable feature detect for browser tab-audio capture? Which browsers or capture surfaces expose getDisplayMedia but still return no audio tracks for tab capture?
💡 Result:
Checking navigator.mediaDevices.getDisplayMedia alone is not a reliable feature detection method for tab-audio capture [1][2]. While the presence of the method indicates support for screen sharing, it does not guarantee that the browser supports audio capture or will actually return an audio track when requested [3][4][2]. Under the W3C specification, browsers are permitted to return a video track without an audio track, even if audio is requested, and the user agent is not required to provide audio support [4]. Consequently, relying solely on the existence of the API will lead to false positives [2]. Browsers that expose getDisplayMedia but do not return audio tracks for tab capture include: 1. Mozilla Firefox: Firefox supports getDisplayMedia for video capture but ignores the audio constraint [2][5][6]. It will not return a tab audio track [2][6]. 2. Apple Safari: Safari (on macOS) supports screen capture but similarly ignores the audio constraint, resulting in no audio tracks being returned [2][6]. 3. Mobile Browsers: Mobile browsers (e.g., Safari on iOS, Chrome on Android) generally do not support getDisplayMedia for screen or audio capture, and on some platforms/versions, they may have historically exposed the method while always failing with an error [2][5][7][8]. To handle this, you should not attempt complex feature detection. Because browsers are designed to silently ignore unknown constraints in getDisplayMedia, you can safely pass audio-related constraints (such as { audio: true }) without first performing feature detection [1][6]. If a browser does not support audio capture, it will simply return a stream containing only a video track [4][9]. Your application logic should verify the existence of an audio track in the returned MediaStream (e.g., by checking stream.getAudioTracks.length > 0) rather than attempting to predict support before the capture request [3][2][9].
Citations:
- 1: https://sendrec.eu/blog/how-we-added-system-audio-to-screen-recording/
- 2: https://livesuggest.ai/blog/tab-audio-sharing-explained/
- 3: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia
- 4: https://w3c.github.io/mediacapture-screen-share/
- 5: https://caniuse.com/mdn-api_mediadevices_getdisplaymedia_audio_capture_support
- 6: https://dev.to/alexneamtu/how-we-added-system-audio-to-screen-recording-54kg
- 7: https://cobaltcapture.com/reference/screen-capture-browser-support
- 8: https://docs.w3cub.com/dom/mediadevices/getdisplaymedia.html
- 9: https://developer.mozilla.org/en-US/docs/Web/API/Screen%5FCapture%5FAPI/Using%5FScreen%5FCapture
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the full implementation of TabAudioInputManager to confirm isSupported/requestStream behavior
FILE="src/frontend/src/features/recordings/recorder/TabAudioInputManager.ts"
if [ -f "$FILE" ]; then
echo "===== $FILE (head) ====="
head -n 220 "$FILE" | nl -ba | sed -n '1,220p'
else
echo "Missing file: $FILE"
fi
# Confirm how supportsTabAudioCapture() maps into tabAudioSupported
HOOK="src/frontend/src/features/recordings/hooks/useRecordingController.ts"
if [ -f "$HOOK" ]; then
echo "===== $HOOK (relevant excerpt) ====="
# Print around the referenced line range if it exists
nl -ba "$HOOK" | sed -n '150,260p'
else
echo "Missing file: $HOOK"
fi
# Confirm where RecordComponent uses tabAudioSupported to show the UI action
COMP="src/frontend/src/features/recordings/components/RecordComponent.tsx"
if [ -f "$COMP" ]; then
echo "===== $COMP (relevant excerpt) ====="
nl -ba "$COMP" | sed -n '120,260p'
else
echo "Missing file: $COMP"
fi
# Search for the specific runtime error string
rg -n "No tab audio source was selected" -S src/frontend/src/features/recordings || true
# Search for supportsTabAudioCapture usage
rg -n "supportsTabAudioCapture" -S src/frontend/src/features/recordings || trueRepository: suitenumerique/dictaphone
Length of output: 219
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/frontend/src/features/recordings/recorder/TabAudioInputManager.ts"
if [ -f "$FILE" ]; then
echo "===== $FILE (first 220 lines) ====="
head -n 220 "$FILE" | cat -n | sed -n '1,220p'
else
echo "Missing file: $FILE"
fi
HOOK="src/frontend/src/features/recordings/hooks/useRecordingController.ts"
if [ -f "$HOOK" ]; then
echo "===== $HOOK (lines 150-260) ====="
sed -n '150,260p' "$HOOK" | cat -n
else
echo "Missing file: $HOOK"
fi
COMP="src/frontend/src/features/recordings/components/RecordComponent.tsx"
if [ -f "$COMP" ]; then
echo "===== $COMP (lines 120-260) ====="
sed -n '120,260p' "$COMP" | cat -n
else
echo "Missing file: $COMP"
fi
echo "===== Error string search ====="
rg -n "No tab audio source was selected" -S src/frontend/src/features/recordings || true
echo "===== supportsTabAudioCapture usage search ====="
rg -n "supportsTabAudioCapture" -S src/frontend/src/features/recordings || trueRepository: suitenumerique/dictaphone
Length of output: 12185
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect RecorderManager and the tab-audio control flow
FILE="src/frontend/src/features/recordings/recorder/RecorderManager.ts"
if [ -f "$FILE" ]; then
echo "===== $FILE (relevant excerpt) ====="
# Print around supportsTabAudioCapture and where requestStream is used
sed -n '1,220p' "$FILE" | cat -n
else
echo "Missing file: $FILE"
fi
HOOK="src/frontend/src/features/recordings/hooks/useRecordingController.ts"
if [ -f "$HOOK" ]; then
echo "===== $HOOK (tab-audio related excerpt) ====="
# Show around the matches we saw (enable/disable paths)
rg -n "enableTabAudioCapture|disableTabAudioCapture|tabAudioRequested|requestStream\\(" "$HOOK" || true
# show larger window around the tab-audio request logic
sed -n '360,580p' "$HOOK" | cat -n
else
echo "Missing file: $HOOK"
fi
# Find where RecordingPictureInPicturePanel uses tabAudioSupported
PIP="src/frontend/src/features/recordings/components/RecordingPictureInPicturePanel.tsx"
if [ -f "$PIP" ]; then
echo "===== $PIP (tab-audio props usage) ====="
rg -n "tabAudioSupported|onTabAudioAction|tabAudioButtonLabel" "$PIP" || true
sed -n '1,240p' "$PIP" | cat -n
else
echo "Missing file: $PIP"
fiRepository: suitenumerique/dictaphone
Length of output: 21227
🏁 Script executed:
#!/bin/bash
set -euo pipefail
HOOK="src/frontend/src/features/recordings/recorder/RecorderManager.ts"
if [ -f "$HOOK" ]; then
rg -n "enableTabAudioCapture|disableTabAudioCapture|requestStream\\(|getDisplayMedia\\(|tabAudioLabel" "$HOOK" || true
echo "----- enable/disable excerpt (approx. 220-420) -----"
sed -n '220,520p' "$HOOK" | cat -n
fi
# Also check if enableTabAudioCapture sets tabAudioLabel
rg -n "tabAudioLabel\\s*=" src/frontend/src/features/recordings/recorder/RecorderManager.ts || trueRepository: suitenumerique/dictaphone
Length of output: 10769
🏁 Script executed:
#!/bin/bash
set -euo pipefail
HOOK="src/frontend/src/features/recordings/hooks/useRecordingController.ts"
rg -n "tabAudioLabel|getTabAudioLabel|tabAudioActive|notifyTabAudioState|onTabAudioStateChange" "$HOOK" || true
# Also inspect where tabAudioLabel is set in the hook's state initialization
rg -n "tabAudioLabel" "$HOOK" || trueRepository: suitenumerique/dictaphone
Length of output: 424
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "tabAudioSupported|tabAudioLabel|tabAudioButtonLabel|onTabAudioAction" src/frontend/src/features/recordings/components src/frontend/src/features/recordings/hooks src/frontend/src/features/recordings -SRepository: suitenumerique/dictaphone
Length of output: 7046
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/frontend/src/features/recordings/components/RecordComponent.tsx"
if [ -f "$FILE" ]; then
echo "===== $FILE (lines 140-240) ====="
sed -n '140,240p' "$FILE" | cat -n
echo "===== $FILE (lines 260-360) ====="
sed -n '260,380p' "$FILE" | cat -n
else
echo "Missing file: $FILE"
fiRepository: suitenumerique/dictaphone
Length of output: 8927
Fix tab-audio “supported” gating to reflect real audio-track availability.
TabAudioInputManager.isSupported() only checks for navigator.mediaDevices?.getDisplayMedia (lines 13-15), but requestStream() throws No tab audio source was selected. when the captured MediaStream contains no audio tracks (lines 38-41). RecorderManager.supportsTabAudioCapture() directly proxies that predicate (line 121-123), useRecordingController maps it to tabAudioSupported, and RecordComponent shows the tab-audio enable button whenever tabAudioSupported is true—so users can be offered a control that will fail at runtime and surface recordingError.
Update the support signal to be based on actual successful capture (e.g., hide/disable the button until a capture yields stream.getAudioTracks().length > 0, and/or downgrade support after the “No tab audio source…” failure), rather than assuming getDisplayMedia implies usable tab audio.
🤖 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 `@src/frontend/src/features/recordings/recorder/TabAudioInputManager.ts` around
lines 13 - 15, isSupported currently only checks for
navigator.mediaDevices?.getDisplayMedia but that can return streams without
audio; change the support gating to reflect actual audio-track availability by
(a) making TabAudioInputManager.isSupported() return a promise/async boolean
that attempts a silent getDisplayMedia({audio:true,video:true}) or otherwise
queries requestStream() and resolves true only if the returned MediaStream has
stream.getAudioTracks().length > 0, (b) update
RecorderManager.supportsTabAudioCapture() to await the new async isSupported or
use a cached flag, and (c) on requestStream() failure with "No tab audio source
was selected." downgrade the cached support flag so subsequent UI
(useRecordingController/RecordComponent) hides/disables the tab-audio control
until a successful capture occurs; reference TabAudioInputManager.isSupported,
TabAudioInputManager.requestStream, and RecorderManager.supportsTabAudioCapture
when implementing this behavior.
| "tabAudio": { | ||
| "add": "Ajouter l'audio d'un onglet", | ||
| "remove": "Retirer l'audio d'un onglet", | ||
| "activeLabel": "Audio onglet : {{label}}" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Consider more grammatically complete French phrasing.
"Audio onglet : {{label}}" is understandable but slightly terse. Consider "Audio de l'onglet : {{label}}" for more natural French.
✨ Optional refinement
- "activeLabel": "Audio onglet : {{label}}"
+ "activeLabel": "Audio de l'onglet : {{label}}"📝 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.
| "activeLabel": "Audio onglet : {{label}}" | |
| "activeLabel": "Audio de l'onglet : {{label}}" |
🤖 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 `@src/frontend/src/locales/fr-FR/record.json` at line 29, The translation
string keyed by "activeLabel" is terse; update its value from "Audio onglet :
{{label}}" to a more natural French phrasing such as "Audio de l'onglet :
{{label}}". Locate the "activeLabel" entry in
src/frontend/src/locales/fr-FR/record.json and replace the string while
preserving the {{label}} interpolation token exactly.
c91ebea to
feeec0d
Compare
Purpose
When a user starts a recording and switches to another tab, they lose sight of the recording interface — no way to see the timer, the sound level, or pause/stop.
This PR implements the Document Picture-in-Picture API (Chrome 116+) to show a small floating window that stays visible on top of all tabs while recording. Based on @FloChehab's PoC
feat/pip-recorder.Proposal
Add a floating PiP window with real-time sound level meter, timer, mic source selector, and pause/stop buttons
Add tab audio capture support via
getDisplayMedia(mic + tab audio mixed via AudioContext)PiP button is hidden on unsupported browsers (Firefox, Safari) — no regression
Fix pre-existing crash on recordings page (
useModals must be used within a ModalProvider)Fix dev-mode 403 on language switch (add
localhost:3001toCSRF_TRUSTED_ORIGINS)Open the app in Chrome 116+, start a recording → "Open mini recorder" button appears
Click it → PiP window opens and stays visible when switching tabs
Timer increments, signal meter reacts to mic
Pause/resume and stop work from the PiP window
Stop redirects to the recordings list
Firefox/Safari: PiP button is absent (no regression)
Closes #96
Summary by CodeRabbit
Release Notes