mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-27 13:54:11 +02:00
Add a typed platform package to decouple the frontend from Tauri (#539)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0068be9ffc
commit
2383f06e71
@@ -5,7 +5,7 @@ import { createFastMutation } from "../hooks/useFastMutation";
|
|||||||
import { wasUpdatedExternally } from "../hooks/useRequestUpdateKey";
|
import { wasUpdatedExternally } from "../hooks/useRequestUpdateKey";
|
||||||
import { createRequestAndNavigate } from "../lib/createRequestAndNavigate";
|
import { createRequestAndNavigate } from "../lib/createRequestAndNavigate";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { showToast } from "../lib/toast";
|
import { showToast } from "../lib/toast";
|
||||||
|
|
||||||
export function looksLikeCurl(text: string) {
|
export function looksLikeCurl(text: string) {
|
||||||
@@ -20,7 +20,7 @@ export const importCurl = createFastMutation<
|
|||||||
mutationKey: ["import_curl"],
|
mutationKey: ["import_curl"],
|
||||||
mutationFn: async ({ overwriteRequestId, command }) => {
|
mutationFn: async ({ overwriteRequestId, command }) => {
|
||||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||||
const importedRequest: HttpRequest = await invokeCmd("cmd_curl_to_request", {
|
const importedRequest: HttpRequest = await rpc("cmd_curl_to_request", {
|
||||||
command,
|
command,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace";
|
|||||||
import { createFastMutation } from "../hooks/useFastMutation";
|
import { createFastMutation } from "../hooks/useFastMutation";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
import { router } from "../lib/router";
|
import { router } from "../lib/router";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
// Allow tab with optional subtab (e.g., "plugins:installed")
|
// Allow tab with optional subtab (e.g., "plugins:installed")
|
||||||
type SettingsTabWithSubtab = SettingsTab | `${SettingsTab}:${string}` | null;
|
type SettingsTabWithSubtab = SettingsTab | `${SettingsTab}:${string}` | null;
|
||||||
@@ -20,7 +20,7 @@ export const openSettings = createFastMutation<void, string, SettingsTabWithSubt
|
|||||||
search: { tab: (tab ?? undefined) as SettingsTab | undefined },
|
search: { tab: (tab ?? undefined) as SettingsTab | undefined },
|
||||||
});
|
});
|
||||||
|
|
||||||
await invokeCmd("cmd_new_child_window", {
|
await rpc("cmd_new_child_window", {
|
||||||
url: location.href,
|
url: location.href,
|
||||||
label: "settings",
|
label: "settings",
|
||||||
title: "Yaak Settings",
|
title: "Yaak Settings",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { getRecentCookieJars } from "../hooks/useRecentCookieJars";
|
|||||||
import { getRecentEnvironments } from "../hooks/useRecentEnvironments";
|
import { getRecentEnvironments } from "../hooks/useRecentEnvironments";
|
||||||
import { getRecentRequests } from "../hooks/useRecentRequests";
|
import { getRecentRequests } from "../hooks/useRecentRequests";
|
||||||
import { router } from "../lib/router";
|
import { router } from "../lib/router";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
export const switchWorkspace = createFastMutation<
|
export const switchWorkspace = createFastMutation<
|
||||||
void,
|
void,
|
||||||
@@ -30,7 +30,7 @@ export const switchWorkspace = createFastMutation<
|
|||||||
params: { workspaceId },
|
params: { workspaceId },
|
||||||
search,
|
search,
|
||||||
});
|
});
|
||||||
await invokeCmd<void>("cmd_new_main_window", { url: location.href });
|
await rpc<void>("cmd_new_main_window", { url: location.href });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { open } from "@tauri-apps/plugin-dialog";
|
|
||||||
import { gitClone } from "@yaakapp-internal/git";
|
import { gitClone } from "@yaakapp-internal/git";
|
||||||
import { Banner, VStack } from "@yaakapp-internal/ui";
|
import { Banner, VStack } from "@yaakapp-internal/ui";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -11,6 +10,7 @@ import { Checkbox } from "./core/Checkbox";
|
|||||||
import { IconButton } from "./core/IconButton";
|
import { IconButton } from "./core/IconButton";
|
||||||
import { PlainInput } from "./core/PlainInput";
|
import { PlainInput } from "./core/PlainInput";
|
||||||
import { promptCredentials } from "./git/credentials";
|
import { promptCredentials } from "./git/credentials";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
hide: () => void;
|
hide: () => void;
|
||||||
@@ -38,7 +38,7 @@ export function CloneGitRepositoryDialog({ hide }: Props) {
|
|||||||
hasSubdirectory && subdirectory ? `${directory}${sep}${subdirectory}` : directory;
|
hasSubdirectory && subdirectory ? `${directory}${sep}${subdirectory}` : directory;
|
||||||
|
|
||||||
const handleSelectDirectory = async () => {
|
const handleSelectDirectory = async () => {
|
||||||
const dir = await open({
|
const dir = await platform.dialog.open({
|
||||||
title: "Select Directory",
|
title: "Select Directory",
|
||||||
directory: true,
|
directory: true,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
|
|||||||
@@ -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 type { LicenseCheckStatus } from "@yaakapp-internal/license";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { useKeyValue } from "../hooks/useKeyValue";
|
import { useKeyValue } from "../hooks/useKeyValue";
|
||||||
import { appInfo } from "../lib/appInfo";
|
import { appInfo } from "../lib/appInfo";
|
||||||
import { pricingUrl } from "../lib/pricingUrl";
|
import { pricingUrl } from "../lib/pricingUrl";
|
||||||
import { DismissibleBanner } from "./core/DismissibleBanner";
|
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_SNOOZE_MS = 7 * 24 * 60 * 60 * 1000;
|
||||||
const COMMERCIAL_USE_BANNER_MESSAGE =
|
const COMMERCIAL_USE_BANNER_MESSAGE =
|
||||||
@@ -95,7 +95,7 @@ async function shouldShowCommercialUsePrompt(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const license = await invoke<LicenseCheckStatus>("plugin:yaak-license|check");
|
const license = await rpc<LicenseCheckStatus>("plugin:yaak-license|check");
|
||||||
return license.status === "personal_use";
|
return license.status === "personal_use";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Failed to check license before commercial-use prompt", err);
|
console.log("Failed to check license before commercial-use prompt", err);
|
||||||
@@ -104,7 +104,7 @@ async function shouldShowCommercialUsePrompt(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openCommercialUsePricing(source: string): Promise<void> {
|
async function openCommercialUsePricing(source: string): Promise<void> {
|
||||||
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 {
|
function isSnoozed(value: string | null, ms: number): boolean {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { VStack } from "@yaakapp-internal/ui";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { router } from "../lib/router";
|
import { router } from "../lib/router";
|
||||||
import { setupOrConfigureEncryption } from "../lib/setupOrConfigureEncryption";
|
import { setupOrConfigureEncryption } from "../lib/setupOrConfigureEncryption";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { showErrorToast } from "../lib/toast";
|
import { showErrorToast } from "../lib/toast";
|
||||||
import { Button } from "./core/Button";
|
import { Button } from "./core/Button";
|
||||||
import { Checkbox } from "./core/Checkbox";
|
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
|
// Do getWorkspaceMeta instead of naively creating one because it might have
|
||||||
// been created already when the store refreshes the workspace meta after
|
// been created already when the store refreshes the workspace meta after
|
||||||
const workspaceMeta = await invokeCmd<WorkspaceMeta>("cmd_get_workspace_meta", {
|
const workspaceMeta = await rpc<WorkspaceMeta>("cmd_get_workspace_meta", {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
});
|
});
|
||||||
await updateModel({
|
await updateModel({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { save } from "@tauri-apps/plugin-dialog";
|
|
||||||
import type { Workspace } from "@yaakapp-internal/models";
|
import type { Workspace } from "@yaakapp-internal/models";
|
||||||
import { workspacesAtom } from "@yaakapp-internal/models";
|
import { workspacesAtom } from "@yaakapp-internal/models";
|
||||||
import { HStack, VStack } from "@yaakapp-internal/ui";
|
import { HStack, VStack } from "@yaakapp-internal/ui";
|
||||||
@@ -7,12 +6,13 @@ import { useCallback, useMemo, useState } from "react";
|
|||||||
import slugify from "slugify";
|
import slugify from "slugify";
|
||||||
import { activeWorkspaceAtom } from "../hooks/useActiveWorkspace";
|
import { activeWorkspaceAtom } from "../hooks/useActiveWorkspace";
|
||||||
import { pluralizeCount } from "../lib/pluralize";
|
import { pluralizeCount } from "../lib/pluralize";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { CommercialUseBanner } from "./CommercialUseBanner";
|
import { CommercialUseBanner } from "./CommercialUseBanner";
|
||||||
import { Button } from "./core/Button";
|
import { Button } from "./core/Button";
|
||||||
import { Checkbox } from "./core/Checkbox";
|
import { Checkbox } from "./core/Checkbox";
|
||||||
import { DetailsBanner } from "./core/DetailsBanner";
|
import { DetailsBanner } from "./core/DetailsBanner";
|
||||||
import { Link } from "./core/Link";
|
import { Link } from "./core/Link";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onHide: () => void;
|
onHide: () => void;
|
||||||
@@ -65,7 +65,7 @@ function ExportDataDialogContent({
|
|||||||
const ids = Object.keys(selectedWorkspaces).filter((k) => selectedWorkspaces[k]);
|
const ids = Object.keys(selectedWorkspaces).filter((k) => selectedWorkspaces[k]);
|
||||||
const workspace = ids.length === 1 ? workspaces.find((w) => w.id === ids[0]) : undefined;
|
const workspace = ids.length === 1 ? workspaces.find((w) => w.id === ids[0]) : undefined;
|
||||||
const slug = workspace ? slugify(workspace.name, { lower: true }) : "workspaces";
|
const slug = workspace ? slugify(workspace.name, { lower: true }) : "workspaces";
|
||||||
const exportPath = await save({
|
const exportPath = await platform.dialog.save({
|
||||||
title: "Export Data",
|
title: "Export Data",
|
||||||
defaultPath: `yaak.${slug}.json`,
|
defaultPath: `yaak.${slug}.json`,
|
||||||
});
|
});
|
||||||
@@ -73,7 +73,7 @@ function ExportDataDialogContent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await invokeCmd("cmd_export_data", {
|
await rpc("cmd_export_data", {
|
||||||
workspaceIds: ids,
|
workspaceIds: ids,
|
||||||
exportPath,
|
exportPath,
|
||||||
includePrivateEnvironments: includePrivateEnvironments,
|
includePrivateEnvironments: includePrivateEnvironments,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { HStack, VStack } from "@yaakapp-internal/ui";
|
|||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import type { FeedbackFeature } from "../lib/featureFeedbackConstants";
|
import type { FeedbackFeature } from "../lib/featureFeedbackConstants";
|
||||||
import { FEEDBACK_FEATURES } 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 { hideToastById, showToast } from "../lib/toast";
|
||||||
import { Button } from "./core/Button";
|
import { Button } from "./core/Button";
|
||||||
import { Input } from "./core/Input";
|
import { Input } from "./core/Input";
|
||||||
@@ -31,7 +31,7 @@ export function FeedbackToast({ feature, onDone }: Props) {
|
|||||||
onDone();
|
onDone();
|
||||||
|
|
||||||
// Fire-and-forget; failures are intentionally ignored
|
// Fire-and-forget; failures are intentionally ignored
|
||||||
invokeCmd("cmd_send_feedback", { feature, text: trimmedText }).catch(() => {});
|
rpc("cmd_send_feedback", { feature, text: trimmedText }).catch(() => {});
|
||||||
showToast({
|
showToast({
|
||||||
id: `feature-feedback-${feature}`,
|
id: `feature-feedback-${feature}`,
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { open } from "@tauri-apps/plugin-dialog";
|
|
||||||
import type { GrpcRequest } from "@yaakapp-internal/models";
|
import type { GrpcRequest } from "@yaakapp-internal/models";
|
||||||
import { Banner, HStack, Icon, InlineCode, VStack } from "@yaakapp-internal/ui";
|
import { Banner, HStack, Icon, InlineCode, VStack } from "@yaakapp-internal/ui";
|
||||||
import { useActiveRequest } from "../hooks/useActiveRequest";
|
import { useActiveRequest } from "../hooks/useActiveRequest";
|
||||||
@@ -8,6 +7,7 @@ import { pluralizeCount } from "../lib/pluralize";
|
|||||||
import { Button } from "./core/Button";
|
import { Button } from "./core/Button";
|
||||||
import { IconButton } from "./core/IconButton";
|
import { IconButton } from "./core/IconButton";
|
||||||
import { Link } from "./core/Link";
|
import { Link } from "./core/Link";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onDone: () => void;
|
onDone: () => void;
|
||||||
@@ -45,7 +45,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp
|
|||||||
color="primary"
|
color="primary"
|
||||||
variant="border"
|
variant="border"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const selected = await open({
|
const selected = await platform.dialog.open({
|
||||||
title: "Select Proto Files",
|
title: "Select Proto Files",
|
||||||
multiple: true,
|
multiple: true,
|
||||||
filters: [{ name: "Proto Files", extensions: ["proto"] }],
|
filters: [{ name: "Proto Files", extensions: ["proto"] }],
|
||||||
@@ -63,7 +63,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp
|
|||||||
variant="border"
|
variant="border"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const selected = await open({
|
const selected = await platform.dialog.open({
|
||||||
title: "Select Proto Directory",
|
title: "Select Proto Directory",
|
||||||
directory: true,
|
directory: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { isTauri } from "@tauri-apps/api/core";
|
import { platform, useCapability } from "@yaakapp-internal/platform";
|
||||||
import { readText } from "@tauri-apps/plugin-clipboard-manager";
|
|
||||||
import { Icon } from "@yaakapp-internal/ui";
|
import { Icon } from "@yaakapp-internal/ui";
|
||||||
import * as m from "motion/react-m";
|
import * as m from "motion/react-m";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -9,12 +8,12 @@ import { showToast } from "../lib/toast";
|
|||||||
import { Button } from "./core/Button";
|
import { Button } from "./core/Button";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offers to create a request from a Curl command on the clipboard. The desktop app can
|
* Offers to create a request from a Curl command on the clipboard. A host that can read
|
||||||
* read the clipboard whenever the window is focused, but a browser can't without
|
* the clipboard on its own offers it whenever the window is focused; one that would have
|
||||||
* prompting for permission, so it waits for the user to paste instead.
|
* to prompt for permission first waits for the user to paste instead.
|
||||||
*/
|
*/
|
||||||
export function ImportCurl() {
|
export function ImportCurl() {
|
||||||
return isTauri() ? <ImportCurlButton /> : <ImportCurlOnPaste />;
|
return useCapability("clipboardRead") ? <ImportCurlButton /> : <ImportCurlOnPaste />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImportCurlButton() {
|
function ImportCurlButton() {
|
||||||
@@ -24,7 +23,7 @@ function ImportCurlButton() {
|
|||||||
|
|
||||||
// oxlint-disable-next-line react-hooks/exhaustive-deps -- none
|
// oxlint-disable-next-line react-hooks/exhaustive-deps -- none
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void readText().then(setClipboardText);
|
void platform.clipboard.readText().then(setClipboardText);
|
||||||
}, [focused]);
|
}, [focused]);
|
||||||
|
|
||||||
if (!looksLikeCurl(clipboardText)) {
|
if (!looksLikeCurl(clipboardText)) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
|
||||||
import type { LicenseCheckStatus } from "@yaakapp-internal/license";
|
import type { LicenseCheckStatus } from "@yaakapp-internal/license";
|
||||||
import { useLicense } from "@yaakapp-internal/license";
|
import { useLicense } from "@yaakapp-internal/license";
|
||||||
import { settingsAtom } from "@yaakapp-internal/models";
|
import { settingsAtom } from "@yaakapp-internal/models";
|
||||||
@@ -14,6 +13,7 @@ import type { ButtonProps } from "./core/Button";
|
|||||||
import { Dropdown, type DropdownItem } from "./core/Dropdown";
|
import { Dropdown, type DropdownItem } from "./core/Dropdown";
|
||||||
import { Icon } from "@yaakapp-internal/ui";
|
import { Icon } from "@yaakapp-internal/ui";
|
||||||
import { PillButton } from "./core/PillButton";
|
import { PillButton } from "./core/PillButton";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
const dismissedAtom = atomWithKVStorage<string | null>("dismissed_license_expired", null);
|
const dismissedAtom = atomWithKVStorage<string | null>("dismissed_license_expired", null);
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ function getDetail(
|
|||||||
leftSlot: <Icon icon="gift" />,
|
leftSlot: <Icon icon="gift" />,
|
||||||
rightSlot: <Icon icon="external_link" size="sm" className="opacity-disabled" />,
|
rightSlot: <Icon icon="external_link" size="sm" className="opacity-disabled" />,
|
||||||
hidden: data.data.changes === 0 || data.data.changesUrl == null,
|
hidden: data.data.changes === 0 || data.data.changesUrl == null,
|
||||||
onSelect: () => openUrl(data.data.changesUrl ?? ""),
|
onSelect: () => platform.openUrl(data.data.changesUrl ?? ""),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "separator",
|
type: "separator",
|
||||||
@@ -63,7 +63,7 @@ function getDetail(
|
|||||||
leftSlot: <Icon icon="refresh" />,
|
leftSlot: <Icon icon="refresh" />,
|
||||||
rightSlot: <Icon icon="external_link" size="sm" className="opacity-disabled" />,
|
rightSlot: <Icon icon="external_link" size="sm" className="opacity-disabled" />,
|
||||||
hidden: data.data.changesUrl == null,
|
hidden: data.data.changesUrl == null,
|
||||||
onSelect: () => openUrl(data.data.billingUrl),
|
onSelect: () => platform.openUrl(data.data.billingUrl),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Enter License Key",
|
label: "Enter License Key",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
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 classNames from "classnames";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
src: string;
|
src: string;
|
||||||
@@ -12,8 +11,8 @@ export function LocalImage({ src: srcPath, className }: Props) {
|
|||||||
const src = useQuery({
|
const src = useQuery({
|
||||||
queryKey: ["local-image", srcPath],
|
queryKey: ["local-image", srcPath],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const p = await resolveResource(srcPath);
|
const p = await platform.files.resolveResource(srcPath);
|
||||||
return convertFileSrc(p);
|
return platform.files.url(p);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
|
||||||
import type { HttpResponse } from "@yaakapp-internal/models";
|
import type { HttpResponse } from "@yaakapp-internal/models";
|
||||||
import { format, formatDistanceToNowStrict } from "date-fns";
|
import { format, formatDistanceToNowStrict } from "date-fns";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
@@ -6,6 +5,7 @@ import { CountBadge } from "./core/CountBadge";
|
|||||||
import { DetailsBanner } from "./core/DetailsBanner";
|
import { DetailsBanner } from "./core/DetailsBanner";
|
||||||
import { IconButton } from "./core/IconButton";
|
import { IconButton } from "./core/IconButton";
|
||||||
import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow";
|
import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
response: HttpResponse;
|
response: HttpResponse;
|
||||||
@@ -45,7 +45,7 @@ export function ResponseHeaders({ response }: Props) {
|
|||||||
iconSize="sm"
|
iconSize="sm"
|
||||||
className="inline-block w-auto h-auto! opacity-50 hover:opacity-100"
|
className="inline-block w-auto h-auto! opacity-50 hover:opacity-100"
|
||||||
icon="external_link"
|
icon="external_link"
|
||||||
onClick={() => openUrl(response.url)}
|
onClick={() => platform.openUrl(response.url)}
|
||||||
title="Open in browser"
|
title="Open in browser"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
|
||||||
import type { HttpResponse } from "@yaakapp-internal/models";
|
import type { HttpResponse } from "@yaakapp-internal/models";
|
||||||
import { IconButton } from "./core/IconButton";
|
import { IconButton } from "./core/IconButton";
|
||||||
import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow";
|
import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
response: HttpResponse;
|
response: HttpResponse;
|
||||||
@@ -26,7 +26,7 @@ export function ResponseInfo({ response }: Props) {
|
|||||||
iconSize="sm"
|
iconSize="sm"
|
||||||
className="inline-block w-auto ml-1 h-auto! opacity-50 hover:opacity-100"
|
className="inline-block w-auto ml-1 h-auto! opacity-50 hover:opacity-100"
|
||||||
icon="external_link"
|
icon="external_link"
|
||||||
onClick={() => openUrl(response.url)}
|
onClick={() => platform.openUrl(response.url)}
|
||||||
title="Open in browser"
|
title="Open in browser"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { open } from "@tauri-apps/plugin-dialog";
|
|
||||||
import { HStack } from "@yaakapp-internal/ui";
|
import { HStack } from "@yaakapp-internal/ui";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import mime from "mime";
|
import mime from "mime";
|
||||||
@@ -41,7 +40,7 @@ export function SelectFile({
|
|||||||
...props
|
...props
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const handleClick = async () => {
|
const handleClick = async () => {
|
||||||
const filePath = await open({
|
const filePath = await platform.dialog.open({
|
||||||
title: directory ? "Select Folder" : "Select File",
|
title: directory ? "Select Folder" : "Select File",
|
||||||
multiple: false,
|
multiple: false,
|
||||||
directory,
|
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
|
// NOTE: This doesn't work for Windows since native drag-n-drop can't work at the same tmie
|
||||||
// as browser drag-n-drop.
|
// as browser drag-n-drop.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let unlisten: (() => void) | undefined;
|
return platform.window.onDragDrop((event) => {
|
||||||
const setup = async () => {
|
if (event.type === "over") {
|
||||||
const webview = getCurrentWebviewWindow();
|
const p = event.position;
|
||||||
unlisten = await webview.onDragDropEvent((event) => {
|
const r = ref.current?.getBoundingClientRect();
|
||||||
if (event.payload.type === "over") {
|
if (r == null) return;
|
||||||
const p = event.payload.position;
|
const isOver = p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom;
|
||||||
const r = ref.current?.getBoundingClientRect();
|
setIsHovering(isOver);
|
||||||
if (r == null) return;
|
} else if (event.type === "drop" && isHovering) {
|
||||||
const isOver = p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom;
|
const p = event.paths[0];
|
||||||
console.log("IS OVER", isOver);
|
if (p) onChange({ filePath: p, contentType: null });
|
||||||
setIsHovering(isOver);
|
setIsHovering(false);
|
||||||
} else if (event.payload.type === "drop" && isHovering) {
|
} else {
|
||||||
console.log("User dropped", event.payload.paths);
|
setIsHovering(false);
|
||||||
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();
|
|
||||||
};
|
|
||||||
}, [isHovering, onChange]);
|
}, [isHovering, onChange]);
|
||||||
|
|
||||||
const filePathWithNameOverride = nameOverride ? `${filePath} (${nameOverride})` : filePath;
|
const filePathWithNameOverride = nameOverride ? `${filePath} (${nameOverride})` : filePath;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useSearch } from "@tanstack/react-router";
|
import { useSearch } from "@tanstack/react-router";
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { type } from "@tauri-apps/plugin-os";
|
|
||||||
import { useLicense } from "@yaakapp-internal/license";
|
import { useLicense } from "@yaakapp-internal/license";
|
||||||
import { pluginsAtom, settingsAtom } from "@yaakapp-internal/models";
|
import { pluginsAtom, settingsAtom } from "@yaakapp-internal/models";
|
||||||
import { HeaderSize, HStack, Icon } from "@yaakapp-internal/ui";
|
import { HeaderSize, HStack, Icon } from "@yaakapp-internal/ui";
|
||||||
@@ -60,7 +59,7 @@ export default function Settings({ hide }: Props) {
|
|||||||
hide();
|
hide();
|
||||||
} else {
|
} else {
|
||||||
// It's being shown in a window, so close the window
|
// 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
|
onlyXWindowControl
|
||||||
size="md"
|
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"
|
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}
|
hideWindowControls={settings.hideWindowControls}
|
||||||
useNativeTitlebar={settings.useNativeTitlebar}
|
useNativeTitlebar={settings.useNativeTitlebar}
|
||||||
interfaceScale={settings.interfaceScale}
|
interfaceScale={settings.interfaceScale}
|
||||||
@@ -85,7 +84,7 @@ export default function Settings({ hide }: Props) {
|
|||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
className="w-full h-full grid grid-cols-[1fr_auto] pointer-events-none"
|
className="w-full h-full grid grid-cols-[1fr_auto] pointer-events-none"
|
||||||
>
|
>
|
||||||
<div className={classNames(type() === "macos" ? "text-center" : "pl-2")}>Settings</div>
|
<div className={classNames(platform.osType() === "macos" ? "text-center" : "pl-2")}>Settings</div>
|
||||||
</HStack>
|
</HStack>
|
||||||
</HeaderSize>
|
</HeaderSize>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { revealItemInDir } from "@tauri-apps/plugin-opener";
|
|
||||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||||
import { Heading, VStack } from "@yaakapp-internal/ui";
|
import { Heading, VStack } from "@yaakapp-internal/ui";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
@@ -9,6 +8,7 @@ import { CargoFeature } from "../CargoFeature";
|
|||||||
import { CommercialUseBanner } from "../CommercialUseBanner";
|
import { CommercialUseBanner } from "../CommercialUseBanner";
|
||||||
import { DismissibleBanner } from "../core/DismissibleBanner";
|
import { DismissibleBanner } from "../core/DismissibleBanner";
|
||||||
import { IconButton } from "../core/IconButton";
|
import { IconButton } from "../core/IconButton";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import {
|
import {
|
||||||
ModelSettingRowBoolean,
|
ModelSettingRowBoolean,
|
||||||
ModelSettingSelectControl,
|
ModelSettingSelectControl,
|
||||||
@@ -152,7 +152,7 @@ export function SettingsGeneral() {
|
|||||||
{
|
{
|
||||||
title: revealInFinderText,
|
title: revealInFinderText,
|
||||||
icon: "folder_open",
|
icon: "folder_open",
|
||||||
onClick: () => revealItemInDir(appInfo.appDataDir),
|
onClick: () => platform.revealItemInDir(appInfo.appDataDir),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -168,7 +168,7 @@ export function SettingsGeneral() {
|
|||||||
{
|
{
|
||||||
title: revealInFinderText,
|
title: revealInFinderText,
|
||||||
icon: "folder_open",
|
icon: "folder_open",
|
||||||
onClick: () => revealItemInDir(appInfo.appLogDir),
|
onClick: () => platform.revealItemInDir(appInfo.appLogDir),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type } from "@tauri-apps/plugin-os";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { useFonts } from "@yaakapp-internal/fonts";
|
import { useFonts } from "@yaakapp-internal/fonts";
|
||||||
import { useLicense } from "@yaakapp-internal/license";
|
import { useLicense } from "@yaakapp-internal/license";
|
||||||
import type { EditorKeymap, Settings } from "@yaakapp-internal/models";
|
import type { EditorKeymap, Settings } from "@yaakapp-internal/models";
|
||||||
@@ -9,7 +9,7 @@ import { useState } from "react";
|
|||||||
import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace";
|
import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace";
|
||||||
import { showConfirm } from "../../lib/confirm";
|
import { showConfirm } from "../../lib/confirm";
|
||||||
import { pricingUrl } from "../../lib/pricingUrl";
|
import { pricingUrl } from "../../lib/pricingUrl";
|
||||||
import { invokeCmd } from "../../lib/tauri";
|
import { rpc } from "../../lib/rpc";
|
||||||
import { CargoFeature } from "../CargoFeature";
|
import { CargoFeature } from "../CargoFeature";
|
||||||
import { Button } from "../core/Button";
|
import { Button } from "../core/Button";
|
||||||
import { Checkbox } from "../core/Checkbox";
|
import { Checkbox } from "../core/Checkbox";
|
||||||
@@ -176,7 +176,7 @@ export function SettingsInterface() {
|
|||||||
|
|
||||||
<SettingsSection title="Window">
|
<SettingsSection title="Window">
|
||||||
<NativeTitlebarSetting settings={settings} />
|
<NativeTitlebarSetting settings={settings} />
|
||||||
{type() !== "macos" && (
|
{platform.osType() !== "macos" && (
|
||||||
<ModelSettingRowBoolean
|
<ModelSettingRowBoolean
|
||||||
model={settings}
|
model={settings}
|
||||||
modelKey="hideWindowControls"
|
modelKey="hideWindowControls"
|
||||||
@@ -216,7 +216,7 @@ function NativeTitlebarSetting({ settings }: { settings: Settings }) {
|
|||||||
size="xs"
|
size="xs"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await patchModel(settings, { useNativeTitlebar: nativeTitlebar });
|
await patchModel(settings, { useNativeTitlebar: nativeTitlebar });
|
||||||
await invokeCmd("cmd_restart");
|
await rpc("cmd_restart");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Apply and Restart
|
Apply and Restart
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
|
||||||
import { useLicense } from "@yaakapp-internal/license";
|
import { useLicense } from "@yaakapp-internal/license";
|
||||||
import { Banner, HStack, Icon, VStack } from "@yaakapp-internal/ui";
|
import { Banner, HStack, Icon, VStack } from "@yaakapp-internal/ui";
|
||||||
import { differenceInDays } from "date-fns";
|
import { differenceInDays } from "date-fns";
|
||||||
@@ -12,6 +11,7 @@ import { Button } from "../core/Button";
|
|||||||
import { Link } from "../core/Link";
|
import { Link } from "../core/Link";
|
||||||
import { PlainInput } from "../core/PlainInput";
|
import { PlainInput } from "../core/PlainInput";
|
||||||
import { Separator } from "../core/Separator";
|
import { Separator } from "../core/Separator";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export function SettingsLicense() {
|
export function SettingsLicense() {
|
||||||
return (
|
return (
|
||||||
@@ -135,7 +135,7 @@ function SettingsLicenseCmp() {
|
|||||||
<Button
|
<Button
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => openUrl("https://yaak.app/dashboard?intent=app.license.support")}
|
onClick={() => platform.openUrl("https://yaak.app/dashboard?intent=app.license.support")}
|
||||||
rightSlot={<Icon icon="external_link" />}
|
rightSlot={<Icon icon="external_link" />}
|
||||||
>
|
>
|
||||||
Direct Support
|
Direct Support
|
||||||
@@ -151,7 +151,7 @@ function SettingsLicenseCmp() {
|
|||||||
color="primary"
|
color="primary"
|
||||||
rightSlot={<Icon icon="external_link" />}
|
rightSlot={<Icon icon="external_link" />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
openUrl(pricingUrl(`app.license.purchase.${check.data?.status ?? "unknown"}`))
|
platform.openUrl(pricingUrl(`app.license.purchase.${check.data?.status ?? "unknown"}`))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Purchase License
|
Purchase License
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
|
||||||
import type { Plugin } from "@yaakapp-internal/models";
|
import type { Plugin } from "@yaakapp-internal/models";
|
||||||
import { patchModel, pluginsAtom } from "@yaakapp-internal/models";
|
import { patchModel, pluginsAtom } from "@yaakapp-internal/models";
|
||||||
import type { PluginVersion } from "@yaakapp-internal/plugins";
|
import type { PluginVersion } from "@yaakapp-internal/plugins";
|
||||||
@@ -39,6 +38,7 @@ import { PlainInput } from "../core/PlainInput";
|
|||||||
import { TabContent, Tabs } from "../core/Tabs/Tabs";
|
import { TabContent, Tabs } from "../core/Tabs/Tabs";
|
||||||
import { EmptyStateText } from "../EmptyStateText";
|
import { EmptyStateText } from "../EmptyStateText";
|
||||||
import { SelectFile } from "../SelectFile";
|
import { SelectFile } from "../SelectFile";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface SettingsPluginsProps {
|
interface SettingsPluginsProps {
|
||||||
defaultSubtab?: string;
|
defaultSubtab?: string;
|
||||||
@@ -113,7 +113,7 @@ export function SettingsPlugins({ defaultSubtab }: SettingsPluginsProps) {
|
|||||||
icon="help"
|
icon="help"
|
||||||
title="View documentation"
|
title="View documentation"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
openUrl("https://yaak.app/docs/plugin-development/plugins-quick-start")
|
platform.openUrl("https://yaak.app/docs/plugin-development/plugins-quick-start")
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
|
||||||
import { useLicense } from "@yaakapp-internal/license";
|
import { useLicense } from "@yaakapp-internal/license";
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { openSettings } from "../commands/openSettings";
|
import { openSettings } from "../commands/openSettings";
|
||||||
@@ -13,6 +12,7 @@ import { Dropdown } from "./core/Dropdown";
|
|||||||
import { Icon } from "@yaakapp-internal/ui";
|
import { Icon } from "@yaakapp-internal/ui";
|
||||||
import { IconButton } from "./core/IconButton";
|
import { IconButton } from "./core/IconButton";
|
||||||
import { KeyboardShortcutsDialog } from "./KeyboardShortcutsDialog";
|
import { KeyboardShortcutsDialog } from "./KeyboardShortcutsDialog";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export function SettingsDropdown() {
|
export function SettingsDropdown() {
|
||||||
const exportData = useExportData();
|
const exportData = useExportData();
|
||||||
@@ -62,7 +62,7 @@ export function SettingsDropdown() {
|
|||||||
{
|
{
|
||||||
label: "Create Run Button",
|
label: "Create Run Button",
|
||||||
leftSlot: <Icon icon="rocket" />,
|
leftSlot: <Icon icon="rocket" />,
|
||||||
onSelect: () => openUrl("https://yaak.app/button/new"),
|
onSelect: () => platform.openUrl("https://yaak.app/button/new"),
|
||||||
},
|
},
|
||||||
{ type: "separator", label: `Yaak v${appInfo.version}` },
|
{ type: "separator", label: `Yaak v${appInfo.version}` },
|
||||||
{
|
{
|
||||||
@@ -78,26 +78,26 @@ export function SettingsDropdown() {
|
|||||||
leftSlot: <Icon icon="circle_dollar_sign" />,
|
leftSlot: <Icon icon="circle_dollar_sign" />,
|
||||||
rightSlot: <Icon icon="external_link" color="success" className="opacity-60" />,
|
rightSlot: <Icon icon="external_link" color="success" className="opacity-60" />,
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
openUrl(pricingUrl(`app.menu.purchase.${check.data?.status ?? "unknown"}`)),
|
platform.openUrl(pricingUrl(`app.menu.purchase.${check.data?.status ?? "unknown"}`)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Install CLI",
|
label: "Install CLI",
|
||||||
hidden: appInfo.cliVersion != null,
|
hidden: appInfo.cliVersion != null,
|
||||||
leftSlot: <Icon icon="square_terminal" />,
|
leftSlot: <Icon icon="square_terminal" />,
|
||||||
rightSlot: <Icon icon="external_link" color="secondary" />,
|
rightSlot: <Icon icon="external_link" color="secondary" />,
|
||||||
onSelect: () => openUrl("https://yaak.app/docs/cli"),
|
onSelect: () => platform.openUrl("https://yaak.app/docs/cli"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Feedback",
|
label: "Feedback",
|
||||||
leftSlot: <Icon icon="chat" />,
|
leftSlot: <Icon icon="chat" />,
|
||||||
rightSlot: <Icon icon="external_link" color="secondary" />,
|
rightSlot: <Icon icon="external_link" color="secondary" />,
|
||||||
onSelect: () => openUrl("https://yaak.app/feedback"),
|
onSelect: () => platform.openUrl("https://yaak.app/feedback"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Changelog",
|
label: "Changelog",
|
||||||
leftSlot: <Icon icon="cake" />,
|
leftSlot: <Icon icon="cake" />,
|
||||||
rightSlot: <Icon icon="external_link" color="secondary" />,
|
rightSlot: <Icon icon="external_link" color="secondary" />,
|
||||||
onSelect: () => openUrl(`https://yaak.app/changelog/${appInfo.version}`),
|
onSelect: () => platform.openUrl(`https://yaak.app/changelog/${appInfo.version}`),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { Extension } from "@codemirror/state";
|
import type { Extension } from "@codemirror/state";
|
||||||
import { Compartment } from "@codemirror/state";
|
import { Compartment } from "@codemirror/state";
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
|
||||||
import { debounce } from "@yaakapp-internal/lib";
|
import { debounce } from "@yaakapp-internal/lib";
|
||||||
import { gitMutations } from "@yaakapp-internal/git";
|
import { gitMutations } from "@yaakapp-internal/git";
|
||||||
import type { GitStatus } from "@yaakapp-internal/git";
|
import type { GitStatus } from "@yaakapp-internal/git";
|
||||||
@@ -44,7 +43,8 @@ import { getFolderActions } from "../hooks/useFolderActions";
|
|||||||
import { getGrpcRequestActions } from "../hooks/useGrpcRequestActions";
|
import { getGrpcRequestActions } from "../hooks/useGrpcRequestActions";
|
||||||
import { useHotKey } from "../hooks/useHotKey";
|
import { useHotKey } from "../hooks/useHotKey";
|
||||||
import { getHttpRequestActions } from "../hooks/useHttpRequestActions";
|
import { getHttpRequestActions } from "../hooks/useHttpRequestActions";
|
||||||
import { useListenToTauriEvent } from "../hooks/useListenToTauriEvent";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
import { usePlatformEvent } from "../hooks/usePlatformEvent";
|
||||||
import { getModelAncestors } from "../hooks/useModelAncestors";
|
import { getModelAncestors } from "../hooks/useModelAncestors";
|
||||||
import { sendAnyHttpRequest } from "../hooks/useSendAnyHttpRequest";
|
import { sendAnyHttpRequest } from "../hooks/useSendAnyHttpRequest";
|
||||||
import { useSidebarHidden } from "../hooks/useSidebarHidden";
|
import { useSidebarHidden } from "../hooks/useSidebarHidden";
|
||||||
@@ -135,10 +135,10 @@ function Sidebar({ className }: { className?: string }) {
|
|||||||
// Focus new sidebar models created by the user in this window. Writes from other
|
// Focus new sidebar models created by the user in this window. Writes from other
|
||||||
// sources (import, sync, CLI) can carry thousands of models and shouldn't move
|
// sources (import, sync, CLI) can carry thousands of models and shouldn't move
|
||||||
// the selection.
|
// the selection.
|
||||||
useListenToTauriEvent<ModelPayload[]>("model_writes", ({ payload: payloads }) => {
|
usePlatformEvent<ModelPayload[]>("model_writes", (payloads) => {
|
||||||
for (const payload of payloads) {
|
for (const payload of payloads) {
|
||||||
if (payload.updateSource.type !== "window") continue;
|
if (payload.updateSource.type !== "window") continue;
|
||||||
if (payload.updateSource.label !== getCurrentWebviewWindow().label) continue;
|
if (payload.updateSource.label !== platform.window.label) continue;
|
||||||
if (!isSidebarLeafModel(payload.model)) continue;
|
if (!isSidebarLeafModel(payload.model)) continue;
|
||||||
if (!(payload.change.type === "upsert" && payload.change.created)) continue;
|
if (!(payload.change.type === "upsert" && payload.change.created)) continue;
|
||||||
treeRef.current?.selectItem(payload.model.id, true);
|
treeRef.current?.selectItem(payload.model.id, true);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { readDir } from "@tauri-apps/plugin-fs";
|
|
||||||
import { Banner, VStack } from "@yaakapp-internal/ui";
|
import { Banner, VStack } from "@yaakapp-internal/ui";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { openWorkspaceFromSyncDir } from "../commands/openWorkspaceFromSyncDir";
|
import { openWorkspaceFromSyncDir } from "../commands/openWorkspaceFromSyncDir";
|
||||||
@@ -6,6 +5,7 @@ import { Button } from "./core/Button";
|
|||||||
import { Checkbox } from "./core/Checkbox";
|
import { Checkbox } from "./core/Checkbox";
|
||||||
import { SettingRowBoolean, SettingRowDirectory } from "./core/SettingRow";
|
import { SettingRowBoolean, SettingRowDirectory } from "./core/SettingRow";
|
||||||
import { SelectFile } from "./SelectFile";
|
import { SelectFile } from "./SelectFile";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export interface SyncToFilesystemSettingProps {
|
export interface SyncToFilesystemSettingProps {
|
||||||
layout?: "form" | "settings";
|
layout?: "form" | "settings";
|
||||||
@@ -24,7 +24,7 @@ export function SyncToFilesystemSetting({
|
|||||||
|
|
||||||
const handleFilePathChange = async (filePath: string | null) => {
|
const handleFilePathChange = async (filePath: string | null) => {
|
||||||
if (filePath != null) {
|
if (filePath != null) {
|
||||||
const files = await readDir(filePath);
|
const files = await platform.files.readDir(filePath);
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
setSyncDir(filePath);
|
setSyncDir(filePath);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type } from "@tauri-apps/plugin-os";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { settingsAtom, workspacesAtom } from "@yaakapp-internal/models";
|
import { settingsAtom, workspacesAtom } from "@yaakapp-internal/models";
|
||||||
import { Banner, HeaderSize, HStack, SidebarLayout } from "@yaakapp-internal/ui";
|
import { Banner, HeaderSize, HStack, SidebarLayout } from "@yaakapp-internal/ui";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
@@ -53,7 +53,7 @@ export function Workspace() {
|
|||||||
|
|
||||||
const workspaces = useAtomValue(workspacesAtom);
|
const workspaces = useAtomValue(workspacesAtom);
|
||||||
const settings = useAtomValue(settingsAtom);
|
const settings = useAtomValue(settingsAtom);
|
||||||
const osType = type();
|
const osType = platform.osType();
|
||||||
const [width, setWidth] = useSidebarWidth();
|
const [width, setWidth] = useSidebarWidth();
|
||||||
const [sidebarHidden, setSidebarHidden] = useSidebarHidden();
|
const [sidebarHidden, setSidebarHidden] = useSidebarHidden();
|
||||||
const [floatingSidebarHidden, setFloatingSidebarHidden] = useFloatingSidebarHidden();
|
const [floatingSidebarHidden, setFloatingSidebarHidden] = useFloatingSidebarHidden();
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { open } from "@tauri-apps/plugin-dialog";
|
|
||||||
import { revealItemInDir } from "@tauri-apps/plugin-opener";
|
|
||||||
import { getModel, settingsAtom, workspacesAtom } from "@yaakapp-internal/models";
|
import { getModel, settingsAtom, workspacesAtom } from "@yaakapp-internal/models";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
@@ -27,6 +25,7 @@ import { Icon } from "@yaakapp-internal/ui";
|
|||||||
import type { RadioDropdownItem } from "./core/RadioDropdown";
|
import type { RadioDropdownItem } from "./core/RadioDropdown";
|
||||||
import { RadioDropdown } from "./core/RadioDropdown";
|
import { RadioDropdown } from "./core/RadioDropdown";
|
||||||
import { SwitchWorkspaceDialog } from "./SwitchWorkspaceDialog";
|
import { SwitchWorkspaceDialog } from "./SwitchWorkspaceDialog";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
type Props = Pick<ButtonProps, "className" | "justify" | "forDropdown" | "leftSlot">;
|
type Props = Pick<ButtonProps, "className" | "justify" | "forDropdown" | "leftSlot">;
|
||||||
|
|
||||||
@@ -76,7 +75,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
label: "Open Folder",
|
label: "Open Folder",
|
||||||
leftSlot: <Icon icon="folder_open" />,
|
leftSlot: <Icon icon="folder_open" />,
|
||||||
onSelect: async () => {
|
onSelect: async () => {
|
||||||
const dir = await open({
|
const dir = await platform.dialog.open({
|
||||||
title: "Select Workspace Directory",
|
title: "Select Workspace Directory",
|
||||||
directory: true,
|
directory: true,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
@@ -120,7 +119,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
leftSlot: <Icon icon="folder_symlink" />,
|
leftSlot: <Icon icon="folder_symlink" />,
|
||||||
onSelect: async () => {
|
onSelect: async () => {
|
||||||
if (workspaceMeta?.settingSyncDir == null) return;
|
if (workspaceMeta?.settingSyncDir == null) return;
|
||||||
await revealItemInDir(workspaceMeta.settingSyncDir);
|
await platform.revealItemInDir(workspaceMeta.settingSyncDir);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
useSensor,
|
useSensor,
|
||||||
useSensors,
|
useSensors,
|
||||||
} from "@dnd-kit/core";
|
} from "@dnd-kit/core";
|
||||||
import { basename } from "@tauri-apps/api/path";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import type { WrappedEnvironmentVariable } from "../../hooks/useEnvironmentVariables";
|
import type { WrappedEnvironmentVariable } from "../../hooks/useEnvironmentVariables";
|
||||||
@@ -34,6 +33,7 @@ import { Input } from "./Input";
|
|||||||
import { ensurePairId } from "./PairEditor.util";
|
import { ensurePairId } from "./PairEditor.util";
|
||||||
import type { RadioDropdownItem } from "./RadioDropdown";
|
import type { RadioDropdownItem } from "./RadioDropdown";
|
||||||
import { RadioDropdown } from "./RadioDropdown";
|
import { RadioDropdown } from "./RadioDropdown";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export interface PairEditorHandle {
|
export interface PairEditorHandle {
|
||||||
/**
|
/**
|
||||||
@@ -851,7 +851,7 @@ function FileActionsDropdown({
|
|||||||
leftSlot: <Icon icon="file_code" />,
|
leftSlot: <Icon icon="file_code" />,
|
||||||
onSelect: async () => {
|
onSelect: async () => {
|
||||||
console.log("PAIR", pair);
|
console.log("PAIR", pair);
|
||||||
const defaultFilename = await basename(pair.value ?? "");
|
const defaultFilename = await platform.files.basename(pair.value ?? "");
|
||||||
const filename = await showPrompt({
|
const filename = await showPrompt({
|
||||||
id: "filename",
|
id: "filename",
|
||||||
title: "Override Filename",
|
title: "Override Filename",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type } from "@tauri-apps/plugin-os";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { HStack } from "@yaakapp-internal/ui";
|
import { HStack } from "@yaakapp-internal/ui";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import type { CSSProperties, ReactNode } from "react";
|
import type { CSSProperties, ReactNode } from "react";
|
||||||
@@ -66,7 +66,7 @@ export function Select<T extends string>({
|
|||||||
<Label htmlFor={id} visuallyHidden={hideLabel} className={labelClassName} help={help}>
|
<Label htmlFor={id} visuallyHidden={hideLabel} className={labelClassName} help={help}>
|
||||||
{label}
|
{label}
|
||||||
</Label>
|
</Label>
|
||||||
{type() === "macos" && !filterable ? (
|
{platform.osType() === "macos" && !filterable ? (
|
||||||
<HStack
|
<HStack
|
||||||
space={2}
|
space={2}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
bodyPath?: string;
|
bodyPath?: string;
|
||||||
@@ -12,7 +12,7 @@ export function AudioViewer({ bodyPath, data, mimeType }: Props) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (bodyPath) {
|
if (bodyPath) {
|
||||||
setSrc(convertFileSrc(bodyPath));
|
setSrc(platform.files.url(bodyPath));
|
||||||
} else if (data) {
|
} else if (data) {
|
||||||
// The type matters here in a way it doesn't for an image: a media element goes by what
|
// The type matters here in a way it doesn't for an image: a media element goes by what
|
||||||
// the blob declares rather than sniffing it, so an Ogg labelled as MP3 won't play
|
// the blob declares rather than sniffing it, so an Ogg labelled as MP3 won't play
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
type Props = { className?: string; mimeType?: string } & (
|
type Props = { className?: string; mimeType?: string } & (
|
||||||
| {
|
| {
|
||||||
@@ -18,7 +18,7 @@ export function ImageViewer({ className, mimeType, ...props }: Props) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (bodyPath != null) {
|
if (bodyPath != null) {
|
||||||
setSrc(convertFileSrc(bodyPath));
|
setSrc(platform.files.url(bodyPath));
|
||||||
} else if (data != null) {
|
} else if (data != null) {
|
||||||
const blob = new Blob([data], { type: mimeType ?? "image/png" });
|
const blob = new Blob([data], { type: mimeType ?? "image/png" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import "react-pdf/dist/Page/TextLayer.css";
|
import "react-pdf/dist/Page/TextLayer.css";
|
||||||
import "react-pdf/dist/Page/AnnotationLayer.css";
|
import "react-pdf/dist/Page/AnnotationLayer.css";
|
||||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
|
||||||
import "./PdfViewer.css";
|
import "./PdfViewer.css";
|
||||||
import type { PDFDocumentProxy } from "pdfjs-dist";
|
import type { PDFDocumentProxy } from "pdfjs-dist";
|
||||||
import { useMemo, useRef, useState } from "react";
|
import { useMemo, useRef, useState } from "react";
|
||||||
import { Document, Page } from "react-pdf";
|
import { Document, Page } from "react-pdf";
|
||||||
import { useContainerSize } from "@yaakapp-internal/ui";
|
import { useContainerSize } from "@yaakapp-internal/ui";
|
||||||
import { fireAndForget } from "../../lib/fireAndForget";
|
import { fireAndForget } from "../../lib/fireAndForget";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
fireAndForget(
|
fireAndForget(
|
||||||
import("react-pdf").then(({ pdfjs }) => {
|
import("react-pdf").then(({ pdfjs }) => {
|
||||||
@@ -37,7 +37,7 @@ export function PdfViewer({ bodyPath, data }: Props) {
|
|||||||
// `Document` renders its "Failed to load PDF file" state for that frame before recovering
|
// `Document` renders its "Failed to load PDF file" state for that frame before recovering
|
||||||
const src = useMemo(() => {
|
const src = useMemo(() => {
|
||||||
if (bodyPath) {
|
if (bodyPath) {
|
||||||
return convertFileSrc(bodyPath);
|
return platform.files.url(bodyPath);
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
// Create a copy to avoid "Buffer is already detached" errors
|
// Create a copy to avoid "Buffer is already detached" errors
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
bodyPath?: string;
|
bodyPath?: string;
|
||||||
@@ -12,7 +12,7 @@ export function VideoViewer({ bodyPath, data, mimeType }: Props) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (bodyPath) {
|
if (bodyPath) {
|
||||||
setSrc(convertFileSrc(bodyPath));
|
setSrc(platform.files.url(bodyPath));
|
||||||
} else if (data) {
|
} else if (data) {
|
||||||
// As in AudioViewer: a media element trusts the declared type instead of sniffing
|
// As in AudioViewer: a media element trusts the declared type instead of sniffing
|
||||||
const blob = new Blob([new Uint8Array(data)], { type: mimeType ?? "video/mp4" });
|
const blob = new Blob([new Uint8Array(data)], { type: mimeType ?? "video/mp4" });
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Listen for settings changes, the re-compute theme
|
// Listen for settings changes, the re-compute theme
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import type { ModelPayload } from "@yaakapp-internal/models";
|
import type { ModelPayload } from "@yaakapp-internal/models";
|
||||||
import { fireAndForget } from "./lib/fireAndForget";
|
import { fireAndForget } from "./lib/fireAndForget";
|
||||||
import { getSettings } from "./lib/settings";
|
import { getSettings } from "./lib/settings";
|
||||||
@@ -8,12 +8,12 @@ function setFontSizeOnDocument(fontSize: number) {
|
|||||||
document.documentElement.style.fontSize = `${fontSize}px`;
|
document.documentElement.style.fontSize = `${fontSize}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
listen<ModelPayload[]>("model_writes", async (event) => {
|
platform.listen<ModelPayload[]>("model_writes", (payloads) => {
|
||||||
for (const payload of event.payload) {
|
for (const payload of payloads) {
|
||||||
if (payload.change.type !== "upsert") continue;
|
if (payload.change.type !== "upsert") continue;
|
||||||
if (payload.model.model !== "settings") continue;
|
if (payload.model.model !== "settings") continue;
|
||||||
setFontSizeOnDocument(payload.model.interfaceFontSize);
|
setFontSizeOnDocument(payload.model.interfaceFontSize);
|
||||||
}
|
}
|
||||||
}).catch(console.error);
|
});
|
||||||
|
|
||||||
fireAndForget(getSettings().then((settings) => setFontSizeOnDocument(settings.interfaceFontSize)));
|
fireAndForget(getSettings().then((settings) => setFontSizeOnDocument(settings.interfaceFontSize)));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Listen for settings changes, the re-compute theme
|
// Listen for settings changes, the re-compute theme
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import type { ModelPayload, Settings } from "@yaakapp-internal/models";
|
import type { ModelPayload, Settings } from "@yaakapp-internal/models";
|
||||||
import { fireAndForget } from "./lib/fireAndForget";
|
import { fireAndForget } from "./lib/fireAndForget";
|
||||||
import { getSettings } from "./lib/settings";
|
import { getSettings } from "./lib/settings";
|
||||||
@@ -12,12 +12,12 @@ function setFonts(settings: Settings) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
listen<ModelPayload[]>("model_writes", async (event) => {
|
platform.listen<ModelPayload[]>("model_writes", (payloads) => {
|
||||||
for (const payload of event.payload) {
|
for (const payload of payloads) {
|
||||||
if (payload.change.type !== "upsert") continue;
|
if (payload.change.type !== "upsert") continue;
|
||||||
if (payload.model.model !== "settings") continue;
|
if (payload.model.model !== "settings") continue;
|
||||||
setFonts(payload.model);
|
setFonts(payload.model);
|
||||||
}
|
}
|
||||||
}).catch(console.error);
|
});
|
||||||
|
|
||||||
fireAndForget(getSettings().then((settings) => setFonts(settings)));
|
fireAndForget(getSettings().then((settings) => setFonts(settings)));
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { event } from "@tauri-apps/api";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { useFastMutation } from "./useFastMutation";
|
import { useFastMutation } from "./useFastMutation";
|
||||||
|
|
||||||
export function useCancelHttpResponse(id: string | null) {
|
export function useCancelHttpResponse(id: string | null) {
|
||||||
return useFastMutation<void>({
|
return useFastMutation<void>({
|
||||||
mutationKey: ["cancel_http_response", id],
|
mutationKey: ["cancel_http_response", id],
|
||||||
mutationFn: () => event.emit(`cancel_http_response_${id}`),
|
mutationFn: () => platform.emit(`cancel_http_response_${id}`),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { InlineCode } from "@yaakapp-internal/ui";
|
|||||||
import { showAlert } from "../lib/alert";
|
import { showAlert } from "../lib/alert";
|
||||||
import { appInfo } from "../lib/appInfo";
|
import { appInfo } from "../lib/appInfo";
|
||||||
import { minPromiseMillis } from "../lib/minPromiseMillis";
|
import { minPromiseMillis } from "../lib/minPromiseMillis";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
export function useCheckForUpdates() {
|
export function useCheckForUpdates() {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["check_for_updates"],
|
mutationKey: ["check_for_updates"],
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
const hasUpdate: boolean = await minPromiseMillis(invokeCmd("cmd_check_for_updates"), 500);
|
const hasUpdate: boolean = await minPromiseMillis(rpc("cmd_check_for_updates"), 500);
|
||||||
if (!hasUpdate) {
|
if (!hasUpdate) {
|
||||||
showAlert({
|
showAlert({
|
||||||
id: "no-updates",
|
id: "no-updates",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { useFastMutation } from "./useFastMutation";
|
import { useFastMutation } from "./useFastMutation";
|
||||||
|
|
||||||
export function useDeleteGrpcConnections(requestId?: string) {
|
export function useDeleteGrpcConnections(requestId?: string) {
|
||||||
@@ -6,7 +6,7 @@ export function useDeleteGrpcConnections(requestId?: string) {
|
|||||||
mutationKey: ["delete_grpc_connections", requestId],
|
mutationKey: ["delete_grpc_connections", requestId],
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
if (requestId === undefined) return;
|
if (requestId === undefined) return;
|
||||||
await invokeCmd("cmd_delete_all_grpc_connections", { requestId });
|
await rpc("cmd_delete_all_grpc_connections", { requestId });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { useFastMutation } from "./useFastMutation";
|
import { useFastMutation } from "./useFastMutation";
|
||||||
|
|
||||||
export function useDeleteHttpResponses(requestId?: string) {
|
export function useDeleteHttpResponses(requestId?: string) {
|
||||||
@@ -6,7 +6,7 @@ export function useDeleteHttpResponses(requestId?: string) {
|
|||||||
mutationKey: ["delete_http_responses", requestId],
|
mutationKey: ["delete_http_responses", requestId],
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
if (requestId === undefined) return;
|
if (requestId === undefined) return;
|
||||||
await invokeCmd("cmd_delete_all_http_responses", { requestId });
|
await rpc("cmd_delete_all_http_responses", { requestId });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { showAlert } from "../lib/alert";
|
|||||||
import { showConfirmDelete } from "../lib/confirm";
|
import { showConfirmDelete } from "../lib/confirm";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
import { pluralizeCount } from "../lib/pluralize";
|
import { pluralizeCount } from "../lib/pluralize";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
||||||
import { useFastMutation } from "./useFastMutation";
|
import { useFastMutation } from "./useFastMutation";
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ export function useDeleteSendHistory() {
|
|||||||
if (!confirmed) return false;
|
if (!confirmed) return false;
|
||||||
|
|
||||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||||
await invokeCmd("cmd_delete_send_history", { workspaceId });
|
await rpc("cmd_delete_send_history", { workspaceId });
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
GetFolderActionsResponse,
|
GetFolderActionsResponse,
|
||||||
} from "@yaakapp-internal/plugins";
|
} from "@yaakapp-internal/plugins";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { usePluginsKey } from "./usePlugins";
|
import { usePluginsKey } from "./usePlugins";
|
||||||
|
|
||||||
export type CallableFolderAction = Pick<FolderAction, "label" | "icon"> & {
|
export type CallableFolderAction = Pick<FolderAction, "label" | "icon"> & {
|
||||||
@@ -30,7 +30,7 @@ export function useFolderActions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getFolderActions() {
|
export async function getFolderActions() {
|
||||||
const responses = await invokeCmd<GetFolderActionsResponse[]>("cmd_folder_actions");
|
const responses = await rpc<GetFolderActionsResponse[]>("cmd_folder_actions");
|
||||||
const actions = responses.flatMap((r) =>
|
const actions = responses.flatMap((r) =>
|
||||||
r.actions.map((a, i) => ({
|
r.actions.map((a, i) => ({
|
||||||
label: a.label,
|
label: a.label,
|
||||||
@@ -41,7 +41,7 @@ export async function getFolderActions() {
|
|||||||
pluginRefId: r.pluginRefId,
|
pluginRefId: r.pluginRefId,
|
||||||
args: { folder },
|
args: { folder },
|
||||||
};
|
};
|
||||||
await invokeCmd("cmd_call_folder_action", { req: payload });
|
await rpc("cmd_call_folder_action", { req: payload });
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import { emit } from "@tauri-apps/api/event";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import type { GrpcConnection, GrpcRequest } from "@yaakapp-internal/models";
|
import type { GrpcConnection, GrpcRequest } from "@yaakapp-internal/models";
|
||||||
import { flushAllModelWrites } from "@yaakapp-internal/models";
|
import { flushAllModelWrites } from "@yaakapp-internal/models";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
import { minPromiseMillis } from "../lib/minPromiseMillis";
|
import { minPromiseMillis } from "../lib/minPromiseMillis";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { activeEnvironmentIdAtom, useActiveEnvironment } from "./useActiveEnvironment";
|
import { activeEnvironmentIdAtom, useActiveEnvironment } from "./useActiveEnvironment";
|
||||||
import { useDebouncedValue } from "@yaakapp-internal/ui";
|
import { useDebouncedValue } from "@yaakapp-internal/ui";
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ export function useGrpc(
|
|||||||
mutationKey: ["grpc_go", conn?.id],
|
mutationKey: ["grpc_go", conn?.id],
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
await flushAllModelWrites(); // The backend reads the request from the DB
|
await flushAllModelWrites(); // The backend reads the request from the DB
|
||||||
return invokeCmd<void>("cmd_grpc_go", {
|
return rpc<void>("cmd_grpc_go", {
|
||||||
requestId,
|
requestId,
|
||||||
environmentId: environment?.id,
|
environmentId: environment?.id,
|
||||||
protoFiles,
|
protoFiles,
|
||||||
@@ -36,17 +36,17 @@ export function useGrpc(
|
|||||||
const send = useMutation({
|
const send = useMutation({
|
||||||
mutationKey: ["grpc_send", conn?.id],
|
mutationKey: ["grpc_send", conn?.id],
|
||||||
mutationFn: ({ message }: { message: string }) =>
|
mutationFn: ({ message }: { message: string }) =>
|
||||||
emit(`grpc_client_msg_${conn?.id ?? "none"}`, { Message: message }),
|
platform.emit(`grpc_client_msg_${conn?.id ?? "none"}`, { Message: message }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const cancel = useMutation({
|
const cancel = useMutation({
|
||||||
mutationKey: ["grpc_cancel", conn?.id ?? "n/a"],
|
mutationKey: ["grpc_cancel", conn?.id ?? "n/a"],
|
||||||
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? "none"}`, "Cancel"),
|
mutationFn: () => platform.emit(`grpc_client_msg_${conn?.id ?? "none"}`, "Cancel"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const commit = useMutation({
|
const commit = useMutation({
|
||||||
mutationKey: ["grpc_commit", conn?.id ?? "n/a"],
|
mutationKey: ["grpc_commit", conn?.id ?? "n/a"],
|
||||||
mutationFn: () => emit(`grpc_client_msg_${conn?.id ?? "none"}`, "Commit"),
|
mutationFn: () => platform.emit(`grpc_client_msg_${conn?.id ?? "none"}`, "Commit"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const debouncedUrl = useDebouncedValue<string>(req?.url ?? "", 1000);
|
const debouncedUrl = useDebouncedValue<string>(req?.url ?? "", 1000);
|
||||||
@@ -61,7 +61,7 @@ export function useGrpc(
|
|||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
const environmentId = jotaiStore.get(activeEnvironmentIdAtom);
|
const environmentId = jotaiStore.get(activeEnvironmentIdAtom);
|
||||||
return minPromiseMillis<ReflectResponseService[]>(
|
return minPromiseMillis<ReflectResponseService[]>(
|
||||||
invokeCmd("cmd_grpc_reflect", { requestId, protoFiles, environmentId }),
|
rpc("cmd_grpc_reflect", { requestId, protoFiles, environmentId }),
|
||||||
300,
|
300,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
GrpcRequestAction,
|
GrpcRequestAction,
|
||||||
} from "@yaakapp-internal/plugins";
|
} from "@yaakapp-internal/plugins";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { getGrpcProtoFiles } from "./useGrpcProtoFiles";
|
import { getGrpcProtoFiles } from "./useGrpcProtoFiles";
|
||||||
import { usePluginsKey } from "./usePlugins";
|
import { usePluginsKey } from "./usePlugins";
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ export function useGrpcRequestActions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getGrpcRequestActions() {
|
export async function getGrpcRequestActions() {
|
||||||
const responses = await invokeCmd<GetGrpcRequestActionsResponse[]>("cmd_grpc_request_actions");
|
const responses = await rpc<GetGrpcRequestActionsResponse[]>("cmd_grpc_request_actions");
|
||||||
|
|
||||||
return responses.flatMap((r) =>
|
return responses.flatMap((r) =>
|
||||||
r.actions.map((a, i) => ({
|
r.actions.map((a, i) => ({
|
||||||
@@ -46,7 +46,7 @@ export async function getGrpcRequestActions() {
|
|||||||
pluginRefId: r.pluginRefId,
|
pluginRefId: r.pluginRefId,
|
||||||
args: { grpcRequest, protoFiles },
|
args: { grpcRequest, protoFiles },
|
||||||
};
|
};
|
||||||
await invokeCmd("cmd_call_grpc_request_action", { req: payload });
|
await rpc("cmd_call_grpc_request_action", { req: payload });
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type } from "@tauri-apps/plugin-os";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { debounce } from "@yaakapp-internal/lib";
|
import { debounce } from "@yaakapp-internal/lib";
|
||||||
import { settingsAtom } from "@yaakapp-internal/models";
|
import { settingsAtom } from "@yaakapp-internal/models";
|
||||||
import { atom, useAtomValue } from "jotai";
|
import { atom, useAtomValue } from "jotai";
|
||||||
@@ -102,7 +102,7 @@ const defaultHotkeysOther: Record<HotkeyAction, string[]> = {
|
|||||||
|
|
||||||
/** Get the default hotkeys for the current platform */
|
/** Get the default hotkeys for the current platform */
|
||||||
export const defaultHotkeys: Record<HotkeyAction, string[]> =
|
export const defaultHotkeys: Record<HotkeyAction, string[]> =
|
||||||
type() === "macos" ? defaultHotkeysMac : defaultHotkeysOther;
|
platform.osType() === "macos" ? defaultHotkeysMac : defaultHotkeysOther;
|
||||||
|
|
||||||
/** Atom that provides the effective hotkeys by merging defaults with user settings */
|
/** Atom that provides the effective hotkeys by merging defaults with user settings */
|
||||||
export const hotkeysAtom = atom((get) => {
|
export const hotkeysAtom = atom((get) => {
|
||||||
@@ -318,7 +318,7 @@ export function getHotkeyScope(action: HotkeyAction): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function formatHotkeyString(trigger: string): string[] {
|
export function formatHotkeyString(trigger: string): string[] {
|
||||||
const os = type();
|
const os = platform.osType();
|
||||||
const parts = trigger.split("+");
|
const parts = trigger.split("+");
|
||||||
const labelParts: string[] = [];
|
const labelParts: string[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { GetHttpAuthenticationSummaryResponse } from "@yaakapp-internal/plu
|
|||||||
import { atom, useAtomValue } from "jotai";
|
import { atom, useAtomValue } from "jotai";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { showErrorToast } from "../lib/toast";
|
import { showErrorToast } from "../lib/toast";
|
||||||
import { usePluginsKey } from "./usePlugins";
|
import { usePluginsKey } from "./usePlugins";
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ export function useSubscribeHttpAuthentication() {
|
|||||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
try {
|
try {
|
||||||
const result = await invokeCmd<GetHttpAuthenticationSummaryResponse[]>(
|
const result = await rpc<GetHttpAuthenticationSummaryResponse[]>(
|
||||||
"cmd_get_http_authentication_summaries",
|
"cmd_get_http_authentication_summaries",
|
||||||
);
|
);
|
||||||
setNumResults(result.length);
|
setNumResults(result.length);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type { GetHttpAuthenticationConfigResponse, JsonPrimitive } from "@yaakap
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { md5 } from "js-md5";
|
import { md5 } from "js-md5";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { activeEnvironmentIdAtom } from "./useActiveEnvironment";
|
import { activeEnvironmentIdAtom } from "./useActiveEnvironment";
|
||||||
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ export function useHttpAuthenticationConfig(
|
|||||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (authName == null || authName === "inherit") return null;
|
if (authName == null || authName === "inherit") return null;
|
||||||
const config = await invokeCmd<GetHttpAuthenticationConfigResponse>(
|
const config = await rpc<GetHttpAuthenticationConfigResponse>(
|
||||||
"cmd_get_http_authentication_config",
|
"cmd_get_http_authentication_config",
|
||||||
{
|
{
|
||||||
authName,
|
authName,
|
||||||
@@ -65,7 +65,7 @@ export function useHttpAuthenticationConfig(
|
|||||||
call: async (
|
call: async (
|
||||||
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||||
) => {
|
) => {
|
||||||
await invokeCmd("cmd_call_http_authentication_action", {
|
await rpc("cmd_call_http_authentication_action", {
|
||||||
pluginRefId: config.pluginRefId,
|
pluginRefId: config.pluginRefId,
|
||||||
actionIndex: i,
|
actionIndex: i,
|
||||||
authName,
|
authName,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
HttpRequestAction,
|
HttpRequestAction,
|
||||||
} from "@yaakapp-internal/plugins";
|
} from "@yaakapp-internal/plugins";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { usePluginsKey } from "./usePlugins";
|
import { usePluginsKey } from "./usePlugins";
|
||||||
|
|
||||||
export type CallableHttpRequestAction = Pick<HttpRequestAction, "label" | "icon"> & {
|
export type CallableHttpRequestAction = Pick<HttpRequestAction, "label" | "icon"> & {
|
||||||
@@ -30,7 +30,7 @@ export function useHttpRequestActions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getHttpRequestActions() {
|
export async function getHttpRequestActions() {
|
||||||
const responses = await invokeCmd<GetHttpRequestActionsResponse[]>("cmd_http_request_actions");
|
const responses = await rpc<GetHttpRequestActionsResponse[]>("cmd_http_request_actions");
|
||||||
const actions = responses.flatMap((r) =>
|
const actions = responses.flatMap((r) =>
|
||||||
r.actions.map((a, i) => ({
|
r.actions.map((a, i) => ({
|
||||||
label: a.label,
|
label: a.label,
|
||||||
@@ -41,7 +41,7 @@ export async function getHttpRequestActions() {
|
|||||||
pluginRefId: r.pluginRefId,
|
pluginRefId: r.pluginRefId,
|
||||||
args: { httpRequest },
|
args: { httpRequest },
|
||||||
};
|
};
|
||||||
await invokeCmd("cmd_call_http_request_action", { req: payload });
|
await rpc("cmd_call_http_request_action", { req: payload });
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import type { HttpResponse } from "@yaakapp-internal/models";
|
import type { HttpResponse } from "@yaakapp-internal/models";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
export function useHttpRequestBody(response: HttpResponse | null) {
|
export function useHttpRequestBody(response: HttpResponse | null) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -18,7 +18,7 @@ export async function getRequestBodyText(response: HttpResponse | null) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await invokeCmd<number[] | null>("cmd_http_request_body", {
|
const data = await rpc<number[] | null>("cmd_http_request_body", {
|
||||||
responseId: response.id,
|
responseId: response.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
|
||||||
import type { HttpResponse, HttpResponseEvent } from "@yaakapp-internal/models";
|
import type { HttpResponse, HttpResponseEvent } from "@yaakapp-internal/models";
|
||||||
import {
|
import {
|
||||||
httpResponseEventsAtom,
|
httpResponseEventsAtom,
|
||||||
@@ -8,6 +7,7 @@ import {
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { fireAndForget } from "../lib/fireAndForget";
|
import { fireAndForget } from "../lib/fireAndForget";
|
||||||
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
export function useHttpResponseEvents(response: HttpResponse | null) {
|
export function useHttpResponseEvents(response: HttpResponse | null) {
|
||||||
const allEvents = useAtomValue(httpResponseEventsAtom);
|
const allEvents = useAtomValue(httpResponseEventsAtom);
|
||||||
@@ -20,7 +20,7 @@ export function useHttpResponseEvents(response: HttpResponse | null) {
|
|||||||
|
|
||||||
// Fetch events from database, filtering out events from other responses and merging atomically
|
// Fetch events from database, filtering out events from other responses and merging atomically
|
||||||
fireAndForget(
|
fireAndForget(
|
||||||
invoke<HttpResponseEvent[]>("cmd_get_http_response_events", { responseId: response.id }).then(
|
rpc<HttpResponseEvent[]>("cmd_get_http_response_events", { responseId: response.id }).then(
|
||||||
(events) =>
|
(events) =>
|
||||||
mergeModelsInStore("http_response_event", events, (e) => e.responseId === response.id),
|
mergeModelsInStore("http_response_event", events, (e) => e.responseId === response.id),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
|
||||||
import type { GraphQlIntrospection, HttpRequest } from "@yaakapp-internal/models";
|
import type { GraphQlIntrospection, HttpRequest } from "@yaakapp-internal/models";
|
||||||
import type { GraphQLSchema, IntrospectionQuery } from "graphql";
|
import type { GraphQLSchema, IntrospectionQuery } from "graphql";
|
||||||
import { buildClientSchema, getIntrospectionQuery } from "graphql";
|
import { buildClientSchema, getIntrospectionQuery } from "graphql";
|
||||||
@@ -9,6 +9,7 @@ import { getResponseBodyText } from "../lib/responseBody";
|
|||||||
import { sendEphemeralRequest } from "../lib/sendEphemeralRequest";
|
import { sendEphemeralRequest } from "../lib/sendEphemeralRequest";
|
||||||
import { useActiveEnvironment } from "./useActiveEnvironment";
|
import { useActiveEnvironment } from "./useActiveEnvironment";
|
||||||
import { useDebouncedValue } from "@yaakapp-internal/ui";
|
import { useDebouncedValue } from "@yaakapp-internal/ui";
|
||||||
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
const introspectionRequestBody = JSON.stringify({
|
const introspectionRequestBody = JSON.stringify({
|
||||||
query: getIntrospectionQuery(),
|
query: getIntrospectionQuery(),
|
||||||
@@ -32,7 +33,7 @@ export function useIntrospectGraphQL(
|
|||||||
|
|
||||||
const upsertIntrospection = useCallback(
|
const upsertIntrospection = useCallback(
|
||||||
async (content: string | null) => {
|
async (content: string | null) => {
|
||||||
const v = await invoke<GraphQlIntrospection>("models_upsert_graphql_introspection", {
|
const v = await rpc<GraphQlIntrospection>("models_upsert_graphql_introspection", {
|
||||||
requestId: baseRequest.id,
|
requestId: baseRequest.id,
|
||||||
workspaceId: baseRequest.workspaceId,
|
workspaceId: baseRequest.workspaceId,
|
||||||
content: content ?? "",
|
content: content ?? "",
|
||||||
@@ -119,7 +120,7 @@ function useIntrospectionResult(request: HttpRequest) {
|
|||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["introspection", request.id],
|
queryKey: ["introspection", request.id],
|
||||||
queryFn: async () =>
|
queryFn: async () =>
|
||||||
invoke<GraphQlIntrospection | null>("models_get_graphql_introspection", {
|
rpc<GraphQlIntrospection | null>("models_get_graphql_introspection", {
|
||||||
requestId: request.id,
|
requestId: request.id,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import type { EventCallback, EventName } from "@tauri-apps/api/event";
|
|
||||||
import { listen } from "@tauri-apps/api/event";
|
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
export function useListenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) {
|
|
||||||
const handlerRef = useRef(fn);
|
|
||||||
useEffect(() => {
|
|
||||||
handlerRef.current = fn;
|
|
||||||
}, [fn]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return listenToTauriEvent<T>(event, (p) => handlerRef.current(p));
|
|
||||||
}, [event]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listenToTauriEvent<T>(event: EventName, fn: EventCallback<T>) {
|
|
||||||
const unsubPromise = listen<T>(
|
|
||||||
event,
|
|
||||||
fn,
|
|
||||||
// Listen to `emit_all()` events or events specific to the current window
|
|
||||||
{ target: { label: getCurrentWebviewWindow().label, kind: "Window" } },
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
unsubPromise.then((unsub) => unsub()).catch(console.error);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
|
||||||
import type { GrpcConnection, GrpcEvent } from "@yaakapp-internal/models";
|
import type { GrpcConnection, GrpcEvent } from "@yaakapp-internal/models";
|
||||||
import {
|
import {
|
||||||
grpcConnectionsAtom,
|
grpcConnectionsAtom,
|
||||||
@@ -11,6 +10,7 @@ import { useEffect, useMemo } from "react";
|
|||||||
import { fireAndForget } from "../lib/fireAndForget";
|
import { fireAndForget } from "../lib/fireAndForget";
|
||||||
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
|
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
|
||||||
import { activeRequestIdAtom } from "./useActiveRequestId";
|
import { activeRequestIdAtom } from "./useActiveRequestId";
|
||||||
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
const pinnedGrpcConnectionIdsAtom = atomWithKVStorage<Record<string, string | null>>(
|
const pinnedGrpcConnectionIdsAtom = atomWithKVStorage<Record<string, string | null>>(
|
||||||
"pinned-grpc-connection-ids",
|
"pinned-grpc-connection-ids",
|
||||||
@@ -71,7 +71,7 @@ export function useGrpcEvents(connectionId: string | null) {
|
|||||||
|
|
||||||
// Fetch events from database, filtering out events from other connections and merging atomically
|
// Fetch events from database, filtering out events from other connections and merging atomically
|
||||||
fireAndForget(
|
fireAndForget(
|
||||||
invoke<GrpcEvent[]>("models_grpc_events", { connectionId }).then((events) =>
|
rpc<GrpcEvent[]>("models_grpc_events", { connectionId }).then((events) =>
|
||||||
mergeModelsInStore("grpc_event", events, (e) => e.connectionId === connectionId),
|
mergeModelsInStore("grpc_event", events, (e) => e.connectionId === connectionId),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
|
||||||
import type { WebsocketConnection, WebsocketEvent } from "@yaakapp-internal/models";
|
import type { WebsocketConnection, WebsocketEvent } from "@yaakapp-internal/models";
|
||||||
import {
|
import {
|
||||||
mergeModelsInStore,
|
mergeModelsInStore,
|
||||||
@@ -12,6 +11,7 @@ import { fireAndForget } from "../lib/fireAndForget";
|
|||||||
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
|
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
import { activeRequestIdAtom } from "./useActiveRequestId";
|
import { activeRequestIdAtom } from "./useActiveRequestId";
|
||||||
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
const pinnedWebsocketConnectionIdAtom = atomWithKVStorage<Record<string, string | null>>(
|
const pinnedWebsocketConnectionIdAtom = atomWithKVStorage<Record<string, string | null>>(
|
||||||
"pinned-websocket-connection-ids",
|
"pinned-websocket-connection-ids",
|
||||||
@@ -58,7 +58,7 @@ export function useWebsocketEvents(connectionId: string | null) {
|
|||||||
|
|
||||||
// Fetch events from database, filtering out events from other connections and merging atomically
|
// Fetch events from database, filtering out events from other connections and merging atomically
|
||||||
fireAndForget(
|
fireAndForget(
|
||||||
invoke<WebsocketEvent[]>("models_websocket_events", { connectionId }).then((events) =>
|
rpc<WebsocketEvent[]>("models_websocket_events", { connectionId }).then((events) =>
|
||||||
mergeModelsInStore("websocket_event", events, (e) => e.connectionId === connectionId),
|
mergeModelsInStore("websocket_event", events, (e) => e.connectionId === connectionId),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
/** Subscribe to a backend event for the lifetime of the component. */
|
||||||
|
export function usePlatformEvent<T>(event: string, fn: (payload: T) => void) {
|
||||||
|
const handlerRef = useRef(fn);
|
||||||
|
useEffect(() => {
|
||||||
|
handlerRef.current = fn;
|
||||||
|
}, [fn]);
|
||||||
|
|
||||||
|
useEffect(() => platform.listen<T>(event, (payload) => handlerRef.current(payload)), [event]);
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import { pluginsAtom } from "@yaakapp-internal/models";
|
|||||||
import type { PluginMetadata } from "@yaakapp-internal/plugins";
|
import type { PluginMetadata } from "@yaakapp-internal/plugins";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { queryClient } from "../lib/queryClient";
|
import { queryClient } from "../lib/queryClient";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
function pluginInfoKey(id: string | null, plugin: Plugin | null) {
|
function pluginInfoKey(id: string | null, plugin: Plugin | null) {
|
||||||
return ["plugin_info", id ?? "n/a", plugin?.updatedAt ?? "n/a"];
|
return ["plugin_info", id ?? "n/a", plugin?.updatedAt ?? "n/a"];
|
||||||
@@ -19,7 +19,7 @@ export function usePluginInfo(id: string | null) {
|
|||||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
if (id == null) return null;
|
if (id == null) return null;
|
||||||
return invokeCmd<PluginMetadata>("cmd_plugin_info", { id });
|
return rpc<PluginMetadata>("cmd_plugin_info", { id });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { changeModelStoreWorkspace, pluginsAtom } from "@yaakapp-internal/models
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
import { minPromiseMillis } from "../lib/minPromiseMillis";
|
import { minPromiseMillis } from "../lib/minPromiseMillis";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
||||||
import { useDebouncedValue } from "@yaakapp-internal/ui";
|
import { useDebouncedValue } from "@yaakapp-internal/ui";
|
||||||
import { invalidateAllPluginInfo } from "./usePluginInfo";
|
import { invalidateAllPluginInfo } from "./usePluginInfo";
|
||||||
@@ -26,7 +26,7 @@ export function useRefreshPlugins() {
|
|||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
await minPromiseMillis(
|
await minPromiseMillis(
|
||||||
(async () => {
|
(async () => {
|
||||||
await invokeCmd("cmd_reload_plugins");
|
await rpc("cmd_reload_plugins");
|
||||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||||
await changeModelStoreWorkspace(workspaceId); // Force refresh models
|
await changeModelStoreWorkspace(workspaceId); // Force refresh models
|
||||||
invalidateAllPluginInfo();
|
invalidateAllPluginInfo();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query";
|
|||||||
import type { RenderPurpose } from "@yaakapp-internal/plugins";
|
import type { RenderPurpose } from "@yaakapp-internal/plugins";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { minPromiseMillis } from "../lib/minPromiseMillis";
|
import { minPromiseMillis } from "../lib/minPromiseMillis";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { useActiveEnvironment } from "./useActiveEnvironment";
|
import { useActiveEnvironment } from "./useActiveEnvironment";
|
||||||
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export async function renderTemplate({
|
|||||||
purpose: RenderPurpose;
|
purpose: RenderPurpose;
|
||||||
ignoreError?: boolean;
|
ignoreError?: boolean;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
return invokeCmd("cmd_render_template", {
|
return rpc("cmd_render_template", {
|
||||||
template,
|
template,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environmentId,
|
environmentId,
|
||||||
@@ -67,5 +67,5 @@ export async function decryptTemplate({
|
|||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
environmentId: string | null;
|
environmentId: string | null;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
return invokeCmd("cmd_decrypt_template", { template, workspaceId, environmentId });
|
return rpc("cmd_decrypt_template", { template, workspaceId, environmentId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import type { ModelPayload } from "@yaakapp-internal/models";
|
import type { ModelPayload } from "@yaakapp-internal/models";
|
||||||
import { atom, useAtomValue } from "jotai";
|
import { atom, useAtomValue } from "jotai";
|
||||||
import { generateId } from "../lib/generateId";
|
import { generateId } from "../lib/generateId";
|
||||||
@@ -6,26 +6,24 @@ import { jotaiStore } from "../lib/jotai";
|
|||||||
|
|
||||||
const requestUpdateKeyAtom = atom<Record<string, string>>({});
|
const requestUpdateKeyAtom = atom<Record<string, string>>({});
|
||||||
|
|
||||||
getCurrentWebviewWindow()
|
platform.listen<ModelPayload[]>("model_writes", (payloads) => {
|
||||||
.listen<ModelPayload[]>("model_writes", ({ payload: payloads }) => {
|
const changedIds: string[] = [];
|
||||||
const changedIds: string[] = [];
|
for (const payload of payloads) {
|
||||||
for (const payload of payloads) {
|
if (payload.change.type !== "upsert") continue;
|
||||||
if (payload.change.type !== "upsert") continue;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(payload.model.model === "http_request" ||
|
(payload.model.model === "http_request" ||
|
||||||
payload.model.model === "grpc_request" ||
|
payload.model.model === "grpc_request" ||
|
||||||
payload.model.model === "websocket_request") &&
|
payload.model.model === "websocket_request") &&
|
||||||
((payload.updateSource.type === "window" &&
|
((payload.updateSource.type === "window" &&
|
||||||
payload.updateSource.label !== getCurrentWebviewWindow().label) ||
|
payload.updateSource.label !== platform.window.label) ||
|
||||||
payload.updateSource.type !== "window")
|
payload.updateSource.type !== "window")
|
||||||
) {
|
) {
|
||||||
changedIds.push(payload.model.id);
|
changedIds.push(payload.model.id);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (changedIds.length > 0) wasUpdatedExternally(changedIds);
|
}
|
||||||
})
|
if (changedIds.length > 0) wasUpdatedExternally(changedIds);
|
||||||
.catch(console.error);
|
});
|
||||||
|
|
||||||
export function wasUpdatedExternally(changedRequestIds: string | string[]) {
|
export function wasUpdatedExternally(changedRequestIds: string | string[]) {
|
||||||
const ids = Array.isArray(changedRequestIds) ? changedRequestIds : [changedRequestIds];
|
const ids = Array.isArray(changedRequestIds) ? changedRequestIds : [changedRequestIds];
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { save } from "@tauri-apps/plugin-dialog";
|
|
||||||
import type { HttpResponse } from "@yaakapp-internal/models";
|
import type { HttpResponse } from "@yaakapp-internal/models";
|
||||||
import { getModel } from "@yaakapp-internal/models";
|
import { getModel } from "@yaakapp-internal/models";
|
||||||
import mime from "mime";
|
import mime from "mime";
|
||||||
import slugify from "slugify";
|
import slugify from "slugify";
|
||||||
import { InlineCode } from "@yaakapp-internal/ui";
|
import { InlineCode } from "@yaakapp-internal/ui";
|
||||||
import { getContentTypeFromHeaders } from "../lib/model_util";
|
import { getContentTypeFromHeaders } from "../lib/model_util";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { showToast } from "../lib/toast";
|
import { showToast } from "../lib/toast";
|
||||||
import { useFastMutation } from "./useFastMutation";
|
import { useFastMutation } from "./useFastMutation";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export function useSaveResponse(response: HttpResponse | null) {
|
export function useSaveResponse(response: HttpResponse | null) {
|
||||||
return useFastMutation({
|
return useFastMutation({
|
||||||
@@ -21,11 +21,11 @@ export function useSaveResponse(response: HttpResponse | null) {
|
|||||||
const contentType = getContentTypeFromHeaders(response.headers) ?? "unknown";
|
const contentType = getContentTypeFromHeaders(response.headers) ?? "unknown";
|
||||||
const ext = mime.getExtension(contentType);
|
const ext = mime.getExtension(contentType);
|
||||||
const slug = slugify(request.name || "response", { lower: true });
|
const slug = slugify(request.name || "response", { lower: true });
|
||||||
const filepath = await save({
|
const filepath = await platform.dialog.save({
|
||||||
defaultPath: ext ? `${slug}.${ext}` : slug,
|
defaultPath: ext ? `${slug}.${ext}` : slug,
|
||||||
title: "Save Response",
|
title: "Save Response",
|
||||||
});
|
});
|
||||||
await invokeCmd("cmd_save_response", { responseId: response.id, filepath });
|
await rpc("cmd_save_response", { responseId: response.id, filepath });
|
||||||
showToast({
|
showToast({
|
||||||
message: (
|
message: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { HttpResponse } from "@yaakapp-internal/models";
|
import type { HttpResponse } from "@yaakapp-internal/models";
|
||||||
import { flushAllModelWrites } from "@yaakapp-internal/models";
|
import { flushAllModelWrites } from "@yaakapp-internal/models";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { getActiveCookieJar } from "./useActiveCookieJar";
|
import { getActiveCookieJar } from "./useActiveCookieJar";
|
||||||
import { getActiveEnvironment } from "./useActiveEnvironment";
|
import { getActiveEnvironment } from "./useActiveEnvironment";
|
||||||
import { createFastMutation, useFastMutation } from "./useFastMutation";
|
import { createFastMutation, useFastMutation } from "./useFastMutation";
|
||||||
@@ -12,7 +12,7 @@ async function sendAnyHttpRequestById(id: string | null): Promise<HttpResponse |
|
|||||||
|
|
||||||
await flushAllModelWrites();
|
await flushAllModelWrites();
|
||||||
|
|
||||||
return invokeCmd("cmd_send_http_request", {
|
return rpc("cmd_send_http_request", {
|
||||||
requestId: id,
|
requestId: id,
|
||||||
environmentId: getActiveEnvironment()?.id,
|
environmentId: getActiveEnvironment()?.id,
|
||||||
cookieJarId: getActiveCookieJar()?.id,
|
cookieJarId: getActiveCookieJar()?.id,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { type } from "@tauri-apps/plugin-os";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { useIsFullscreen } from "@yaakapp-internal/ui";
|
import { useIsFullscreen } from "@yaakapp-internal/ui";
|
||||||
|
|
||||||
export function useStoplightsVisible() {
|
export function useStoplightsVisible() {
|
||||||
const fullscreen = useIsFullscreen();
|
const fullscreen = useIsFullscreen();
|
||||||
const stoplightsVisible = type() === "macos" && !fullscreen;
|
const stoplightsVisible = platform.osType() === "macos" && !fullscreen;
|
||||||
return stoplightsVisible;
|
return stoplightsVisible;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { settingsAtom } from "@yaakapp-internal/models";
|
import { settingsAtom } from "@yaakapp-internal/models";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
@@ -11,7 +11,7 @@ export function useSyncFontSizeSetting() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { interfaceScale, editorFontSize } = settings;
|
const { interfaceScale, editorFontSize } = settings;
|
||||||
getCurrentWebviewWindow().setZoom(interfaceScale).catch(console.error);
|
platform.window.setZoom(interfaceScale).catch(console.error);
|
||||||
document.documentElement.style.setProperty("--editor-font-size", `${editorFontSize}px`);
|
document.documentElement.style.setProperty("--editor-font-size", `${editorFontSize}px`);
|
||||||
}, [settings]);
|
}, [settings]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useHotKey } from "./useHotKey";
|
import { useHotKey } from "./useHotKey";
|
||||||
import { useListenToTauriEvent } from "./useListenToTauriEvent";
|
import { usePlatformEvent } from "./usePlatformEvent";
|
||||||
import { useZoom } from "./useZoom";
|
import { useZoom } from "./useZoom";
|
||||||
|
|
||||||
export function useSyncZoomSetting() {
|
export function useSyncZoomSetting() {
|
||||||
@@ -8,9 +8,9 @@ export function useSyncZoomSetting() {
|
|||||||
// shortcuts for Windows/Linux
|
// shortcuts for Windows/Linux
|
||||||
const zoom = useZoom();
|
const zoom = useZoom();
|
||||||
useHotKey("app.zoom_in", zoom.zoomIn);
|
useHotKey("app.zoom_in", zoom.zoomIn);
|
||||||
useListenToTauriEvent("zoom_in", zoom.zoomIn);
|
usePlatformEvent("zoom_in", zoom.zoomIn);
|
||||||
useHotKey("app.zoom_out", zoom.zoomOut);
|
useHotKey("app.zoom_out", zoom.zoomOut);
|
||||||
useListenToTauriEvent("zoom_out", zoom.zoomOut);
|
usePlatformEvent("zoom_out", zoom.zoomOut);
|
||||||
useHotKey("app.zoom_reset", zoom.zoomReset);
|
useHotKey("app.zoom_reset", zoom.zoomReset);
|
||||||
useListenToTauriEvent("zoom_reset", zoom.zoomReset);
|
usePlatformEvent("zoom_reset", zoom.zoomReset);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import type { GetTemplateFunctionConfigResponse, JsonPrimitive } from "@yaakapp-internal/plugins";
|
import type { GetTemplateFunctionConfigResponse, JsonPrimitive } from "@yaakapp-internal/plugins";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { md5 } from "js-md5";
|
import { md5 } from "js-md5";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { activeEnvironmentIdAtom } from "./useActiveEnvironment";
|
import { activeEnvironmentIdAtom } from "./useActiveEnvironment";
|
||||||
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
import { activeWorkspaceIdAtom } from "./useActiveWorkspace";
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ export async function getTemplateFunctionConfig(
|
|||||||
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||||
environmentId: string | undefined,
|
environmentId: string | undefined,
|
||||||
) {
|
) {
|
||||||
const config = await invokeCmd<GetTemplateFunctionConfigResponse>(
|
const config = await rpc<GetTemplateFunctionConfigResponse>(
|
||||||
"cmd_template_function_config",
|
"cmd_template_function_config",
|
||||||
{
|
{
|
||||||
functionName,
|
functionName,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
import { atom, useAtomValue, useSetAtom } from "jotai";
|
import { atom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import type { TwigCompletionOption } from "../components/core/Editor/twig/completion";
|
import type { TwigCompletionOption } from "../components/core/Editor/twig/completion";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { usePluginsKey } from "./usePlugins";
|
import { usePluginsKey } from "./usePlugins";
|
||||||
|
|
||||||
const templateFunctionsAtom = atom<TemplateFunction[]>([]);
|
const templateFunctionsAtom = atom<TemplateFunction[]>([]);
|
||||||
@@ -49,7 +49,7 @@ export function useSubscribeTemplateFunctions() {
|
|||||||
refetchInterval: numFns > 0 ? Number.POSITIVE_INFINITY : 1000,
|
refetchInterval: numFns > 0 ? Number.POSITIVE_INFINITY : 1000,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const result = await invokeCmd<GetTemplateFunctionSummaryResponse[]>(
|
const result = await rpc<GetTemplateFunctionSummaryResponse[]>(
|
||||||
"cmd_template_function_summaries",
|
"cmd_template_function_summaries",
|
||||||
);
|
);
|
||||||
setNumFns(result.length);
|
setNumFns(result.length);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import type { Tokens } from "@yaakapp-internal/templates";
|
import type { Tokens } from "@yaakapp-internal/templates";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
export function useTemplateTokensToString(tokens: Tokens) {
|
export function useTemplateTokensToString(tokens: Tokens) {
|
||||||
return useQuery<string>({
|
return useQuery<string>({
|
||||||
@@ -11,5 +11,5 @@ export function useTemplateTokensToString(tokens: Tokens) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function templateTokensToString(tokens: Tokens): Promise<string> {
|
export async function templateTokensToString(tokens: Tokens): Promise<string> {
|
||||||
return invokeCmd("cmd_template_tokens_to_string", { tokens });
|
return rpc("cmd_template_tokens_to_string", { tokens });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
WebsocketRequestAction,
|
WebsocketRequestAction,
|
||||||
} from "@yaakapp-internal/plugins";
|
} from "@yaakapp-internal/plugins";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { usePluginsKey } from "./usePlugins";
|
import { usePluginsKey } from "./usePlugins";
|
||||||
|
|
||||||
export type CallableWebSocketRequestAction = Pick<WebsocketRequestAction, "label" | "icon"> & {
|
export type CallableWebSocketRequestAction = Pick<WebsocketRequestAction, "label" | "icon"> & {
|
||||||
@@ -30,7 +30,7 @@ export function useWebsocketRequestActions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getWebsocketRequestActions() {
|
export async function getWebsocketRequestActions() {
|
||||||
const responses = await invokeCmd<GetWebsocketRequestActionsResponse[]>(
|
const responses = await rpc<GetWebsocketRequestActionsResponse[]>(
|
||||||
"cmd_websocket_request_actions",
|
"cmd_websocket_request_actions",
|
||||||
);
|
);
|
||||||
const actions = responses.flatMap((r) =>
|
const actions = responses.flatMap((r) =>
|
||||||
@@ -43,7 +43,7 @@ export async function getWebsocketRequestActions() {
|
|||||||
pluginRefId: r.pluginRefId,
|
pluginRefId: r.pluginRefId,
|
||||||
args: { websocketRequest },
|
args: { websocketRequest },
|
||||||
};
|
};
|
||||||
await invokeCmd("cmd_call_websocket_request_action", { req: payload });
|
await rpc("cmd_call_websocket_request_action", { req: payload });
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { fireAndForget } from "../lib/fireAndForget";
|
|
||||||
|
|
||||||
export function useWindowFocus() {
|
export function useWindowFocus() {
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisten = getCurrentWebviewWindow().onFocusChanged((e) => {
|
return platform.window.onFocusChanged(setVisible);
|
||||||
setVisible(e.payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
fireAndForget(unlisten.then((fn) => fn()));
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return visible;
|
return visible;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
WorkspaceAction,
|
WorkspaceAction,
|
||||||
} from "@yaakapp-internal/plugins";
|
} from "@yaakapp-internal/plugins";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { invokeCmd } from "../lib/tauri";
|
import { rpc } from "../lib/rpc";
|
||||||
import { usePluginsKey } from "./usePlugins";
|
import { usePluginsKey } from "./usePlugins";
|
||||||
|
|
||||||
export type CallableWorkspaceAction = Pick<WorkspaceAction, "label" | "icon"> & {
|
export type CallableWorkspaceAction = Pick<WorkspaceAction, "label" | "icon"> & {
|
||||||
@@ -30,7 +30,7 @@ export function useWorkspaceActions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkspaceActions() {
|
export async function getWorkspaceActions() {
|
||||||
const responses = await invokeCmd<GetWorkspaceActionsResponse[]>("cmd_workspace_actions");
|
const responses = await rpc<GetWorkspaceActionsResponse[]>("cmd_workspace_actions");
|
||||||
const actions = responses.flatMap((r) =>
|
const actions = responses.flatMap((r) =>
|
||||||
r.actions.map((a, i) => ({
|
r.actions.map((a, i) => ({
|
||||||
label: a.label,
|
label: a.label,
|
||||||
@@ -41,7 +41,7 @@ export async function getWorkspaceActions() {
|
|||||||
pluginRefId: r.pluginRefId,
|
pluginRefId: r.pluginRefId,
|
||||||
args: { workspace },
|
args: { workspace },
|
||||||
};
|
};
|
||||||
await invokeCmd("cmd_call_workspace_action", { req: payload });
|
await rpc("cmd_call_workspace_action", { req: payload });
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { AnyModel, ModelPayload } from "@yaakapp-internal/models";
|
|||||||
import { watchWorkspaceFiles } from "@yaakapp-internal/sync";
|
import { watchWorkspaceFiles } from "@yaakapp-internal/sync";
|
||||||
import { syncWorkspace } from "../commands/commands";
|
import { syncWorkspace } from "../commands/commands";
|
||||||
import { activeWorkspaceIdAtom, activeWorkspaceMetaAtom } from "../hooks/useActiveWorkspace";
|
import { activeWorkspaceIdAtom, activeWorkspaceMetaAtom } from "../hooks/useActiveWorkspace";
|
||||||
import { listenToTauriEvent } from "../hooks/useListenToTauriEvent";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
|
|
||||||
export function initSync() {
|
export function initSync() {
|
||||||
@@ -33,8 +33,8 @@ const syncAfterModelWrite = eagerDebounceAsync(sync, 1000);
|
|||||||
* simply add long-lived subscribers for the lifetime of the app.
|
* simply add long-lived subscribers for the lifetime of the app.
|
||||||
*/
|
*/
|
||||||
function initModelListeners() {
|
function initModelListeners() {
|
||||||
listenToTauriEvent<ModelPayload[]>("model_writes", (p) => {
|
platform.listen<ModelPayload[]>("model_writes", (payloads) => {
|
||||||
if (p.payload.some((payload) => isModelRelevant(payload.model))) syncAfterModelWrite();
|
if (payloads.some((payload) => isModelRelevant(payload.model))) syncAfterModelWrite();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getIdentifier } from "@tauri-apps/api/app";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
|
|
||||||
export interface AppInfo {
|
export interface AppInfo {
|
||||||
isDev: boolean;
|
isDev: boolean;
|
||||||
@@ -16,8 +16,8 @@ export interface AppInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const appInfo = {
|
export const appInfo = {
|
||||||
...(await invokeCmd("cmd_metadata")),
|
...(await rpc("cmd_metadata")),
|
||||||
identifier: await getIdentifier(),
|
identifier: await platform.appIdentifier(),
|
||||||
} as AppInfo;
|
} as AppInfo;
|
||||||
|
|
||||||
console.log("App info", appInfo);
|
console.log("App info", appInfo);
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { clear, writeText } from "@tauri-apps/plugin-clipboard-manager";
|
|
||||||
import { showToast } from "./toast";
|
import { showToast } from "./toast";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export function copyToClipboard(
|
export function copyToClipboard(
|
||||||
text: string | null,
|
text: string | null,
|
||||||
{ disableToast }: { disableToast?: boolean } = {},
|
{ disableToast }: { disableToast?: boolean } = {},
|
||||||
) {
|
) {
|
||||||
if (text == null) {
|
if (text == null) {
|
||||||
clear().catch(console.error);
|
platform.clipboard.clear().catch(console.error);
|
||||||
} else {
|
} else {
|
||||||
writeText(text).catch(console.error);
|
platform.clipboard.writeText(text).catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text !== "" && !disableToast) {
|
if (text !== "" && !disableToast) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { HttpRequestHeader } from "@yaakapp-internal/models";
|
import type { HttpRequestHeader } from "@yaakapp-internal/models";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global default headers fetched from the backend.
|
* Global default headers fetched from the backend.
|
||||||
* These are static and fetched once on module load.
|
* These are static and fetched once on module load.
|
||||||
*/
|
*/
|
||||||
export const defaultHeaders: HttpRequestHeader[] = await invokeCmd("cmd_default_headers");
|
export const defaultHeaders: HttpRequestHeader[] = await rpc("cmd_default_headers");
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { parseTemplate } from "@yaakapp-internal/templates";
|
|||||||
import { activeEnvironmentIdAtom } from "../hooks/useActiveEnvironment";
|
import { activeEnvironmentIdAtom } from "../hooks/useActiveEnvironment";
|
||||||
import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace";
|
import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace";
|
||||||
import { jotaiStore } from "./jotai";
|
import { jotaiStore } from "./jotai";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
|
|
||||||
export function analyzeTemplate(template: string): "global_secured" | "local_secured" | "insecure" {
|
export function analyzeTemplate(template: string): "global_secured" | "local_secured" | "insecure" {
|
||||||
let secureTags = 0;
|
let secureTags = 0;
|
||||||
@@ -39,7 +39,7 @@ export async function convertTemplateToInsecure(template: string) {
|
|||||||
|
|
||||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom) ?? "n/a";
|
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom) ?? "n/a";
|
||||||
const environmentId = jotaiStore.get(activeEnvironmentIdAtom) ?? null;
|
const environmentId = jotaiStore.get(activeEnvironmentIdAtom) ?? null;
|
||||||
return invokeCmd<string>("cmd_decrypt_template", { template, workspaceId, environmentId });
|
return rpc<string>("cmd_decrypt_template", { template, workspaceId, environmentId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function convertTemplateToSecure(template: string): Promise<string> {
|
export async function convertTemplateToSecure(template: string): Promise<string> {
|
||||||
@@ -53,5 +53,5 @@ export async function convertTemplateToSecure(template: string): Promise<string>
|
|||||||
|
|
||||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom) ?? "n/a";
|
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom) ?? "n/a";
|
||||||
const environmentId = jotaiStore.get(activeEnvironmentIdAtom) ?? null;
|
const environmentId = jotaiStore.get(activeEnvironmentIdAtom) ?? null;
|
||||||
return invokeCmd<string>("cmd_secure_template", { template, workspaceId, environmentId });
|
return rpc<string>("cmd_secure_template", { template, workspaceId, environmentId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import vkBeautify from "vkbeautify";
|
import vkBeautify from "vkbeautify";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
|
|
||||||
export async function tryFormatJson(text: string): Promise<string> {
|
export async function tryFormatJson(text: string): Promise<string> {
|
||||||
if (text === "") return text;
|
if (text === "") return text;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await invokeCmd<string>("cmd_format_json", { text });
|
const result = await rpc<string>("cmd_format_json", { text });
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("Failed to format JSON", err);
|
console.warn("Failed to format JSON", err);
|
||||||
@@ -24,7 +24,7 @@ export async function tryFormatGraphql(text: string): Promise<string> {
|
|||||||
if (text === "") return text;
|
if (text === "") return text;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await invokeCmd<string>("cmd_format_graphql", { text });
|
return await rpc<string>("cmd_format_graphql", { text });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("Failed to format GraphQL", err);
|
console.warn("Failed to format GraphQL", err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { showDialog } from "./dialog";
|
|||||||
import { jotaiStore } from "./jotai";
|
import { jotaiStore } from "./jotai";
|
||||||
import { pluralizeCount } from "./pluralize";
|
import { pluralizeCount } from "./pluralize";
|
||||||
import { router } from "./router";
|
import { router } from "./router";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
|
|
||||||
export const importData = createFastMutation({
|
export const importData = createFastMutation({
|
||||||
mutationKey: ["import_data"],
|
mutationKey: ["import_data"],
|
||||||
@@ -50,7 +50,7 @@ export const importData = createFastMutation({
|
|||||||
|
|
||||||
async function performImport(filePath: string): Promise<boolean> {
|
async function performImport(filePath: string): Promise<boolean> {
|
||||||
const activeWorkspace = jotaiStore.get(activeWorkspaceAtom);
|
const activeWorkspace = jotaiStore.get(activeWorkspaceAtom);
|
||||||
const imported = await invokeCmd<BatchUpsertResult>("cmd_import_data", {
|
const imported = await rpc<BatchUpsertResult>("cmd_import_data", {
|
||||||
filePath,
|
filePath,
|
||||||
workspaceId: activeWorkspace?.id,
|
workspaceId: activeWorkspace?.id,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { emit } from "@tauri-apps/api/event";
|
|
||||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
|
||||||
import { debounce } from "@yaakapp-internal/lib";
|
import { debounce } from "@yaakapp-internal/lib";
|
||||||
import type {
|
import type {
|
||||||
FormInput,
|
FormInput,
|
||||||
@@ -20,23 +18,23 @@ import { Button } from "../components/core/Button";
|
|||||||
import { ButtonInfiniteLoading } from "../components/core/ButtonInfiniteLoading";
|
import { ButtonInfiniteLoading } from "../components/core/ButtonInfiniteLoading";
|
||||||
|
|
||||||
// Listen for toasts
|
// Listen for toasts
|
||||||
import { listenToTauriEvent } from "../hooks/useListenToTauriEvent";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { updateAvailableAtom } from "./atoms";
|
import { updateAvailableAtom } from "./atoms";
|
||||||
import { stringToColor } from "./color";
|
import { stringToColor } from "./color";
|
||||||
import { generateId } from "./generateId";
|
import { generateId } from "./generateId";
|
||||||
import { jotaiStore } from "./jotai";
|
import { jotaiStore } from "./jotai";
|
||||||
import { showPrompt } from "./prompt";
|
import { showPrompt } from "./prompt";
|
||||||
import { showPromptForm } from "./prompt-form";
|
import { showPromptForm } from "./prompt-form";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
import { showToast } from "./toast";
|
import { showToast } from "./toast";
|
||||||
|
|
||||||
export function initGlobalListeners() {
|
export function initGlobalListeners() {
|
||||||
listenToTauriEvent<ShowToastRequest>("show_toast", (event) => {
|
platform.listen<ShowToastRequest>("show_toast", (payload) => {
|
||||||
showToast({ ...event.payload });
|
showToast({ ...payload });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show errors for any plugins that failed to load during startup
|
// Show errors for any plugins that failed to load during startup
|
||||||
void invokeCmd<[string, string][]>("cmd_plugin_init_errors").then((errors) => {
|
void rpc<[string, string][]>("cmd_plugin_init_errors").then((errors) => {
|
||||||
for (const [dir, err] of errors) {
|
for (const [dir, err] of errors) {
|
||||||
const name = dir.split(/[/\\]/).pop() ?? dir;
|
const name = dir.split(/[/\\]/).pop() ?? dir;
|
||||||
showToast({
|
showToast({
|
||||||
@@ -61,13 +59,13 @@ export function initGlobalListeners() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
listenToTauriEvent("settings", () => openSettings.mutate(null));
|
platform.listen("settings", () => openSettings.mutate(null));
|
||||||
|
|
||||||
// Track active dynamic form dialogs so follow-up input updates can reach them
|
// Track active dynamic form dialogs so follow-up input updates can reach them
|
||||||
const activeForms = new Map<string, (inputs: FormInput[]) => void>();
|
const activeForms = new Map<string, (inputs: FormInput[]) => void>();
|
||||||
|
|
||||||
// Listen for plugin events
|
// Listen for plugin events
|
||||||
listenToTauriEvent<InternalEvent>("plugin_event", async ({ payload: event }) => {
|
platform.listen<InternalEvent>("plugin_event", async (event) => {
|
||||||
if (event.payload.type === "prompt_text_request") {
|
if (event.payload.type === "prompt_text_request") {
|
||||||
const value = await showPrompt(event.payload);
|
const value = await showPrompt(event.payload);
|
||||||
const result: InternalEvent = {
|
const result: InternalEvent = {
|
||||||
@@ -81,7 +79,7 @@ export function initGlobalListeners() {
|
|||||||
value,
|
value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await emit(event.id, result);
|
await platform.emit(event.id, result);
|
||||||
} else if (event.payload.type === "prompt_form_request") {
|
} else if (event.payload.type === "prompt_form_request") {
|
||||||
if (event.replyId != null) {
|
if (event.replyId != null) {
|
||||||
// Follow-up update from plugin runtime — update the active dialog's inputs
|
// Follow-up update from plugin runtime — update the active dialog's inputs
|
||||||
@@ -106,7 +104,7 @@ export function initGlobalListeners() {
|
|||||||
done,
|
done,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
void emit(event.id, result);
|
void platform.emit(event.id, result);
|
||||||
};
|
};
|
||||||
|
|
||||||
const values = await showPromptForm({
|
const values = await showPromptForm({
|
||||||
@@ -127,24 +125,24 @@ export function initGlobalListeners() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
listenToTauriEvent<string>("update_installed", async ({ payload: version }) => {
|
platform.listen<string>("update_installed", async (version) => {
|
||||||
console.log("Got update installed event", version);
|
console.log("Got update installed event", version);
|
||||||
showUpdateInstalledToast(version);
|
showUpdateInstalledToast(version);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for update events
|
// Listen for update events
|
||||||
listenToTauriEvent<UpdateInfo>("update_available", async ({ payload }) => {
|
platform.listen<UpdateInfo>("update_available", async (payload) => {
|
||||||
console.log("Got update available", payload);
|
console.log("Got update available", payload);
|
||||||
void showUpdateAvailableToast(payload);
|
void showUpdateAvailableToast(payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
listenToTauriEvent<YaakNotification>("notification", ({ payload }) => {
|
platform.listen<YaakNotification>("notification", (payload) => {
|
||||||
console.log("Got notification event", payload);
|
console.log("Got notification event", payload);
|
||||||
showNotificationToast(payload);
|
showNotificationToast(payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for plugin update events
|
// Listen for plugin update events
|
||||||
listenToTauriEvent<PluginUpdateNotification>("plugin_updates_available", ({ payload }) => {
|
platform.listen<PluginUpdateNotification>("plugin_updates_available", (payload) => {
|
||||||
console.log("Got plugin updates event", payload);
|
console.log("Got plugin updates event", payload);
|
||||||
showPluginUpdatesToast(payload);
|
showPluginUpdatesToast(payload);
|
||||||
});
|
});
|
||||||
@@ -171,7 +169,7 @@ function showUpdateInstalledToast(version: string) {
|
|||||||
loadingChildren="Restarting..."
|
loadingChildren="Restarting..."
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
hide();
|
hide();
|
||||||
setTimeout(() => invokeCmd("cmd_restart", {}), 200);
|
setTimeout(() => rpc("cmd_restart", {}), 200);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Relaunch Yaak
|
Relaunch Yaak
|
||||||
@@ -187,7 +185,7 @@ async function showUpdateAvailableToast(updateInfo: UpdateInfo) {
|
|||||||
jotaiStore.set(updateAvailableAtom, { version, downloaded });
|
jotaiStore.set(updateAvailableAtom, { version, downloaded });
|
||||||
|
|
||||||
// Acknowledge the event, so we don't time out and try the fallback update logic
|
// Acknowledge the event, so we don't time out and try the fallback update logic
|
||||||
await emit<UpdateResponse>(replyEventId, { type: "ack" });
|
await platform.emit(replyEventId, { type: "ack" } satisfies UpdateResponse);
|
||||||
|
|
||||||
showToast({
|
showToast({
|
||||||
id: UPDATE_TOAST_ID,
|
id: UPDATE_TOAST_ID,
|
||||||
@@ -209,10 +207,10 @@ async function showUpdateAvailableToast(updateInfo: UpdateInfo) {
|
|||||||
className="min-w-40"
|
className="min-w-40"
|
||||||
loadingChildren={downloaded ? "Installing..." : "Downloading..."}
|
loadingChildren={downloaded ? "Installing..." : "Downloading..."}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await emit<UpdateResponse>(replyEventId, {
|
await platform.emit(replyEventId, {
|
||||||
type: "action",
|
type: "action",
|
||||||
action: "install",
|
action: "install",
|
||||||
});
|
} satisfies UpdateResponse);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{downloaded ? "Install Now" : "Download and Install"}
|
{downloaded ? "Install Now" : "Download and Install"}
|
||||||
@@ -223,7 +221,7 @@ async function showUpdateAvailableToast(updateInfo: UpdateInfo) {
|
|||||||
variant="border"
|
variant="border"
|
||||||
rightSlot={<Icon icon="external_link" />}
|
rightSlot={<Icon icon="external_link" />}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await openUrl(`https://yaak.app/changelog/${version}`);
|
await platform.openUrl(`https://yaak.app/changelog/${version}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
What's New
|
What's New
|
||||||
@@ -304,7 +302,7 @@ function showNotificationToast(n: YaakNotification) {
|
|||||||
</VStack>
|
</VStack>
|
||||||
),
|
),
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
invokeCmd("cmd_dismiss_notification", { notificationId: n.id }).catch(console.error);
|
rpc("cmd_dismiss_notification", { notificationId: n.id }).catch(console.error);
|
||||||
},
|
},
|
||||||
action: ({ hide }) => {
|
action: ({ hide }) => {
|
||||||
return actionLabel && actionUrl ? (
|
return actionLabel && actionUrl ? (
|
||||||
@@ -315,7 +313,7 @@ function showNotificationToast(n: YaakNotification) {
|
|||||||
rightSlot={<Icon icon="external_link" />}
|
rightSlot={<Icon icon="external_link" />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
hide();
|
hide();
|
||||||
return openUrl(actionUrl);
|
return platform.openUrl(actionUrl);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{actionLabel}
|
{actionLabel}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { save } from "@tauri-apps/plugin-dialog";
|
|
||||||
import { Icon } from "@yaakapp-internal/ui";
|
import { Icon } from "@yaakapp-internal/ui";
|
||||||
import mime from "mime";
|
import mime from "mime";
|
||||||
import { createElement } from "react";
|
import { createElement } from "react";
|
||||||
@@ -7,8 +6,9 @@ import type { SniffedValue } from "../components/core/Editor/sniffValue";
|
|||||||
import { isEncodedRun } from "../components/core/Editor/sniffValue";
|
import { isEncodedRun } from "../components/core/Editor/sniffValue";
|
||||||
import { copyToClipboard } from "./copy";
|
import { copyToClipboard } from "./copy";
|
||||||
import { fireAndForget } from "./fireAndForget";
|
import { fireAndForget } from "./fireAndForget";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
import { showToast } from "./toast";
|
import { showToast } from "./toast";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How the value is written, which is the thing worth knowing about it — that it is base64
|
* How the value is written, which is the thing worth knowing about it — that it is base64
|
||||||
@@ -229,7 +229,7 @@ function toBase64(bytes: Uint8Array): string {
|
|||||||
*/
|
*/
|
||||||
export async function saveValue(text: string, sniffed: SniffedValue | null, name: string) {
|
export async function saveValue(text: string, sniffed: SniffedValue | null, name: string) {
|
||||||
const ext = sniffed == null ? "txt" : (mime.getExtension(sniffed.mime) ?? "bin");
|
const ext = sniffed == null ? "txt" : (mime.getExtension(sniffed.mime) ?? "bin");
|
||||||
const filepath = await save({ defaultPath: `${name}.${ext}`, title: "Save Value" });
|
const filepath = await platform.dialog.save({ defaultPath: `${name}.${ext}`, title: "Save Value" });
|
||||||
if (filepath == null) {
|
if (filepath == null) {
|
||||||
return; // Cancelled
|
return; // Cancelled
|
||||||
}
|
}
|
||||||
@@ -241,6 +241,6 @@ export async function saveValue(text: string, sniffed: SniffedValue | null, name
|
|||||||
? normalizeBase64(payloadOf(text, sniffed))
|
? normalizeBase64(payloadOf(text, sniffed))
|
||||||
: toBase64(decodeValue(text, sniffed));
|
: toBase64(decodeValue(text, sniffed));
|
||||||
|
|
||||||
await invokeCmd("cmd_save_base64_to_binary", { filepath, data });
|
await rpc("cmd_save_base64_to_binary", { filepath, data });
|
||||||
showToast({ message: `Saved to ${filepath}` });
|
showToast({ message: `Saved to ${filepath}` });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { readFile } from "@tauri-apps/plugin-fs";
|
|
||||||
import type { HttpResponse } from "@yaakapp-internal/models";
|
import type { HttpResponse } from "@yaakapp-internal/models";
|
||||||
import type { FilterResponse } from "@yaakapp-internal/plugins";
|
import type { FilterResponse } from "@yaakapp-internal/plugins";
|
||||||
import type { ServerSentEvent, SseSummary } from "@yaakapp-internal/sse";
|
import type { ServerSentEvent, SseSummary } from "@yaakapp-internal/sse";
|
||||||
import { candidateJsonPayloadsFromSseText, computeSseSummary } from "@yaakapp-internal/sse";
|
import { candidateJsonPayloadsFromSseText, computeSseSummary } from "@yaakapp-internal/sse";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export async function getResponseBodyText({
|
export async function getResponseBodyText({
|
||||||
response,
|
response,
|
||||||
@@ -12,7 +12,7 @@ export async function getResponseBodyText({
|
|||||||
response: HttpResponse;
|
response: HttpResponse;
|
||||||
filter: string | null;
|
filter: string | null;
|
||||||
}): Promise<string | null> {
|
}): Promise<string | null> {
|
||||||
const result = await invokeCmd<FilterResponse>("cmd_http_response_body", {
|
const result = await rpc<FilterResponse>("cmd_http_response_body", {
|
||||||
response,
|
response,
|
||||||
filter,
|
filter,
|
||||||
});
|
});
|
||||||
@@ -29,7 +29,7 @@ export async function getResponseBodyEventSource(
|
|||||||
): Promise<ServerSentEvent[]> {
|
): Promise<ServerSentEvent[]> {
|
||||||
if (!response.bodyPath) return [];
|
if (!response.bodyPath) return [];
|
||||||
try {
|
try {
|
||||||
const events = await invokeCmd<ServerSentEvent[]>("cmd_get_sse_events", {
|
const events = await rpc<ServerSentEvent[]>("cmd_get_sse_events", {
|
||||||
filePath: response.bodyPath,
|
filePath: response.bodyPath,
|
||||||
});
|
});
|
||||||
if (events.length > 0) {
|
if (events.length > 0) {
|
||||||
@@ -39,7 +39,7 @@ export async function getResponseBodyEventSource(
|
|||||||
// Fall back to raw JSON frame parsing for non-standard SSE-like responses.
|
// Fall back to raw JSON frame parsing for non-standard SSE-like responses.
|
||||||
}
|
}
|
||||||
|
|
||||||
const bytes = await readFile(response.bodyPath);
|
const bytes = await platform.files.readFile(response.bodyPath);
|
||||||
const text = new TextDecoder("utf-8").decode(bytes);
|
const text = new TextDecoder("utf-8").decode(bytes);
|
||||||
return candidateJsonPayloadsFromSseText(text).map((data, index) => ({
|
return candidateJsonPayloadsFromSseText(text).map((data, index) => ({
|
||||||
data,
|
data,
|
||||||
@@ -55,7 +55,7 @@ export async function getResponseBodySseSummary(
|
|||||||
): Promise<SseSummary> {
|
): Promise<SseSummary> {
|
||||||
if (!response.bodyPath) return { fragmentCount: 0, summary: "" };
|
if (!response.bodyPath) return { fragmentCount: 0, summary: "" };
|
||||||
|
|
||||||
const bytes = await readFile(response.bodyPath);
|
const bytes = await platform.files.readFile(response.bodyPath);
|
||||||
const text = new TextDecoder("utf-8").decode(bytes);
|
const text = new TextDecoder("utf-8").decode(bytes);
|
||||||
return computeSseSummary(text, resultKeyPath);
|
return computeSseSummary(text, resultKeyPath);
|
||||||
}
|
}
|
||||||
@@ -64,5 +64,5 @@ export async function getResponseBodyBytes(
|
|||||||
response: HttpResponse,
|
response: HttpResponse,
|
||||||
): Promise<Uint8Array<ArrayBuffer> | null> {
|
): Promise<Uint8Array<ArrayBuffer> | null> {
|
||||||
if (!response.bodyPath) return null;
|
if (!response.bodyPath) return null;
|
||||||
return readFile(response.bodyPath);
|
return platform.files.readFile(response.bodyPath);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type } from "@tauri-apps/plugin-os";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
const os = type();
|
const os = platform.osType();
|
||||||
export const revealInFinderText =
|
export const revealInFinderText =
|
||||||
os === "macos"
|
os === "macos"
|
||||||
? "Reveal in Finder"
|
? "Reveal in Finder"
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import type { InvokeArgs } from "@tauri-apps/api/core";
|
import type { RpcPayload } from "@yaakapp-internal/platform";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
type TauriCmd =
|
/**
|
||||||
|
* Every backend command the client sends.
|
||||||
|
*
|
||||||
|
* Listing them keeps typos out and gives us the inventory to check the Rust
|
||||||
|
* side against. Once the app's commands move onto `RpcRouter`, this union is
|
||||||
|
* replaced by the generated `RpcSchema` and the payload and result types come
|
||||||
|
* with it, the way `apps/yaak-proxy/lib/rpc.ts` already works.
|
||||||
|
*/
|
||||||
|
type AppCmd =
|
||||||
| "cmd_call_grpc_request_action"
|
| "cmd_call_grpc_request_action"
|
||||||
| "cmd_call_http_authentication_action"
|
| "cmd_call_http_authentication_action"
|
||||||
| "cmd_call_http_request_action"
|
| "cmd_call_http_request_action"
|
||||||
@@ -53,14 +61,15 @@ type TauriCmd =
|
|||||||
| "cmd_send_http_request"
|
| "cmd_send_http_request"
|
||||||
| "cmd_template_function_summaries"
|
| "cmd_template_function_summaries"
|
||||||
| "cmd_template_function_config"
|
| "cmd_template_function_config"
|
||||||
| "cmd_template_tokens_to_string";
|
| "cmd_template_tokens_to_string"
|
||||||
|
| "models_get_graphql_introspection"
|
||||||
|
| "models_get_settings"
|
||||||
|
| "models_grpc_events"
|
||||||
|
| "models_upsert_graphql_introspection"
|
||||||
|
| "models_websocket_events"
|
||||||
|
| "plugin:yaak-license|check";
|
||||||
|
|
||||||
export async function invokeCmd<T>(cmd: TauriCmd, args?: InvokeArgs): Promise<T> {
|
/** Call a backend command. */
|
||||||
// console.log('RUN COMMAND', cmd, args);
|
export function rpc<T>(cmd: AppCmd, payload?: RpcPayload): Promise<T> {
|
||||||
try {
|
return platform.rpc<T>(cmd, payload);
|
||||||
return await invoke(cmd, args);
|
|
||||||
} catch (err) {
|
|
||||||
console.warn("Tauri command error", cmd, err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { HttpRequest, HttpResponse } from "@yaakapp-internal/models";
|
import type { HttpRequest, HttpResponse } from "@yaakapp-internal/models";
|
||||||
import { getActiveCookieJar } from "../hooks/useActiveCookieJar";
|
import { getActiveCookieJar } from "../hooks/useActiveCookieJar";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
|
|
||||||
export async function sendEphemeralRequest(
|
export async function sendEphemeralRequest(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
@@ -8,7 +8,7 @@ export async function sendEphemeralRequest(
|
|||||||
): Promise<HttpResponse> {
|
): Promise<HttpResponse> {
|
||||||
// Remove some things that we don't want to associate
|
// Remove some things that we don't want to associate
|
||||||
const newRequest = { ...request };
|
const newRequest = { ...request };
|
||||||
return invokeCmd("cmd_send_ephemeral_request", {
|
return rpc("cmd_send_ephemeral_request", {
|
||||||
request: newRequest,
|
request: newRequest,
|
||||||
environmentId,
|
environmentId,
|
||||||
cookieJarId: getActiveCookieJar()?.id,
|
cookieJarId: getActiveCookieJar()?.id,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
|
||||||
import type { Settings } from "@yaakapp-internal/models";
|
import type { Settings } from "@yaakapp-internal/models";
|
||||||
|
import { rpc } from "./rpc";
|
||||||
|
|
||||||
export function getSettings(): Promise<Settings> {
|
export function getSettings(): Promise<Settings> {
|
||||||
return invoke<Settings>("models_get_settings");
|
return rpc<Settings>("models_get_settings");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import {
|
|||||||
resolveAppearance,
|
resolveAppearance,
|
||||||
type Appearance,
|
type Appearance,
|
||||||
} from "@yaakapp-internal/theme";
|
} from "@yaakapp-internal/theme";
|
||||||
import { invokeCmd } from "./tauri";
|
import { rpc } from "./rpc";
|
||||||
|
|
||||||
export async function getThemes() {
|
export async function getThemes() {
|
||||||
const themes = (await invokeCmd<GetThemesResponse[]>("cmd_get_themes")).flatMap((t) => t.themes);
|
const themes = (await rpc<GetThemesResponse[]>("cmd_get_themes")).flatMap((t) => t.themes);
|
||||||
themes.sort((a, b) => a.label.localeCompare(b.label));
|
themes.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
// Remove duplicates, in case multiple plugins provide the same theme
|
// Remove duplicates, in case multiple plugins provide the same theme
|
||||||
const uniqueThemes = Array.from(new Map(themes.map((t) => [t.id, t])).values());
|
const uniqueThemes = Array.from(new Map(themes.map((t) => [t.id, t])).values());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import "./main.css";
|
import "./main.css";
|
||||||
import { RouterProvider } from "@tanstack/react-router";
|
import { RouterProvider } from "@tanstack/react-router";
|
||||||
import { type } from "@tauri-apps/plugin-os";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { changeModelStoreWorkspace, initModelStore } from "@yaakapp-internal/models";
|
import { changeModelStoreWorkspace, initModelStore } from "@yaakapp-internal/models";
|
||||||
import { setPlatformOnDocument } from "@yaakapp-internal/theme";
|
import { setPlatformOnDocument } from "@yaakapp-internal/theme";
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
@@ -11,7 +11,7 @@ import { initGlobalListeners } from "./lib/initGlobalListeners";
|
|||||||
import { jotaiStore } from "./lib/jotai";
|
import { jotaiStore } from "./lib/jotai";
|
||||||
import { router } from "./lib/router";
|
import { router } from "./lib/router";
|
||||||
|
|
||||||
const osType = type();
|
const osType = platform.osType();
|
||||||
setPlatformOnDocument(osType);
|
setPlatformOnDocument(osType);
|
||||||
|
|
||||||
window.addEventListener("keydown", (e) => {
|
window.addEventListener("keydown", (e) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { createRootRoute, Outlet } from "@tanstack/react-router";
|
import { createRootRoute, Outlet } from "@tanstack/react-router";
|
||||||
import { type } from "@tauri-apps/plugin-os";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Provider as JotaiProvider } from "jotai";
|
import { Provider as JotaiProvider } from "jotai";
|
||||||
import { LazyMotion, MotionConfig } from "motion/react";
|
import { LazyMotion, MotionConfig } from "motion/react";
|
||||||
@@ -46,7 +46,7 @@ function RouteComponent() {
|
|||||||
function Layout() {
|
function Layout() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames("w-full h-full", type() === "linux" && "border border-border-subtle")}
|
className={classNames("w-full h-full", platform.osType() === "linux" && "border border-border-subtle")}
|
||||||
>
|
>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { listen } from "@tauri-apps/api/event";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
|
||||||
import { type as osType } from "@tauri-apps/plugin-os";
|
|
||||||
import { setWindowTheme } from "@yaakapp-internal/mac-window";
|
import { setWindowTheme } from "@yaakapp-internal/mac-window";
|
||||||
import type { ModelPayload } from "@yaakapp-internal/models";
|
import type { ModelPayload } from "@yaakapp-internal/models";
|
||||||
import type { Appearance } from "@yaakapp-internal/theme";
|
import type { Appearance } from "@yaakapp-internal/theme";
|
||||||
@@ -17,7 +15,7 @@ import { getResolvedTheme } from "./lib/themes";
|
|||||||
// a good appearance guess so we're not waiting too long
|
// a good appearance guess so we're not waiting too long
|
||||||
let preferredAppearance: Appearance = getInitialAppearance();
|
let preferredAppearance: Appearance = getInitialAppearance();
|
||||||
let linuxSystemAppearanceAvailable =
|
let linuxSystemAppearanceAvailable =
|
||||||
osType() === "linux" && window.__YAAK_INITIAL_APPEARANCE_SOURCE__ === "linux-system";
|
platform.osType() === "linux" && window.__YAAK_INITIAL_APPEARANCE_SOURCE__ === "linux-system";
|
||||||
let configureThemeGeneration = 0;
|
let configureThemeGeneration = 0;
|
||||||
let windowShown = false;
|
let windowShown = false;
|
||||||
|
|
||||||
@@ -41,20 +39,20 @@ async function configureThemeAndShow() {
|
|||||||
windowShown = true;
|
windowShown = true;
|
||||||
// To prevent theme flashing, the backend hides new windows by default, so we
|
// To prevent theme flashing, the backend hides new windows by default, so we
|
||||||
// need to show it here, after configuring the theme for the first time.
|
// need to show it here, after configuring the theme for the first time.
|
||||||
await getCurrentWebviewWindow().show();
|
await platform.window.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for settings changes, the re-compute theme
|
// Listen for settings changes, the re-compute theme
|
||||||
listen<ModelPayload[]>("model_writes", async (event) => {
|
platform.listen<ModelPayload[]>("model_writes", async (payloads) => {
|
||||||
const relevant = event.payload.some(
|
const relevant = payloads.some(
|
||||||
(p) =>
|
(p) =>
|
||||||
p.change.type === "upsert" &&
|
p.change.type === "upsert" &&
|
||||||
(p.model.model === "settings" || p.model.model === "plugin"),
|
(p.model.model === "settings" || p.model.model === "plugin"),
|
||||||
);
|
);
|
||||||
if (!relevant) return;
|
if (!relevant) return;
|
||||||
await configureThemeAndShow();
|
await configureThemeAndShow();
|
||||||
}).catch(console.error);
|
});
|
||||||
|
|
||||||
async function configureTheme(): Promise<boolean> {
|
async function configureTheme(): Promise<boolean> {
|
||||||
const generation = ++configureThemeGeneration;
|
const generation = ++configureThemeGeneration;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { Fonts } from "./bindings/gen_fonts";
|
import { Fonts } from "./bindings/gen_fonts";
|
||||||
|
|
||||||
export async function listFonts() {
|
export async function listFonts() {
|
||||||
return invoke<Fonts>("plugin:yaak-fonts|list", {});
|
return platform.rpc<Fonts>("plugin:yaak-fonts|list", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFonts() {
|
export function useFonts() {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
|
||||||
import { appInfo } from "@yaakapp/yaak-client/lib/appInfo";
|
import { appInfo } from "@yaakapp/yaak-client/lib/appInfo";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { LicenseCheckStatus } from "./bindings/license";
|
import { LicenseCheckStatus } from "./bindings/license";
|
||||||
@@ -13,24 +12,21 @@ export function useLicense() {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const activate = useMutation<void, string, { licenseKey: string }>({
|
const activate = useMutation<void, string, { licenseKey: string }>({
|
||||||
mutationKey: ["license.activate"],
|
mutationKey: ["license.activate"],
|
||||||
mutationFn: (payload) => invoke("plugin:yaak-license|activate", payload),
|
mutationFn: (payload) => platform.rpc("plugin:yaak-license|activate", payload),
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const deactivate = useMutation<void, string, void>({
|
const deactivate = useMutation<void, string, void>({
|
||||||
mutationKey: ["license.deactivate"],
|
mutationKey: ["license.deactivate"],
|
||||||
mutationFn: () => invoke("plugin:yaak-license|deactivate"),
|
mutationFn: () => platform.rpc("plugin:yaak-license|deactivate"),
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check the license again after a license is activated
|
// Check the license again after a license is activated
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisten = listen("license-activated", async () => {
|
return platform.listen("license-activated", () => {
|
||||||
await queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY });
|
void queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY });
|
||||||
});
|
});
|
||||||
return () => {
|
|
||||||
void unlisten.then((fn) => fn());
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const check = useQuery<LicenseCheckStatus | null, string>({
|
const check = useQuery<LicenseCheckStatus | null, string>({
|
||||||
@@ -41,7 +37,7 @@ export function useLicense() {
|
|||||||
if (!appInfo.featureLicense) {
|
if (!appInfo.featureLicense) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return invoke<LicenseCheckStatus>("plugin:yaak-license|check");
|
return platform.rpc<LicenseCheckStatus>("plugin:yaak-license|check");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export function setWindowTitle(title: string) {
|
export function setWindowTitle(title: string) {
|
||||||
invoke("plugin:yaak-mac-window|set_title", { title }).catch(console.error);
|
platform.rpc("plugin:yaak-mac-window|set_title", { title }).catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setWindowTheme(bgColor: string) {
|
export function setWindowTheme(bgColor: string) {
|
||||||
invoke("plugin:yaak-mac-window|set_theme", { bgColor }).catch(console.error);
|
platform.rpc("plugin:yaak-mac-window|set_theme", { bgColor }).catch(console.error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
|
|
||||||
export function enableEncryption(workspaceId: string) {
|
export function enableEncryption(workspaceId: string) {
|
||||||
return invoke<void>("cmd_enable_encryption", { workspaceId });
|
return platform.rpc<void>("cmd_enable_encryption", { workspaceId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function revealWorkspaceKey(workspaceId: string) {
|
export function revealWorkspaceKey(workspaceId: string) {
|
||||||
return invoke<string>("cmd_reveal_workspace_key", { workspaceId });
|
return platform.rpc<string>("cmd_reveal_workspace_key", { workspaceId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setWorkspaceKey(args: { workspaceId: string; key: string }) {
|
export function setWorkspaceKey(args: { workspaceId: string; key: string }) {
|
||||||
return invoke<void>("cmd_set_workspace_key", args);
|
return platform.rpc<void>("cmd_set_workspace_key", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function disableEncryption(workspaceId: string) {
|
export function disableEncryption(workspaceId: string) {
|
||||||
return invoke<void>("cmd_disable_encryption", { workspaceId });
|
return platform.rpc<void>("cmd_disable_encryption", { workspaceId });
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-46
@@ -1,6 +1,5 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Channel, invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { emit } from "@tauri-apps/api/event";
|
|
||||||
import { createFastMutation } from "@yaakapp/yaak-client/hooks/useFastMutation";
|
import { createFastMutation } from "@yaakapp/yaak-client/hooks/useFastMutation";
|
||||||
import { queryClient } from "@yaakapp/yaak-client/lib/queryClient";
|
import { queryClient } from "@yaakapp/yaak-client/lib/queryClient";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
@@ -59,18 +58,17 @@ export function invalidateGitWorktreeStatus(dir?: string) {
|
|||||||
export function useGitWorktreeStatus(dir: string, refreshKey?: string) {
|
export function useGitWorktreeStatus(dir: string, refreshKey?: string) {
|
||||||
return useQuery<GitWorktreeStatus, string>({
|
return useQuery<GitWorktreeStatus, string>({
|
||||||
queryKey: gitWorktreeStatusQueryKey(dir, refreshKey),
|
queryKey: gitWorktreeStatusQueryKey(dir, refreshKey),
|
||||||
queryFn: () => invoke("cmd_git_worktree_status", { dir }),
|
queryFn: () => platform.rpc("cmd_git_worktree_status", { dir }),
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function watchGitWorktreeStatus(dir: string, callback: (status: GitWorktreeStatus) => void) {
|
export function watchGitWorktreeStatus(dir: string, callback: (status: GitWorktreeStatus) => void) {
|
||||||
const channel = new Channel<GitWorktreeStatus>();
|
const unlistenPromise = platform.rpcStream<GitWatchResult, GitWorktreeStatus>(
|
||||||
channel.onmessage = callback;
|
"cmd_git_watch_worktree_status",
|
||||||
const unlistenPromise = invoke<GitWatchResult>("cmd_git_watch_worktree_status", {
|
{ dir },
|
||||||
dir,
|
callback,
|
||||||
channel,
|
);
|
||||||
});
|
|
||||||
|
|
||||||
void unlistenPromise
|
void unlistenPromise
|
||||||
.then(({ unlistenEvent }) => {
|
.then(({ unlistenEvent }) => {
|
||||||
@@ -89,7 +87,7 @@ export function watchGitWorktreeStatus(dir: string, callback: (status: GitWorktr
|
|||||||
function useGitFetchAll(dir: string, refreshKey?: string) {
|
function useGitFetchAll(dir: string, refreshKey?: string) {
|
||||||
return useQuery<void, string>({
|
return useQuery<void, string>({
|
||||||
queryKey: ["git", "fetch_all", dir, refreshKey],
|
queryKey: ["git", "fetch_all", dir, refreshKey],
|
||||||
queryFn: () => invoke("cmd_git_fetch_all", { dir }),
|
queryFn: () => platform.rpc("cmd_git_fetch_all", { dir }),
|
||||||
refetchInterval: 10 * 60_000,
|
refetchInterval: 10 * 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -98,7 +96,7 @@ function useGitBranchInfoQuery(dir: string, refreshKey?: string, fetchAllUpdated
|
|||||||
return useQuery<GitBranchInfo, string>({
|
return useQuery<GitBranchInfo, string>({
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
queryKey: ["git", "branch_info", dir, refreshKey, fetchAllUpdatedAt],
|
queryKey: ["git", "branch_info", dir, refreshKey, fetchAllUpdatedAt],
|
||||||
queryFn: () => invoke("cmd_git_branch_info", { dir }),
|
queryFn: () => platform.rpc("cmd_git_branch_info", { dir }),
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -113,8 +111,8 @@ export function useGitLog(dir: string, refreshKey?: string, relaPath?: string) {
|
|||||||
queryKey: ["git", "log", dir, refreshKey, relaPath],
|
queryKey: ["git", "log", dir, refreshKey, relaPath],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
relaPath == null
|
relaPath == null
|
||||||
? invoke("cmd_git_log", { dir })
|
? platform.rpc("cmd_git_log", { dir })
|
||||||
: invoke("cmd_git_log_for_file", { dir, relaPath }),
|
: platform.rpc("cmd_git_log_for_file", { dir, relaPath }),
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -129,7 +127,7 @@ export function useGitFileDiffForCommit(
|
|||||||
queryKey: ["git", "file_diff_for_commit", dir, relaPath, commitOid],
|
queryKey: ["git", "file_diff_for_commit", dir, relaPath, commitOid],
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
if (commitOid == null) throw new Error("Missing commit oid");
|
if (commitOid == null) throw new Error("Missing commit oid");
|
||||||
return invoke("cmd_git_file_diff_for_commit", { dir, relaPath, commitOid });
|
return platform.rpc("cmd_git_file_diff_for_commit", { dir, relaPath, commitOid });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -149,7 +147,7 @@ export function useGit(dir: string, callbacks: GitCallbacks, refreshKey?: string
|
|||||||
status: useQuery<GitStatusSummary, string>({
|
status: useQuery<GitStatusSummary, string>({
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
queryKey: ["git", "status", dir, refreshKey, fetchAll.dataUpdatedAt],
|
queryKey: ["git", "status", dir, refreshKey, fetchAll.dataUpdatedAt],
|
||||||
queryFn: () => invoke("cmd_git_status", { dir }),
|
queryFn: () => platform.rpc("cmd_git_status", { dir }),
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -169,21 +167,21 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
if (remote == null) throw new Error("No remote found");
|
if (remote == null) throw new Error("No remote found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await invoke<PushResult>("cmd_git_push", { dir });
|
const result = await platform.rpc<PushResult>("cmd_git_push", { dir });
|
||||||
if (result.type !== "needs_credentials") return result;
|
if (result.type !== "needs_credentials") return result;
|
||||||
|
|
||||||
// Needs credentials, prompt for them
|
// Needs credentials, prompt for them
|
||||||
const creds = await callbacks.promptCredentials(result);
|
const creds = await callbacks.promptCredentials(result);
|
||||||
if (creds == null) throw new Error("Canceled");
|
if (creds == null) throw new Error("Canceled");
|
||||||
|
|
||||||
await invoke("cmd_git_add_credential", {
|
await platform.rpc("cmd_git_add_credential", {
|
||||||
remoteUrl: result.url,
|
remoteUrl: result.url,
|
||||||
username: creds.username,
|
username: creds.username,
|
||||||
password: creds.password,
|
password: creds.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Push again
|
// Push again
|
||||||
return invoke<PushResult>("cmd_git_push", { dir });
|
return platform.rpc<PushResult>("cmd_git_push", { dir });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleError = (err: unknown) => {
|
const handleError = (err: unknown) => {
|
||||||
@@ -198,32 +196,32 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
return {
|
return {
|
||||||
init: createFastMutation<void, string, void>({
|
init: createFastMutation<void, string, void>({
|
||||||
mutationKey: ["git", "init"],
|
mutationKey: ["git", "init"],
|
||||||
mutationFn: () => invoke("cmd_git_initialize", { dir }),
|
mutationFn: () => platform.rpc("cmd_git_initialize", { dir }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
add: createFastMutation<void, string, { relaPaths: string[] }>({
|
add: createFastMutation<void, string, { relaPaths: string[] }>({
|
||||||
mutationKey: ["git", "add", dir],
|
mutationKey: ["git", "add", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_add", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_add", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
addRemote: createFastMutation<GitRemote, string, GitRemote>({
|
addRemote: createFastMutation<GitRemote, string, GitRemote>({
|
||||||
mutationKey: ["git", "add-remote"],
|
mutationKey: ["git", "add-remote"],
|
||||||
mutationFn: (args) => invoke("cmd_git_add_remote", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_add_remote", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
rmRemote: createFastMutation<void, string, { name: string }>({
|
rmRemote: createFastMutation<void, string, { name: string }>({
|
||||||
mutationKey: ["git", "rm-remote", dir],
|
mutationKey: ["git", "rm-remote", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_rm_remote", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_rm_remote", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
createBranch: createFastMutation<void, string, { branch: string; base?: string }>({
|
createBranch: createFastMutation<void, string, { branch: string; base?: string }>({
|
||||||
mutationKey: ["git", "branch", dir],
|
mutationKey: ["git", "branch", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_branch", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
mergeBranch: createFastMutation<void, string, { branch: string }>({
|
mergeBranch: createFastMutation<void, string, { branch: string }>({
|
||||||
mutationKey: ["git", "merge", dir],
|
mutationKey: ["git", "merge", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_merge_branch", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_merge_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
deleteBranch: createFastMutation<
|
deleteBranch: createFastMutation<
|
||||||
@@ -232,33 +230,33 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
{ branch: string; force?: boolean }
|
{ branch: string; force?: boolean }
|
||||||
>({
|
>({
|
||||||
mutationKey: ["git", "delete-branch", dir],
|
mutationKey: ["git", "delete-branch", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_delete_branch", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_delete_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
deleteRemoteBranch: createFastMutation<void, string, { branch: string }>({
|
deleteRemoteBranch: createFastMutation<void, string, { branch: string }>({
|
||||||
mutationKey: ["git", "delete-remote-branch", dir],
|
mutationKey: ["git", "delete-remote-branch", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_delete_remote_branch", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_delete_remote_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
renameBranch: createFastMutation<void, string, { oldName: string; newName: string }>({
|
renameBranch: createFastMutation<void, string, { oldName: string; newName: string }>({
|
||||||
mutationKey: ["git", "rename-branch", dir],
|
mutationKey: ["git", "rename-branch", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_rename_branch", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_rename_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
checkout: createFastMutation<string, string, { branch: string; force: boolean }>({
|
checkout: createFastMutation<string, string, { branch: string; force: boolean }>({
|
||||||
mutationKey: ["git", "checkout", dir],
|
mutationKey: ["git", "checkout", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_checkout", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_checkout", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
commit: createFastMutation<void, string, { message: string }>({
|
commit: createFastMutation<void, string, { message: string }>({
|
||||||
mutationKey: ["git", "commit", dir],
|
mutationKey: ["git", "commit", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_commit", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_commit", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
commitAndPush: createFastMutation<PushResult, string, { message: string }>({
|
commitAndPush: createFastMutation<PushResult, string, { message: string }>({
|
||||||
mutationKey: ["git", "commit_push", dir],
|
mutationKey: ["git", "commit_push", dir],
|
||||||
mutationFn: async (args) => {
|
mutationFn: async (args) => {
|
||||||
await invoke("cmd_git_commit", { dir, ...args });
|
await platform.rpc("cmd_git_commit", { dir, ...args });
|
||||||
return push();
|
return push();
|
||||||
},
|
},
|
||||||
onSuccess,
|
onSuccess,
|
||||||
@@ -272,20 +270,20 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
pull: createFastMutation<PullResult, string, void>({
|
pull: createFastMutation<PullResult, string, void>({
|
||||||
mutationKey: ["git", "pull", dir],
|
mutationKey: ["git", "pull", dir],
|
||||||
async mutationFn() {
|
async mutationFn() {
|
||||||
const result = await invoke<PullResult>("cmd_git_pull", { dir });
|
const result = await platform.rpc<PullResult>("cmd_git_pull", { dir });
|
||||||
|
|
||||||
if (result.type === "needs_credentials") {
|
if (result.type === "needs_credentials") {
|
||||||
const creds = await callbacks.promptCredentials(result);
|
const creds = await callbacks.promptCredentials(result);
|
||||||
if (creds == null) throw new Error("Canceled");
|
if (creds == null) throw new Error("Canceled");
|
||||||
|
|
||||||
await invoke("cmd_git_add_credential", {
|
await platform.rpc("cmd_git_add_credential", {
|
||||||
remoteUrl: result.url,
|
remoteUrl: result.url,
|
||||||
username: creds.username,
|
username: creds.username,
|
||||||
password: creds.password,
|
password: creds.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pull again after credentials
|
// Pull again after credentials
|
||||||
return invoke<PullResult>("cmd_git_pull", { dir });
|
return platform.rpc<PullResult>("cmd_git_pull", { dir });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.type === "uncommitted_changes") {
|
if (result.type === "uncommitted_changes") {
|
||||||
@@ -294,8 +292,8 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
.then(async (strategy) => {
|
.then(async (strategy) => {
|
||||||
if (strategy === "cancel") return;
|
if (strategy === "cancel") return;
|
||||||
|
|
||||||
await invoke("cmd_git_reset_changes", { dir });
|
await platform.rpc("cmd_git_reset_changes", { dir });
|
||||||
return invoke<PullResult>("cmd_git_pull", { dir });
|
return platform.rpc<PullResult>("cmd_git_pull", { dir });
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await onSuccess();
|
await onSuccess();
|
||||||
@@ -310,14 +308,14 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
if (strategy === "cancel") return;
|
if (strategy === "cancel") return;
|
||||||
|
|
||||||
if (strategy === "force_reset") {
|
if (strategy === "force_reset") {
|
||||||
return invoke<PullResult>("cmd_git_pull_force_reset", {
|
return platform.rpc<PullResult>("cmd_git_pull_force_reset", {
|
||||||
dir,
|
dir,
|
||||||
remote: result.remote,
|
remote: result.remote,
|
||||||
branch: result.branch,
|
branch: result.branch,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return invoke<PullResult>("cmd_git_pull_merge", {
|
return platform.rpc<PullResult>("cmd_git_pull_merge", {
|
||||||
dir,
|
dir,
|
||||||
remote: result.remote,
|
remote: result.remote,
|
||||||
branch: result.branch,
|
branch: result.branch,
|
||||||
@@ -335,17 +333,17 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
}),
|
}),
|
||||||
unstage: createFastMutation<void, string, { relaPaths: string[] }>({
|
unstage: createFastMutation<void, string, { relaPaths: string[] }>({
|
||||||
mutationKey: ["git", "unstage", dir],
|
mutationKey: ["git", "unstage", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_unstage", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_unstage", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
resetChanges: createFastMutation<void, string, void>({
|
resetChanges: createFastMutation<void, string, void>({
|
||||||
mutationKey: ["git", "reset-changes", dir],
|
mutationKey: ["git", "reset-changes", dir],
|
||||||
mutationFn: () => invoke("cmd_git_reset_changes", { dir }),
|
mutationFn: () => platform.rpc("cmd_git_reset_changes", { dir }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
restore: createFastMutation<void, string, { relaPaths: string[] }>({
|
restore: createFastMutation<void, string, { relaPaths: string[] }>({
|
||||||
mutationKey: ["git", "restore", dir],
|
mutationKey: ["git", "restore", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_restore_files", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_restore_files", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
restoreFileFromCommit: createFastMutation<
|
restoreFileFromCommit: createFastMutation<
|
||||||
@@ -354,18 +352,18 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
{ commitOid: string; relaPath: string }
|
{ commitOid: string; relaPath: string }
|
||||||
>({
|
>({
|
||||||
mutationKey: ["git", "restore-file-from-commit", dir],
|
mutationKey: ["git", "restore-file-from-commit", dir],
|
||||||
mutationFn: (args) => invoke("cmd_git_restore_file_from_commit", { dir, ...args }),
|
mutationFn: (args) => platform.rpc("cmd_git_restore_file_from_commit", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
} as const;
|
} as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getRemotes(dir: string) {
|
async function getRemotes(dir: string) {
|
||||||
return invoke<GitRemote[]>("cmd_git_remotes", { dir });
|
return platform.rpc<GitRemote[]>("cmd_git_remotes", { dir });
|
||||||
}
|
}
|
||||||
|
|
||||||
function unlistenGitWatcher(unlistenEvent: string) {
|
function unlistenGitWatcher(unlistenEvent: string) {
|
||||||
void emit(unlistenEvent).then(() => {
|
void platform.emit(unlistenEvent).then(() => {
|
||||||
removeGitWatchKey(unlistenEvent);
|
removeGitWatchKey(unlistenEvent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -404,7 +402,7 @@ export async function gitClone(
|
|||||||
error: string | null;
|
error: string | null;
|
||||||
}) => Promise<GitCredentials | null>,
|
}) => Promise<GitCredentials | null>,
|
||||||
): Promise<CloneResult> {
|
): Promise<CloneResult> {
|
||||||
const result = await invoke<CloneResult>("cmd_git_clone", { url, dir });
|
const result = await platform.rpc<CloneResult>("cmd_git_clone", { url, dir });
|
||||||
if (result.type !== "needs_credentials") return result;
|
if (result.type !== "needs_credentials") return result;
|
||||||
|
|
||||||
// Prompt for credentials
|
// Prompt for credentials
|
||||||
@@ -415,11 +413,11 @@ export async function gitClone(
|
|||||||
if (creds == null) return { type: "cancelled" };
|
if (creds == null) return { type: "cancelled" };
|
||||||
|
|
||||||
// Store credentials and retry
|
// Store credentials and retry
|
||||||
await invoke("cmd_git_add_credential", {
|
await platform.rpc("cmd_git_add_credential", {
|
||||||
remoteUrl: result.url,
|
remoteUrl: result.url,
|
||||||
username: creds.username,
|
username: creds.username,
|
||||||
password: creds.password,
|
password: creds.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
return invoke<CloneResult>("cmd_git_clone", { url, dir });
|
return platform.rpc<CloneResult>("cmd_git_clone", { url, dir });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
|
||||||
import { debounce } from "@yaakapp-internal/lib";
|
import { debounce } from "@yaakapp-internal/lib";
|
||||||
import { AnyModel, ModelPayload } from "../bindings/gen_models";
|
import { AnyModel, ModelPayload } from "../bindings/gen_models";
|
||||||
import { modelStoreDataAtom } from "./atoms";
|
import { modelStoreDataAtom } from "./atoms";
|
||||||
@@ -16,37 +15,35 @@ export function initModelStore(store: JotaiStore) {
|
|||||||
// Don't lose debounced patches if the window closes while one is pending
|
// Don't lose debounced patches if the window closes while one is pending
|
||||||
window.addEventListener("beforeunload", flushAllPendingPatches);
|
window.addEventListener("beforeunload", flushAllPendingPatches);
|
||||||
|
|
||||||
getCurrentWebviewWindow()
|
platform.listen<ModelPayload[]>("model_writes", (payloads) => {
|
||||||
.listen<ModelPayload[]>("model_writes", ({ payload: payloads }) => {
|
mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => {
|
||||||
mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => {
|
// Apply the entire batch in one update, cloning each touched bucket only
|
||||||
// Apply the entire batch in one update, cloning each touched bucket only
|
// once. Bulk writes (imports, sync, CLI) can carry hundreds of models.
|
||||||
// once. Bulk writes (imports, sync, CLI) can carry hundreds of models.
|
const next = { ...prev };
|
||||||
const next = { ...prev };
|
const clonedBuckets = new Set<AnyModel["model"]>();
|
||||||
const clonedBuckets = new Set<AnyModel["model"]>();
|
let changed = false;
|
||||||
let changed = false;
|
|
||||||
|
|
||||||
for (const payload of payloads) {
|
for (const payload of payloads) {
|
||||||
if (shouldIgnoreModel(payload)) continue;
|
if (shouldIgnoreModel(payload)) continue;
|
||||||
if (isUnsafeObjectKey(payload.model.model)) continue;
|
if (isUnsafeObjectKey(payload.model.model)) continue;
|
||||||
if (isUnsafeObjectKey(payload.model.id)) continue;
|
if (isUnsafeObjectKey(payload.model.id)) continue;
|
||||||
|
|
||||||
if (payload.change.type === "upsert") {
|
if (payload.change.type === "upsert") {
|
||||||
const modelType = payload.model.model;
|
const modelType = payload.model.model;
|
||||||
if (!clonedBuckets.has(modelType)) {
|
if (!clonedBuckets.has(modelType)) {
|
||||||
next[modelType] = { ...next[modelType] } as never;
|
next[modelType] = { ...next[modelType] } as never;
|
||||||
clonedBuckets.add(modelType);
|
clonedBuckets.add(modelType);
|
||||||
}
|
|
||||||
(next[modelType] as Record<string, AnyModel>)[payload.model.id] = payload.model;
|
|
||||||
changed = true;
|
|
||||||
} else {
|
|
||||||
changed = applyModelDelete(next, clonedBuckets, payload.model) || changed;
|
|
||||||
}
|
}
|
||||||
|
(next[modelType] as Record<string, AnyModel>)[payload.model.id] = payload.model;
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
changed = applyModelDelete(next, clonedBuckets, payload.model) || changed;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return changed ? next : prev;
|
return changed ? next : prev;
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
.catch(console.error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,7 +208,7 @@ let _activeWorkspaceId: string | null = null;
|
|||||||
|
|
||||||
export async function changeModelStoreWorkspace(workspaceId: string | null) {
|
export async function changeModelStoreWorkspace(workspaceId: string | null) {
|
||||||
console.log("Syncing models with new workspace", workspaceId);
|
console.log("Syncing models with new workspace", workspaceId);
|
||||||
const workspaceModelsStr = await invoke<string>("models_workspace_models", {
|
const workspaceModelsStr = await platform.rpc<string>("models_workspace_models", {
|
||||||
workspaceId, // NOTE: if no workspace id provided, it will just fetch global models
|
workspaceId, // NOTE: if no workspace id provided, it will just fetch global models
|
||||||
});
|
});
|
||||||
const workspaceModels = JSON.parse(workspaceModelsStr) as AnyModel[];
|
const workspaceModels = JSON.parse(workspaceModelsStr) as AnyModel[];
|
||||||
@@ -288,7 +285,7 @@ export async function patchModel<M extends AnyModel["model"], T extends ExtractM
|
|||||||
export async function updateModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
export async function updateModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
||||||
model: T,
|
model: T,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return trackModelWrite(invoke<string>("models_upsert", { model }));
|
return trackModelWrite(platform.rpc<string>("models_upsert", { model }));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteModelById<
|
export async function deleteModelById<
|
||||||
@@ -305,7 +302,7 @@ export async function deleteModel<M extends AnyModel["model"], T extends Extract
|
|||||||
if (model == null) {
|
if (model == null) {
|
||||||
throw new Error("Failed to delete null model");
|
throw new Error("Failed to delete null model");
|
||||||
}
|
}
|
||||||
await trackModelWrite(invoke<string>("models_delete", { model }));
|
await trackModelWrite(platform.rpc<string>("models_delete", { model }));
|
||||||
|
|
||||||
// Apply the delete locally right away so callers can rely on the store once the
|
// Apply the delete locally right away so callers can rely on the store once the
|
||||||
// promise resolves. The backend echo arrives async, so anything that reads the
|
// promise resolves. The backend echo arrives async, so anything that reads the
|
||||||
@@ -331,20 +328,20 @@ export async function duplicateModel<
|
|||||||
await flushAllModelWrites();
|
await flushAllModelWrites();
|
||||||
|
|
||||||
return trackModelWrite(
|
return trackModelWrite(
|
||||||
invoke<string>("models_duplicate", { modelType: model.model, modelId: model.id }),
|
platform.rpc<string>("models_duplicate", { modelType: model.model, modelId: model.id }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createGlobalModel<T extends Exclude<AnyModel, { workspaceId: string }>>(
|
export async function createGlobalModel<T extends Exclude<AnyModel, { workspaceId: string }>>(
|
||||||
patch: Partial<T> & Pick<T, "model">,
|
patch: Partial<T> & Pick<T, "model">,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return trackModelWrite(invoke<string>("models_upsert", { model: patch }));
|
return trackModelWrite(platform.rpc<string>("models_upsert", { model: patch }));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createWorkspaceModel<T extends Extract<AnyModel, { workspaceId: string }>>(
|
export async function createWorkspaceModel<T extends Extract<AnyModel, { workspaceId: string }>>(
|
||||||
patch: Partial<T> & Pick<T, "model" | "workspaceId">,
|
patch: Partial<T> & Pick<T, "model" | "workspaceId">,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return trackModelWrite(invoke<string>("models_upsert", { model: patch }));
|
return trackModelWrite(platform.rpc<string>("models_upsert", { model: patch }));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replaceModelsInStore<
|
export function replaceModelsInStore<
|
||||||
@@ -399,7 +396,7 @@ function shouldIgnoreModel({ model, updateSource }: ModelPayload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Never ignore same-window updates
|
// Never ignore same-window updates
|
||||||
if (updateSource.label === getCurrentWebviewWindow().label) {
|
if (updateSource.label === platform.window.label) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { PluginNameVersion, PluginSearchResponse, PluginUpdatesResponse } from "./bindings/gen_api";
|
import { PluginNameVersion, PluginSearchResponse, PluginUpdatesResponse } from "./bindings/gen_api";
|
||||||
|
|
||||||
export * from "./bindings/gen_models";
|
export * from "./bindings/gen_models";
|
||||||
@@ -6,25 +6,25 @@ export * from "./bindings/gen_events";
|
|||||||
export * from "./bindings/gen_search";
|
export * from "./bindings/gen_search";
|
||||||
|
|
||||||
export async function searchPlugins(query: string) {
|
export async function searchPlugins(query: string) {
|
||||||
return invoke<PluginSearchResponse>("cmd_plugins_search", { query });
|
return platform.rpc<PluginSearchResponse>("cmd_plugins_search", { query });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installPlugin(name: string, version: string | null) {
|
export async function installPlugin(name: string, version: string | null) {
|
||||||
return invoke<void>("cmd_plugins_install", { name, version });
|
return platform.rpc<void>("cmd_plugins_install", { name, version });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uninstallPlugin(pluginId: string) {
|
export async function uninstallPlugin(pluginId: string) {
|
||||||
return invoke<void>("cmd_plugins_uninstall", { pluginId });
|
return platform.rpc<void>("cmd_plugins_uninstall", { pluginId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkPluginUpdates() {
|
export async function checkPluginUpdates() {
|
||||||
return invoke<PluginUpdatesResponse>("cmd_plugins_updates", {});
|
return platform.rpc<PluginUpdatesResponse>("cmd_plugins_updates", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateAllPlugins() {
|
export async function updateAllPlugins() {
|
||||||
return invoke<PluginNameVersion[]>("cmd_plugins_update_all", {});
|
return platform.rpc<PluginNameVersion[]>("cmd_plugins_update_all", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installPluginFromDirectory(directory: string) {
|
export async function installPluginFromDirectory(directory: string) {
|
||||||
return invoke<void>("cmd_plugins_install_from_directory", { directory });
|
return platform.rpc<void>("cmd_plugins_install_from_directory", { directory });
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-13
@@ -1,5 +1,4 @@
|
|||||||
import { Channel, invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { emit } from "@tauri-apps/api/event";
|
|
||||||
import type { WatchResult } from "@yaakapp-internal/tauri-client";
|
import type { WatchResult } from "@yaakapp-internal/tauri-client";
|
||||||
import { SyncOp } from "./bindings/gen_sync";
|
import { SyncOp } from "./bindings/gen_sync";
|
||||||
import { WatchEvent } from "./bindings/gen_watch";
|
import { WatchEvent } from "./bindings/gen_watch";
|
||||||
@@ -7,18 +6,18 @@ import { WatchEvent } from "./bindings/gen_watch";
|
|||||||
export * from "./bindings/gen_models";
|
export * from "./bindings/gen_models";
|
||||||
|
|
||||||
export async function calculateSync(workspaceId: string, syncDir: string) {
|
export async function calculateSync(workspaceId: string, syncDir: string) {
|
||||||
return invoke<SyncOp[]>("cmd_sync_calculate", {
|
return platform.rpc<SyncOp[]>("cmd_sync_calculate", {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
syncDir,
|
syncDir,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function calculateSyncFsOnly(dir: string) {
|
export async function calculateSyncFsOnly(dir: string) {
|
||||||
return invoke<SyncOp[]>("cmd_sync_calculate_fs", { dir });
|
return platform.rpc<SyncOp[]>("cmd_sync_calculate_fs", { dir });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function applySync(workspaceId: string, syncDir: string, syncOps: SyncOp[]) {
|
export async function applySync(workspaceId: string, syncDir: string, syncOps: SyncOp[]) {
|
||||||
return invoke<void>("cmd_sync_apply", {
|
return platform.rpc<void>("cmd_sync_apply", {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
syncDir,
|
syncDir,
|
||||||
syncOps: syncOps,
|
syncOps: syncOps,
|
||||||
@@ -31,13 +30,11 @@ export function watchWorkspaceFiles(
|
|||||||
callback: (e: WatchEvent) => void,
|
callback: (e: WatchEvent) => void,
|
||||||
) {
|
) {
|
||||||
console.log("Watching workspace files", workspaceId, syncDir);
|
console.log("Watching workspace files", workspaceId, syncDir);
|
||||||
const channel = new Channel<WatchEvent>();
|
const unlistenPromise = platform.rpcStream<WatchResult, WatchEvent>(
|
||||||
channel.onmessage = callback;
|
"cmd_sync_watch",
|
||||||
const unlistenPromise = invoke<WatchResult>("cmd_sync_watch", {
|
{ workspaceId, syncDir },
|
||||||
workspaceId,
|
callback,
|
||||||
syncDir,
|
);
|
||||||
channel,
|
|
||||||
});
|
|
||||||
|
|
||||||
void unlistenPromise.then(({ unlistenEvent }) => {
|
void unlistenPromise.then(({ unlistenEvent }) => {
|
||||||
addWatchKey(unlistenEvent);
|
addWatchKey(unlistenEvent);
|
||||||
@@ -53,7 +50,7 @@ export function watchWorkspaceFiles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function unlistenToWatcher(unlistenEvent: string) {
|
function unlistenToWatcher(unlistenEvent: string) {
|
||||||
void emit(unlistenEvent).then(() => {
|
void platform.emit(unlistenEvent).then(() => {
|
||||||
removeWatchKey(unlistenEvent);
|
removeWatchKey(unlistenEvent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { WebsocketConnection } from "@yaakapp-internal/models";
|
import { WebsocketConnection } from "@yaakapp-internal/models";
|
||||||
|
|
||||||
export function deleteWebsocketConnections(requestId: string) {
|
export function deleteWebsocketConnections(requestId: string) {
|
||||||
return invoke("cmd_ws_delete_connections", {
|
return platform.rpc("cmd_ws_delete_connections", {
|
||||||
requestId,
|
requestId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ export function connectWebsocket({
|
|||||||
environmentId: string | null;
|
environmentId: string | null;
|
||||||
cookieJarId: string | null;
|
cookieJarId: string | null;
|
||||||
}) {
|
}) {
|
||||||
return invoke("cmd_ws_connect", {
|
return platform.rpc("cmd_ws_connect", {
|
||||||
requestId,
|
requestId,
|
||||||
environmentId,
|
environmentId,
|
||||||
cookieJarId,
|
cookieJarId,
|
||||||
@@ -24,7 +24,7 @@ export function connectWebsocket({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function closeWebsocket({ connectionId }: { connectionId: string }) {
|
export function closeWebsocket({ connectionId }: { connectionId: string }) {
|
||||||
return invoke("cmd_ws_close", {
|
return platform.rpc("cmd_ws_close", {
|
||||||
connectionId,
|
connectionId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ export function sendWebsocket({
|
|||||||
connectionId: string;
|
connectionId: string;
|
||||||
environmentId: string | null;
|
environmentId: string | null;
|
||||||
}) {
|
}) {
|
||||||
return invoke("cmd_ws_send", {
|
return platform.rpc("cmd_ws_send", {
|
||||||
connectionId,
|
connectionId,
|
||||||
environmentId,
|
environmentId,
|
||||||
});
|
});
|
||||||
|
|||||||
Generated
+17
@@ -13,6 +13,7 @@
|
|||||||
"packages/tailwind-config",
|
"packages/tailwind-config",
|
||||||
"packages/model-store",
|
"packages/model-store",
|
||||||
"packages/common-lib",
|
"packages/common-lib",
|
||||||
|
"packages/platform",
|
||||||
"packages/plugin-runtime",
|
"packages/plugin-runtime",
|
||||||
"packages/plugin-runtime-types",
|
"packages/plugin-runtime-types",
|
||||||
"plugins-external/mcp-server",
|
"plugins-external/mcp-server",
|
||||||
@@ -5736,6 +5737,10 @@
|
|||||||
"resolved": "crates/yaak-models",
|
"resolved": "crates/yaak-models",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@yaakapp-internal/platform": {
|
||||||
|
"resolved": "packages/platform",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@yaakapp-internal/plugin-runtime": {
|
"node_modules/@yaakapp-internal/plugin-runtime": {
|
||||||
"resolved": "packages/plugin-runtime",
|
"resolved": "packages/plugin-runtime",
|
||||||
"link": true
|
"link": true
|
||||||
@@ -17175,6 +17180,18 @@
|
|||||||
"jotai": "^2.18.0"
|
"jotai": "^2.18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/platform": {
|
||||||
|
"name": "@yaakapp-internal/platform",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2.11.0",
|
||||||
|
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
|
||||||
|
"@tauri-apps/plugin-dialog": "^2.7.1",
|
||||||
|
"@tauri-apps/plugin-fs": "^2.5.1",
|
||||||
|
"@tauri-apps/plugin-opener": "^2.5.4",
|
||||||
|
"@tauri-apps/plugin-os": "^2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"packages/plugin-runtime": {
|
"packages/plugin-runtime": {
|
||||||
"name": "@yaakapp-internal/plugin-runtime",
|
"name": "@yaakapp-internal/plugin-runtime",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"packages/tailwind-config",
|
"packages/tailwind-config",
|
||||||
"packages/model-store",
|
"packages/model-store",
|
||||||
"packages/common-lib",
|
"packages/common-lib",
|
||||||
|
"packages/platform",
|
||||||
"packages/plugin-runtime",
|
"packages/plugin-runtime",
|
||||||
"packages/plugin-runtime-types",
|
"packages/plugin-runtime-types",
|
||||||
"plugins-external/mcp-server",
|
"plugins-external/mcp-server",
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "@yaakapp-internal/platform",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"types": "src/index.ts",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2.11.0",
|
||||||
|
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
|
||||||
|
"@tauri-apps/plugin-dialog": "^2.7.1",
|
||||||
|
"@tauri-apps/plugin-fs": "^2.5.1",
|
||||||
|
"@tauri-apps/plugin-opener": "^2.5.4",
|
||||||
|
"@tauri-apps/plugin-os": "^2.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { platform } from "./registry";
|
||||||
|
import type { CapabilityName } from "./types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current host supports a feature.
|
||||||
|
*
|
||||||
|
* A hook so components can gate on it directly, and so this can become reactive
|
||||||
|
* later — a browser tab gains and loses capabilities when the local bridge comes
|
||||||
|
* and goes. Capabilities are fixed for the life of the desktop app, so today it
|
||||||
|
* is a plain read.
|
||||||
|
*/
|
||||||
|
export function useCapability(name: CapabilityName): boolean {
|
||||||
|
return platform.capabilities[name];
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { setPlatform } from "./registry";
|
||||||
|
import { createTauriPlatform } from "./tauri";
|
||||||
|
|
||||||
|
// Desktop is the only host today, so it is installed unconditionally and
|
||||||
|
// synchronously — several modules call commands while the module graph is still
|
||||||
|
// evaluating, so there is no later moment to do this in.
|
||||||
|
//
|
||||||
|
// This line is the swap point. A browser build selects its own host here, and
|
||||||
|
// because nothing else in the app imports a host directly, that is the whole
|
||||||
|
// change.
|
||||||
|
setPlatform(createTauriPlatform());
|
||||||
|
|
||||||
|
export * from "./capabilities";
|
||||||
|
export { platform, setPlatform } from "./registry";
|
||||||
|
export * from "./types";
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import type { Platform } from "./types";
|
||||||
|
|
||||||
|
let installed: Platform | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install the host implementation. Called once at import time by this package's
|
||||||
|
* entry point, before any consumer's module body runs.
|
||||||
|
*
|
||||||
|
* It stays swappable at runtime because a browser build has to choose between
|
||||||
|
* talking to a local bridge and running in-page, and it can only find out which
|
||||||
|
* after it has tried to reach the bridge.
|
||||||
|
*/
|
||||||
|
export function setPlatform(next: Platform): void {
|
||||||
|
installed = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function host(): Platform {
|
||||||
|
if (installed == null) {
|
||||||
|
throw new Error("No platform installed. Import @yaakapp-internal/platform before using it.");
|
||||||
|
}
|
||||||
|
return installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The host, for everyone else to use.
|
||||||
|
*
|
||||||
|
* This is a fixed object that forwards to whatever is installed, so modules can
|
||||||
|
* import it at any time — including at module scope, which several boot-time
|
||||||
|
* modules do — without capturing a stale implementation.
|
||||||
|
*
|
||||||
|
* A host that has to connect before it can work (a WebSocket to the bridge)
|
||||||
|
* buffers inside its own `rpc`; nothing here waits on a connection, because the
|
||||||
|
* app's boot sequence calls commands while the module graph is still evaluating
|
||||||
|
* and cannot be made to wait.
|
||||||
|
*/
|
||||||
|
export const platform: Platform = {
|
||||||
|
get capabilities() {
|
||||||
|
return host().capabilities;
|
||||||
|
},
|
||||||
|
get window() {
|
||||||
|
return host().window;
|
||||||
|
},
|
||||||
|
get clipboard() {
|
||||||
|
return host().clipboard;
|
||||||
|
},
|
||||||
|
get dialog() {
|
||||||
|
return host().dialog;
|
||||||
|
},
|
||||||
|
get files() {
|
||||||
|
return host().files;
|
||||||
|
},
|
||||||
|
rpc: (cmd, payload) => host().rpc(cmd, payload),
|
||||||
|
rpcStream: (cmd, payload, onMessage) => host().rpcStream(cmd, payload, onMessage),
|
||||||
|
listen: (event, callback) => host().listen(event, callback),
|
||||||
|
emit: (event, payload) => host().emit(event, payload),
|
||||||
|
openUrl: (url) => host().openUrl(url),
|
||||||
|
revealItemInDir: (path) => host().revealItemInDir(path),
|
||||||
|
osType: () => host().osType(),
|
||||||
|
appIdentifier: () => host().appIdentifier(),
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user