feat(launch): add draggable floating webcam preview overlay#163
feat(launch): add draggable floating webcam preview overlay#163longmaba wants to merge 4 commits intowebadderall:mainfrom
Conversation
Show a live webcam preview as soon as webcam is enabled, keep it visible as a larger floating overlay, and allow drag repositioning across the HUD overlay area for expression monitoring while recording. Made-with: Cursor
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
Draggable Webcam Preview Styling src/components/launch/LaunchWindow.module.css |
Added .recordingWebcamPreview, .recordingWebcamPreview:active, and .recordingWebcamPreviewVideo to style a fixed, elevated, draggable preview container and its video (object-fit: cover, grab/grabbing cursors, rounded corner overflow handling). |
Recording Webcam Preview Logic & UI src/components/launch/LaunchWindow.tsx |
Added webcamPreviewOffset state, recordingWebcamPreviewRef, previewStreamRef, drag start tracking and pointer handlers to move the preview; replaced direct ref-based stream attachment with callback ref functions to attach same MediaStream to both preview and main nodes; introduced showRecordingWebcamPreview / shouldStreamWebcamPreview, reset offset when webcam disabled; adjusted HUD overlay expansion/height logic to account for floating preview; changed webcam dropdown preview to use callback ref; added conditional mirrored, muted preview element; refined HUD drag-end handling to consider actual dragging. |
Sequence Diagram(s)
sequenceDiagram
participant User as User (pointer)
participant Preview as RecordingWebcamPreview
participant Component as LaunchWindow
participant Stream as MediaStream
User->>Preview: pointerdown (start drag)
Preview->>Component: handleWebcamPreviewPointerDown (store start, set pointer capture)
loop dragging
User->>Preview: pointermove
Preview->>Component: handleWebcamPreviewPointerMove (compute delta)
Component->>Preview: update transform (apply webcamPreviewOffset)
end
User->>Preview: pointerup / pointercancel
Preview->>Component: handleWebcamPreviewPointerUp (release capture, finalize offset)
Component->>Stream: attachPreviewStreamToNode() (assign stream to preview & main video nodes)
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
🐇 I hop and nudge a tiny view,
A mirrored pane that follows you,
Drag my ear, then let me roam,
A fluffy frame to call your own,
Sniffing pixels, soft and true 🥕
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | 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 (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The title clearly and concisely describes the main feature addition: a draggable floating webcam preview overlay. It is specific, directly related to the changeset, and summarizes the primary change effectively. |
| Description check | ✅ Passed | The PR description follows the template well with complete sections: Description, Motivation, Type of Change, Related Issues, Screenshots, Testing Guide, and Checklist. All key sections are filled out appropriately with detailed testing steps and a screenshot included. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
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 @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/components/launch/LaunchWindow.tsx (1)
273-285: Consider adding optional boundary awareness to prevent dragging completely off-screen.The preview can currently be dragged entirely out of the viewport. While unbounded dragging provides maximum flexibility, users may accidentally lose the preview. Consider:
- Clamping to keep at least a portion visible
- Adding a "reset position" action
- Storing position in persisted state for convenience
This is optional since the PR testing guide indicates "repositioned freely" is expected behavior.
🤖 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 273 - 285, handleWebcamPreviewPointerMove currently updates position without bounds; clamp the computed new coordinates before calling setWebcamPreviewOffset by computing viewport and preview dimensions and limiting x/y so at least a portion remains on-screen, using webcamPreviewDragStartRef to derive originX/originY and the delta, add an optional reset action (e.g., resetWebcamPreviewPosition) to restore a default location, and persist offsets (e.g., via localStorage or a parent state setter) when dragging ends so position survives reloads; update references to handleWebcamPreviewPointerMove, webcamPreviewDragStartRef, and setWebcamPreviewOffset accordingly.
🤖 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 354-361: The cleanup reads webcamPreviewRef.current and
recordingWebcamPreviewRef.current directly which can be stale; capture their
current values (e.g., const initialWebcam = webcamPreviewRef.current and const
initialRecordingWebcam = recordingWebcamPreviewRef.current) and also capture
previewStream into a local variable at the start of the effect, then use those
locals inside the cleanup to pause and null srcObject and to stop tracks; update
the cleanup in the effect that defines these refs/previewStream accordingly to
reference the captured locals instead of the ref.current properties.
---
Nitpick comments:
In `@src/components/launch/LaunchWindow.tsx`:
- Around line 273-285: handleWebcamPreviewPointerMove currently updates position
without bounds; clamp the computed new coordinates before calling
setWebcamPreviewOffset by computing viewport and preview dimensions and limiting
x/y so at least a portion remains on-screen, using webcamPreviewDragStartRef to
derive originX/originY and the delta, add an optional reset action (e.g.,
resetWebcamPreviewPosition) to restore a default location, and persist offsets
(e.g., via localStorage or a parent state setter) when dragging ends so position
survives reloads; update references to handleWebcamPreviewPointerMove,
webcamPreviewDragStartRef, and setWebcamPreviewOffset accordingly.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6acd0aee-b568-4771-8e86-79c1bead0b4e
📒 Files selected for processing (2)
src/components/launch/LaunchWindow.module.csssrc/components/launch/LaunchWindow.tsx
|
Woow, that what i will doo, it means it basic functionality.. appreciate it |
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 (1)
src/components/launch/LaunchWindow.tsx (1)
303-346:⚠️ Potential issue | 🟡 MinorLate-mounted preview nodes miss the active stream.
initialWebcamandinitialRecordingWebcamare now used for the attach path as well as cleanup. If either<video>mounts after this effect starts—for example, reopening the webcam dropdown while the webcam stays enabled—the new node never getssrcObjectbecause the effect does not rerun on ref changes. Please keep the liveMediaStreamin a ref and reattach it from a callback ref or a small follow-up effect when either preview element mounts.🤖 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 303 - 346, The effect currently captures local variables initialWebcam/initialRecordingWebcam and assigns srcObject only once, so any <video> that mounts later won't receive the active MediaStream; fix by storing the live stream in a stable ref (e.g., previewStreamRef) inside startPreview (set previewStreamRef.current = stream), stop using the captured initial* vars for attach/cleanup, and add a small follow-up: either use callback refs on webcamPreviewRef/recordingWebcamPreviewRef or a useEffect that watches those ref.current values to reattach when they mount by setting videoElement.srcObject = previewStreamRef.current and calling play() with a catch; also update cleanup to stop tracks from previewStreamRef.current and clear previewStreamRef.current when unmounting or stopping.
🤖 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 287-297: The pointer-up handler handleWebcamPreviewPointerUp
currently clears drag state but doesn't restore click-through when the drag ends
outside the HUD, leaving the expanded transparent hudContent intercepting
clicks; update handleWebcamPreviewPointerUp (and the equivalent code around
lines 527-564) to call hudOverlaySetIgnoreMouse(true) when a drag finishes
(i.e., when webcamPreviewDragStartRef.current was set and isDraggingRef.current
is true) and ensure pointer capture is released and isDraggingRef/current are
cleared; also ensure onMouseLeave logic remains consistent by only skipping
hudOverlaySetIgnoreMouse while isDraggingRef is true, but restore ignore-mouse
on drag end so the HUD becomes click-through again.
---
Outside diff comments:
In `@src/components/launch/LaunchWindow.tsx`:
- Around line 303-346: The effect currently captures local variables
initialWebcam/initialRecordingWebcam and assigns srcObject only once, so any
<video> that mounts later won't receive the active MediaStream; fix by storing
the live stream in a stable ref (e.g., previewStreamRef) inside startPreview
(set previewStreamRef.current = stream), stop using the captured initial* vars
for attach/cleanup, and add a small follow-up: either use callback refs on
webcamPreviewRef/recordingWebcamPreviewRef or a useEffect that watches those
ref.current values to reattach when they mount by setting videoElement.srcObject
= previewStreamRef.current and calling play() with a catch; also update cleanup
to stop tracks from previewStreamRef.current and clear previewStreamRef.current
when unmounting or stopping.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 76394bc4-7d2d-4684-818d-c616ccc6de0f
📒 Files selected for processing (1)
src/components/launch/LaunchWindow.tsx
Store the live webcam preview stream in a stable ref and reattach it through callback refs so late-mounted preview videos receive srcObject immediately while preserving drag-end click-through restoration. Made-with: Cursor
|
Implemented in 8ffebef.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/components/launch/LaunchWindow.tsx (2)
222-224: Three identical derived variables are redundant.
showWebcamControls,showRecordingWebcamPreview, andshouldStreamWebcamPreviewall resolve towebcamEnabled. While this might be intentional for future flexibility, currently they add indirection without benefit.Consider consolidating to a single derived variable or using
webcamEnableddirectly where appropriate, unless you anticipate these conditions diverging (e.g.,showRecordingWebcamPreviewbecomingwebcamEnabled && recording).🤖 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 222 - 224, The three derived booleans showWebcamControls, showRecordingWebcamPreview, and shouldStreamWebcamPreview in LaunchWindow.tsx are redundant because they all equal webcamEnabled; consolidate by removing the extra variables and either use webcamEnabled directly at call sites or replace them with a single shared derived name (e.g., showWebcam) so all references (showWebcamControls, showRecordingWebcamPreview, shouldStreamWebcamPreview) are updated to the single variable or webcamEnabled; keep separate variables only if you intentionally plan to diverge conditions (e.g., showRecordingWebcamPreview => webcamEnabled && recording).
568-588: Magic number100000for full work area expansion lacks documentation.Using
100000as a sentinel value for "expand to full work area" is non-obvious. Future maintainers may not understand the intent without examining the Electron-side handler.📝 Proposed fix to add a named constant
+const FULL_WORK_AREA_SIZE = 100000; + const reportHudSize = () => { frameId = 0; const hasFloatingWebcamPreview = showRecordingWebcamPreview; const measuredWidth = hasFloatingWebcamPreview - ? 100000 + ? FULL_WORK_AREA_SIZE : Math.ceil( Math.max( hudBar.getBoundingClientRect().width, hudBar.scrollWidth, hudContent.getBoundingClientRect().width, hudContent.scrollWidth, ) + 24, ); const measuredHeight = hasFloatingWebcamPreview - ? 100000 + ? FULL_WORK_AREA_SIZE : Math.ceil( Math.max(hudContent.getBoundingClientRect().height, hudContent.scrollHeight) + 24, );🤖 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 568 - 588, The code uses a magic sentinel value 100000 to indicate "expand to full work area" when showRecordingWebcamPreview is true (affecting measuredWidth/measuredHeight and calls to window.electronAPI.setHudOverlayCompactWidth and setHudOverlayMeasuredHeight); replace the literal with a well-named constant (e.g., FULL_WORKAREA_SENTINEL or MAX_FULL_WORKAREA) declared near the top of the module, add a short comment explaining its purpose (that it signals the Electron handler to expand to the full work area), and update references of hasFloatingWebcamPreview / measuredWidth / measuredHeight to use this constant so intent is clear and maintainable.
🤖 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`:
- Line 60: The locale mapping in LaunchWindow.tsx has the "zh-CN" entry set to
the corrupted placeholder "??"; locate the locale object (the mapping that
includes the "zh-CN" key) and restore the proper Chinese label by replacing "??"
with "中文" (or the desired display string such as "简体中文") so the language
selector shows the correct label for Chinese users; update any related UI
snapshots or tests that expect the previous label if present.
- Around line 640-643: The window name parsing in LaunchWindow.tsx (inside the
block using isWindow, appName, displayName and s.name) uses the wrong separator
" ? "; change it to match the rest of the codebase by using " — " (em-dash) for
both the includes() check and the split/join operations so appName =
parts[0]?.trim() and displayName = parts.slice(1).join(" — ").trim() behave
consistently with SourceSelector.tsx and handlers.ts.
---
Nitpick comments:
In `@src/components/launch/LaunchWindow.tsx`:
- Around line 222-224: The three derived booleans showWebcamControls,
showRecordingWebcamPreview, and shouldStreamWebcamPreview in LaunchWindow.tsx
are redundant because they all equal webcamEnabled; consolidate by removing the
extra variables and either use webcamEnabled directly at call sites or replace
them with a single shared derived name (e.g., showWebcam) so all references
(showWebcamControls, showRecordingWebcamPreview, shouldStreamWebcamPreview) are
updated to the single variable or webcamEnabled; keep separate variables only if
you intentionally plan to diverge conditions (e.g., showRecordingWebcamPreview
=> webcamEnabled && recording).
- Around line 568-588: The code uses a magic sentinel value 100000 to indicate
"expand to full work area" when showRecordingWebcamPreview is true (affecting
measuredWidth/measuredHeight and calls to
window.electronAPI.setHudOverlayCompactWidth and setHudOverlayMeasuredHeight);
replace the literal with a well-named constant (e.g., FULL_WORKAREA_SENTINEL or
MAX_FULL_WORKAREA) declared near the top of the module, add a short comment
explaining its purpose (that it signals the Electron handler to expand to the
full work area), and update references of hasFloatingWebcamPreview /
measuredWidth / measuredHeight to use this constant so intent is clear and
maintainable.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: aefbd788-436f-4b7a-ac24-f003e705119c
📒 Files selected for processing (1)
src/components/launch/LaunchWindow.tsx
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 196-198: The UI shows webcam controls based only on webcamEnabled
which leaves the HUD in an "on" state when getUserMedia fails; update the webcam
startup flow (the code that calls getUserMedia / acquires the stream) to only
set showWebcamControls, showRecordingWebcamPreview and shouldStreamWebcamPreview
true after a successful stream acquisition, or alternatively revert
webcamEnabled to false inside the catch path where the getUserMedia error is
handled; locate the stream acquisition function (the method that calls
navigator.mediaDevices.getUserMedia / acquireWebcamStream / startPreview) and
either gate rendering on the actual acquired MediaStream state or ensure the
catch block resets webcamEnabled and clears any partial state so the HUD and
both preview surfaces are not shown when opening the camera fails.
- Around line 1161-1179: The current mouse-based drag handlers (onMouseDown ->
handleMove/handleUp) can get stuck if mouseup occurs outside the renderer;
replace them with pointer events and pointer capture so release is guaranteed.
On the element's pointerdown (instead of onMouseDown) call
e.currentTarget.setPointerCapture(e.pointerId), set isDraggingRef.current =
true, and call window.electronAPI?.hudOverlayDrag?.("start", e.screenX,
e.screenY); attach pointermove and pointerup handlers that call
hudOverlayDrag("move"/"end") and clear isDraggingRef; in pointermove check
ev.buttons (or rely on pointer events state) and in pointerup release capture
via e.currentTarget.releasePointerCapture(e.pointerId) and call
window.electronAPI?.hudOverlaySetIgnoreMouse?.(true) if a drag occurred; remove
the pointer listeners in pointerup to avoid stuck listeners (update references
to handleMove, handleUp, isDraggingRef, hudOverlayDrag, hudOverlaySetIgnoreMouse
accordingly).
- Around line 575-583: The outside-click guard is using dropdownRef which is
attached to the full 100vh HUD wrapper so contains(...) always returns true;
update the logic so the click test targets the actual dropdown/project browser
element instead of the overlay wrapper (e.g., create/use a separate ref like
projectDropdownRef or previewDropdownRef or query the specific dropdown node
inside the component) in the useEffect handler that removes the dropdown:
replace dropdownRef.current.contains(e.target) with a check against the specific
dropdown DOM node and keep calling setActiveDropdown("none") and
setProjectBrowserOpen(false) when the click is outside that specific node.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: fd789b5f-2fe7-40d8-8402-53f1ad5a67e7
📒 Files selected for processing (1)
src/components/launch/LaunchWindow.tsx
| const showWebcamControls = webcamEnabled; | ||
| const showRecordingWebcamPreview = webcamEnabled; | ||
| const shouldStreamWebcamPreview = webcamEnabled; |
There was a problem hiding this comment.
Don't keep the webcam UI "on" after preview startup fails.
Both preview surfaces are rendered from webcamEnabled, but the getUserMedia failure path only logs. If permission is denied or the selected camera cannot be opened, the HUD still expands and both previews show an active webcam state with no stream attached. Gate these sections on successful stream acquisition, or revert the enabled flag in the catch path.
Also applies to: 307-342, 1043-1055, 1214-1234
🤖 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 196 - 198, The UI shows
webcam controls based only on webcamEnabled which leaves the HUD in an "on"
state when getUserMedia fails; update the webcam startup flow (the code that
calls getUserMedia / acquires the stream) to only set showWebcamControls,
showRecordingWebcamPreview and shouldStreamWebcamPreview true after a successful
stream acquisition, or alternatively revert webcamEnabled to false inside the
catch path where the getUserMedia error is handled; locate the stream
acquisition function (the method that calls navigator.mediaDevices.getUserMedia
/ acquireWebcamStream / startPreview) and either gate rendering on the actual
acquired MediaStream state or ensure the catch block resets webcamEnabled and
clears any partial state so the HUD and both preview surfaces are not shown when
opening the camera fails.
| useEffect(() => { | ||
| const handleClick = (e: MouseEvent) => { | ||
| if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) { | ||
| setActiveDropdown("none"); | ||
| setProjectBrowserOpen(false); | ||
| } | ||
| }; | ||
| document.addEventListener("mousedown", handleClick); | ||
| return () => document.removeEventListener("mousedown", handleClick); |
There was a problem hiding this comment.
The outside-click guard can never fire inside the enlarged HUD window.
dropdownRef is attached to the 100vh wrapper, so dropdownRef.current.contains(e.target) is true for every click inside the overlay window. With the larger expanded HUD used for the floating preview, clicks on the bar or preview won't dismiss an open menu/project browser anymore.
💡 Simple fix
- <div className="w-full flex items-end justify-center bg-transparent overflow-visible pb-5" style={{ height: "100vh" }} ref={dropdownRef}>
+ <div className="w-full flex items-end justify-center bg-transparent overflow-visible pb-5" style={{ height: "100vh" }}>- <div className={styles.menuArea}>
+ <div className={styles.menuArea} ref={dropdownRef}>Also applies to: 911-912, 924-924
🤖 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 575 - 583, The
outside-click guard is using dropdownRef which is attached to the full 100vh HUD
wrapper so contains(...) always returns true; update the logic so the click test
targets the actual dropdown/project browser element instead of the overlay
wrapper (e.g., create/use a separate ref like projectDropdownRef or
previewDropdownRef or query the specific dropdown node inside the component) in
the useEffect handler that removes the dropdown: replace
dropdownRef.current.contains(e.target) with a check against the specific
dropdown DOM node and keep calling setActiveDropdown("none") and
setProjectBrowserOpen(false) when the click is outside that specific node.
| onMouseDown={(e) => { | ||
| e.preventDefault(); | ||
| isDraggingRef.current = true; | ||
| window.electronAPI?.hudOverlayDrag?.("start", e.screenX, e.screenY); | ||
| const handleMove = (ev: MouseEvent) => { | ||
| window.electronAPI?.hudOverlayDrag?.("move", ev.screenX, ev.screenY); | ||
| }; | ||
| const handleUp = () => { | ||
| const wasDragging = isDraggingRef.current; | ||
| isDraggingRef.current = false; | ||
| window.electronAPI?.hudOverlayDrag?.("end", 0, 0); | ||
| if (wasDragging) { | ||
| window.electronAPI?.hudOverlaySetIgnoreMouse?.(true); | ||
| } | ||
| document.removeEventListener("mousemove", handleMove); | ||
| document.removeEventListener("mouseup", handleUp); | ||
| }; | ||
| document.addEventListener("mousemove", handleMove); | ||
| document.addEventListener("mouseup", handleUp); |
There was a problem hiding this comment.
The HUD-bar drag can get stuck after an off-window mouseup.
This path relies on document mousemove/mouseup, and handleMove never checks ev.buttons. If the button is released outside the renderer, hudOverlayDrag("end") never fires, the listeners stay attached, and a later plain mousemove can keep moving the HUD because electron/windows.ts:187-201 still has hudDragOffset. Reusing the floating preview's pointer-capture approach here would close that hole.
🤖 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 1161 - 1179, The current
mouse-based drag handlers (onMouseDown -> handleMove/handleUp) can get stuck if
mouseup occurs outside the renderer; replace them with pointer events and
pointer capture so release is guaranteed. On the element's pointerdown (instead
of onMouseDown) call e.currentTarget.setPointerCapture(e.pointerId), set
isDraggingRef.current = true, and call
window.electronAPI?.hudOverlayDrag?.("start", e.screenX, e.screenY); attach
pointermove and pointerup handlers that call hudOverlayDrag("move"/"end") and
clear isDraggingRef; in pointermove check ev.buttons (or rely on pointer events
state) and in pointerup release capture via
e.currentTarget.releasePointerCapture(e.pointerId) and call
window.electronAPI?.hudOverlaySetIgnoreMouse?.(true) if a drag occurred; remove
the pointer listeners in pointerup to avoid stuck listeners (update references
to handleMove, handleUp, isDraggingRef, hudOverlayDrag, hudOverlaySetIgnoreMouse
accordingly).
|
Will review as soon as I have time |
Pull Request Template
Description
Adds a larger floating webcam preview to the launch HUD that appears as soon as webcam is enabled, remains visible during recording, and can be dragged to reposition for live expression monitoring.
Motivation
The previous webcam preview was too small and only visible in limited contexts, making it hard to check framing and expression while recording. This change improves usability by making preview visibility immediate, enlarging the preview, and allowing flexible placement.
Type of Change
Related Issue(s)
N/A
Screenshots / Video
Please attach before merge.
Screenshot (if applicable):
Testing Guide
Checklist
Summary by CodeRabbit
New Features
Style
Behavior