fix: fix the problem of local path not opening (#650)

* fix: fix the problem of local path not opening

* docs: update changelog

* chore: remove pizza-engine
This commit is contained in:
ayangweb
2025-06-10 17:26:19 +08:00
committed by GitHub
parent 7a2dde7448
commit 8ed808c591
13 changed files with 372 additions and 136 deletions

View File

@@ -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: arrow keys still navigated search when menu opened with Cmd+K #642
- fix: input lost when reopening dialog after search #644 - fix: input lost when reopening dialog after search #644
- fix: web page unmount event #645 - fix: web page unmount event #645
- fix: fix the problem of local path not opening #650
### ✈️ Improvements ### ✈️ Improvements

View File

@@ -27,6 +27,7 @@
"@tauri-apps/plugin-global-shortcut": "~2.0.0", "@tauri-apps/plugin-global-shortcut": "~2.0.0",
"@tauri-apps/plugin-http": "~2.0.2", "@tauri-apps/plugin-http": "~2.0.2",
"@tauri-apps/plugin-log": "~2.4.0", "@tauri-apps/plugin-log": "~2.4.0",
"@tauri-apps/plugin-opener": "^2.2.7",
"@tauri-apps/plugin-os": "^2.2.1", "@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-process": "^2.2.1", "@tauri-apps/plugin-process": "^2.2.1",
"@tauri-apps/plugin-shell": "^2.2.1", "@tauri-apps/plugin-shell": "^2.2.1",

10
pnpm-lock.yaml generated
View File

@@ -35,6 +35,9 @@ importers:
'@tauri-apps/plugin-log': '@tauri-apps/plugin-log':
specifier: ~2.4.0 specifier: ~2.4.0
version: 2.4.0 version: 2.4.0
'@tauri-apps/plugin-opener':
specifier: ^2.2.7
version: 2.2.7
'@tauri-apps/plugin-os': '@tauri-apps/plugin-os':
specifier: ^2.2.1 specifier: ^2.2.1
version: 2.2.1 version: 2.2.1
@@ -1265,6 +1268,9 @@ packages:
'@tauri-apps/plugin-log@2.4.0': '@tauri-apps/plugin-log@2.4.0':
resolution: {integrity: sha512-j7yrDtLNmayCBOO2esl3aZv9jSXy2an8MDLry3Ys9ZXerwUg35n1Y2uD8HoCR+8Ng/EUgx215+qOUfJasjYrHw==} 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': '@tauri-apps/plugin-os@2.2.1':
resolution: {integrity: sha512-cNYpNri2CCc6BaNeB6G/mOtLvg8dFyFQyCUdf2y0K8PIAKGEWdEcu8DECkydU2B+oj4OJihDPD2de5K6cbVl9A==} resolution: {integrity: sha512-cNYpNri2CCc6BaNeB6G/mOtLvg8dFyFQyCUdf2y0K8PIAKGEWdEcu8DECkydU2B+oj4OJihDPD2de5K6cbVl9A==}
@@ -4666,6 +4672,10 @@ snapshots:
dependencies: dependencies:
'@tauri-apps/api': 2.5.0 '@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': '@tauri-apps/plugin-os@2.2.1':
dependencies: dependencies:
'@tauri-apps/api': 2.5.0 '@tauri-apps/api': 2.5.0

23
src-tauri/Cargo.lock generated
View File

@@ -866,6 +866,7 @@ dependencies = [
"tauri-plugin-http", "tauri-plugin-http",
"tauri-plugin-log", "tauri-plugin-log",
"tauri-plugin-macos-permissions", "tauri-plugin-macos-permissions",
"tauri-plugin-opener",
"tauri-plugin-os", "tauri-plugin-os",
"tauri-plugin-process", "tauri-plugin-process",
"tauri-plugin-screenshots", "tauri-plugin-screenshots",
@@ -6279,6 +6280,28 @@ dependencies = [
"thiserror 2.0.12", "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]] [[package]]
name = "tauri-plugin-os" name = "tauri-plugin-os"
version = "2.2.1" version = "2.2.1"

View File

@@ -98,6 +98,7 @@ derive_more = { version = "2.0.1", features = ["display"] }
anyhow = "1.0.98" anyhow = "1.0.98"
function_name = "0.3.0" function_name = "0.3.0"
regex = "1.11.1" regex = "1.11.1"
tauri-plugin-opener = "2"
[target."cfg(target_os = \"macos\")".dependencies] [target."cfg(target_os = \"macos\")".dependencies]
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" }

View File

@@ -71,6 +71,7 @@
"process:default", "process:default",
"updater:default", "updater:default",
"windows-version:default", "windows-version:default",
"log:default" "log:default",
"opener:default"
] ]
} }

