Feat/pro recording hud#483
Conversation
📝 WalkthroughWalkthroughUpdates to recording UX and window protections: tray menu now offers "Show Recording Controls" and restores HUD when recording stops; HUD overlay gets platform content-protection; countdown overlay gains an SVG ring + animations; HUD bar UI and styles change while recording; button prop type refactor and a small hook cleanup. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d1658ff975
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| </div> | ||
| </button> | ||
| </div> | ||
| {!recording && ( |
There was a problem hiding this comment.
Keep HUD window controls available while recording on Linux
createHudOverlayWindow enables content protection only on macOS/Windows (electron/windows.ts), so Linux captures can still include the HUD. This new !recording gate removes the sidebar during recording, which also removes the sendHudOverlayHide minimize action; on Linux that leaves users without an in-app way to hide the HUD, so the overlay can be permanently burned into full-screen recordings. Please keep a hide/minimize path available when content protection is unsupported.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/launch/LaunchWindow.tsx (1)
550-580:⚠️ Potential issue | 🟠 MajorGive the stop/record button a real accessible name.
In recording mode the button’s visible content is a pulsing dot, stop icon, and timer, so assistive tech may announce only the time. Add a stable
aria-label/titlefor start vs stop.Proposed fix
<button className={`flex items-center justify-center rounded-full p-2 transition-[min-width,background-color] duration-150 ${recording ? "min-w-[78px]" : "min-w-[36px]"} ${styles.electronNoDrag} ${ recording ? paused ? "bg-amber-500/10 hover:bg-amber-500/15" : "bg-red-500/12 hover:bg-red-500/16" : "bg-white/5 hover:bg-white/[0.08]" }`} onClick={toggleRecording} disabled={!hasSelectedSource && !recording} + aria-label={recording ? t("tooltips.stopRecording") : t("tooltips.startRecording")} + title={recording ? t("tooltips.stopRecording") : t("tooltips.startRecording")} style={{ flex: "0 0 auto" }} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/launch/LaunchWindow.tsx` around lines 550 - 580, The record/stop button in LaunchWindow.tsx lacks an accessible name; update the button element (the one using toggleRecording, recording, paused, hasSelectedSource) to include a conditional aria-label and title that reflect state — e.g. when recording is false use "Start recording" (or "Start recording — no source selected" if !hasSelectedSource), and when recording is true use "Stop recording" (optionally include paused state like "Resume recording" vs "Pause recording" if you want finer granularity). Ensure both aria-label and title are set consistently so assistive tech and hover tooltips announce the correct action.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@electron/main.ts`:
- Around line 226-231: The tray menu label currently uses mainT("common",
"actions.open") with a generic fallback "Show Controls" which may render as
“Open” and confuse users during recording; change the label to use a
recording-specific i18n key (e.g., mainT("tray",
"actions.showRecordingControls") or similar) or a clear literal fallback like
"Show Recording Controls", updating the entry where label is set and keeping the
click handler that calls showMainWindow(); ensure the new i18n key exists in
translations or add it alongside the other tray strings.
In `@src/components/launch/LaunchWindow.module.css`:
- Around line 85-92: The keyframe name recordingPulse violates the stylelint
kebab-case rule; rename the `@keyframes` identifier to recording-pulse and update
the .recordingPulse rule's animation property to use "recording-pulse 1.5s
ease-in-out infinite" so both the `@keyframes` block and the .recordingPulse
animation reference the new kebab-case name.
---
Outside diff comments:
In `@src/components/launch/LaunchWindow.tsx`:
- Around line 550-580: The record/stop button in LaunchWindow.tsx lacks an
accessible name; update the button element (the one using toggleRecording,
recording, paused, hasSelectedSource) to include a conditional aria-label and
title that reflect state — e.g. when recording is false use "Start recording"
(or "Start recording — no source selected" if !hasSelectedSource), and when
recording is true use "Stop recording" (optionally include paused state like
"Resume recording" vs "Pause recording" if you want finer granularity). Ensure
both aria-label and title are set consistently so assistive tech and hover
tooltips announce the correct action.
🪄 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: bd917584-01df-4994-bf1f-ff4ce8c0f630
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
electron/main.tselectron/windows.tssrc/components/launch/CountdownOverlay.tsxsrc/components/launch/LaunchWindow.module.csssrc/components/launch/LaunchWindow.tsxsrc/components/ui/button.tsx
|
Hi, thanks for reviewing! Could you please approve the workflows so CI checks can run? |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/launch/LaunchWindow.tsx (1)
555-586:⚠️ Potential issue | 🟠 MajorAdd an accessible name to the primary record/stop button.
Right now screen readers may only get SVG noise or the elapsed time, not “start recording” / “stop recording”. Lowkey important for the HUD’s “always accessible” goal.
♿ Proposed fix
{/* Record/Stop group */} <button + type="button" + aria-label={recording ? t("tooltips.stopRecording") : t("tooltips.startRecording")} + title={recording ? t("tooltips.stopRecording") : t("tooltips.startRecording")} className={`flex items-center justify-center rounded-full p-2 transition-[min-width,background-color] duration-150 ${recording ? "min-w-[78px]" : "min-w-[36px]"} ${styles.electronNoDrag} ${ recording ? pausedIf those tooltip keys don’t exist yet, add localized strings alongside the other launch tooltip labels.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/launch/LaunchWindow.tsx` around lines 555 - 586, Add an accessible name to the primary record/stop button so screen readers get “start recording” / “stop recording” instead of SVG/noise: update the button in LaunchWindow (the element using onClick={toggleRecording}, recording, paused and hasSelectedSource) to include an appropriate aria-label that switches based on recording/paused state (e.g. use localized tooltip keys next to the other launch tooltip labels); ensure the aria-label reflects the action (“Start recording” vs “Stop recording” or “Resume/ Pause” as needed) and falls back to a default string if localization keys are missing.src/hooks/useScreenRecorder.ts (1)
406-448:⚠️ Potential issue | 🔴 CriticalMove
safeHideCountdownOverlayabove the effect — this currently hits the TDZ.Line 448 reads
safeHideCountdownOverlayin the dependency array before theconstis initialized on line 475, so the component throws during render. kinda cursed 2am bug.🐛 Proposed fix
+ const safeHideCountdownOverlay = useCallback(async (runId: number) => { + try { + await window.electronAPI.hideCountdownOverlay(runId); + } catch (error) { + console.warn("Failed to hide countdown overlay:", error); + } + }, []); + useEffect(() => { let cleanup: (() => void) | undefined; if (window.electronAPI?.onStopRecordingFromTray) { @@ teardownMedia(); }; }, [teardownMedia, safeHideCountdownOverlay]); @@ - const safeHideCountdownOverlay = useCallback(async (runId: number) => { - try { - await window.electronAPI.hideCountdownOverlay(runId); - } catch (error) { - console.warn("Failed to hide countdown overlay:", error); - } - }, []); - const isCountdownRunActive = (runId?: number) =>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useScreenRecorder.ts` around lines 406 - 448, The effect references safeHideCountdownOverlay in its dependency array before that function/constant is declared, causing a TDZ error; move the declaration/definition of safeHideCountdownOverlay (or wrap it in useCallback) so it appears above the useEffect that uses it, then update the effect to depend on that already-initialized safeHideCountdownOverlay; ensure references to countdownRunId, stopRecording.current, allowAutoFinalize.current, restarting.current, discardRecordingId.current, screenRecorder, webcamRecorder and teardownMedia remain valid after reordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/launch/LaunchWindow.tsx`:
- Around line 634-709: Summary: The quit button remains visible during active
Linux recordings because the outer render condition is wrong; this exposes
app.quit() via sendHudOverlayClose. Fix: prevent rendering or disable the close
button when platform === "linux" && recording by either updating the outer
condition (change (!recording || platform === "linux") to (!recording ||
platform !== "linux")) or, more surgically, wrap the button that calls
sendHudOverlayClose (the button with title t("tooltips.closeApp")) in a
conditional that skips rendering when platform === "linux" && recording (or
replace it with a disabled/hidden variant); keep the minimize/hide HUD button
unchanged. Ensure references: sendHudOverlayClose, recording, platform, and the
close button with title t("tooltips.closeApp").
---
Outside diff comments:
In `@src/components/launch/LaunchWindow.tsx`:
- Around line 555-586: Add an accessible name to the primary record/stop button
so screen readers get “start recording” / “stop recording” instead of SVG/noise:
update the button in LaunchWindow (the element using onClick={toggleRecording},
recording, paused and hasSelectedSource) to include an appropriate aria-label
that switches based on recording/paused state (e.g. use localized tooltip keys
next to the other launch tooltip labels); ensure the aria-label reflects the
action (“Start recording” vs “Stop recording” or “Resume/ Pause” as needed) and
falls back to a default string if localization keys are missing.
In `@src/hooks/useScreenRecorder.ts`:
- Around line 406-448: The effect references safeHideCountdownOverlay in its
dependency array before that function/constant is declared, causing a TDZ error;
move the declaration/definition of safeHideCountdownOverlay (or wrap it in
useCallback) so it appears above the useEffect that uses it, then update the
effect to depend on that already-initialized safeHideCountdownOverlay; ensure
references to countdownRunId, stopRecording.current, allowAutoFinalize.current,
restarting.current, discardRecordingId.current, screenRecorder, webcamRecorder
and teardownMedia remain valid after reordering.
🪄 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: 7f04bb70-72d3-4910-99ca-12f4707c2722
📒 Files selected for processing (4)
electron/main.tssrc/components/launch/LaunchWindow.module.csssrc/components/launch/LaunchWindow.tsxsrc/hooks/useScreenRecorder.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/launch/LaunchWindow.module.css
| {(!recording || platform === "linux") && ( | ||
| <div className={`${hudSidebarClasses} ${styles.electronNoDrag}`}> | ||
| <div className={`${styles.languageMenuContainer} ${styles.electronNoDrag}`}> | ||
| <button | ||
| ref={languageTriggerRef} | ||
| type="button" | ||
| aria-label={t("language")} | ||
| aria-expanded={isLanguageMenuOpen} | ||
| aria-haspopup="menu" | ||
| onClick={() => setIsLanguageMenuOpen((open) => !open)} | ||
| className={`h-8 w-8 rounded-lg border border-white/10 bg-white/5 text-white/85 shadow-none transition-colors hover:bg-white/10 ${styles.electronNoDrag}`} | ||
| > | ||
| <div className="flex w-full items-center justify-center"> | ||
| <Languages size={13} className="text-white/75" /> | ||
| </div> | ||
| </button> | ||
| </div> | ||
|
|
||
| {isLanguageMenuOpen | ||
| ? createPortal( | ||
| <div | ||
| ref={languageMenuPanelRef} | ||
| role="menu" | ||
| className={`${styles.languageMenuPanel} ${styles.languageMenuScroll} ${styles.electronNoDrag}`} | ||
| style={ | ||
| { | ||
| WebkitAppRegion: "no-drag", | ||
| pointerEvents: "auto", | ||
| right: `${languageMenuStyle.right}px`, | ||
| top: `${languageMenuStyle.top}px`, | ||
| maxHeight: `${languageMenuStyle.maxHeight}px`, | ||
| } as React.CSSProperties | ||
| } | ||
| onPointerDown={(event) => event.stopPropagation()} | ||
| > | ||
| {availableLocales.map((loc) => ( | ||
| <button | ||
| key={loc} | ||
| type="button" | ||
| role="menuitemradio" | ||
| aria-checked={loc === locale} | ||
| onClick={() => { | ||
| setLocale(loc); | ||
| resolveSystemLocaleSuggestion(); | ||
| setIsLanguageMenuOpen(false); | ||
| }} | ||
| className={`${styles.languageMenuItem} ${loc === locale ? styles.languageMenuItemActive : ""}`} | ||
| > | ||
| <span className="truncate">{getLocaleName(loc)}</span> | ||
| {loc === locale ? <Check size={11} className="text-white/85" /> : null} | ||
| </button> | ||
| ))} | ||
| </div>, | ||
| document.body, | ||
| ) | ||
| : null} | ||
| {isLanguageMenuOpen | ||
| ? createPortal( | ||
| <div | ||
| ref={languageMenuPanelRef} | ||
| role="menu" | ||
| className={`${styles.languageMenuPanel} ${styles.languageMenuScroll} ${styles.electronNoDrag}`} | ||
| style={ | ||
| { | ||
| WebkitAppRegion: "no-drag", | ||
| pointerEvents: "auto", | ||
| right: `${languageMenuStyle.right}px`, | ||
| top: `${languageMenuStyle.top}px`, | ||
| maxHeight: `${languageMenuStyle.maxHeight}px`, | ||
| } as React.CSSProperties | ||
| } | ||
| onPointerDown={(event) => event.stopPropagation()} | ||
| > | ||
| {availableLocales.map((loc) => ( | ||
| <button | ||
| key={loc} | ||
| type="button" | ||
| role="menuitemradio" | ||
| aria-checked={loc === locale} | ||
| onClick={() => { | ||
| setLocale(loc); | ||
| resolveSystemLocaleSuggestion(); | ||
| setIsLanguageMenuOpen(false); | ||
| }} | ||
| className={`${styles.languageMenuItem} ${loc === locale ? styles.languageMenuItemActive : ""}`} | ||
| > | ||
| <span className="truncate">{getLocaleName(loc)}</span> | ||
| {loc === locale ? <Check size={11} className="text-white/85" /> : null} | ||
| </button> | ||
| ))} | ||
| </div>, | ||
| document.body, | ||
| ) | ||
| : null} | ||
|
|
||
| {/* Window controls */} | ||
| <div className="flex items-center gap-0.5"> | ||
| <button | ||
| className={windowBtnClasses} | ||
| title={t("tooltips.hideHUD")} | ||
| onClick={sendHudOverlayHide} | ||
| > | ||
| {getIcon("minimize", "text-white")} | ||
| </button> | ||
| <button | ||
| className={windowBtnClasses} | ||
| title={t("tooltips.closeApp")} | ||
| onClick={sendHudOverlayClose} | ||
| > | ||
| {getIcon("close", "text-white")} | ||
| </button> | ||
| {/* Window controls */} | ||
| <div className="flex items-center gap-0.5"> | ||
| <button | ||
| className={windowBtnClasses} | ||
| title={t("tooltips.hideHUD")} | ||
| onClick={sendHudOverlayHide} | ||
| > | ||
| {getIcon("minimize", "text-white")} | ||
| </button> | ||
| <button | ||
| className={windowBtnClasses} | ||
| title={t("tooltips.closeApp")} | ||
| onClick={sendHudOverlayClose} | ||
| > | ||
| {getIcon("close", "text-white")} | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
Don’t expose the quit button during active Linux recordings.
Because sendHudOverlayClose maps to app.quit() in electron/main.ts, Line 700 keeps a data-loss path available while recording on Linux. Keeping “hide HUD” is useful; keeping “quit app” is lowkey risky.
🛡️ Proposed fix
{/* Window controls */}
<div className="flex items-center gap-0.5">
<button
className={windowBtnClasses}
title={t("tooltips.hideHUD")}
onClick={sendHudOverlayHide}
>
{getIcon("minimize", "text-white")}
</button>
- <button
- className={windowBtnClasses}
- title={t("tooltips.closeApp")}
- onClick={sendHudOverlayClose}
- >
- {getIcon("close", "text-white")}
- </button>
+ {!recording && (
+ <button
+ className={windowBtnClasses}
+ title={t("tooltips.closeApp")}
+ onClick={sendHudOverlayClose}
+ >
+ {getIcon("close", "text-white")}
+ </button>
+ )}
</div>📝 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.
| {(!recording || platform === "linux") && ( | |
| <div className={`${hudSidebarClasses} ${styles.electronNoDrag}`}> | |
| <div className={`${styles.languageMenuContainer} ${styles.electronNoDrag}`}> | |
| <button | |
| ref={languageTriggerRef} | |
| type="button" | |
| aria-label={t("language")} | |
| aria-expanded={isLanguageMenuOpen} | |
| aria-haspopup="menu" | |
| onClick={() => setIsLanguageMenuOpen((open) => !open)} | |
| className={`h-8 w-8 rounded-lg border border-white/10 bg-white/5 text-white/85 shadow-none transition-colors hover:bg-white/10 ${styles.electronNoDrag}`} | |
| > | |
| <div className="flex w-full items-center justify-center"> | |
| <Languages size={13} className="text-white/75" /> | |
| </div> | |
| </button> | |
| </div> | |
| {isLanguageMenuOpen | |
| ? createPortal( | |
| <div | |
| ref={languageMenuPanelRef} | |
| role="menu" | |
| className={`${styles.languageMenuPanel} ${styles.languageMenuScroll} ${styles.electronNoDrag}`} | |
| style={ | |
| { | |
| WebkitAppRegion: "no-drag", | |
| pointerEvents: "auto", | |
| right: `${languageMenuStyle.right}px`, | |
| top: `${languageMenuStyle.top}px`, | |
| maxHeight: `${languageMenuStyle.maxHeight}px`, | |
| } as React.CSSProperties | |
| } | |
| onPointerDown={(event) => event.stopPropagation()} | |
| > | |
| {availableLocales.map((loc) => ( | |
| <button | |
| key={loc} | |
| type="button" | |
| role="menuitemradio" | |
| aria-checked={loc === locale} | |
| onClick={() => { | |
| setLocale(loc); | |
| resolveSystemLocaleSuggestion(); | |
| setIsLanguageMenuOpen(false); | |
| }} | |
| className={`${styles.languageMenuItem} ${loc === locale ? styles.languageMenuItemActive : ""}`} | |
| > | |
| <span className="truncate">{getLocaleName(loc)}</span> | |
| {loc === locale ? <Check size={11} className="text-white/85" /> : null} | |
| </button> | |
| ))} | |
| </div>, | |
| document.body, | |
| ) | |
| : null} | |
| {isLanguageMenuOpen | |
| ? createPortal( | |
| <div | |
| ref={languageMenuPanelRef} | |
| role="menu" | |
| className={`${styles.languageMenuPanel} ${styles.languageMenuScroll} ${styles.electronNoDrag}`} | |
| style={ | |
| { | |
| WebkitAppRegion: "no-drag", | |
| pointerEvents: "auto", | |
| right: `${languageMenuStyle.right}px`, | |
| top: `${languageMenuStyle.top}px`, | |
| maxHeight: `${languageMenuStyle.maxHeight}px`, | |
| } as React.CSSProperties | |
| } | |
| onPointerDown={(event) => event.stopPropagation()} | |
| > | |
| {availableLocales.map((loc) => ( | |
| <button | |
| key={loc} | |
| type="button" | |
| role="menuitemradio" | |
| aria-checked={loc === locale} | |
| onClick={() => { | |
| setLocale(loc); | |
| resolveSystemLocaleSuggestion(); | |
| setIsLanguageMenuOpen(false); | |
| }} | |
| className={`${styles.languageMenuItem} ${loc === locale ? styles.languageMenuItemActive : ""}`} | |
| > | |
| <span className="truncate">{getLocaleName(loc)}</span> | |
| {loc === locale ? <Check size={11} className="text-white/85" /> : null} | |
| </button> | |
| ))} | |
| </div>, | |
| document.body, | |
| ) | |
| : null} | |
| {/* Window controls */} | |
| <div className="flex items-center gap-0.5"> | |
| <button | |
| className={windowBtnClasses} | |
| title={t("tooltips.hideHUD")} | |
| onClick={sendHudOverlayHide} | |
| > | |
| {getIcon("minimize", "text-white")} | |
| </button> | |
| <button | |
| className={windowBtnClasses} | |
| title={t("tooltips.closeApp")} | |
| onClick={sendHudOverlayClose} | |
| > | |
| {getIcon("close", "text-white")} | |
| </button> | |
| {/* Window controls */} | |
| <div className="flex items-center gap-0.5"> | |
| <button | |
| className={windowBtnClasses} | |
| title={t("tooltips.hideHUD")} | |
| onClick={sendHudOverlayHide} | |
| > | |
| {getIcon("minimize", "text-white")} | |
| </button> | |
| <button | |
| className={windowBtnClasses} | |
| title={t("tooltips.closeApp")} | |
| onClick={sendHudOverlayClose} | |
| > | |
| {getIcon("close", "text-white")} | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| {(!recording || platform === "linux") && ( | |
| <div className={`${hudSidebarClasses} ${styles.electronNoDrag}`}> | |
| <div className={`${styles.languageMenuContainer} ${styles.electronNoDrag}`}> | |
| <button | |
| ref={languageTriggerRef} | |
| type="button" | |
| aria-label={t("language")} | |
| aria-expanded={isLanguageMenuOpen} | |
| aria-haspopup="menu" | |
| onClick={() => setIsLanguageMenuOpen((open) => !open)} | |
| className={`h-8 w-8 rounded-lg border border-white/10 bg-white/5 text-white/85 shadow-none transition-colors hover:bg-white/10 ${styles.electronNoDrag}`} | |
| > | |
| <div className="flex w-full items-center justify-center"> | |
| <Languages size={13} className="text-white/75" /> | |
| </div> | |
| </button> | |
| </div> | |
| {isLanguageMenuOpen | |
| ? createPortal( | |
| <div | |
| ref={languageMenuPanelRef} | |
| role="menu" | |
| className={`${styles.languageMenuPanel} ${styles.languageMenuScroll} ${styles.electronNoDrag}`} | |
| style={ | |
| { | |
| WebkitAppRegion: "no-drag", | |
| pointerEvents: "auto", | |
| right: `${languageMenuStyle.right}px`, | |
| top: `${languageMenuStyle.top}px`, | |
| maxHeight: `${languageMenuStyle.maxHeight}px`, | |
| } as React.CSSProperties | |
| } | |
| onPointerDown={(event) => event.stopPropagation()} | |
| > | |
| {availableLocales.map((loc) => ( | |
| <button | |
| key={loc} | |
| type="button" | |
| role="menuitemradio" | |
| aria-checked={loc === locale} | |
| onClick={() => { | |
| setLocale(loc); | |
| resolveSystemLocaleSuggestion(); | |
| setIsLanguageMenuOpen(false); | |
| }} | |
| className={`${styles.languageMenuItem} ${loc === locale ? styles.languageMenuItemActive : ""}`} | |
| > | |
| <span className="truncate">{getLocaleName(loc)}</span> | |
| {loc === locale ? <Check size={11} className="text-white/85" /> : null} | |
| </button> | |
| ))} | |
| </div>, | |
| document.body, | |
| ) | |
| : null} | |
| {/* Window controls */} | |
| <div className="flex items-center gap-0.5"> | |
| <button | |
| className={windowBtnClasses} | |
| title={t("tooltips.hideHUD")} | |
| onClick={sendHudOverlayHide} | |
| > | |
| {getIcon("minimize", "text-white")} | |
| </button> | |
| {!recording && ( | |
| <button | |
| className={windowBtnClasses} | |
| title={t("tooltips.closeApp")} | |
| onClick={sendHudOverlayClose} | |
| > | |
| {getIcon("close", "text-white")} | |
| </button> | |
| )} | |
| </div> | |
| </div> | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/launch/LaunchWindow.tsx` around lines 634 - 709, Summary: The
quit button remains visible during active Linux recordings because the outer
render condition is wrong; this exposes app.quit() via sendHudOverlayClose. Fix:
prevent rendering or disable the close button when platform === "linux" &&
recording by either updating the outer condition (change (!recording || platform
=== "linux") to (!recording || platform !== "linux")) or, more surgically, wrap
the button that calls sendHudOverlayClose (the button with title
t("tooltips.closeApp")) in a conditional that skips rendering when platform ===
"linux" && recording (or replace it with a disabled/hidden variant); keep the
minimize/hide HUD button unchanged. Ensure references: sendHudOverlayClose,
recording, platform, and the close button with title t("tooltips.closeApp").
|
hey @siyadhkc , pls attach video and screenshots of the changes you have made |
Description
This PR implements a "High-End" Recording HUD experience. It ensures the controls remain accessible to the user during active capture while being completely invisible to the final recorded video through system-level content protection.
Motivation
Previously, the HUD would auto-hide to avoid appearing in the video. This made it difficult for users to access "Stop" and "Pause" buttons without using shortcuts. This update solves that UX friction while improving the aesthetic quality of the app.
Type of Change
Features Added
setContentProtection(true)in Electron (Windows/macOS) to exclude the HUD window from screen capture.Testing
Checklist
Summary by CodeRabbit
New Features
Improvements