-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: Deeplinks Support + Raycast Extension #1548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: Deeplinks Support + Raycast Extension #1548
Conversation
|
@richiemcilroy Please review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
12 files reviewed, 3 comments
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
12 files reviewed, 3 comments
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
12 files reviewed, 7 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>
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
12 files reviewed, 4 comments
| #[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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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