1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum CaptureMode {
8 Screen,
10 Window,
12 Area,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum RecorderState {
19 Idle,
21 Countdown {
23 seconds: u8,
25 },
26 Recording,
28 Paused,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct OverlayOptionFixture {
35 pub id: &'static str,
37 pub label: &'static str,
39 pub hint: &'static str,
41 pub enabled: bool,
43}
44
45#[must_use]
47pub fn sample_overlay_options() -> Vec<OverlayOptionFixture> {
48 vec![
49 OverlayOptionFixture {
50 id: "overlay-keypress",
51 label: "Show keypresses",
52 hint: "Render ⌘C / ⌃-space badges on top of the recording",
53 enabled: true,
54 },
55 OverlayOptionFixture {
56 id: "overlay-clicks",
57 label: "Highlight clicks",
58 hint: "Soft ripple around each mouse click",
59 enabled: true,
60 },
61 OverlayOptionFixture {
62 id: "overlay-desktop",
63 label: "Hide desktop icons",
64 hint: "Blur the wallpaper area while recording",
65 enabled: false,
66 },
67 OverlayOptionFixture {
68 id: "overlay-sensitive",
69 label: "Redact sensitive areas",
70 hint: "Auto-blur regions marked as sensitive",
71 enabled: false,
72 },
73 ]
74}
75
76#[must_use]
79pub fn sample_on_screen_options(
80 sensitive_disabled: bool,
81) -> Vec<crate::components::recorder::OnScreenOptionView> {
82 use crate::components::recorder::{OnScreenOptionKind, OnScreenOptionView};
83 vec![
84 OnScreenOptionView {
85 id: OnScreenOptionKind::CleanDesktop,
86 title: "Clean up the desktop".to_owned(),
87 description: "Hide icons + the dock for a clean recording. Wallpaper stays.".to_owned(),
88 enabled: true,
89 disabled: false,
90 },
91 OnScreenOptionView {
92 id: OnScreenOptionKind::ShowKeys,
93 title: "Show keys you press".to_owned(),
94 description: "Render ⌘C / ⌃-space badges over the recording so viewers can follow along.".to_owned(),
95 enabled: true,
96 disabled: false,
97 },
98 OnScreenOptionView {
99 id: OnScreenOptionKind::BlurSensitiveInfo,
100 title: "Blur sensitive info".to_owned(),
101 description: "Auto-detect password fields and other sensitive regions, and blur them. Coming soon.".to_owned(),
102 enabled: false,
103 disabled: sensitive_disabled,
104 },
105 ]
106}
107
108#[must_use]
110pub fn sample_on_screen_options_all_on() -> Vec<crate::components::recorder::OnScreenOptionView> {
111 sample_on_screen_options(false)
112 .into_iter()
113 .map(|mut o| {
114 o.enabled = true;
115 o.disabled = false;
116 o
117 })
118 .collect()
119}
120
121#[must_use]
123pub fn sample_on_screen_options_long_copy() -> Vec<crate::components::recorder::OnScreenOptionView>
124{
125 use crate::components::recorder::{OnScreenOptionKind, OnScreenOptionView};
126 vec![
127 OnScreenOptionView {
128 id: OnScreenOptionKind::CleanDesktop,
129 title: "Clean up the desktop background and remove distracting icons".to_owned(),
130 description: "Hide every desktop icon plus the entire dock for a noise-free recording. Wallpaper remains visible. This setting reverts when the recording ends; nothing changes permanently on disk.".to_owned(),
131 enabled: true,
132 disabled: false,
133 },
134 OnScreenOptionView {
135 id: OnScreenOptionKind::ShowKeys,
136 title: "Show keys you press during the recording".to_owned(),
137 description: "Render small badges in the bottom-right showing each modifier + key press. Useful for tutorial-style content where viewers want to follow along with what shortcut was used.".to_owned(),
138 enabled: false,
139 disabled: false,
140 },
141 ]
142}
143
144#[must_use]
148pub fn sample_recording_controls(
149 state: crate::components::recorder::StartRecordingState,
150) -> crate::components::recorder::RecordingControlsView {
151 use crate::components::recorder::{
152 RecordingControlsView, format_auto_zoom_label, format_countdown_label,
153 };
154 RecordingControlsView {
155 auto_zoom_label: format_auto_zoom_label(Some(2.0)),
156 countdown_label: format_countdown_label(3),
157 shortcuts: vec!["⌘".to_owned(), "⇧".to_owned(), "2".to_owned()],
158 start_state: state,
159 }
160}
161
162#[must_use]
165pub fn sample_recording_controls_compact() -> crate::components::recorder::RecordingControlsView {
166 use crate::components::recorder::{
167 RecordingControlsView, StartRecordingState, format_auto_zoom_label, format_countdown_label,
168 };
169 RecordingControlsView {
170 auto_zoom_label: format_auto_zoom_label(None),
171 countdown_label: format_countdown_label(0),
172 shortcuts: vec!["⌘".to_owned(), "R".to_owned()],
173 start_state: StartRecordingState::Ready,
174 }
175}
176
177#[must_use]
181pub fn sample_save_panel_choosing() -> crate::components::recorder::SavePanelView {
182 use crate::components::recorder::{SaveFormat, SavePanelView};
183 SavePanelView::Choosing {
184 output_dir: "/Users/you/Movies/Screen".to_owned(),
185 format: SaveFormat::Mp4H264,
186 busy: false,
187 }
188}
189
190#[must_use]
194pub fn sample_save_panel_exporting() -> crate::components::recorder::SavePanelView {
195 use crate::components::recorder::{SaveFormat, SavePanelView};
196 SavePanelView::Choosing {
197 output_dir: "/Users/you/Movies/Screen".to_owned(),
198 format: SaveFormat::WebmVp9,
199 busy: true,
200 }
201}
202
203#[must_use]
207pub fn sample_save_panel_saved() -> crate::components::recorder::SavePanelView {
208 use crate::components::recorder::SavePanelView;
209 SavePanelView::Saved {
210 path: "/Users/you/Movies/Screen/Screen-2026-05-25-143012.mp4".to_owned(),
211 }
212}
213
214#[must_use]
216pub fn sample_tray_record_popover(
217 open: crate::components::recorder::OpenRecorderPopoverKind,
218) -> crate::components::recorder::TrayRecordPopoverView {
219 use crate::components::recorder::{
220 OnScreenSummaryView, StartRecordingState, TrayRecordPopoverView, WorkspaceSwitcherView,
221 format_on_screen_summary,
222 };
223 use crate::components::shell::AppSection;
224 use crate::fixtures::audio_apps::{sample_audio_apps, sample_system_audio_view};
225 use crate::fixtures::devices::{
226 sample_capture_source_camera, sample_capture_source_microphone, sample_display_source,
227 };
228 use crate::fixtures::workspaces::sample_workspace_views;
229
230 let workspaces = sample_workspace_views();
231 let on_screen = sample_on_screen_options(false);
232 let summary = format_on_screen_summary(&on_screen);
233 let apps = sample_audio_apps();
234 let total = apps.len();
235 TrayRecordPopoverView {
236 active_section: AppSection::Record,
237 capture_mode: CaptureMode::Screen,
238 workspaces: WorkspaceSwitcherView {
239 workspaces,
240 selected_id: "ws-northwind",
241 },
242 display: sample_display_source(true),
243 camera: sample_capture_source_camera(true, false),
244 microphone: sample_capture_source_microphone(true, false, Some(0.35)),
245 system_audio: sample_system_audio_view(true, false, &apps, total),
246 on_screen_summary: OnScreenSummaryView {
247 summary,
248 options: on_screen,
249 },
250 controls: sample_recording_controls(StartRecordingState::Ready),
251 open,
252 }
253}
254
255#[must_use]
257pub fn sample_tray_record_popover_start_disabled()
258-> crate::components::recorder::TrayRecordPopoverView {
259 use crate::components::recorder::{OpenRecorderPopoverKind, StartRecordingState};
260 let mut v = sample_tray_record_popover(OpenRecorderPopoverKind::None);
261 v.controls = sample_recording_controls(StartRecordingState::Disabled);
262 v
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn overlay_options_non_empty() {
271 assert!(!sample_overlay_options().is_empty());
272 }
273
274 #[test]
275 fn tray_record_popover_is_record_section_by_default() {
276 use crate::components::recorder::OpenRecorderPopoverKind;
277 use crate::components::shell::AppSection;
278 let v = sample_tray_record_popover(OpenRecorderPopoverKind::None);
279 assert!(matches!(v.active_section, AppSection::Record));
280 assert!(!v.on_screen_summary.summary.is_empty());
281 }
282
283 #[test]
284 fn recording_controls_preserve_shortcut_order() {
285 use crate::components::recorder::StartRecordingState;
286 let v = sample_recording_controls(StartRecordingState::Ready);
287 assert_eq!(v.shortcuts, vec!["⌘", "⇧", "2"]);
288 }
291
292 #[test]
293 fn recording_controls_compact_drops_zoom_and_countdown() {
294 let v = sample_recording_controls_compact();
295 assert!(v.auto_zoom_label.contains("off"));
296 assert!(v.countdown_label.starts_with("No"));
297 }
298
299 #[test]
300 fn on_screen_options_have_one_per_kind() {
301 use crate::components::recorder::OnScreenOptionKind;
302 let opts = sample_on_screen_options(false);
303 assert_eq!(opts.len(), 3);
304 for kind in [
305 OnScreenOptionKind::CleanDesktop,
306 OnScreenOptionKind::ShowKeys,
307 OnScreenOptionKind::BlurSensitiveInfo,
308 ] {
309 assert!(opts.iter().any(|o| o.id == kind));
310 }
311 }
312}