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() {