From 2383f06e715be085cf526839e9919e715dc1e0be Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Fri, 14 Aug 2026 12:44:01 -0700 Subject: [PATCH] Add a typed platform package to decouple the frontend from Tauri (#539) Co-authored-by: Claude Opus 5 --- apps/yaak-client/commands/importCurl.ts | 4 +- apps/yaak-client/commands/openSettings.tsx | 4 +- apps/yaak-client/commands/switchWorkspace.tsx | 4 +- .../components/CloneGitRepositoryDialog.tsx | 4 +- .../components/CommercialUseBanner.tsx | 8 +- .../components/CreateWorkspaceDialog.tsx | 4 +- .../components/ExportDataDialog.tsx | 8 +- apps/yaak-client/components/FeedbackToast.tsx | 4 +- .../components/GrpcProtoSelectionDialog.tsx | 6 +- apps/yaak-client/components/ImportCurl.tsx | 13 +- apps/yaak-client/components/LicenseBadge.tsx | 6 +- apps/yaak-client/components/LocalImage.tsx | 7 +- .../components/ResponseHeaders.tsx | 4 +- apps/yaak-client/components/ResponseInfo.tsx | 4 +- apps/yaak-client/components/SelectFile.tsx | 46 ++-- .../components/Settings/Settings.tsx | 9 +- .../components/Settings/SettingsGeneral.tsx | 6 +- .../components/Settings/SettingsInterface.tsx | 8 +- .../components/Settings/SettingsLicense.tsx | 6 +- .../components/Settings/SettingsPlugins.tsx | 4 +- .../components/SettingsDropdown.tsx | 12 +- apps/yaak-client/components/Sidebar.tsx | 8 +- .../components/SyncToFilesystemSetting.tsx | 4 +- apps/yaak-client/components/Workspace.tsx | 4 +- .../components/WorkspaceActionsDropdown.tsx | 7 +- .../components/core/PairEditor.tsx | 4 +- apps/yaak-client/components/core/Select.tsx | 4 +- .../responseViewers/AudioViewer.tsx | 4 +- .../responseViewers/ImageViewer.tsx | 4 +- .../components/responseViewers/PdfViewer.tsx | 4 +- .../responseViewers/VideoViewer.tsx | 4 +- apps/yaak-client/font-size.ts | 8 +- apps/yaak-client/font.ts | 8 +- .../hooks/useCancelHttpResponse.ts | 4 +- apps/yaak-client/hooks/useCheckForUpdates.tsx | 4 +- .../hooks/useDeleteGrpcConnections.ts | 4 +- .../hooks/useDeleteHttpResponses.ts | 4 +- .../hooks/useDeleteSendHistory.tsx | 4 +- apps/yaak-client/hooks/useFolderActions.ts | 6 +- apps/yaak-client/hooks/useGrpc.ts | 14 +- .../hooks/useGrpcRequestActions.ts | 6 +- apps/yaak-client/hooks/useHotKey.ts | 6 +- .../hooks/useHttpAuthentication.ts | 4 +- .../hooks/useHttpAuthenticationConfig.ts | 6 +- .../hooks/useHttpRequestActions.ts | 6 +- apps/yaak-client/hooks/useHttpRequestBody.ts | 4 +- .../hooks/useHttpResponseEvents.ts | 4 +- .../yaak-client/hooks/useIntrospectGraphQL.ts | 7 +- .../hooks/useListenToTauriEvent.ts | 28 --- .../hooks/usePinnedGrpcConnection.ts | 4 +- .../hooks/usePinnedWebsocketConnection.ts | 4 +- apps/yaak-client/hooks/usePlatformEvent.ts | 12 + apps/yaak-client/hooks/usePluginInfo.ts | 4 +- apps/yaak-client/hooks/usePlugins.ts | 4 +- apps/yaak-client/hooks/useRenderTemplate.ts | 6 +- apps/yaak-client/hooks/useRequestUpdateKey.ts | 36 ++- apps/yaak-client/hooks/useSaveResponse.tsx | 8 +- .../hooks/useSendAnyHttpRequest.ts | 4 +- .../yaak-client/hooks/useStoplightsVisible.ts | 4 +- .../hooks/useSyncFontSizeSetting.ts | 4 +- apps/yaak-client/hooks/useSyncZoomSetting.ts | 8 +- .../hooks/useTemplateFunctionConfig.ts | 4 +- .../hooks/useTemplateFunctions.tsx | 4 +- .../hooks/useTemplateTokensToString.ts | 4 +- .../hooks/useWebsocketRequestActions.ts | 6 +- apps/yaak-client/hooks/useWindowFocus.ts | 11 +- apps/yaak-client/hooks/useWorkspaceActions.ts | 6 +- apps/yaak-client/init/sync.ts | 6 +- apps/yaak-client/lib/appInfo.ts | 8 +- apps/yaak-client/lib/copy.ts | 6 +- apps/yaak-client/lib/defaultHeaders.ts | 4 +- apps/yaak-client/lib/encryption.ts | 6 +- apps/yaak-client/lib/formatters.ts | 6 +- apps/yaak-client/lib/importData.tsx | 4 +- apps/yaak-client/lib/initGlobalListeners.tsx | 42 ++-- apps/yaak-client/lib/largeValue.ts | 8 +- apps/yaak-client/lib/responseBody.ts | 14 +- apps/yaak-client/lib/reveal.ts | 4 +- apps/yaak-client/lib/{tauri.ts => rpc.ts} | 33 ++- apps/yaak-client/lib/sendEphemeralRequest.ts | 4 +- apps/yaak-client/lib/settings.ts | 4 +- apps/yaak-client/lib/themes.ts | 4 +- apps/yaak-client/main.tsx | 4 +- apps/yaak-client/routes/__root.tsx | 4 +- apps/yaak-client/theme.ts | 14 +- crates-tauri/yaak-fonts/index.ts | 4 +- crates-tauri/yaak-license/index.ts | 16 +- crates-tauri/yaak-mac-window/index.ts | 6 +- crates/yaak-crypto/index.ts | 10 +- crates/yaak-git/index.ts | 90 ++++--- crates/yaak-models/guest-js/store.ts | 67 +++--- crates/yaak-plugins/index.ts | 14 +- crates/yaak-sync/index.ts | 23 +- crates/yaak-ws/index.ts | 10 +- package-lock.json | 17 ++ package.json | 1 + packages/platform/package.json | 19 ++ packages/platform/src/capabilities.ts | 14 ++ packages/platform/src/index.ts | 15 ++ packages/platform/src/registry.ts | 60 +++++ packages/platform/src/tauri/index.ts | 155 ++++++++++++ packages/platform/src/types.ts | 226 ++++++++++++++++++ packages/platform/tsconfig.json | 12 + packages/theme/src/appearance.ts | 31 +-- packages/ui/src/components/WindowControls.tsx | 8 +- packages/ui/src/components/tree/Tree.tsx | 4 +- packages/ui/src/hooks/useIsFullscreen.ts | 4 +- 107 files changed, 954 insertions(+), 505 deletions(-) delete mode 100644 apps/yaak-client/hooks/useListenToTauriEvent.ts create mode 100644 apps/yaak-client/hooks/usePlatformEvent.ts rename apps/yaak-client/lib/{tauri.ts => rpc.ts} (63%) create mode 100644 packages/platform/package.json create mode 100644 packages/platform/src/capabilities.ts create mode 100644 packages/platform/src/index.ts create mode 100644 packages/platform/src/registry.ts create mode 100644 packages/platform/src/tauri/index.ts create mode 100644 packages/platform/src/types.ts create mode 100644 packages/platform/tsconfig.json diff --git a/apps/yaak-client/commands/importCurl.ts b/apps/yaak-client/commands/importCurl.ts index c264c43f..37973c39 100644 --- a/apps/yaak-client/commands/importCurl.ts +++ b/apps/yaak-client/commands/importCurl.ts @@ -5,7 +5,7 @@ import { createFastMutation } from "../hooks/useFastMutation"; import { wasUpdatedExternally } from "../hooks/useRequestUpdateKey"; import { createRequestAndNavigate } from "../lib/createRequestAndNavigate"; import { jotaiStore } from "../lib/jotai"; -import { invokeCmd } from "../lib/tauri"; +import { rpc } from "../lib/rpc"; import { showToast } from "../lib/toast"; export function looksLikeCurl(text: string) { @@ -20,7 +20,7 @@ export const importCurl = createFastMutation< mutationKey: ["import_curl"], mutationFn: async ({ overwriteRequestId, command }) => { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); - const importedRequest: HttpRequest = await invokeCmd("cmd_curl_to_request", { + const importedRequest: HttpRequest = await rpc("cmd_curl_to_request", { command, workspaceId, }); diff --git a/apps/yaak-client/commands/openSettings.tsx b/apps/yaak-client/commands/openSettings.tsx index 0db60659..3ab0aa44 100644 --- a/apps/yaak-client/commands/openSettings.tsx +++ b/apps/yaak-client/commands/openSettings.tsx @@ -3,7 +3,7 @@ import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; import { createFastMutation } from "../hooks/useFastMutation"; import { jotaiStore } from "../lib/jotai"; import { router } from "../lib/router"; -import { invokeCmd } from "../lib/tauri"; +import { rpc } from "../lib/rpc"; // Allow tab with optional subtab (e.g., "plugins:installed") type SettingsTabWithSubtab = SettingsTab | `${SettingsTab}:${string}` | null; @@ -20,7 +20,7 @@ export const openSettings = createFastMutation("cmd_new_main_window", { url: location.href }); + await rpc("cmd_new_main_window", { url: location.href }); return; } diff --git a/apps/yaak-client/components/CloneGitRepositoryDialog.tsx b/apps/yaak-client/components/CloneGitRepositoryDialog.tsx index b7048a5f..16021101 100644 --- a/apps/yaak-client/components/CloneGitRepositoryDialog.tsx +++ b/apps/yaak-client/components/CloneGitRepositoryDialog.tsx @@ -1,4 +1,3 @@ -import { open } from "@tauri-apps/plugin-dialog"; import { gitClone } from "@yaakapp-internal/git"; import { Banner, VStack } from "@yaakapp-internal/ui"; import { useState } from "react"; @@ -11,6 +10,7 @@ import { Checkbox } from "./core/Checkbox"; import { IconButton } from "./core/IconButton"; import { PlainInput } from "./core/PlainInput"; import { promptCredentials } from "./git/credentials"; +import { platform } from "@yaakapp-internal/platform"; interface Props { hide: () => void; @@ -38,7 +38,7 @@ export function CloneGitRepositoryDialog({ hide }: Props) { hasSubdirectory && subdirectory ? `${directory}${sep}${subdirectory}` : directory; const handleSelectDirectory = async () => { - const dir = await open({ + const dir = await platform.dialog.open({ title: "Select Directory", directory: true, multiple: false, diff --git a/apps/yaak-client/components/CommercialUseBanner.tsx b/apps/yaak-client/components/CommercialUseBanner.tsx index f1719f28..6d968900 100644 --- a/apps/yaak-client/components/CommercialUseBanner.tsx +++ b/apps/yaak-client/components/CommercialUseBanner.tsx @@ -1,11 +1,11 @@ -import { invoke } from "@tauri-apps/api/core"; -import { openUrl } from "@tauri-apps/plugin-opener"; import type { LicenseCheckStatus } from "@yaakapp-internal/license"; import { useCallback, useEffect, useRef, useState } from "react"; import { useKeyValue } from "../hooks/useKeyValue"; import { appInfo } from "../lib/appInfo"; import { pricingUrl } from "../lib/pricingUrl"; import { DismissibleBanner } from "./core/DismissibleBanner"; +import { rpc } from "../lib/rpc"; +import { platform } from "@yaakapp-internal/platform"; const COMMERCIAL_USE_SNOOZE_MS = 7 * 24 * 60 * 60 * 1000; const COMMERCIAL_USE_BANNER_MESSAGE = @@ -95,7 +95,7 @@ async function shouldShowCommercialUsePrompt(): Promise { } try { - const license = await invoke("plugin:yaak-license|check"); + const license = await rpc("plugin:yaak-license|check"); return license.status === "personal_use"; } catch (err) { console.log("Failed to check license before commercial-use prompt", err); @@ -104,7 +104,7 @@ async function shouldShowCommercialUsePrompt(): Promise { } async function openCommercialUsePricing(source: string): Promise { - await openUrl(pricingUrl(`app.commercial-use.${source}`)).catch(console.error); + await platform.openUrl(pricingUrl(`app.commercial-use.${source}`)).catch(console.error); } function isSnoozed(value: string | null, ms: number): boolean { diff --git a/apps/yaak-client/components/CreateWorkspaceDialog.tsx b/apps/yaak-client/components/CreateWorkspaceDialog.tsx index 339206d5..25e73612 100644 --- a/apps/yaak-client/components/CreateWorkspaceDialog.tsx +++ b/apps/yaak-client/components/CreateWorkspaceDialog.tsx @@ -5,7 +5,7 @@ import { VStack } from "@yaakapp-internal/ui"; import { useState } from "react"; import { router } from "../lib/router"; import { setupOrConfigureEncryption } from "../lib/setupOrConfigureEncryption"; -import { invokeCmd } from "../lib/tauri"; +import { rpc } from "../lib/rpc"; import { showErrorToast } from "../lib/toast"; import { Button } from "./core/Button"; import { Checkbox } from "./core/Checkbox"; @@ -39,7 +39,7 @@ export function CreateWorkspaceDialog({ hide }: Props) { // Do getWorkspaceMeta instead of naively creating one because it might have // been created already when the store refreshes the workspace meta after - const workspaceMeta = await invokeCmd("cmd_get_workspace_meta", { + const workspaceMeta = await rpc("cmd_get_workspace_meta", { workspaceId, }); await updateModel({ diff --git a/apps/yaak-client/components/ExportDataDialog.tsx b/apps/yaak-client/components/ExportDataDialog.tsx index 620182be..a43e340f 100644 --- a/apps/yaak-client/components/ExportDataDialog.tsx +++ b/apps/yaak-client/components/ExportDataDialog.tsx @@ -1,4 +1,3 @@ -import { save } from "@tauri-apps/plugin-dialog"; import type { Workspace } from "@yaakapp-internal/models"; import { workspacesAtom } from "@yaakapp-internal/models"; import { HStack, VStack } from "@yaakapp-internal/ui"; @@ -7,12 +6,13 @@ import { useCallback, useMemo, useState } from "react"; import slugify from "slugify"; import { activeWorkspaceAtom } from "../hooks/useActiveWorkspace"; import { pluralizeCount } from "../lib/pluralize"; -import { invokeCmd } from "../lib/tauri"; +import { rpc } from "../lib/rpc"; import { CommercialUseBanner } from "./CommercialUseBanner"; import { Button } from "./core/Button"; import { Checkbox } from "./core/Checkbox"; import { DetailsBanner } from "./core/DetailsBanner"; import { Link } from "./core/Link"; +import { platform } from "@yaakapp-internal/platform"; interface Props { onHide: () => void; @@ -65,7 +65,7 @@ function ExportDataDialogContent({ const ids = Object.keys(selectedWorkspaces).filter((k) => selectedWorkspaces[k]); const workspace = ids.length === 1 ? workspaces.find((w) => w.id === ids[0]) : undefined; const slug = workspace ? slugify(workspace.name, { lower: true }) : "workspaces"; - const exportPath = await save({ + const exportPath = await platform.dialog.save({ title: "Export Data", defaultPath: `yaak.${slug}.json`, }); @@ -73,7 +73,7 @@ function ExportDataDialogContent({ return; } - await invokeCmd("cmd_export_data", { + await rpc("cmd_export_data", { workspaceIds: ids, exportPath, includePrivateEnvironments: includePrivateEnvironments, diff --git a/apps/yaak-client/components/FeedbackToast.tsx b/apps/yaak-client/components/FeedbackToast.tsx index e80e75c3..bdd770c9 100644 --- a/apps/yaak-client/components/FeedbackToast.tsx +++ b/apps/yaak-client/components/FeedbackToast.tsx @@ -2,7 +2,7 @@ import { HStack, VStack } from "@yaakapp-internal/ui"; import { useRef, useState } from "react"; import type { FeedbackFeature } from "../lib/featureFeedbackConstants"; import { FEEDBACK_FEATURES } from "../lib/featureFeedbackConstants"; -import { invokeCmd } from "../lib/tauri"; +import { rpc } from "../lib/rpc"; import { hideToastById, showToast } from "../lib/toast"; import { Button } from "./core/Button"; import { Input } from "./core/Input"; @@ -31,7 +31,7 @@ export function FeedbackToast({ feature, onDone }: Props) { onDone(); // Fire-and-forget; failures are intentionally ignored - invokeCmd("cmd_send_feedback", { feature, text: trimmedText }).catch(() => {}); + rpc("cmd_send_feedback", { feature, text: trimmedText }).catch(() => {}); showToast({ id: `feature-feedback-${feature}`, timeout: 3000, diff --git a/apps/yaak-client/components/GrpcProtoSelectionDialog.tsx b/apps/yaak-client/components/GrpcProtoSelectionDialog.tsx index 902acf44..cafa422e 100644 --- a/apps/yaak-client/components/GrpcProtoSelectionDialog.tsx +++ b/apps/yaak-client/components/GrpcProtoSelectionDialog.tsx @@ -1,4 +1,3 @@ -import { open } from "@tauri-apps/plugin-dialog"; import type { GrpcRequest } from "@yaakapp-internal/models"; import { Banner, HStack, Icon, InlineCode, VStack } from "@yaakapp-internal/ui"; import { useActiveRequest } from "../hooks/useActiveRequest"; @@ -8,6 +7,7 @@ import { pluralizeCount } from "../lib/pluralize"; import { Button } from "./core/Button"; import { IconButton } from "./core/IconButton"; import { Link } from "./core/Link"; +import { platform } from "@yaakapp-internal/platform"; interface Props { onDone: () => void; @@ -45,7 +45,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp color="primary" variant="border" onClick={async () => { - const selected = await open({ + const selected = await platform.dialog.open({ title: "Select Proto Files", multiple: true, filters: [{ name: "Proto Files", extensions: ["proto"] }], @@ -63,7 +63,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp variant="border" color="primary" onClick={async () => { - const selected = await open({ + const selected = await platform.dialog.open({ title: "Select Proto Directory", directory: true, }); diff --git a/apps/yaak-client/components/ImportCurl.tsx b/apps/yaak-client/components/ImportCurl.tsx index 5ddd1990..49e43a9d 100644 --- a/apps/yaak-client/components/ImportCurl.tsx +++ b/apps/yaak-client/components/ImportCurl.tsx @@ -1,5 +1,4 @@ -import { isTauri } from "@tauri-apps/api/core"; -import { readText } from "@tauri-apps/plugin-clipboard-manager"; +import { platform, useCapability } from "@yaakapp-internal/platform"; import { Icon } from "@yaakapp-internal/ui"; import * as m from "motion/react-m"; import { useEffect, useState } from "react"; @@ -9,12 +8,12 @@ import { showToast } from "../lib/toast"; import { Button } from "./core/Button"; /** - * Offers to create a request from a Curl command on the clipboard. The desktop app can - * read the clipboard whenever the window is focused, but a browser can't without - * prompting for permission, so it waits for the user to paste instead. + * Offers to create a request from a Curl command on the clipboard. A host that can read + * the clipboard on its own offers it whenever the window is focused; one that would have + * to prompt for permission first waits for the user to paste instead. */ export function ImportCurl() { - return isTauri() ? : ; + return useCapability("clipboardRead") ? : ; } function ImportCurlButton() { @@ -24,7 +23,7 @@ function ImportCurlButton() { // oxlint-disable-next-line react-hooks/exhaustive-deps -- none useEffect(() => { - void readText().then(setClipboardText); + void platform.clipboard.readText().then(setClipboardText); }, [focused]); if (!looksLikeCurl(clipboardText)) { diff --git a/apps/yaak-client/components/LicenseBadge.tsx b/apps/yaak-client/components/LicenseBadge.tsx index 5401ef67..a0cf9da0 100644 --- a/apps/yaak-client/components/LicenseBadge.tsx +++ b/apps/yaak-client/components/LicenseBadge.tsx @@ -1,4 +1,3 @@ -import { openUrl } from "@tauri-apps/plugin-opener"; import type { LicenseCheckStatus } from "@yaakapp-internal/license"; import { useLicense } from "@yaakapp-internal/license"; import { settingsAtom } from "@yaakapp-internal/models"; @@ -14,6 +13,7 @@ import type { ButtonProps } from "./core/Button"; import { Dropdown, type DropdownItem } from "./core/Dropdown"; import { Icon } from "@yaakapp-internal/ui"; import { PillButton } from "./core/PillButton"; +import { platform } from "@yaakapp-internal/platform"; const dismissedAtom = atomWithKVStorage("dismissed_license_expired", null); @@ -52,7 +52,7 @@ function getDetail( leftSlot: , rightSlot: , hidden: data.data.changes === 0 || data.data.changesUrl == null, - onSelect: () => openUrl(data.data.changesUrl ?? ""), + onSelect: () => platform.openUrl(data.data.changesUrl ?? ""), }, { type: "separator", @@ -63,7 +63,7 @@ function getDetail( leftSlot: , rightSlot: , hidden: data.data.changesUrl == null, - onSelect: () => openUrl(data.data.billingUrl), + onSelect: () => platform.openUrl(data.data.billingUrl), }, { label: "Enter License Key", diff --git a/apps/yaak-client/components/LocalImage.tsx b/apps/yaak-client/components/LocalImage.tsx index 5c4f2719..498b060f 100644 --- a/apps/yaak-client/components/LocalImage.tsx +++ b/apps/yaak-client/components/LocalImage.tsx @@ -1,7 +1,6 @@ import { useQuery } from "@tanstack/react-query"; -import { convertFileSrc } from "@tauri-apps/api/core"; -import { resolveResource } from "@tauri-apps/api/path"; import classNames from "classnames"; +import { platform } from "@yaakapp-internal/platform"; interface Props { src: string; @@ -12,8 +11,8 @@ export function LocalImage({ src: srcPath, className }: Props) { const src = useQuery({ queryKey: ["local-image", srcPath], queryFn: async () => { - const p = await resolveResource(srcPath); - return convertFileSrc(p); + const p = await platform.files.resolveResource(srcPath); + return platform.files.url(p); }, }); diff --git a/apps/yaak-client/components/ResponseHeaders.tsx b/apps/yaak-client/components/ResponseHeaders.tsx index 218d5165..ea1182ff 100644 --- a/apps/yaak-client/components/ResponseHeaders.tsx +++ b/apps/yaak-client/components/ResponseHeaders.tsx @@ -1,4 +1,3 @@ -import { openUrl } from "@tauri-apps/plugin-opener"; import type { HttpResponse } from "@yaakapp-internal/models"; import { format, formatDistanceToNowStrict } from "date-fns"; import { useMemo } from "react"; @@ -6,6 +5,7 @@ import { CountBadge } from "./core/CountBadge"; import { DetailsBanner } from "./core/DetailsBanner"; import { IconButton } from "./core/IconButton"; import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; +import { platform } from "@yaakapp-internal/platform"; interface Props { response: HttpResponse; @@ -45,7 +45,7 @@ export function ResponseHeaders({ response }: Props) { iconSize="sm" className="inline-block w-auto h-auto! opacity-50 hover:opacity-100" icon="external_link" - onClick={() => openUrl(response.url)} + onClick={() => platform.openUrl(response.url)} title="Open in browser" /> diff --git a/apps/yaak-client/components/ResponseInfo.tsx b/apps/yaak-client/components/ResponseInfo.tsx index c4e2b98f..378895fc 100644 --- a/apps/yaak-client/components/ResponseInfo.tsx +++ b/apps/yaak-client/components/ResponseInfo.tsx @@ -1,7 +1,7 @@ -import { openUrl } from "@tauri-apps/plugin-opener"; import type { HttpResponse } from "@yaakapp-internal/models"; import { IconButton } from "./core/IconButton"; import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; +import { platform } from "@yaakapp-internal/platform"; interface Props { response: HttpResponse; @@ -26,7 +26,7 @@ export function ResponseInfo({ response }: Props) { iconSize="sm" className="inline-block w-auto ml-1 h-auto! opacity-50 hover:opacity-100" icon="external_link" - onClick={() => openUrl(response.url)} + onClick={() => platform.openUrl(response.url)} title="Open in browser" /> diff --git a/apps/yaak-client/components/SelectFile.tsx b/apps/yaak-client/components/SelectFile.tsx index af84f3dd..bc148723 100644 --- a/apps/yaak-client/components/SelectFile.tsx +++ b/apps/yaak-client/components/SelectFile.tsx @@ -1,5 +1,4 @@ -import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; -import { open } from "@tauri-apps/plugin-dialog"; +import { platform } from "@yaakapp-internal/platform"; import { HStack } from "@yaakapp-internal/ui"; import classNames from "classnames"; import mime from "mime"; @@ -41,7 +40,7 @@ export function SelectFile({ ...props }: Props) { const handleClick = async () => { - const filePath = await open({ + const filePath = await platform.dialog.open({ title: directory ? "Select Folder" : "Select File", multiple: false, directory, @@ -64,32 +63,21 @@ export function SelectFile({ // NOTE: This doesn't work for Windows since native drag-n-drop can't work at the same tmie // as browser drag-n-drop. useEffect(() => { - let unlisten: (() => void) | undefined; - const setup = async () => { - const webview = getCurrentWebviewWindow(); - unlisten = await webview.onDragDropEvent((event) => { - if (event.payload.type === "over") { - const p = event.payload.position; - const r = ref.current?.getBoundingClientRect(); - if (r == null) return; - const isOver = p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom; - console.log("IS OVER", isOver); - setIsHovering(isOver); - } else if (event.payload.type === "drop" && isHovering) { - console.log("User dropped", event.payload.paths); - const p = event.payload.paths[0]; - if (p) onChange({ filePath: p, contentType: null }); - setIsHovering(false); - } else { - console.log("File drop cancelled"); - setIsHovering(false); - } - }); - }; - setup().catch(console.error); - return () => { - if (unlisten) unlisten(); - }; + return platform.window.onDragDrop((event) => { + if (event.type === "over") { + const p = event.position; + const r = ref.current?.getBoundingClientRect(); + if (r == null) return; + const isOver = p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom; + setIsHovering(isOver); + } else if (event.type === "drop" && isHovering) { + const p = event.paths[0]; + if (p) onChange({ filePath: p, contentType: null }); + setIsHovering(false); + } else { + setIsHovering(false); + } + }); }, [isHovering, onChange]); const filePathWithNameOverride = nameOverride ? `${filePath} (${nameOverride})` : filePath; diff --git a/apps/yaak-client/components/Settings/Settings.tsx b/apps/yaak-client/components/Settings/Settings.tsx index e71e06eb..6a57b271 100644 --- a/apps/yaak-client/components/Settings/Settings.tsx +++ b/apps/yaak-client/components/Settings/Settings.tsx @@ -1,6 +1,5 @@ import { useSearch } from "@tanstack/react-router"; -import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; -import { type } from "@tauri-apps/plugin-os"; +import { platform } from "@yaakapp-internal/platform"; import { useLicense } from "@yaakapp-internal/license"; import { pluginsAtom, settingsAtom } from "@yaakapp-internal/models"; import { HeaderSize, HStack, Icon } from "@yaakapp-internal/ui"; @@ -60,7 +59,7 @@ export default function Settings({ hide }: Props) { hide(); } else { // It's being shown in a window, so close the window - await getCurrentWebviewWindow().close(); + await platform.window.close(); } }); @@ -75,7 +74,7 @@ export default function Settings({ hide }: Props) { onlyXWindowControl size="md" className="x-theme-appHeader bg-surface text-text-subtle flex items-center justify-center border-b border-border-subtle text-sm font-semibold" - osType={type()} + osType={platform.osType()} hideWindowControls={settings.hideWindowControls} useNativeTitlebar={settings.useNativeTitlebar} interfaceScale={settings.interfaceScale} @@ -85,7 +84,7 @@ export default function Settings({ hide }: Props) { justifyContent="center" className="w-full h-full grid grid-cols-[1fr_auto] pointer-events-none" > -
Settings
+
Settings
)} diff --git a/apps/yaak-client/components/Settings/SettingsGeneral.tsx b/apps/yaak-client/components/Settings/SettingsGeneral.tsx index bd8a0957..d36758f9 100644 --- a/apps/yaak-client/components/Settings/SettingsGeneral.tsx +++ b/apps/yaak-client/components/Settings/SettingsGeneral.tsx @@ -1,4 +1,3 @@ -import { revealItemInDir } from "@tauri-apps/plugin-opener"; import { patchModel, settingsAtom } from "@yaakapp-internal/models"; import { Heading, VStack } from "@yaakapp-internal/ui"; import { useAtomValue } from "jotai"; @@ -9,6 +8,7 @@ import { CargoFeature } from "../CargoFeature"; import { CommercialUseBanner } from "../CommercialUseBanner"; import { DismissibleBanner } from "../core/DismissibleBanner"; import { IconButton } from "../core/IconButton"; +import { platform } from "@yaakapp-internal/platform"; import { ModelSettingRowBoolean, ModelSettingSelectControl, @@ -152,7 +152,7 @@ export function SettingsGeneral() { { title: revealInFinderText, icon: "folder_open", - onClick: () => revealItemInDir(appInfo.appDataDir), + onClick: () => platform.revealItemInDir(appInfo.appDataDir), }, ]} /> @@ -168,7 +168,7 @@ export function SettingsGeneral() { { title: revealInFinderText, icon: "folder_open", - onClick: () => revealItemInDir(appInfo.appLogDir), + onClick: () => platform.revealItemInDir(appInfo.appLogDir), }, ]} /> diff --git a/apps/yaak-client/components/Settings/SettingsInterface.tsx b/apps/yaak-client/components/Settings/SettingsInterface.tsx index 7974d9ab..baffa273 100644 --- a/apps/yaak-client/components/Settings/SettingsInterface.tsx +++ b/apps/yaak-client/components/Settings/SettingsInterface.tsx @@ -1,4 +1,4 @@ -import { type } from "@tauri-apps/plugin-os"; +import { platform } from "@yaakapp-internal/platform"; import { useFonts } from "@yaakapp-internal/fonts"; import { useLicense } from "@yaakapp-internal/license"; import type { EditorKeymap, Settings } from "@yaakapp-internal/models"; @@ -9,7 +9,7 @@ import { useState } from "react"; import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace"; import { showConfirm } from "../../lib/confirm"; import { pricingUrl } from "../../lib/pricingUrl"; -import { invokeCmd } from "../../lib/tauri"; +import { rpc } from "../../lib/rpc"; import { CargoFeature } from "../CargoFeature"; import { Button } from "../core/Button"; import { Checkbox } from "../core/Checkbox"; @@ -176,7 +176,7 @@ export function SettingsInterface() { - {type() !== "macos" && ( + {platform.osType() !== "macos" && ( { await patchModel(settings, { useNativeTitlebar: nativeTitlebar }); - await invokeCmd("cmd_restart"); + await rpc("cmd_restart"); }} > Apply and Restart diff --git a/apps/yaak-client/components/Settings/SettingsLicense.tsx b/apps/yaak-client/components/Settings/SettingsLicense.tsx index baa4bd6d..f2ac0016 100644 --- a/apps/yaak-client/components/Settings/SettingsLicense.tsx +++ b/apps/yaak-client/components/Settings/SettingsLicense.tsx @@ -1,4 +1,3 @@ -import { openUrl } from "@tauri-apps/plugin-opener"; import { useLicense } from "@yaakapp-internal/license"; import { Banner, HStack, Icon, VStack } from "@yaakapp-internal/ui"; import { differenceInDays } from "date-fns"; @@ -12,6 +11,7 @@ import { Button } from "../core/Button"; import { Link } from "../core/Link"; import { PlainInput } from "../core/PlainInput"; import { Separator } from "../core/Separator"; +import { platform } from "@yaakapp-internal/platform"; export function SettingsLicense() { return ( @@ -135,7 +135,7 @@ function SettingsLicenseCmp() {