-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: add deeplinks support and Raycast extension #1583
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,7 @@ import "./styles/theme.css"; | |||||||||||||||||||
| import { CapErrorBoundary } from "./components/CapErrorBoundary"; | ||||||||||||||||||||
| import { generalSettingsStore } from "./store"; | ||||||||||||||||||||
| import { initAnonymousUser } from "./utils/analytics"; | ||||||||||||||||||||
| import { initDeepLinkCommands } from "./utils/deep-link-commands"; | ||||||||||||||||||||
| import { type AppTheme, commands } from "./utils/tauri"; | ||||||||||||||||||||
| import titlebar from "./utils/titlebar-state"; | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -102,6 +103,7 @@ function Inner() { | |||||||||||||||||||
|
|
||||||||||||||||||||
| onMount(() => { | ||||||||||||||||||||
| initAnonymousUser(); | ||||||||||||||||||||
| initDeepLinkCommands(); | ||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This initialization conflicts with the existing deep link handler already registered in Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/App.tsx
Line: 106:106
Comment:
This initialization conflicts with the existing deep link handler already registered in `apps/desktop/src-tauri/src/lib.rs:3344-3346`. The Rust handler via `tauri_plugin_deep_link` is the correct approach and this TypeScript handler should be removed.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||
| }); | ||||||||||||||||||||
|
Comment on lines
104
to
107
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| return ( | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,230 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { listen } from "@tauri-apps/api/event"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { onOpenUrl } from "@tauri-apps/plugin-deep-link"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+2
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused import.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { commands } from "./tauri"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { RecordingMode, StartRecordingInputs } from "./tauri"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Small consistency nit: repo guidelines disallow code comments, but this file adds a bunch of docblocks/inline |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Deep link command handlers for Cap | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Supports: cap://record, cap://stop, cap://pause, cap://resume, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * cap://toggle-pause, cap://switch-mic, cap://switch-camera | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+9
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove JSDoc comments. Per CLAUDE.md:363-368 and AGENTS.md:23, NO CODE COMMENTS are allowed in this codebase ( Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/utils/deep-link-commands.ts
Line: 6:9
Comment:
Remove JSDoc comments. Per CLAUDE.md:363-368 and AGENTS.md:23, NO CODE COMMENTS are allowed in this codebase (`//`, `/* */`, or `/** */`). Code must be self-explanatory through naming, types, and structure.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface DeepLinkCommand { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: "record" | "stop" | "pause" | "resume" | "toggle-pause" | "switch-mic" | "switch-camera"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| params?: Record<string, string>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Parse deep link URL and extract command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function parseDeepLinkCommand(url: string): DeepLinkCommand | null { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const urlObj = new URL(url); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Only handle cap:// protocol | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (urlObj.protocol !== "cap:") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The URL scheme
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/utils/deep-link-commands.ts
Line: 23:26
Comment:
The URL scheme `cap:` doesn't match the configured scheme in `apps/desktop/src-tauri/tauri.conf.json:33` which is `cap-desktop`. Deep links will fail because the protocol is incorrect.
```suggestion
if (urlObj.protocol !== "cap-desktop:") {
return null;
}
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const action = urlObj.hostname as DeepLinkCommand["action"]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const params: Record<string, string> = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| urlObj.searchParams.forEach((value, key) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| params[key] = value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+33
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parsing approach using URL hostname for actions conflicts with the existing deep link architecture. The existing Rust implementation expects URLs in format Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/utils/deep-link-commands.ts
Line: 28:33
Comment:
The parsing approach using URL hostname for actions conflicts with the existing deep link architecture. The existing Rust implementation expects URLs in format `cap-desktop://action?value=<json>` where actions are serialized as JSON (see `deeplink_actions.rs:95-104`). This implementation uses a completely different pattern that won't integrate with the existing system.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate action | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const validActions: DeepLinkCommand["action"][] = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "record", "stop", "pause", "resume", "toggle-pause", "switch-mic", "switch-camera" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!validActions.includes(action)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn(`Unknown deep link action: ${action}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { action, params }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Failed to parse deep link:", error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove all JSDoc comments ( Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/utils/deep-link-commands.ts
Line: 20:49
Comment:
Remove all JSDoc comments (`/** ... */`). The codebase strictly prohibits code comments of any form per CLAUDE.md:363-368.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Execute deep link command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function executeDeepLinkCommand(command: DeepLinkCommand): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { action, params = {} } = command; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`Executing deep link command: ${action}`, params); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (action) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "record": | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await handleRecordCommand(params); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "stop": | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await handleStopCommand(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "pause": | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await handlePauseCommand(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "resume": | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await handleResumeCommand(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "toggle-pause": | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await handleTogglePauseCommand(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "switch-mic": | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await handleSwitchMicCommand(params); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "switch-camera": | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await handleSwitchCameraCommand(params); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn(`Unhandled deep link action: ${action}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Handle record command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Params: mode ("instant" | "studio"), camera?, microphone? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function handleRecordCommand(params: Record<string, string>): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mode = (params.mode as RecordingMode) || "instant"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set recording mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await commands.setRecordingMode(mode); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const inputs: StartRecordingInputs = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| mode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+97
to
+99
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/utils/deep-link-commands.ts
Line: 97:99
Comment:
The `StartRecordingInputs` type expects a `capture_target` of type `ScreenCaptureTarget` (union of Display/Window with id), but this passes a string `"screen"` which will cause a type error. See the correct usage in `deeplink_actions.rs:122-133`.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| capture_target: params.target || "screen", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add camera if specified | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (params.camera) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| inputs.camera_label = params.camera; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add microphone if specified | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (params.microphone) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| inputs.audio_inputs = [{ label: params.microphone, device_type: "mic" }]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = await commands.startRecording(inputs); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (result !== "Started") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(`Failed to start recording: ${result}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Handle stop command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function handleStopCommand(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await commands.stopRecording(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Handle pause command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function handlePauseCommand(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await commands.pauseRecording(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Handle resume command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function handleResumeCommand(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await commands.resumeRecording(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Handle toggle pause command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function handleTogglePauseCommand(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await commands.togglePauseRecording(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Handle switch microphone command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Params: label (microphone name/device ID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function handleSwitchMicCommand(params: Record<string, string>): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const label = params.label || params.device; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!label) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("No microphone label provided"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| await commands.setMicInput(label); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Handle switch camera command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Params: id (camera device ID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function handleSwitchCameraCommand(params: Record<string, string>): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const id = params.id || params.device; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!id) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("No camera ID provided"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| await commands.setCameraInput(id, true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Initialize deep link command listener | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Returns unsubscribe function | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function initDeepLinkCommands(): Promise<() => void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log("Initializing deep link commands..."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const unsubscribe = await onOpenUrl(async (urls) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const url of urls) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const command = parseDeepLinkCommand(url); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (command) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await executeDeepLinkCommand(command); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(`Failed to execute command from ${url}:`, error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return unsubscribe; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Generate deep link URL for a command | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function generateDeepLink( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: DeepLinkCommand["action"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| params?: Record<string, string> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = new URL(`cap://${action}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+203
to
+210
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/utils/deep-link-commands.ts
Line: 203:210
Comment:
The `generateDeepLink` function generates URLs with the wrong scheme (`cap://`) instead of the configured `cap-desktop://` scheme from `tauri.conf.json`.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (params) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Object.entries(params).forEach(([key, value]) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| url.searchParams.set(key, value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+205
to
+215
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return url.toString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Export convenience functions for generating deep links | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const deepLinks = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| record: (params?: { mode?: RecordingMode; camera?: string; microphone?: string }) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| generateDeepLink("record", params as Record<string, string>), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+222
to
+223
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This cast can also leak
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| stop: () => generateDeepLink("stop"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| pause: () => generateDeepLink("pause"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| resume: () => generateDeepLink("resume"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| togglePause: () => generateDeepLink("toggle-pause"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| switchMic: (label: string) => generateDeepLink("switch-mic", { label }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| switchCamera: (id: string) => generateDeepLink("switch-camera", { id }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+230
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This entire file duplicates existing deep link functionality already implemented in Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/utils/deep-link-commands.ts
Line: 1:230
Comment:
This entire file duplicates existing deep link functionality already implemented in `apps/desktop/src-tauri/src/deeplink_actions.rs`. The Rust implementation is already registered and handles deep links via the `tauri_plugin_deep_link` plugin (see `apps/desktop/src-tauri/src/lib.rs:3344-3346`). Adding a second TypeScript-based handler creates conflicting implementations and architectural inconsistency.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # Cap Raycast Extension | ||
|
|
||
| Control Cap screen recording from Raycast. | ||
|
|
||
| ## Features | ||
|
|
||
| - **Start Recording** - Start a new screen recording instantly | ||
| - **Stop Recording** - Stop the current recording | ||
| - **Pause Recording** - Pause the recording | ||
| - **Resume Recording** - Resume a paused recording | ||
| - **Toggle Pause** - Toggle between pause and resume | ||
| - **Switch Microphone** - Change to a different microphone input | ||
| - **Switch Camera** - Change to a different camera input | ||
|
|
||
| ## Installation | ||
|
|
||
| 1. Make sure you have [Raycast](https://raycast.com/) installed | ||
| 2. Install the Cap extension from the Raycast Store | ||
| 3. Ensure [Cap](https://cap.so) is installed on your Mac | ||
|
|
||
| ## Deep Link Protocol | ||
|
|
||
| This extension uses Cap's deep link protocol to control the app: | ||
|
|
||
| - `cap://record` - Start recording | ||
| - `cap://stop` - Stop recording | ||
| - `cap://pause` - Pause recording | ||
| - `cap://resume` - Resume recording | ||
| - `cap://toggle-pause` - Toggle pause state | ||
| - `cap://switch-mic?label=<id>` - Switch microphone | ||
| - `cap://switch-camera?id=<id>` - Switch camera | ||
|
Comment on lines
+23
to
+31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documentation lists incorrect URL scheme. Cap uses Prompt To Fix With AIThis is a comment left during a code review.
Path: extensions/raycast-cap/README.md
Line: 23:31
Comment:
Documentation lists incorrect URL scheme. Cap uses `cap-desktop://` (configured in `apps/desktop/src-tauri/tauri.conf.json`), not `cap://`. All examples need updating to use the correct scheme.
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| ## Requirements | ||
|
|
||
| - macOS 11.0 or later | ||
| - Cap app installed | ||
| - Raycast v1.50.0 or later | ||
|
|
||
| ## Development | ||
|
|
||
| ```bash | ||
| cd extensions/raycast-cap | ||
| npm install | ||
| npm run dev | ||
| ``` | ||
|
|
||
| ## License | ||
|
|
||
| MIT | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| { | ||
| "name": "cap", | ||
| "title": "Cap", | ||
| "description": "Control Cap screen recording from Raycast", | ||
| "icon": "cap-icon.png", | ||
| "author": "divol89", | ||
| "categories": ["Productivity", "Media"], | ||
| "license": "MIT", | ||
| "commands": [ | ||
| { | ||
| "name": "start-recording", | ||
| "title": "Start Recording", | ||
| "description": "Start a new screen recording", | ||
| "mode": "no-view" | ||
| }, | ||
| { | ||
| "name": "stop-recording", | ||
| "title": "Stop Recording", | ||
| "description": "Stop the current recording", | ||
| "mode": "no-view" | ||
| }, | ||
| { | ||
| "name": "pause-recording", | ||
| "title": "Pause Recording", | ||
| "description": "Pause the current recording", | ||
| "mode": "no-view" | ||
| }, | ||
| { | ||
| "name": "resume-recording", | ||
| "title": "Resume Recording", | ||
| "description": "Resume the paused recording", | ||
| "mode": "no-view" | ||
| }, | ||
| { | ||
| "name": "toggle-pause", | ||
| "title": "Toggle Pause", | ||
| "description": "Toggle pause/resume recording", | ||
| "mode": "no-view" | ||
| }, | ||
| { | ||
| "name": "switch-microphone", | ||
| "title": "Switch Microphone", | ||
| "description": "Switch to a different microphone", | ||
| "mode": "view" | ||
| }, | ||
| { | ||
| "name": "switch-camera", | ||
| "title": "Switch Camera", | ||
| "description": "Switch to a different camera", | ||
| "mode": "view" | ||
| } | ||
| ], | ||
| "dependencies": { | ||
| "@raycast/api": "^1.64.0", | ||
| "@raycast/utils": "^1.10.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^20.8.10", | ||
| "typescript": "^5.2.2" | ||
| }, | ||
| "scripts": { | ||
| "build": "ray build -e dist", | ||
| "dev": "ray develop", | ||
| "fix-lint": "ray fix-lint", | ||
| "lint": "ray lint" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { showToast, Toast } from "@raycast/api"; | ||
| import { openDeepLink, generateDeepLink } from "./utils"; | ||
|
|
||
| export default async function Command() { | ||
| try { | ||
| await openDeepLink(generateDeepLink("pause")); | ||
|
|
||
| await showToast({ | ||
| style: Toast.Style.Success, | ||
| title: "Paused Recording", | ||
| }); | ||
| } catch (error) { | ||
| await showToast({ | ||
| style: Toast.Style.Failure, | ||
| title: "Failed to Pause Recording", | ||
| message: String(error), | ||
| }); | ||
| } | ||
| } |
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.
Importing a module that duplicates existing Rust-based deep link handling and uses incorrect URL schemes.
Prompt To Fix With AI