diff --git a/docs/content.en/docs/release-notes/_index.md b/docs/content.en/docs/release-notes/_index.md index 97f353ac..64c8722b 100644 --- a/docs/content.en/docs/release-notes/_index.md +++ b/docs/content.en/docs/release-notes/_index.md @@ -35,6 +35,7 @@ Information about release notes of Coco Server is provided here. - fix: arrow keys still navigated search when menu opened with Cmd+K #642 - fix: input lost when reopening dialog after search #644 - fix: web page unmount event #645 +- fix: fix the problem of local path not opening #650 ### ✈️ Improvements diff --git a/package.json b/package.json index 02f1fd2a..8727a080 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@tauri-apps/plugin-global-shortcut": "~2.0.0", "@tauri-apps/plugin-http": "~2.0.2", "@tauri-apps/plugin-log": "~2.4.0", + "@tauri-apps/plugin-opener": "^2.2.7", "@tauri-apps/plugin-os": "^2.2.1", "@tauri-apps/plugin-process": "^2.2.1", "@tauri-apps/plugin-shell": "^2.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6cc23bb1..bedc14e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: '@tauri-apps/plugin-log': specifier: ~2.4.0 version: 2.4.0 + '@tauri-apps/plugin-opener': + specifier: ^2.2.7 + version: 2.2.7 '@tauri-apps/plugin-os': specifier: ^2.2.1 version: 2.2.1 @@ -1265,6 +1268,9 @@ packages: '@tauri-apps/plugin-log@2.4.0': resolution: {integrity: sha512-j7yrDtLNmayCBOO2esl3aZv9jSXy2an8MDLry3Ys9ZXerwUg35n1Y2uD8HoCR+8Ng/EUgx215+qOUfJasjYrHw==} + '@tauri-apps/plugin-opener@2.2.7': + resolution: {integrity: sha512-uduEyvOdjpPOEeDRrhwlCspG/f9EQalHumWBtLBnp3fRp++fKGLqDOyUhSIn7PzX45b/rKep//ZQSAQoIxobLA==} + '@tauri-apps/plugin-os@2.2.1': resolution: {integrity: sha512-cNYpNri2CCc6BaNeB6G/mOtLvg8dFyFQyCUdf2y0K8PIAKGEWdEcu8DECkydU2B+oj4OJihDPD2de5K6cbVl9A==} @@ -4666,6 +4672,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.5.0 + '@tauri-apps/plugin-opener@2.2.7': + dependencies: + '@tauri-apps/api': 2.5.0 + '@tauri-apps/plugin-os@2.2.1': dependencies: '@tauri-apps/api': 2.5.0 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 44da7c68..3a7d773d 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -866,6 +866,7 @@ dependencies = [ "tauri-plugin-http", "tauri-plugin-log", "tauri-plugin-macos-permissions", + "tauri-plugin-opener", "tauri-plugin-os", "tauri-plugin-process", "tauri-plugin-screenshots", @@ -6279,6 +6280,28 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "tauri-plugin-opener" +version = "2.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66644b71a31ec1a8a52c4a16575edd28cf763c87cf4a7da24c884122b5c77097" +dependencies = [ + "dunce", + "glob", + "objc2-app-kit 0.3.1", + "objc2-foundation 0.3.1", + "open", + "schemars", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", + "url", + "windows 0.61.1", + "zbus", +] + [[package]] name = "tauri-plugin-os" version = "2.2.1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 95f04d14..dc0087a9 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -98,6 +98,7 @@ derive_more = { version = "2.0.1", features = ["display"] } anyhow = "1.0.98" function_name = "0.3.0" regex = "1.11.1" +tauri-plugin-opener = "2" [target."cfg(target_os = \"macos\")".dependencies] tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index b9920b11..dc20e2d8 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -71,6 +71,7 @@ "process:default", "updater:default", "windows-version:default", - "log:default" + "log:default", + "opener:default" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 8bcc4492..cf7cca4e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -88,7 +88,8 @@ pub fn run() { .plugin(tauri_plugin_screenshots::init()) .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_updater::Builder::new().build()) - .plugin(tauri_plugin_windows_version::init()); + .plugin(tauri_plugin_windows_version::init()) + .plugin(tauri_plugin_opener::init()); // Conditional compilation for macOS #[cfg(target_os = "macos")] diff --git a/src/components/Settings/Extensions/components/Details/Applications/index.tsx b/src/components/Settings/Extensions/components/Details/Applications/index.tsx index e266356f..f5e13f32 100644 --- a/src/components/Settings/Extensions/components/Details/Applications/index.tsx +++ b/src/components/Settings/Extensions/components/Details/Applications/index.tsx @@ -106,7 +106,7 @@ const Applications = () => { { - platformAdapter.openExternal(item); + platformAdapter.revealItemInDir(item); }} /> diff --git a/src/types/platform.ts b/src/types/platform.ts index a81d7546..79e6ce03 100644 --- a/src/types/platform.ts +++ b/src/types/platform.ts @@ -102,13 +102,14 @@ export interface SystemOperations { checkUpdate: () => Promise; relaunchApp: () => Promise; isTauri: () => boolean; - openExternal: (url: string) => Promise; + openUrl: (url: string) => Promise; commands: (commandName: string, ...args: any[]) => Promise; isWindows10: () => Promise; + revealItemInDir: (path: string) => Promise; } // Base platform adapter interface export interface BasePlatformAdapter extends WindowOperations, - ThemeAndEvents, - SystemOperations { } + ThemeAndEvents, + SystemOperations {} diff --git a/src/utils/index.ts b/src/utils/index.ts index eb8c20a2..3062579c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -72,7 +72,7 @@ export const OpenURLWithBrowser = async (url?: string) => { if (!url) return; if (IsTauri()) { try { - await platformAdapter.openExternal(url); + await platformAdapter.openUrl(url); await platformAdapter.commands("hide_coco"); console.log("URL opened in default browser"); } catch (error) { diff --git a/src/utils/tauri.ts b/src/utils/tauri.ts index 9885aead..2aa7f966 100644 --- a/src/utils/tauri.ts +++ b/src/utils/tauri.ts @@ -1,4 +1,3 @@ - // This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. /** user-defined commands **/ @@ -51,8 +50,18 @@ export const commands = { async getCurrentRecording(): Promise> { return await TAURI_INVOKE("get_current_recording"); }, - async exportVideo(videoId: string, project: ProjectConfiguration, progress: TAURI_CHANNEL, force: boolean): Promise { - return await TAURI_INVOKE("export_video", { videoId, project, progress, force }); + async exportVideo( + videoId: string, + project: ProjectConfiguration, + progress: TAURI_CHANNEL, + force: boolean + ): Promise { + return await TAURI_INVOKE("export_video", { + videoId, + project, + progress, + force, + }); }, async copyFileToPath(src: string, dst: string): Promise { return await TAURI_INVOKE("copy_file_to_path", { src, dst }); @@ -66,10 +75,15 @@ export const commands = { async openFilePath(path: string): Promise { return await TAURI_INVOKE("open_file_path", { path }); }, - async getVideoMetadata(videoId: string, videoType: VideoType | null): Promise { + async getVideoMetadata( + videoId: string, + videoType: VideoType | null + ): Promise { return await TAURI_INVOKE("get_video_metadata", { videoId, videoType }); }, - async createEditorInstance(videoId: string): Promise { + async createEditorInstance( + videoId: string + ): Promise { return await TAURI_INVOKE("create_editor_instance", { videoId }); }, async startPlayback(videoId: string): Promise { @@ -78,10 +92,16 @@ export const commands = { async stopPlayback(videoId: string): Promise { await TAURI_INVOKE("stop_playback", { videoId }); }, - async setPlayheadPosition(videoId: string, frameNumber: number): Promise { + async setPlayheadPosition( + videoId: string, + frameNumber: number + ): Promise { await TAURI_INVOKE("set_playhead_position", { videoId, frameNumber }); }, - async setProjectConfig(videoId: string, config: ProjectConfiguration): Promise { + async setProjectConfig( + videoId: string, + config: ProjectConfiguration + ): Promise { await TAURI_INVOKE("set_project_config", { videoId, config }); }, async openEditor(id: string): Promise { @@ -99,7 +119,10 @@ export const commands = { async requestPermission(permission: OSPermission): Promise { await TAURI_INVOKE("request_permission", { permission }); }, - async uploadExportedVideo(videoId: string, mode: UploadMode): Promise { + async uploadExportedVideo( + videoId: string, + mode: UploadMode + ): Promise { return await TAURI_INVOKE("upload_exported_video", { videoId, mode }); }, async uploadScreenshot(screenshotPath: string): Promise { @@ -108,13 +131,16 @@ export const commands = { async getRecordingMeta(id: string, fileType: string): Promise { return await TAURI_INVOKE("get_recording_meta", { id, fileType }); }, - async saveFileDialog(fileName: string, fileType: string): Promise { + async saveFileDialog( + fileName: string, + fileType: string + ): Promise { return await TAURI_INVOKE("save_file_dialog", { fileName, fileType }); }, - async listRecordings(): Promise<([string, string, RecordingMeta])[]> { + async listRecordings(): Promise<[string, string, RecordingMeta][]> { return await TAURI_INVOKE("list_recordings"); }, - async listScreenshots(): Promise<([string, string, RecordingMeta])[]> { + async listScreenshots(): Promise<[string, string, RecordingMeta][]> { return await TAURI_INVOKE("list_screenshots"); }, async checkUpgradedAndUpdate(): Promise { @@ -144,7 +170,9 @@ export const commands = { async sendFeedbackRequest(feedback: string): Promise { return await TAURI_INVOKE("send_feedback_request", { feedback }); }, - async positionTrafficLights(controlsInset: [number, number] | null): Promise { + async positionTrafficLights( + controlsInset: [number, number] | null + ): Promise { await TAURI_INVOKE("position_traffic_lights", { controlsInset }); }, async setTheme(theme: AppTheme): Promise { @@ -158,31 +186,30 @@ export const commands = { }, async writeClipboardString(text: string): Promise { return await TAURI_INVOKE("write_clipboard_string", { text }); - } -} + }, +}; /** user-defined events **/ - export const events = __makeEvents__<{ - audioInputLevelChange: AudioInputLevelChange, - authenticationInvalid: AuthenticationInvalid, - currentRecordingChanged: CurrentRecordingChanged, - editorStateChanged: EditorStateChanged, - newNotification: NewNotification, - newRecordingAdded: NewRecordingAdded, - newScreenshotAdded: NewScreenshotAdded, - recordingMetaChanged: RecordingMetaChanged, - recordingOptionsChanged: RecordingOptionsChanged, - recordingStarted: RecordingStarted, - recordingStopped: RecordingStopped, - renderFrameEvent: RenderFrameEvent, - requestNewScreenshot: RequestNewScreenshot, - requestOpenSettings: RequestOpenSettings, - requestRestartRecording: RequestRestartRecording, - requestStartRecording: RequestStartRecording, - requestStopRecording: RequestStopRecording, - uploadProgress: UploadProgress + audioInputLevelChange: AudioInputLevelChange; + authenticationInvalid: AuthenticationInvalid; + currentRecordingChanged: CurrentRecordingChanged; + editorStateChanged: EditorStateChanged; + newNotification: NewNotification; + newRecordingAdded: NewRecordingAdded; + newScreenshotAdded: NewScreenshotAdded; + recordingMetaChanged: RecordingMetaChanged; + recordingOptionsChanged: RecordingOptionsChanged; + recordingStarted: RecordingStarted; + recordingStopped: RecordingStopped; + renderFrameEvent: RenderFrameEvent; + requestNewScreenshot: RequestNewScreenshot; + requestOpenSettings: RequestOpenSettings; + requestRestartRecording: RequestRestartRecording; + requestStartRecording: RequestStartRecording; + requestStopRecording: RequestStopRecording; + uploadProgress: UploadProgress; }>({ audioInputLevelChange: "audio-input-level-change", authenticationInvalid: "authentication-invalid", @@ -201,91 +228,247 @@ export const events = __makeEvents__<{ requestRestartRecording: "request-restart-recording", requestStartRecording: "request-start-recording", requestStopRecording: "request-stop-recording", - uploadProgress: "upload-progress" -}) + uploadProgress: "upload-progress", +}); /** user-defined constants **/ - - /** user-defined types **/ -export type AppEndpoint = string -export type AppTheme = "auto" | "light" | "dark" -export type WindowTheme = "light" | "dark" -export type AspectRatio = "wide" | "vertical" | "square" | "classic" | "tall" -export type Audio = { duration: number; sample_rate: number; channels: number } -export type AudioConfiguration = { mute: boolean; improve: boolean } -export type AudioInputLevelChange = number -export type AudioMeta = { path: string } -export type AuthStore = { token: string; user_id: string | null; expires: number; plan: Plan | null } -export type AuthenticationInvalid = null -export type BackgroundConfiguration = { source: BackgroundSource; blur: number; padding: number; rounding: number; inset: number; crop: Crop | null } -export type BackgroundSource = { type: "wallpaper"; id: number } | { type: "image"; path: string | null } | { type: "color"; value: [number, number, number] } | { type: "gradient"; from: [number, number, number]; to: [number, number, number]; angle?: number } -export type Bounds = { x: number; y: number; width: number; height: number } -export type Camera = { hide: boolean; mirror: boolean; position: CameraPosition; size: number; zoom_size: number | null; rounding: number; shadow: number } -export type CameraMeta = { path: string } -export type CameraPosition = { x: CameraXPosition; y: CameraYPosition } -export type CameraXPosition = "left" | "center" | "right" -export type CameraYPosition = "top" | "bottom" -export type CaptureScreen = { id: number; name: string } -export type CaptureWindow = { id: number; owner_name: string; name: string; bounds: Bounds } -export type Crop = { position: XY; size: XY } -export type CurrentRecordingChanged = null -export type CursorAnimationStyle = "regular" | "slow" | "fast" -export type CursorConfiguration = { hideWhenIdle: boolean; size: number; type: CursorType; animationStyle: CursorAnimationStyle } -export type CursorType = "pointer" | "circle" -export type Display = { path: string } -export type EditorStateChanged = { playhead_position: number } -export type Flags = { recordMouse: boolean; split: boolean; pauseResume: boolean; zoom: boolean; customS3: boolean } -export type GeneralSettingsStore = { uploadIndividualFiles?: boolean; openEditorAfterRecording?: boolean; hideDockIcon?: boolean; autoCreateShareableLink?: boolean; enableNotifications?: boolean; disableAutoOpenLinks?: boolean; hasCompletedStartup?: boolean; theme?: AppTheme } -export type Hotkey = { code: string; meta: boolean; ctrl: boolean; alt: boolean; shift: boolean } -export type HotkeyAction = "startRecording" | "stopRecording" | "restartRecording" | "takeScreenshot" -export type HotkeysConfiguration = { show: boolean } -export type HotkeysStore = { hotkeys: { [key in HotkeyAction]: Hotkey } } -export type JsonValue = [T] -export type MultipleSegment = { display: Display; camera?: CameraMeta | null; audio?: AudioMeta | null; cursor?: string | null } -export type MultipleSegments = { segments: MultipleSegment[]; cursors: { [key in string]: string } } -export type NewNotification = { title: string; body: string; is_error: boolean } -export type NewRecordingAdded = { path: string } -export type NewScreenshotAdded = { path: string } -export type OSPermission = "screenRecording" | "camera" | "microphone" | "accessibility" -export type OSPermissionStatus = "notNeeded" | "empty" | "granted" | "denied" -export type OSPermissionsCheck = { screenRecording: OSPermissionStatus; microphone: OSPermissionStatus; camera: OSPermissionStatus; accessibility: OSPermissionStatus } -export type Plan = { upgraded: boolean; last_checked: number } -export type PreCreatedVideo = { id: string; link: string; config: S3UploadMeta } -export type ProjectConfiguration = { aspectRatio: AspectRatio | null; background: BackgroundConfiguration; camera: Camera; audio: AudioConfiguration; cursor: CursorConfiguration; hotkeys: HotkeysConfiguration; timeline?: TimelineConfiguration | null; motionBlur: number | null } -export type ProjectRecordings = { segments: SegmentRecordings[] } -export type RecordingInfo = { captureTarget: ScreenCaptureTarget } -export type RecordingMeta = ({ segment: SingleSegment } | { inner: MultipleSegments }) & { pretty_name: string; sharing?: SharingMeta | null } -export type RecordingMetaChanged = { id: string } -export type RecordingOptions = { captureTarget: ScreenCaptureTarget; cameraLabel: string | null; audioInputName: string | null } -export type RecordingOptionsChanged = null -export type RecordingStarted = null -export type RecordingStopped = { path: string } -export type RenderFrameEvent = { frame_number: number } -export type RenderProgress = { type: "Starting"; total_frames: number } | { type: "EstimatedTotalFrames"; total_frames: number } | { type: "FrameRendered"; current_frame: number } -export type RequestNewScreenshot = null -export type RequestOpenSettings = { page: string } -export type RequestRestartRecording = null -export type RequestStartRecording = null -export type RequestStopRecording = null -export type S3UploadMeta = { id: string; user_id: string; aws_region?: string; aws_bucket?: string } -export type ScreenCaptureTarget = ({ variant: "window" } & CaptureWindow) | ({ variant: "screen" } & CaptureScreen) -export type SegmentRecordings = { display: Video; camera: Video | null; audio: Audio | null } -export type SerializedEditorInstance = { framesSocketUrl: string; recordingDuration: number; savedProjectConfig: ProjectConfiguration; recordings: ProjectRecordings; path: string; prettyName: string } -export type SharingMeta = { id: string; link: string } -export type ShowCapWindow = "Setup" | "Main" | { Settings: { page: string | null } } | { Editor: { project_id: string } } | "PrevRecordings" | "WindowCaptureOccluder" | { Camera: { ws_port: number } } | { InProgressRecording: { position: [number, number] | null } } | "Upgrade" -export type SingleSegment = { display: Display; camera?: CameraMeta | null; audio?: AudioMeta | null; cursor?: string | null } -export type TimelineConfiguration = { segments: TimelineSegment[]; zoomSegments?: ZoomSegment[] } -export type TimelineSegment = { recordingSegment: number | null; timescale: number; start: number; end: number } -export type UploadMode = { Initial: { pre_created_video: PreCreatedVideo | null } } | "Reupload" -export type UploadProgress = { stage: string; progress: number; message: string } -export type UploadResult = { Success: string } | "NotAuthenticated" | "PlanCheckFailed" | "UpgradeRequired" -export type Video = { duration: number; width: number; height: number } -export type VideoRecordingMetadata = { duration: number; size: number } -export type VideoType = "screen" | "output" -export type XY = { x: T; y: T } -export type ZoomSegment = { start: number; end: number; amount: number } +export type AppEndpoint = string; +export type AppTheme = "auto" | "light" | "dark"; +export type WindowTheme = "light" | "dark"; +export type AspectRatio = "wide" | "vertical" | "square" | "classic" | "tall"; +export type Audio = { duration: number; sample_rate: number; channels: number }; +export type AudioConfiguration = { mute: boolean; improve: boolean }; +export type AudioInputLevelChange = number; +export type AudioMeta = { path: string }; +export type AuthStore = { + token: string; + user_id: string | null; + expires: number; + plan: Plan | null; +}; +export type AuthenticationInvalid = null; +export type BackgroundConfiguration = { + source: BackgroundSource; + blur: number; + padding: number; + rounding: number; + inset: number; + crop: Crop | null; +}; +export type BackgroundSource = + | { type: "wallpaper"; id: number } + | { type: "image"; path: string | null } + | { type: "color"; value: [number, number, number] } + | { + type: "gradient"; + from: [number, number, number]; + to: [number, number, number]; + angle?: number; + }; +export type Bounds = { x: number; y: number; width: number; height: number }; +export type Camera = { + hide: boolean; + mirror: boolean; + position: CameraPosition; + size: number; + zoom_size: number | null; + rounding: number; + shadow: number; +}; +export type CameraMeta = { path: string }; +export type CameraPosition = { x: CameraXPosition; y: CameraYPosition }; +export type CameraXPosition = "left" | "center" | "right"; +export type CameraYPosition = "top" | "bottom"; +export type CaptureScreen = { id: number; name: string }; +export type CaptureWindow = { + id: number; + owner_name: string; + name: string; + bounds: Bounds; +}; +export type Crop = { position: XY; size: XY }; +export type CurrentRecordingChanged = null; +export type CursorAnimationStyle = "regular" | "slow" | "fast"; +export type CursorConfiguration = { + hideWhenIdle: boolean; + size: number; + type: CursorType; + animationStyle: CursorAnimationStyle; +}; +export type CursorType = "pointer" | "circle"; +export type Display = { path: string }; +export type EditorStateChanged = { playhead_position: number }; +export type Flags = { + recordMouse: boolean; + split: boolean; + pauseResume: boolean; + zoom: boolean; + customS3: boolean; +}; +export type GeneralSettingsStore = { + uploadIndividualFiles?: boolean; + openEditorAfterRecording?: boolean; + hideDockIcon?: boolean; + autoCreateShareableLink?: boolean; + enableNotifications?: boolean; + disableAutoOpenLinks?: boolean; + hasCompletedStartup?: boolean; + theme?: AppTheme; +}; +export type Hotkey = { + code: string; + meta: boolean; + ctrl: boolean; + alt: boolean; + shift: boolean; +}; +export type HotkeyAction = + | "startRecording" + | "stopRecording" + | "restartRecording" + | "takeScreenshot"; +export type HotkeysConfiguration = { show: boolean }; +export type HotkeysStore = { hotkeys: { [key in HotkeyAction]: Hotkey } }; +export type JsonValue = [T]; +export type MultipleSegment = { + display: Display; + camera?: CameraMeta | null; + audio?: AudioMeta | null; + cursor?: string | null; +}; +export type MultipleSegments = { + segments: MultipleSegment[]; + cursors: { [key in string]: string }; +}; +export type NewNotification = { + title: string; + body: string; + is_error: boolean; +}; +export type NewRecordingAdded = { path: string }; +export type NewScreenshotAdded = { path: string }; +export type OSPermission = + | "screenRecording" + | "camera" + | "microphone" + | "accessibility"; +export type OSPermissionStatus = "notNeeded" | "empty" | "granted" | "denied"; +export type OSPermissionsCheck = { + screenRecording: OSPermissionStatus; + microphone: OSPermissionStatus; + camera: OSPermissionStatus; + accessibility: OSPermissionStatus; +}; +export type Plan = { upgraded: boolean; last_checked: number }; +export type PreCreatedVideo = { + id: string; + link: string; + config: S3UploadMeta; +}; +export type ProjectConfiguration = { + aspectRatio: AspectRatio | null; + background: BackgroundConfiguration; + camera: Camera; + audio: AudioConfiguration; + cursor: CursorConfiguration; + hotkeys: HotkeysConfiguration; + timeline?: TimelineConfiguration | null; + motionBlur: number | null; +}; +export type ProjectRecordings = { segments: SegmentRecordings[] }; +export type RecordingInfo = { captureTarget: ScreenCaptureTarget }; +export type RecordingMeta = ( + | { segment: SingleSegment } + | { inner: MultipleSegments } +) & { pretty_name: string; sharing?: SharingMeta | null }; +export type RecordingMetaChanged = { id: string }; +export type RecordingOptions = { + captureTarget: ScreenCaptureTarget; + cameraLabel: string | null; + audioInputName: string | null; +}; +export type RecordingOptionsChanged = null; +export type RecordingStarted = null; +export type RecordingStopped = { path: string }; +export type RenderFrameEvent = { frame_number: number }; +export type RenderProgress = + | { type: "Starting"; total_frames: number } + | { type: "EstimatedTotalFrames"; total_frames: number } + | { type: "FrameRendered"; current_frame: number }; +export type RequestNewScreenshot = null; +export type RequestOpenSettings = { page: string }; +export type RequestRestartRecording = null; +export type RequestStartRecording = null; +export type RequestStopRecording = null; +export type S3UploadMeta = { + id: string; + user_id: string; + aws_region?: string; + aws_bucket?: string; +}; +export type ScreenCaptureTarget = + | ({ variant: "window" } & CaptureWindow) + | ({ variant: "screen" } & CaptureScreen); +export type SegmentRecordings = { + display: Video; + camera: Video | null; + audio: Audio | null; +}; +export type SerializedEditorInstance = { + framesSocketUrl: string; + recordingDuration: number; + savedProjectConfig: ProjectConfiguration; + recordings: ProjectRecordings; + path: string; + prettyName: string; +}; +export type SharingMeta = { id: string; link: string }; +export type ShowCapWindow = + | "Setup" + | "Main" + | { Settings: { page: string | null } } + | { Editor: { project_id: string } } + | "PrevRecordings" + | "WindowCaptureOccluder" + | { Camera: { ws_port: number } } + | { InProgressRecording: { position: [number, number] | null } } + | "Upgrade"; +export type SingleSegment = { + display: Display; + camera?: CameraMeta | null; + audio?: AudioMeta | null; + cursor?: string | null; +}; +export type TimelineConfiguration = { + segments: TimelineSegment[]; + zoomSegments?: ZoomSegment[]; +}; +export type TimelineSegment = { + recordingSegment: number | null; + timescale: number; + start: number; + end: number; +}; +export type UploadMode = + | { Initial: { pre_created_video: PreCreatedVideo | null } } + | "Reupload"; +export type UploadProgress = { + stage: string; + progress: number; + message: string; +}; +export type UploadResult = + | { Success: string } + | "NotAuthenticated" + | "PlanCheckFailed" + | "UpgradeRequired"; +export type Video = { duration: number; width: number; height: number }; +export type VideoRecordingMetadata = { duration: number; size: number }; +export type VideoType = "screen" | "output"; +export type XY = { x: T; y: T }; +export type ZoomSegment = { start: number; end: number; amount: number }; /** tauri-specta globals **/ @@ -298,14 +481,14 @@ import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webview type __EventObj__ = { listen: ( - cb: TAURI_API_EVENT.EventCallback, + cb: TAURI_API_EVENT.EventCallback ) => ReturnType>; once: ( - cb: TAURI_API_EVENT.EventCallback, + cb: TAURI_API_EVENT.EventCallback ) => ReturnType>; emit: null extends T - ? (payload?: T) => ReturnType - : (payload: T) => ReturnType; + ? (payload?: T) => ReturnType + : (payload: T) => ReturnType; }; export type Result = @@ -313,7 +496,7 @@ export type Result = | { status: "error"; error: E }; function __makeEvents__>( - mappings: Record, + mappings: Record ) { return new Proxy( {} as unknown as { @@ -325,7 +508,7 @@ function __makeEvents__>( get: (_, event) => { const name = mappings[event as keyof T]; - return new Proxy((() => { }) as any, { + return new Proxy((() => {}) as any, { apply: (_, __, [window]: [__WebviewWindow__]) => ({ listen: (arg: any) => window.listen(name, arg), once: (arg: any) => window.once(name, arg), @@ -343,6 +526,6 @@ function __makeEvents__>( }, }); }, - }, + } ); } diff --git a/src/utils/tauriAdapter.ts b/src/utils/tauriAdapter.ts index 936a63a5..c76531b3 100644 --- a/src/utils/tauriAdapter.ts +++ b/src/utils/tauriAdapter.ts @@ -206,10 +206,10 @@ export const createTauriAdapter = (): TauriPlatformAdapter => { return true; }, - async openExternal(url) { - const { open } = await import("@tauri-apps/plugin-shell"); + async openUrl(url) { + const { openUrl } = await import("@tauri-apps/plugin-opener"); - open(url); + openUrl(url); }, isWindows10, @@ -223,5 +223,11 @@ export const createTauriAdapter = (): TauriPlatformAdapter => { }, metadata, + + async revealItemInDir(path) { + const { revealItemInDir } = await import("@tauri-apps/plugin-opener"); + + revealItemInDir(path); + }, }; }; diff --git a/src/utils/webAdapter.ts b/src/utils/webAdapter.ts index a53b56a5..9e4a3449 100644 --- a/src/utils/webAdapter.ts +++ b/src/utils/webAdapter.ts @@ -48,7 +48,7 @@ export const createWebAdapter = (): WebPlatformAdapter => { async listenEvent(event, _callback) { console.log("Web mode simulated event listen", event); - return () => { }; + return () => {}; }, async setAlwaysOnTop(isPinned) { @@ -110,7 +110,7 @@ export const createWebAdapter = (): WebPlatformAdapter => { async listenThemeChanged() { console.log("Web mode simulated theme change listener"); - return () => { }; + return () => {}; }, async getWebviewWindow() { @@ -157,14 +157,14 @@ export const createWebAdapter = (): WebPlatformAdapter => { async listenWindowEvent(event, _callback) { console.log("Web mode simulated listen window event:", event); - return () => { }; + return () => {}; }, isTauri() { return false; }, - async openExternal(url) { + async openUrl(url) { console.log(`Web mode opening URL: ${url}`); window.open(url, "_blank"); }, @@ -177,8 +177,16 @@ export const createWebAdapter = (): WebPlatformAdapter => { }, async metadata(path, options = {}) { - console.log("metadata is not supported in web environment", path, options); + console.log( + "metadata is not supported in web environment", + path, + options + ); return Promise.resolve({ isAbsolute: false }); }, + + async revealItemInDir(path) { + console.log("revealItemInDir is not supported in web environment", path); + }, }; };