mirror of
https://github.com/infinilabs/coco-app.git
synced 2026-02-24 04:01:27 +01:00
* fix: avoid recentering when resizing to compact after leaving ViewExtension * docs: update release note * chore: update
403 lines
11 KiB
TypeScript
403 lines
11 KiB
TypeScript
import type { OpenDialogOptions } from "@tauri-apps/plugin-dialog";
|
|
import { isWindows10 } from "tauri-plugin-windows-version-api";
|
|
import { convertFileSrc, invoke } from "@tauri-apps/api/core";
|
|
import { metadata } from "tauri-plugin-fs-pro-api";
|
|
import { error } from "@tauri-apps/plugin-log";
|
|
|
|
import {
|
|
windowWrapper,
|
|
eventWrapper,
|
|
systemWrapper,
|
|
commandWrapper,
|
|
} from "./wrappers/tauriWrappers";
|
|
import type { BasePlatformAdapter } from "@/types/platform";
|
|
import type { AppTheme } from "@/types/index";
|
|
import { useAppearanceStore } from "@/stores/appearanceStore";
|
|
import { copyToClipboard, dispatchEvent, OpenURLWithBrowser } from ".";
|
|
import { useAppStore } from "@/stores/appStore";
|
|
import { unrequitable } from "@/utils";
|
|
import {
|
|
getCurrentWebviewWindow,
|
|
WebviewWindow,
|
|
} from "@tauri-apps/api/webviewWindow";
|
|
import {
|
|
cursorPosition,
|
|
Monitor,
|
|
monitorFromPoint,
|
|
Theme,
|
|
} from "@tauri-apps/api/window";
|
|
|
|
export interface TauriPlatformAdapter extends BasePlatformAdapter {
|
|
openFileDialog: (
|
|
options: OpenDialogOptions
|
|
) => Promise<string | string[] | null>;
|
|
metadata: typeof metadata;
|
|
error: typeof error;
|
|
openLogDir: () => Promise<void>;
|
|
getCurrentWebviewWindow: () => Promise<WebviewWindow>;
|
|
getWindowTheme: () => Promise<Theme | null>;
|
|
setWindowTheme: (theme: Theme | null) => Promise<void>;
|
|
getAllWindows: () => Promise<WebviewWindow[]>;
|
|
setWindowResizable: (resizable: boolean) => Promise<void>;
|
|
isWindowResizable: () => Promise<boolean>;
|
|
getWindowSize: () => Promise<{ width: number; height: number }>;
|
|
setWindowFullscreen: (enable: boolean) => Promise<void>;
|
|
isWindowMaximized: () => Promise<boolean>;
|
|
setWindowMaximized: (enable: boolean) => Promise<void>;
|
|
getWindowPosition: () => Promise<{ x: number; y: number }>;
|
|
setWindowPosition: (x: number, y: number) => Promise<void>;
|
|
getMonitorFromCursor: () => Promise<Monitor | null>;
|
|
centerOnCurrentMonitor: () => Promise<unknown>;
|
|
}
|
|
|
|
// Create Tauri adapter functions
|
|
export const createTauriAdapter = (): TauriPlatformAdapter => {
|
|
return {
|
|
async setWindowSize(width, height) {
|
|
return windowWrapper.setLogicalSize(width, height);
|
|
},
|
|
async getWindowSize() {
|
|
return windowWrapper.getLogicalSize();
|
|
},
|
|
async setWindowResizable(resizable) {
|
|
return windowWrapper.setResizable(resizable);
|
|
},
|
|
async isWindowResizable() {
|
|
return windowWrapper.isResizable();
|
|
},
|
|
async setWindowFullscreen(enable) {
|
|
return windowWrapper.setFullscreen(enable);
|
|
},
|
|
async isWindowMaximized() {
|
|
return windowWrapper.isMaximized();
|
|
},
|
|
async setWindowMaximized(enable) {
|
|
return windowWrapper.setMaximized(enable);
|
|
},
|
|
async getWindowPosition() {
|
|
return windowWrapper.getLogicalPosition();
|
|
},
|
|
async setWindowPosition(x, y) {
|
|
return windowWrapper.setLogicalPosition(x, y);
|
|
},
|
|
|
|
async getMonitorFromCursor() {
|
|
const appWindow = getCurrentWebviewWindow();
|
|
const factor = await appWindow.scaleFactor();
|
|
|
|
const point = await cursorPosition();
|
|
const { x, y } = point.toLogical(factor);
|
|
|
|
return monitorFromPoint(x, y);
|
|
},
|
|
|
|
async centerOnCurrentMonitor() {
|
|
return windowWrapper.centerOnMonitor();
|
|
},
|
|
|
|
async hideWindow() {
|
|
const window = await windowWrapper.getCurrentWebviewWindow();
|
|
return window?.hide();
|
|
},
|
|
|
|
async showWindow() {
|
|
const window = await windowWrapper.getCurrentWebviewWindow();
|
|
window?.show();
|
|
window?.unminimize();
|
|
return window?.setFocus();
|
|
},
|
|
|
|
async emitEvent(event, payload) {
|
|
return eventWrapper.emit(event, payload);
|
|
},
|
|
|
|
async listenEvent(event, callback) {
|
|
return eventWrapper.listen(event, callback);
|
|
},
|
|
|
|
async checkScreenRecordingPermission() {
|
|
return systemWrapper.checkScreenPermission();
|
|
},
|
|
|
|
async captureMonitorScreenshot(id) {
|
|
return systemWrapper.captureScreen(id, "monitor");
|
|
},
|
|
|
|
async captureWindowScreenshot(id) {
|
|
return systemWrapper.captureScreen(id, "window");
|
|
},
|
|
|
|
commands: commandWrapper.commands,
|
|
|
|
async invokeBackend(command, args) {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
return invoke(command, args);
|
|
},
|
|
|
|
convertFileSrc(path) {
|
|
return convertFileSrc(path);
|
|
},
|
|
|
|
async setAlwaysOnTop(isPinned) {
|
|
const { getCurrentWebviewWindow } = await import(
|
|
"@tauri-apps/api/webviewWindow"
|
|
);
|
|
const window = getCurrentWebviewWindow();
|
|
return window.setAlwaysOnTop(isPinned);
|
|
},
|
|
|
|
async requestScreenRecordingPermission() {
|
|
const { requestScreenRecordingPermission } = await import(
|
|
"tauri-plugin-macos-permissions-api"
|
|
);
|
|
return requestScreenRecordingPermission();
|
|
},
|
|
|
|
async checkMicrophonePermission() {
|
|
const { checkMicrophonePermission } = await import(
|
|
"tauri-plugin-macos-permissions-api"
|
|
);
|
|
return checkMicrophonePermission();
|
|
},
|
|
|
|
async requestMicrophonePermission() {
|
|
const { requestMicrophonePermission } = await import(
|
|
"tauri-plugin-macos-permissions-api"
|
|
);
|
|
return requestMicrophonePermission();
|
|
},
|
|
|
|
async getScreenshotableMonitors() {
|
|
const { getScreenshotableMonitors } = await import(
|
|
"tauri-plugin-screenshots-api"
|
|
);
|
|
return getScreenshotableMonitors();
|
|
},
|
|
|
|
async getScreenshotableWindows() {
|
|
const { getScreenshotableWindows } = await import(
|
|
"tauri-plugin-screenshots-api"
|
|
);
|
|
return getScreenshotableWindows();
|
|
},
|
|
|
|
async openFileDialog(options) {
|
|
const { open } = await import("@tauri-apps/plugin-dialog");
|
|
return open(options);
|
|
},
|
|
|
|
async getFileMetadata(path) {
|
|
const { metadata } = await import("tauri-plugin-fs-pro-api");
|
|
return metadata(path);
|
|
},
|
|
|
|
async getFileIcon(path, size) {
|
|
const { icon } = await import("tauri-plugin-fs-pro-api");
|
|
return icon(path, { size });
|
|
},
|
|
|
|
async checkUpdate() {
|
|
const { check } = await import("@tauri-apps/plugin-updater");
|
|
|
|
const { snapshotUpdate } = useAppearanceStore.getState();
|
|
|
|
const endpoints = [
|
|
"https://release.infinilabs.com/coco/app/.latest.json?target={{target}}&arch={{arch}}¤t_version={{current_version}}",
|
|
];
|
|
|
|
if (snapshotUpdate) {
|
|
endpoints.unshift(
|
|
"https://release.infinilabs.com/coco/app/snapshot/.latest.json?target={{target}}&arch={{arch}}¤t_version={{current_version}}"
|
|
);
|
|
}
|
|
|
|
return check({
|
|
endpoints,
|
|
});
|
|
},
|
|
|
|
async relaunchApp() {
|
|
const { relaunch } = await import("@tauri-apps/plugin-process");
|
|
return relaunch();
|
|
},
|
|
|
|
async listenThemeChanged(callback) {
|
|
const { listen } = await import("@tauri-apps/api/event");
|
|
return listen<AppTheme>("theme-changed", ({ payload }) => {
|
|
callback(payload);
|
|
});
|
|
},
|
|
|
|
async getCurrentWebviewWindow() {
|
|
const { getCurrentWebviewWindow } = await import(
|
|
"@tauri-apps/api/webviewWindow"
|
|
);
|
|
return getCurrentWebviewWindow();
|
|
},
|
|
|
|
async setWindowTheme(theme) {
|
|
const window = await this.getCurrentWebviewWindow();
|
|
if (window) {
|
|
return window.setTheme(theme);
|
|
}
|
|
},
|
|
|
|
async getWindowTheme() {
|
|
const window = await this.getCurrentWebviewWindow();
|
|
if (window) {
|
|
return window.theme();
|
|
}
|
|
return "light";
|
|
},
|
|
|
|
async onThemeChanged(callback) {
|
|
const window = await this.getCurrentWebviewWindow();
|
|
if (window) {
|
|
window.onThemeChanged(callback);
|
|
}
|
|
},
|
|
|
|
async getWindowByLabel(label) {
|
|
const { WebviewWindow } = await import("@tauri-apps/api/webviewWindow");
|
|
const window = await WebviewWindow.getByLabel(label);
|
|
return window;
|
|
},
|
|
|
|
async createWindow(label, options) {
|
|
const { WebviewWindow } = await import("@tauri-apps/api/webviewWindow");
|
|
new WebviewWindow(label, options);
|
|
},
|
|
|
|
async getAllWindows() {
|
|
const { getAllWebviewWindows } = await import(
|
|
"@tauri-apps/api/webviewWindow"
|
|
);
|
|
return getAllWebviewWindows();
|
|
},
|
|
|
|
async createWebviewWindow(label, options) {
|
|
const { WebviewWindow } = await import("@tauri-apps/api/webviewWindow");
|
|
return new WebviewWindow(label, options);
|
|
},
|
|
|
|
async listenWindowEvent(event, callback) {
|
|
const { listen } = await import("@tauri-apps/api/event");
|
|
return listen(event, callback);
|
|
},
|
|
|
|
isTauri() {
|
|
return true;
|
|
},
|
|
|
|
async openUrl(url) {
|
|
const { openUrl } = await import("@tauri-apps/plugin-opener");
|
|
|
|
openUrl(url);
|
|
},
|
|
|
|
isWindows10,
|
|
|
|
async setShadow(enable) {
|
|
const { getCurrentWebviewWindow } = await import(
|
|
"@tauri-apps/api/webviewWindow"
|
|
);
|
|
const appWindow = getCurrentWebviewWindow();
|
|
return appWindow.setShadow(enable);
|
|
},
|
|
|
|
metadata,
|
|
|
|
async revealItemInDir(path) {
|
|
const { revealItemInDir } = await import("@tauri-apps/plugin-opener");
|
|
|
|
revealItemInDir(path);
|
|
},
|
|
|
|
async openSearchItem(data) {
|
|
const { invoke } = await import("@tauri-apps/api/core");
|
|
|
|
console.log("openSearchItem", data);
|
|
|
|
// Extension store needs to be opened in a different way
|
|
if (
|
|
data?.type === "AI Assistant" ||
|
|
data?.id === "Extension Store" ||
|
|
data?.category === "View"
|
|
) {
|
|
return dispatchEvent("Tab", 9);
|
|
}
|
|
|
|
const hideCoco = () => {
|
|
const isPinned = useAppStore.getState().isPinned;
|
|
|
|
if (isPinned) return;
|
|
|
|
return invoke("hide_coco");
|
|
};
|
|
|
|
if (data?.on_opened) {
|
|
await invoke("open", {
|
|
onOpened: data.on_opened,
|
|
extraArgs: null,
|
|
});
|
|
|
|
return hideCoco();
|
|
}
|
|
|
|
if (data?.url) {
|
|
OpenURLWithBrowser(data.url);
|
|
|
|
return hideCoco();
|
|
}
|
|
|
|
if (data?.payload?.result?.value) {
|
|
return copyToClipboard(data.payload.result.value);
|
|
}
|
|
},
|
|
|
|
error,
|
|
|
|
async searchMCPServers(serverId, queryParams) {
|
|
if (await unrequitable()) {
|
|
return [];
|
|
}
|
|
|
|
return await commandWrapper.commands("mcp_server_search", {
|
|
id: serverId,
|
|
queryParams,
|
|
});
|
|
},
|
|
|
|
async searchDataSources(serverId, queryParams) {
|
|
return await commandWrapper.commands("datasource_search", {
|
|
id: serverId,
|
|
queryParams,
|
|
});
|
|
},
|
|
|
|
async fetchAssistant(serverId, queryParams) {
|
|
if (!serverId) {
|
|
throw new Error("currentService is undefined");
|
|
}
|
|
|
|
return await commandWrapper.commands("assistant_search", {
|
|
serverId,
|
|
queryParams,
|
|
});
|
|
},
|
|
|
|
async getCurrentWindowLabel() {
|
|
const window = await windowWrapper.getCurrentWebviewWindow();
|
|
return window.label;
|
|
},
|
|
|
|
async openLogDir() {
|
|
const { revealItemInDir } = await import("@tauri-apps/plugin-opener");
|
|
|
|
const logDir: string = await invoke("app_log_dir");
|
|
|
|
revealItemInDir(logDir);
|
|
},
|
|
};
|
|
};
|