Skip to content

Commit 0103006

Browse files
Merge pull request #1480 from CapSoftware/windows-0.4.0-fixes
misc bug fixes I'm tired pls help
2 parents a158d5d + 729ab3c commit 0103006

File tree

30 files changed

+894
-385
lines changed

30 files changed

+894
-385
lines changed

apps/desktop/src-tauri/src/camera.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ impl CameraPreviewManager {
116116
self.preview.is_some()
117117
}
118118

119+
pub fn notify_window_resized(&self, width: u32, height: u32) {
120+
if let Some(preview) = &self.preview {
121+
preview
122+
.reconfigure
123+
.send(ReconfigureEvent::WindowResized { width, height })
124+
.map_err(|err| error!("Error notifying camera preview of resize: {err}"))
125+
.ok();
126+
}
127+
}
128+
119129
/// Initialize the camera preview for a specific Tauri window
120130
pub async fn init_window(
121131
&mut self,
@@ -169,6 +179,7 @@ impl CameraPreviewManager {
169179
#[derive(Clone)]
170180
enum ReconfigureEvent {
171181
State(CameraPreviewState),
182+
WindowResized { width: u32, height: u32 },
172183
Shutdown,
173184
}
174185

@@ -604,6 +615,10 @@ impl Renderer {
604615
self.reconfigure_gpu_surface(width, height);
605616
}
606617
}
618+
Err(ReconfigureEvent::WindowResized { width, height }) => {
619+
trace!("CameraPreview/ReconfigureEvent.WindowResized({width}x{height})");
620+
self.reconfigure_gpu_surface(width, height);
621+
}
607622
Err(ReconfigureEvent::Shutdown) => return,
608623
}
609624
}

apps/desktop/src-tauri/src/captions.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,10 @@ pub async fn save_captions(
10501050
serde_json::Number::from_f64(settings.word_transition_duration as f64).unwrap(),
10511051
),
10521052
);
1053+
settings_obj.insert(
1054+
"activeWordHighlight".to_string(),
1055+
serde_json::Value::Bool(settings.active_word_highlight),
1056+
);
10531057

