feat: Deeplinks Support + Raycast Extension#1548
feat: Deeplinks Support + Raycast Extension#1548Excellencedev wants to merge 36 commits intoCapSoftware:mainfrom
Conversation
|
@richiemcilroy Please review |
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
|
@greptileai please rereview and update description |
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Additional Comments (1)
-
apps/raycast-extension/src/utils.ts, line 1-30 (link)syntax: Duplicate imports (lines 1 and 3) and duplicate function implementation (lines 7-17 and 18-30). Remove the first set.
12 files reviewed, 3 comments
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
|
@greptileai please rereview and update description |
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
|
@greptileai please rereview and update description |
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
|
https://github.com/greptileai please rereview and update description |
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
|
@greptileai rereview and update description |
| .map(|_| ()) | ||
| } | ||
| DeepLinkAction::StartDefaultRecording => { | ||
| let permissions = crate::permissions::do_permissions_check(false); |
There was a problem hiding this comment.
Minor security/UX thought: StartDefaultRecording is easy to trigger from outside the app (websites can open custom schemes). Might be worth gating recording-control deeplinks behind an opt-in setting or a lightweight confirmation when the trigger isn’t clearly user-initiated (e.g. Raycast).
| #[specta::specta] | ||
| #[instrument(skip(state))] | ||
| pub async fn cycle_mic_input(state: MutableState<'_, App>) -> Result<(), String> { | ||
| if !permissions::do_permissions_check(false) |
There was a problem hiding this comment.
If this is used via deeplink/Raycast, returning Ok(()) on denied permissions makes failures silent. I'd return an error here so callers/logs can distinguish no-op vs success.
| if !permissions::do_permissions_check(false) | |
| if !permissions::do_permissions_check(false).microphone.permitted() { | |
| return Err("Microphone permission denied".to_string()); | |
| } |
| None => camera_list.first(), | ||
| }; | ||
|
|
||
| let next_id = next_camera.map(|c| DeviceOrModelID::DeviceID(c.device_id().to_string())); |
There was a problem hiding this comment.
Minor: using DeviceOrModelID::from_info here preserves ModelID when available (which is usually the more stable identifier).
| let next_id = next_camera.map(|c| DeviceOrModelID::DeviceID(c.device_id().to_string())); | |
| let next_id = next_camera.map(DeviceOrModelID::from_info); |
| #[tauri::command] | ||
| #[specta::specta] | ||
| #[instrument(skip(state))] | ||
| pub async fn cycle_mic_input(state: MutableState<'_, App>) -> Result<(), String> { |
There was a problem hiding this comment.
logic: cycle_mic_input not registered in tauri_specta::collect_commands! around line 2673. Add it after set_camera_input on line 2675 so it's callable from deeplinks.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/lib.rs
Line: 591:594
Comment:
**logic:** `cycle_mic_input` not registered in `tauri_specta::collect_commands!` around line 2673. Add it after `set_camera_input` on line 2675 so it's callable from deeplinks.
How can I resolve this? If you propose a fix, please make it concise.| #[tauri::command] | ||
| #[specta::specta] | ||
| #[instrument(skip(app_handle, state))] | ||
| pub async fn cycle_camera_input( |
There was a problem hiding this comment.
logic: cycle_camera_input not registered in tauri_specta::collect_commands! around line 2673. Add it after cycle_mic_input so it's callable from deeplinks.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/lib.rs
Line: 628:631
Comment:
**logic:** `cycle_camera_input` not registered in `tauri_specta::collect_commands!` around line 2673. Add it after `cycle_mic_input` so it's callable from deeplinks.
How can I resolve this? If you propose a fix, please make it concise.Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
| #[tauri::command] | ||
| #[specta::specta] | ||
| #[instrument(skip(state))] | ||
| pub async fn cycle_mic_input(state: MutableState<'_, App>) -> Result<(), String> { |
There was a problem hiding this comment.
Since this is annotated as a #[tauri::command] + #[specta::specta], make sure it's also included in the tauri_specta::collect_commands![] list; otherwise it won't be exposed to the frontend/typegen.
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
|
@greptileai review |
| "Productivity", | ||
| "Media" | ||
| ], | ||
| "license": "MIT", |
There was a problem hiding this comment.
This repo isn't MIT-licensed overall (see root LICENSE), so MIT here is probably misleading. I'd drop the field unless you intend to license this extension differently.
| "license": "MIT", | |
| "categories": [ | |
| "Productivity", | |
| "Media" | |
| ], | |
| "commands": [ |
There was a problem hiding this comment.
Additional Comments (1)
-
apps/desktop/src-tauri/src/lib.rs, line 2680-2682 (link)logic:
cycle_mic_inputandcycle_camera_inputnot registered in command collection. Add them afterset_camera_inputso they're callable from deeplinks.
12 files reviewed, 7 comments
| DeepLinkAction::PauseRecording => { | ||
| crate::recording::pause_recording(app.clone(), app.state()).await | ||
| } | ||
| DeepLinkAction::ResumeRecording => { | ||
| crate::recording::resume_recording(app.clone(), app.state()).await | ||
| } | ||
| crate::recording::pause_recording(app.clone(), app.state()).await | ||
| } | ||
| DeepLinkAction::ResumeRecording => { | ||
| crate::recording::resume_recording(app.clone(), app.state()).await | ||
| } |
There was a problem hiding this comment.
syntax: duplicate match arms cause compilation error. Lines 196-200 repeat the PauseRecording and ResumeRecording cases.
| DeepLinkAction::PauseRecording => { | |
| crate::recording::pause_recording(app.clone(), app.state()).await | |
| } | |
| DeepLinkAction::ResumeRecording => { | |
| crate::recording::resume_recording(app.clone(), app.state()).await | |
| } | |
| crate::recording::pause_recording(app.clone(), app.state()).await | |
| } | |
| DeepLinkAction::ResumeRecording => { | |
| crate::recording::resume_recording(app.clone(), app.state()).await | |
| } | |
| DeepLinkAction::PauseRecording => { | |
| crate::recording::pause_recording(app.clone(), app.state()).await | |
| } | |
| DeepLinkAction::ResumeRecording => { | |
| crate::recording::resume_recording(app.clone(), app.state()).await | |
| } | |
| DeepLinkAction::SetMicrophone { label } => { |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 190:200
Comment:
**syntax:** duplicate match arms cause compilation error. Lines 196-200 repeat the `PauseRecording` and `ResumeRecording` cases.
```suggestion
DeepLinkAction::PauseRecording => {
crate::recording::pause_recording(app.clone(), app.state()).await
}
DeepLinkAction::ResumeRecording => {
crate::recording::resume_recording(app.clone(), app.state()).await
}
DeepLinkAction::SetMicrophone { label } => {
```
How can I resolve this? If you propose a fix, please make it concise.| if !permissions::do_permissions_check(false).camera.permitted() { | ||
| return Err("Camera permission denied".to_string()); | ||
| } | ||
| if !permissions::do_permissions_check(false).camera.permitted() { | ||
| return Err("Camera permission denied".to_string()); | ||
| } | ||
| } |
There was a problem hiding this comment.
syntax: duplicate permission check and orphaned closing brace cause compilation error. Lines 638-641 repeat the check and have an extra }.
| if !permissions::do_permissions_check(false).camera.permitted() { | |
| return Err("Camera permission denied".to_string()); | |
| } | |
| if !permissions::do_permissions_check(false).camera.permitted() { | |
| return Err("Camera permission denied".to_string()); | |
| } | |
| } | |
| if !permissions::do_permissions_check(false).camera.permitted() { | |
| return Err("Camera permission denied".to_string()); | |
| } | |
| let current_id = { |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/lib.rs
Line: 635:641
Comment:
**syntax:** duplicate permission check and orphaned closing brace cause compilation error. Lines 638-641 repeat the check and have an extra `}`.
```suggestion
if !permissions::do_permissions_check(false).camera.permitted() {
return Err("Camera permission denied".to_string());
}
let current_id = {
```
How can I resolve this? If you propose a fix, please make it concise.| let next_id = next_camera.map(|c| DeviceOrModelID::DeviceID(c.device_id().to_string())); | ||
| let next_id = next_camera.map(|c| match c.model_id() { | ||
| Some(model_id) => DeviceOrModelID::ModelID(model_id.to_string()), | ||
| None => DeviceOrModelID::DeviceID(c.device_id().to_string()), | ||
| }); |
There was a problem hiding this comment.
syntax: next_id is computed twice with different logic - the second assignment overwrites the first. The second one (lines 668-671) is the correct implementation as it prefers ModelID.
| let next_id = next_camera.map(|c| DeviceOrModelID::DeviceID(c.device_id().to_string())); | |
| let next_id = next_camera.map(|c| match c.model_id() { | |
| Some(model_id) => DeviceOrModelID::ModelID(model_id.to_string()), | |
| None => DeviceOrModelID::DeviceID(c.device_id().to_string()), | |
| }); | |
| let next_id = next_camera.map(|c| match c.model_id() { | |
| Some(model_id) => DeviceOrModelID::ModelID(model_id.to_string()), | |
| None => DeviceOrModelID::DeviceID(c.device_id().to_string()), | |
| }); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/lib.rs
Line: 667:671
Comment:
**syntax:** `next_id` is computed twice with different logic - the second assignment overwrites the first. The second one (lines 668-671) is the correct implementation as it prefers ModelID.
```suggestion
let next_id = next_camera.map(|c| match c.model_id() {
Some(model_id) => DeviceOrModelID::ModelID(model_id.to_string()),
None => DeviceOrModelID::DeviceID(c.device_id().to_string()),
});
```
How can I resolve this? If you propose a fix, please make it concise.| 3. `pnpm build` | ||
| 3. `npm run build` |
There was a problem hiding this comment.
syntax: duplicate build step with conflicting commands. Line 18 says pnpm build but line 19 says npm run build.
| 3. `pnpm build` | |
| 3. `npm run build` | |
| 3. `npm run build` |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast-extension/README.md
Line: 18:19
Comment:
**syntax:** duplicate build step with conflicting commands. Line 18 says `pnpm build` but line 19 says `npm run build`.
```suggestion
3. `npm run build`
```
How can I resolve this? If you propose a fix, please make it concise.| DeepLinkAction::CycleMicrophone => { | ||
| let state = app.state::<ArcLock<App>>(); | ||
| crate::cycle_mic_input(state).await | ||
| } |
There was a problem hiding this comment.
style: missing permissions check before cycling microphone. Other microphone operations (like SetMicrophone on line 202) check permissions first.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 219:222
Comment:
**style:** missing permissions check before cycling microphone. Other microphone operations (like `SetMicrophone` on line 202) check permissions first.
How can I resolve this? If you propose a fix, please make it concise.| DeepLinkAction::CycleCamera => { | ||
| let state = app.state::<ArcLock<App>>(); | ||
| crate::cycle_camera_input(app.clone(), state).await | ||
| } |
There was a problem hiding this comment.
style: missing permissions check before cycling camera. Other camera operations (like SetCamera on line 211) check permissions first.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 223:226
Comment:
**style:** missing permissions check before cycling camera. Other camera operations (like `SetCamera` on line 211) check permissions first.
How can I resolve this? If you propose a fix, please make it concise.
Description
Adds deeplink support for controlling recording state and a new Raycast extension.
Changes
PauseRecording,ResumeRecording,CycleMicrophone,CycleCamera,StartDefaultRecordingtodeeplink_actions.rs.lib.rsfor pausing, resuming, and cycling inputs.cap-controlwith commands to Start, Stop, Pause, Resume, and Switch inputs.How to test
pnpm dev:desktop.cap-desktop://action?value="start_default_recording"to start.cap-desktop://action?value="pause_recording"to pause.cap-desktop://action?value="resume_recording"to resume.cap-desktop://action?value="cycle_microphone"to switch mic.apps/raycast-extensionand test commands./closes #1540
/claim #1540
Greptile Summary
Added deeplink support for controlling recording state (pause, resume, cycle microphone/camera) and created a new Raycast extension to invoke these deeplinks. The Raycast extension provides commands for starting, stopping, pausing, resuming recordings, and switching camera/microphone inputs.
Major Changes:
StartDefaultRecording,PauseRecording,ResumeRecording,SetMicrophone,SetCamera,CycleMicrophone,CycleCameracycle_mic_input()andcycle_camera_input()in lib.rscap-desktop://URLsrecording::pause_recording()andrecording::resume_recording()functions from recording.rsCritical Issues Found:
cycle_mic_inputandcycle_camera_inputnot registered in the command collection, making them uncallableCycleMicrophoneandCycleCameradeeplink handlers (inconsistent withSetMicrophoneandSetCamera)Positive Notes:
Confidence Score: 1/5
Important Files Changed
Sequence Diagram
sequenceDiagram participant Raycast participant DeeplinkURL participant DeeplinkHandler participant App participant Recording participant MicFeed participant Camera Note over Raycast,Camera: Start Recording Flow Raycast->>DeeplinkURL: cap-desktop://action?value="start_default_recording" DeeplinkURL->>DeeplinkHandler: Parse action DeeplinkHandler->>DeeplinkHandler: Check screen recording permission DeeplinkHandler->>App: list_displays() App-->>DeeplinkHandler: displays[] DeeplinkHandler->>Recording: start_recording(display, mode=Instant) Recording-->>Raycast: Recording started Note over Raycast,Camera: Pause/Resume Flow Raycast->>DeeplinkURL: cap-desktop://action?value="pause_recording" DeeplinkURL->>DeeplinkHandler: Parse action DeeplinkHandler->>Recording: pause_recording() Recording->>Recording: Emit RecordingEvent::Paused Recording-->>Raycast: Recording paused Raycast->>DeeplinkURL: cap-desktop://action?value="resume_recording" DeeplinkURL->>DeeplinkHandler: Parse action DeeplinkHandler->>Recording: resume_recording() Recording->>Recording: Emit RecordingEvent::Resumed Recording-->>Raycast: Recording resumed Note over Raycast,Camera: Cycle Microphone Flow Raycast->>DeeplinkURL: cap-desktop://action?value="cycle_microphone" DeeplinkURL->>DeeplinkHandler: Parse action DeeplinkHandler->>App: cycle_mic_input() App->>App: Check microphone permission App->>MicFeed: list() MicFeed-->>App: mic_list[] App->>App: Find current + select next App->>App: set_mic_input(next_label) App-->>Raycast: Microphone switched Note over Raycast,Camera: Cycle Camera Flow Raycast->>DeeplinkURL: cap-desktop://action?value="cycle_camera" DeeplinkURL->>DeeplinkHandler: Parse action DeeplinkHandler->>App: cycle_camera_input() App->>App: Check camera permission App->>Camera: list_cameras() Camera-->>App: camera_list[] App->>App: Find current + select next App->>App: set_camera_input(next_id) App-->>Raycast: Camera switched Note over Raycast,Camera: Stop Recording Flow Raycast->>DeeplinkURL: cap-desktop://action?value="stop_recording" DeeplinkURL->>DeeplinkHandler: Parse action DeeplinkHandler->>Recording: stop_recording() Recording-->>Raycast: Recording stopped