View File

@@ -88,7 +88,8 @@ pub fn run() {
.plugin(tauri_plugin_screenshots::init()) .plugin(tauri_plugin_screenshots::init())
.plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_updater::Builder::new().build()) .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 // Conditional compilation for macOS
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]

View File

@@ -106,7 +106,7 @@ const Applications = () => {
<SquareArrowOutUpRight <SquareArrowOutUpRight
className="size-4 cursor-pointer" className="size-4 cursor-pointer"
onClick={() => { onClick={() => {
platformAdapter.openExternal(item); platformAdapter.revealItemInDir(item);
}} }}
/> />

View File

@@ -102,13 +102,14 @@ export interface SystemOperations {
checkUpdate: () => Promise<any>; checkUpdate: () => Promise<any>;
relaunchApp: () => Promise<void>; relaunchApp: () => Promise<void>;
isTauri: () => boolean; isTauri: () => boolean;
openExternal: (url: string) => Promise<void>; openUrl: (url: string) => Promise<void>;
commands: <T>(commandName: string, ...args: any[]) => Promise<T>; commands: <T>(commandName: string, ...args: any[]) => Promise<T>;
isWindows10: () => Promise<boolean>; isWindows10: () => Promise<boolean>;
revealItemInDir: (path: string) => Promise<void>;
} }
// Base platform adapter interface // Base platform adapter interface
export interface BasePlatformAdapter export interface BasePlatformAdapter
extends WindowOperations, extends WindowOperations,
ThemeAndEvents, ThemeAndEvents,
SystemOperations { } SystemOperations {}

View File