10541058
json_obj.insert(
10551059
"settings".to_string(),
@@ -1200,6 +1204,12 @@ pub fn parse_captions_json(json: &str) -> Result<cap_project::CaptionsData, Stri
12001204
.and_then(|v| v.as_f64())
12011205
.unwrap_or(0.25) as f32;
12021206

1207+
let active_word_highlight = settings_obj
1208+
.get("activeWordHighlight")
1209+
.or_else(|| settings_obj.get("active_word_highlight"))
1210+
.and_then(|v| v.as_bool())
1211+
.unwrap_or(false);
1212+
12031213
cap_project::CaptionSettings {
12041214
enabled,
12051215
font,
@@ -1217,6 +1227,7 @@ pub fn parse_captions_json(json: &str) -> Result<cap_project::CaptionsData, Stri
12171227
fade_duration,
12181228
linger_duration,
12191229
word_transition_duration,
1230+
active_word_highlight,
12201231
}
12211232
} else {
12221233
cap_project::CaptionSettings::default()

apps/desktop/src-tauri/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2932,11 +2932,15 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
29322932
match event {
29332933
WindowEvent::CloseRequested { .. } => {
29342934
if let Ok(CapWindowId::Camera) = CapWindowId::from_str(label) {
2935+
tracing::warn!("Camera window CloseRequested event received!");
29352936
tokio::spawn(cleanup_camera_window(app.clone()));
29362937
}
29372938
}
29382939
WindowEvent::Destroyed => {
29392940
if let Ok(window_id) = CapWindowId::from_str(label) {
2941+
if matches!(window_id, CapWindowId::Camera) {
2942+
tracing::warn!("Camera window Destroyed event received!");
2943+
}
29402944
match window_id {
29412945
CapWindowId::Main => {
29422946
let app = app.clone();

apps/desktop/src-tauri/src/recording.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,10 +607,29 @@ pub async fn start_recording(
607607
win.close().ok();
608608
}
609609
}
610+
#[cfg(windows)]
611+
let had_camera_window = CapWindowId::Camera.get(&app).is_some();
612+
#[cfg(windows)]
613+
if had_camera_window {
614+
tracing::info!(
615+
"Closing camera window BEFORE InProgressRecording show (will recreate after)"
616+
);
617+
if let Some(cam_win) = CapWindowId::Camera.get(&app) {
618+
cam_win.close().ok();
619+
}
620+
}
621+
610622
let _ = ShowCapWindow::InProgressRecording { countdown }
611623
.show(&app)
612624
.await;
613625

626+
#[cfg(windows)]
627+
if had_camera_window {
628+
tracing::info!("Recreating camera window after InProgressRecording");
629+
tokio::time::sleep(std::time::Duration::from_millis(150)).await;
630+
ShowCapWindow::Camera.show(&app).await.ok();
631+
}
632+
614633
if let Some(window) = CapWindowId::Main.get(&app) {
615634
let _ = general_settings
616635
.map(|v| v.main_window_recording_start_behaviour)

apps/desktop/src-tauri/src/target_select_overlay.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use base64::prelude::*;
99
use cap_recording::screen_capture::ScreenCaptureTarget;
1010

1111
use crate::{
12-
general_settings,
12+
App, ArcLock, general_settings,
1313
window_exclusion::WindowExclusion,
1414
windows::{CapWindowId, ShowCapWindow},
1515
};
@@ -163,10 +163,13 @@ pub async fn update_camera_overlay_bounds(
163163
.get_webview_window("camera")
164164
.ok_or("Camera window not found")?;
165165

166+
let width_u32 = width as u32;
167+
let height_u32 = height as u32;
168+
166169
window
167170
.set_size(tauri::Size::Physical(tauri::PhysicalSize {
168-
width: width as u32,
169-
height: height as u32,
171+
width: width_u32,
172+
height: height_u32,
170173
}))
171174
.map_err(|e| e.to_string())?;
172175
window
@@ -176,6 +179,16 @@ pub async fn update_camera_overlay_bounds(
176179
}))
177180
.map_err(|e| e.to_string())?;
178181

182+
let scale_factor = window.scale_factor().unwrap_or(1.0);
183+
let logical_width = (width / scale_factor) as u32;
184+
let logical_height = (height / scale_factor) as u32;
185+
186+
let state = app.state::<ArcLock<App>>();
187+
let app_state = state.read().await;
188+
app_state
189+
.camera_preview
190+
.notify_window_resized(logical_width, logical_height);
191+
179192
Ok(())
180193
}
181194

apps/desktop/src/routes/camera.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -589,16 +589,6 @@ function LegacyCameraPreviewPage(props: { disconnected: Accessor<boolean> }) {
589589

590590
let cameraCanvasRef: HTMLCanvasElement | undefined;
591591

592-
createEffect(
593-
on(
594-
() => rawOptions.cameraLabel,
595-
(label) => {
596-
if (label === null) getCurrentWindow().close();
597-
},
598-
{ defer: true },
599-
),
600-
);
601-
602592
onMount(() => getCurrentWindow().show());
603593

604594
return (
@@ -700,15 +690,18 @@ function Canvas(props: {
700690
latestFrame: Accessor<{ width: number; data: ImageData } | null | undefined>;
701691
state: CameraWindowState;
702692
ref: HTMLCanvasElement | undefined;
693+
containerSize?: { width: number; height: number };
703694
}) {
704695
const style = () => {
705696
const frame = props.latestFrame();
706697
if (!frame) return {};
707698

708699
const aspectRatio = frame.data.width / frame.data.height;
709700

710-
// Use state.size directly for immediate feedback
711-
const base = props.state.size;
701+
// Use container size if available (for external resize), otherwise use state.size
702+
const base = props.containerSize
703+
? Math.min(props.containerSize.width, props.containerSize.height)
704+
: props.state.size;
712705

713706
// Replicate window size logic synchronously for the canvas
714707
const winWidth =
@@ -741,7 +734,7 @@ function Canvas(props: {
741734
else
742735
return {
743736
width: base,
744-
height: base * aspectRatio,
737+
height: base / aspectRatio,
745738
};
746739
})();
747740

apps/desktop/src/routes/editor/CaptionsTab.tsx

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ function RgbInput(props: { value: string; onChange: (value: string) => void }) {
150150
}
151151

152152
export function CaptionsTab() {
153-
const { project, setProject, editorInstance, editorState } =
153+
const { project, setProject, editorInstance, editorState, setEditorState } =
154154
useEditorContext();
155155

156156
const getSetting = <K extends keyof CaptionSettings>(
@@ -172,12 +172,18 @@ export function CaptionsTab() {
172172
const [selectedLanguage, setSelectedLanguage] = createSignal("auto");
173173
const [downloadedModels, setDownloadedModels] = createSignal<string[]>([]);
174174

175-
const [isDownloading, setIsDownloading] = createSignal(false);
176-
const [downloadProgress, setDownloadProgress] = createSignal(0);
177-
const [downloadingModel, setDownloadingModel] = createSignal<string | null>(
178-
null,
179-
);
180-
const [isGenerating, setIsGenerating] = createSignal(false);
175+
const isDownloading = () => editorState.captions.isDownloading;
176+
const setIsDownloading = (value: boolean) =>
177+
setEditorState("captions", "isDownloading", value);
178+
const downloadProgress = () => editorState.captions.downloadProgress;
179+
const setDownloadProgress = (value: number) =>
180+
setEditorState("captions", "downloadProgress", value);
181+
const downloadingModel = () => editorState.captions.downloadingModel;
182+
const setDownloadingModel = (value: string | null) =>
183+
setEditorState("captions", "downloadingModel", value);
184+
const isGenerating = () => editorState.captions.isGenerating;
185+
const setIsGenerating = (value: boolean) =>
186+
setEditorState("captions", "isGenerating", value);
181187
const [hasAudio, setHasAudio] = createSignal(false);
182188

183189
createEffect(
@@ -662,6 +668,27 @@ export function CaptionsTab() {
662668
/>
663669
</div>
664670

671+
<div class="flex flex-col gap-2">
672+
<div class="flex items-center justify-between">
673+
<span class="text-gray-11 text-sm">
674+
Active Word Highlight
675+
</span>
676+
<Toggle
677+
checked={getSetting("activeWordHighlight")}
678+
onChange={(checked) =>
679+
updateCaptionSetting("activeWordHighlight", checked)
680+
}
681+
disabled={!hasCaptions()}
682+
/>
683+
</div>
684+
<p class="text-xs text-gray-10">
685+
This is the first version of captions in Cap. Active word
686+
highlighting may be inaccurate in some situations. We're
687+
working on a fix for this and it will be released in
688+
upcoming versions.
689+
</p>
690+
</div>
691+
665692
<div class="flex flex-col gap-2">
666693
<span class="text-gray-11 text-sm">Font Color</span>
667694
<RgbInput

apps/desktop/src/routes/editor/ExportPage.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,10 @@ export function ExportPage() {
14061406
<Button
14071407
variant="gray"
14081408
class="mt-4 hover:underline"
1409-
onClick={() => setExportState({ type: "idle" })}
1409+
onClick={() => {
1410+
setExportState({ type: "idle" });
1411+
handleBack();
1412+
}}
14101413
>
14111414
<IconLucideArrowLeft class="size-4" />
14121415
Back to Editor

apps/desktop/src/routes/editor/context.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,12 @@ export const [EditorContextProvider, useEditorContext] = createContextProvider(
612612
previewTime: null as number | null,
613613
playbackTime: 0,
614614
playing: false,
615+
captions: {
616+
isGenerating: false,
617+
isDownloading: false,
618+
downloadProgress: 0,
619+
downloadingModel: null as string | null,
620+
},
615621
timeline: {
616622
interactMode: "seek" as "seek" | "split",
617623
selection: null as

apps/desktop/src/routes/screenshot-editor/AnnotationLayer.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export function AnnotationLayer(props: {
8686
createEffect(() => {
8787
const rect = props.imageRect;
8888
if (rect.width <= 0 || rect.height <= 0) return;
89+
const masksToRemove: string[] = [];
8990
for (const ann of annotations) {
9091
if (ann.type !== "mask") continue;
9192
const left = clampValue(
@@ -110,6 +111,10 @@ export function AnnotationLayer(props: {
110111
);
111112
const width = Math.max(0, right - left);
112113
const height = Math.max(0, bottom - top);
114+
if (width < 5 || height < 5) {
115+
masksToRemove.push(ann.id);
116+
continue;
117+
}
113118
if (
114119
left !== Math.min(ann.x, ann.x + ann.width) ||
115120
top !== Math.min(ann.y, ann.y + ann.height) ||
@@ -124,6 +129,11 @@ export function AnnotationLayer(props: {
124129
});
125130
}
126131
}
132+
if (masksToRemove.length > 0) {
133+
setAnnotations((prev) =>
134+
prev.filter((a) => !masksToRemove.includes(a.id)),
135+
);
136+
}
127137
});
128138

129139
// Helper to get coordinates in SVG space

0 commit comments

Comments
 (0)