@@ -72,7 +72,7 @@ export const OpenURLWithBrowser = async (url?: string) => {
if (!url) return; if (!url) return;
if (IsTauri()) { if (IsTauri()) {
try { try {
await platformAdapter.openExternal(url); await platformAdapter.openUrl(url);
await platformAdapter.commands("hide_coco"); await platformAdapter.commands("hide_coco");
console.log("URL opened in default browser"); console.log("URL opened in default browser");
} catch (error) { } catch (error) {

View File

@@ -1,4 +1,3 @@
// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. // This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually.
/** user-defined commands **/ /** user-defined commands **/
@@ -51,8 +50,18 @@ export const commands = {
async getCurrentRecording(): Promise<JsonValue<RecordingInfo | null>> { async getCurrentRecording(): Promise<JsonValue<RecordingInfo | null>> {
return await TAURI_INVOKE("get_current_recording"); return await TAURI_INVOKE("get_current_recording");
}, },
async exportVideo(videoId: string, project: ProjectConfiguration, progress: TAURI_CHANNEL<RenderProgress>, force: boolean): Promise<string> { async exportVideo(
return await TAURI_INVOKE("export_video", { videoId, project, progress, force }); videoId: string,
project: ProjectConfiguration,
progress: TAURI_CHANNEL<RenderProgress>,
force: boolean
): Promise<string> {
return await TAURI_INVOKE("export_video", {
videoId,
project,
progress,
force,
});
}, },
async copyFileToPath(src: string, dst: string): Promise<null> { async copyFileToPath(src: string, dst: string): Promise<null> {
return await TAURI_INVOKE("copy_file_to_path", { src, dst }); return await TAURI_INVOKE("copy_file_to_path", { src, dst });
@@ -66,10 +75,15 @@ export const commands = {
async openFilePath(path: string): Promise<null> { async openFilePath(path: string): Promise<null> {
return await TAURI_INVOKE("open_file_path", { path }); return await TAURI_INVOKE("open_file_path", { path });
}, },
async getVideoMetadata(videoId: string, videoType: VideoType | null): Promise<VideoRecordingMetadata> { async getVideoMetadata(
videoId: string,
videoType: VideoType | null
): Promise<VideoRecordingMetadata> {
return await TAURI_INVOKE("get_video_metadata", { videoId, videoType }); return await TAURI_INVOKE("get_video_metadata", { videoId, videoType });
}, },
async createEditorInstance(videoId: string): Promise<SerializedEditorInstance> { async createEditorInstance(
videoId: string
): Promise<SerializedEditorInstance> {
return await TAURI_INVOKE("create_editor_instance", { videoId }); return await TAURI_INVOKE("create_editor_instance", { videoId });
}, },
async startPlayback(videoId: string): Promise<void> { async startPlayback(videoId: string): Promise<void> {
@@ -78,10 +92,16 @@ export const commands = {
async stopPlayback(videoId: string): Promise<void> { async stopPlayback(videoId: string): Promise<void> {
await TAURI_INVOKE("stop_playback", { videoId }); await TAURI_INVOKE("stop_playback", { videoId });
}, },
async setPlayheadPosition(videoId: string, frameNumber: number): Promise<void> { async setPlayheadPosition(
videoId: string,
frameNumber: number
): Promise<void> {
await TAURI_INVOKE("set_playhead_position", { videoId, frameNumber }); await TAURI_INVOKE("set_playhead_position", { videoId, frameNumber });
}, },
async setProjectConfig(videoId: string, config: ProjectConfiguration): Promise<void> { async setProjectConfig(
videoId: string,
config: ProjectConfiguration
): Promise<void> {
await TAURI_INVOKE("set_project_config", { videoId, config }); await TAURI_INVOKE("set_project_config", { videoId, config });
}, },
async openEditor(id: string): Promise<void> { async openEditor(id: string): Promise<void> {
@@ -99,7 +119,10 @@ export const commands = {
async requestPermission(permission: OSPermission): Promise<void> { async requestPermission(permission: OSPermission): Promise<void> {
await TAURI_INVOKE("request_permission", { permission }); await TAURI_INVOKE("request_permission", { permission });
}, },
async uploadExportedVideo(videoId: string, mode: UploadMode): Promise<UploadResult> { async uploadExportedVideo(
videoId: string,
mode: UploadMode
): Promise<UploadResult> {
return await TAURI_INVOKE("upload_exported_video", { videoId, mode }); return await TAURI_INVOKE("upload_exported_video", { videoId, mode });
}, },
async uploadScreenshot(screenshotPath: string): Promise<UploadResult> { async uploadScreenshot(screenshotPath: string): Promise<UploadResult> {
@@ -108,13 +131,16 @@ export const commands = {
async getRecordingMeta(id: string, fileType: string): Promise<RecordingMeta> { async getRecordingMeta(id: string, fileType: string): Promise<RecordingMeta> {
return await TAURI_INVOKE("get_recording_meta", { id, fileType }); return await TAURI_INVOKE("get_recording_meta", { id, fileType });
}, },
async saveFileDialog(fileName: string, fileType: string): Promise<string | null> { async saveFileDialog(
fileName: string,
fileType: string
): Promise<string | null> {
return await TAURI_INVOKE("save_file_dialog", { fileName, fileType }); 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"); return await TAURI_INVOKE("list_recordings");
}, },
async listScreenshots(): Promise<([string, string, RecordingMeta])[]> { async listScreenshots(): Promise<[string, string, RecordingMeta][]> {
return await TAURI_INVOKE("list_screenshots"); return await TAURI_INVOKE("list_screenshots");
}, },
async checkUpgradedAndUpdate(): Promise<boolean> { async checkUpgradedAndUpdate(): Promise<boolean> {
@@ -144,7 +170,9 @@ export const commands = {
async sendFeedbackRequest(feedback: string): Promise<null> { async sendFeedbackRequest(feedback: string): Promise<null> {
return await TAURI_INVOKE("send_feedback_request", { feedback }); return await TAURI_INVOKE("send_feedback_request", { feedback });
}, },
async positionTrafficLights(controlsInset: [number, number] | null): Promise<void> { async positionTrafficLights(
controlsInset: [number, number] | null
): Promise<void> {
await TAURI_INVOKE("position_traffic_lights", { controlsInset }); await TAURI_INVOKE("position_traffic_lights", { controlsInset });
}, },
async setTheme(theme: AppTheme): Promise<void> { async setTheme(theme: AppTheme): Promise<void> {
@@ -158,31 +186,30 @@ export const commands = {
}, },
async writeClipboardString(text: string): Promise<null> { async writeClipboardString(text: string): Promise<null> {
return await TAURI_INVOKE("write_clipboard_string", { text }); return await TAURI_INVOKE("write_clipboard_string", { text });
} },
} };
/** user-defined events **/ /** user-defined events **/
export const events = __makeEvents__<{ export const events = __makeEvents__<{
audioInputLevelChange: AudioInputLevelChange, audioInputLevelChange: AudioInputLevelChange;
authenticationInvalid: AuthenticationInvalid, authenticationInvalid: AuthenticationInvalid;
currentRecordingChanged: CurrentRecordingChanged, currentRecordingChanged: CurrentRecordingChanged;
editorStateChanged: EditorStateChanged, editorStateChanged: EditorStateChanged;
newNotification: NewNotification, newNotification: NewNotification;
newRecordingAdded: NewRecordingAdded, newRecordingAdded: NewRecordingAdded;
newScreenshotAdded: NewScreenshotAdded, newScreenshotAdded: NewScreenshotAdded;
recordingMetaChanged: RecordingMetaChanged, recordingMetaChanged: RecordingMetaChanged;
recordingOptionsChanged: RecordingOptionsChanged, recordingOptionsChanged: RecordingOptionsChanged;
recordingStarted: RecordingStarted, recordingStarted: RecordingStarted;
recordingStopped: RecordingStopped, recordingStopped: RecordingStopped;
renderFrameEvent: RenderFrameEvent, renderFrameEvent: RenderFrameEvent;
requestNewScreenshot: RequestNewScreenshot, requestNewScreenshot: RequestNewScreenshot;
requestOpenSettings: RequestOpenSettings, requestOpenSettings: RequestOpenSettings;
requestRestartRecording: RequestRestartRecording, requestRestartRecording: RequestRestartRecording;
requestStartRecording: RequestStartRecording, requestStartRecording: RequestStartRecording;
requestStopRecording: RequestStopRecording, requestStopRecording: RequestStopRecording;
uploadProgress: UploadProgress uploadProgress: UploadProgress;
}>({ }>({
audioInputLevelChange: "audio-input-level-change", audioInputLevelChange: "audio-input-level-change",
authenticationInvalid: "authentication-invalid", authenticationInvalid: "authentication-invalid",
@@ -201,91 +228,247 @@ export const events = __makeEvents__<{
requestRestartRecording: "request-restart-recording", requestRestartRecording: "request-restart-recording",
requestStartRecording: "request-start-recording", requestStartRecording: "request-start-recording",
requestStopRecording: "request-stop-recording", requestStopRecording: "request-stop-recording",
uploadProgress: "upload-progress" uploadProgress: "upload-progress",
}) });
/** user-defined constants **/ /** user-defined constants **/
/** user-defined types **/ /** user-defined types **/
export type AppEndpoint = string export type AppEndpoint = string;
export type AppTheme = "auto" | "light" | "dark" export type AppTheme = "auto" | "light" | "dark";
export type WindowTheme = "light" | "dark" export type WindowTheme = "light" | "dark";
export type AspectRatio = "wide" | "vertical" | "square" | "classic" | "tall" export type AspectRatio = "wide" | "vertical" | "square" | "classic" | "tall";
export type Audio = { duration: number; sample_rate: number; channels: number } export type Audio = { duration: number; sample_rate: number; channels: number };
export type AudioConfiguration = { mute: boolean; improve: boolean } export type AudioConfiguration = { mute: boolean; improve: boolean };
export type AudioInputLevelChange = number export type AudioInputLevelChange = number;
export type AudioMeta = { path: string } export type AudioMeta = { path: string };
export type AuthStore = { token: string; user_id: string | null; expires: number; plan: Plan | null } export type AuthStore = {
export type AuthenticationInvalid = null token: string;
export type BackgroundConfiguration = { source: BackgroundSource; blur: number; padding: number; rounding: number; inset: number; crop: Crop | null } user_id: string | 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 } expires: number;
export type Bounds = { x: number; y: number; width: number; height: number } plan: Plan | null;
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 AuthenticationInvalid = null;
export type CameraPosition = { x: CameraXPosition; y: CameraYPosition } export type BackgroundConfiguration = {
export type CameraXPosition = "left" | "center" | "right" source: BackgroundSource;
export type CameraYPosition = "top" | "bottom" blur: number;
export type CaptureScreen = { id: number; name: string } padding: number;
export type CaptureWindow = { id: number; owner_name: string; name: string; bounds: Bounds } rounding: number;
export type Crop = { position: XY<number>; size: XY<number> } inset: number;
export type CurrentRecordingChanged = null crop: Crop | null;
export type CursorAnimationStyle = "regular" | "slow" | "fast" };
export type CursorConfiguration = { hideWhenIdle: boolean; size: number; type: CursorType; animationStyle: CursorAnimationStyle } export type BackgroundSource =
export type CursorType = "pointer" | "circle" | { type: "wallpaper"; id: number }
export type Display = { path: string } | { type: "image"; path: string | null }
export type EditorStateChanged = { playhead_position: number } | { type: "color"; value: [number, number, 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 } type: "gradient";
export type Hotkey = { code: string; meta: boolean; ctrl: boolean; alt: boolean; shift: boolean } from: [number, number, number];
export type HotkeyAction = "startRecording" | "stopRecording" | "restartRecording" | "takeScreenshot" to: [number, number, number];
export type HotkeysConfiguration = { show: boolean } angle?: number;
export type HotkeysStore = { hotkeys: { [key in HotkeyAction]: Hotkey } } };
export type JsonValue<T> = [T] export type Bounds = { x: number; y: number; width: number; height: number };
export type MultipleSegment = { display: Display; camera?: CameraMeta | null; audio?: AudioMeta | null; cursor?: string | null } export type Camera = {
export type MultipleSegments = { segments: MultipleSegment[]; cursors: { [key in string]: string } } hide: boolean;
export type NewNotification = { title: string; body: string; is_error: boolean } mirror: boolean;
export type NewRecordingAdded = { path: string } position: CameraPosition;
export type NewScreenshotAdded = { path: string } size: number;
export type OSPermission = "screenRecording" | "camera" | "microphone" | "accessibility" zoom_size: number | null;
export type OSPermissionStatus = "notNeeded" | "empty" | "granted" | "denied" rounding: number;
export type OSPermissionsCheck = { screenRecording: OSPermissionStatus; microphone: OSPermissionStatus; camera: OSPermissionStatus; accessibility: OSPermissionStatus } shadow: number;
export type Plan = { upgraded: boolean; last_checked: number } };
export type PreCreatedVideo = { id: string; link: string; config: S3UploadMeta } export type CameraMeta = { path: string };
export type ProjectConfiguration = { aspectRatio: AspectRatio | null; background: BackgroundConfiguration; camera: Camera; audio: AudioConfiguration; cursor: CursorConfiguration; hotkeys: HotkeysConfiguration; timeline?: TimelineConfiguration | null; motionBlur: number | null } export type CameraPosition = { x: CameraXPosition; y: CameraYPosition };
export type ProjectRecordings = { segments: SegmentRecordings[] } export type CameraXPosition = "left" | "center" | "right";
export type RecordingInfo = { captureTarget: ScreenCaptureTarget } export type CameraYPosition = "top" | "bottom";
export type RecordingMeta = ({ segment: SingleSegment } | { inner: MultipleSegments }) & { pretty_name: string; sharing?: SharingMeta | null } export type CaptureScreen = { id: number; name: string };
export type RecordingMetaChanged = { id: string } export type CaptureWindow = {
export type RecordingOptions = { captureTarget: ScreenCaptureTarget; cameraLabel: string | null; audioInputName: string | null } id: number;
export type RecordingOptionsChanged = null owner_name: string;
export type RecordingStarted = null name: string;
export type RecordingStopped = { path: string } bounds: Bounds;
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 Crop = { position: XY<number>; size: XY<number> };
export type RequestNewScreenshot = null export type CurrentRecordingChanged = null;
export type RequestOpenSettings = { page: string } export type CursorAnimationStyle = "regular" | "slow" | "fast";
export type RequestRestartRecording = null export type CursorConfiguration = {
export type RequestStartRecording = null hideWhenIdle: boolean;
export type RequestStopRecording = null size: number;
export type S3UploadMeta = { id: string; user_id: string; aws_region?: string; aws_bucket?: string } type: CursorType;
export type ScreenCaptureTarget = ({ variant: "window" } & CaptureWindow) | ({ variant: "screen" } & CaptureScreen) animationStyle: CursorAnimationStyle;
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 CursorType = "pointer" | "circle";
export type SharingMeta = { id: string; link: string } export type Display = { path: 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 EditorStateChanged = { playhead_position: number };
export type SingleSegment = { display: Display; camera?: CameraMeta | null; audio?: AudioMeta | null; cursor?: string | null } export type Flags = {
export type TimelineConfiguration = { segments: TimelineSegment[]; zoomSegments?: ZoomSegment[] } recordMouse: boolean;
export type TimelineSegment = { recordingSegment: number | null; timescale: number; start: number; end: number } split: boolean;
export type UploadMode = { Initial: { pre_created_video: PreCreatedVideo | null } } | "Reupload" pauseResume: boolean;
export type UploadProgress = { stage: string; progress: number; message: string } zoom: boolean;
export type UploadResult = { Success: string } | "NotAuthenticated" | "PlanCheckFailed" | "UpgradeRequired" customS3: boolean;
export type Video = { duration: number; width: number; height: number } };
export type VideoRecordingMetadata = { duration: number; size: number } export type GeneralSettingsStore = {
export type VideoType = "screen" | "output" uploadIndividualFiles?: boolean;
export type XY<T> = { x: T; y: T } openEditorAfterRecording?: boolean;
export type ZoomSegment = { start: number; end: number; amount: number } 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> = [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<T> = { x: T; y: T };
export type ZoomSegment = { start: number; end: number; amount: number };
/** tauri-specta globals **/ /** tauri-specta globals **/
@@ -298,14 +481,14 @@ import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webview
type __EventObj__<T> = { type __EventObj__<T> = {
listen: ( listen: (
cb: TAURI_API_EVENT.EventCallback<T>, cb: TAURI_API_EVENT.EventCallback<T>
) => ReturnType<typeof TAURI_API_EVENT.listen<T>>; ) => ReturnType<typeof TAURI_API_EVENT.listen<T>>;
once: ( once: (
cb: TAURI_API_EVENT.EventCallback<T>, cb: TAURI_API_EVENT.EventCallback<T>
) => ReturnType<typeof TAURI_API_EVENT.once<T>>; ) => ReturnType<typeof TAURI_API_EVENT.once<T>>;
emit: null extends T emit: null extends T
? (payload?: T) => ReturnType<typeof TAURI_API_EVENT.emit> ? (payload?: T) => ReturnType<typeof TAURI_API_EVENT.emit>
: (payload: T) => ReturnType<typeof TAURI_API_EVENT.emit>; : (payload: T) => ReturnType<typeof TAURI_API_EVENT.emit>;
}; };
export type Result<T, E> = export type Result<T, E> =
@@ -313,7 +496,7 @@ export type Result<T, E> =
| { status: "error"; error: E }; | { status: "error"; error: E };
function __makeEvents__<T extends Record<string, any>>( function __makeEvents__<T extends Record<string, any>>(
mappings: Record<keyof T, string>, mappings: Record<keyof T, string>
) { ) {
return new Proxy( return new Proxy(
{} as unknown as { {} as unknown as {
@@ -325,7 +508,7 @@ function __makeEvents__<T extends Record<string, any>>(
get: (_, event) => { get: (_, event) => {
const name = mappings[event as keyof T]; const name = mappings[event as keyof T];
return new Proxy((() => { }) as any, { return new Proxy((() => {}) as any, {
apply: (_, __, [window]: [__WebviewWindow__]) => ({ apply: (_, __, [window]: [__WebviewWindow__]) => ({
listen: (arg: any) => window.listen(name, arg), listen: (arg: any) => window.listen(name, arg),
once: (arg: any) => window.once(name, arg), once: (arg: any) => window.once(name, arg),
@@ -343,6 +526,6 @@ function __makeEvents__<T extends Record<string, any>>(
}, },
}); });
}, },
}, }
); );
} }

View File

@@ -206,10 +206,10 @@ export const createTauriAdapter = (): TauriPlatformAdapter => {
return true; return true;
}, },
async openExternal(url) { async openUrl(url) {
const { open } = await import("@tauri-apps/plugin-shell"); const { openUrl } = await import("@tauri-apps/plugin-opener");
open(url); openUrl(url);
}, },
isWindows10, isWindows10,
@@ -223,5 +223,11 @@ export const createTauriAdapter = (): TauriPlatformAdapter => {
}, },
metadata, metadata,
async revealItemInDir(path) {
const { revealItemInDir } = await import("@tauri-apps/plugin-opener");
revealItemInDir(path);
},
}; };
}; };

View File

@@ -48,7 +48,7 @@ export const createWebAdapter = (): WebPlatformAdapter => {
async listenEvent(event, _callback) { async listenEvent(event, _callback) {
console.log("Web mode simulated event listen", event); console.log("Web mode simulated event listen", event);
return () => { }; return () => {};
}, },
async setAlwaysOnTop(isPinned) { async setAlwaysOnTop(isPinned) {
@@ -110,7 +110,7 @@ export const createWebAdapter = (): WebPlatformAdapter => {
async listenThemeChanged() { async listenThemeChanged() {
console.log("Web mode simulated theme change listener"); console.log("Web mode simulated theme change listener");
return () => { }; return () => {};
}, },
async getWebviewWindow() { async getWebviewWindow() {
@@ -157,14 +157,14 @@ export const createWebAdapter = (): WebPlatformAdapter => {
async listenWindowEvent(event, _callback) { async listenWindowEvent(event, _callback) {
console.log("Web mode simulated listen window event:", event); console.log("Web mode simulated listen window event:", event);
return () => { }; return () => {};
}, },
isTauri() { isTauri() {
return false; return false;
}, },
async openExternal(url) { async openUrl(url) {
console.log(`Web mode opening URL: ${url}`); console.log(`Web mode opening URL: ${url}`);
window.open(url, "_blank"); window.open(url, "_blank");
}, },
@@ -177,8 +177,16 @@ export const createWebAdapter = (): WebPlatformAdapter => {
}, },
async metadata(path, options = {}) { 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 }); return Promise.resolve({ isAbsolute: false });
}, },
async revealItemInDir(path) {
console.log("revealItemInDir is not supported in web environment", path);
},
}; };
}; };