Unverified 提交 5d2e87fc authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

IPC Contracts (#2276)

<!-- CURSOR_SUMMARY --> > [!NOTE] > Modernizes IPC usage app-wide to new typed, namespaced contracts. > > - Refactors calls from `IpcClient` to `ipc.*` namespaces (system, app, chat, github, git, version, languageModel, neon, visualEditing, context, misc, capacitor, template, events, upgrade, agent, proposal) > - Updates type imports from `"@/ipc/ipc_types"` to `"@/ipc/types"` (e.g., `ListedApp`, security types) and adapts optional fields > - Aligns method signatures to object params and new return shapes (e.g., `getAppVersion` → `{ version }`); replaces various URL opens/restarts/screenshots with `ipc.system.*` > - Moves custom theme and theme generation ops to `ipc.template.*`; adds maxOutputTokens in custom model dialogs; adjusts provider/model management APIs > - Switches GitHub/Vercel/Neon/Supabase/Capacitor connectors and branching flows to new IPC endpoints; updates event subscriptions for GitHub device flow > - Normalizes logging/preview interactions and visual editing apply/analyze via `ipc.misc`/`ipc.visualEditing` > > Potential follow-ups: verify all parameter objects and event handlers, and update any remaining legacy imports. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 7c574fe53296ba5c9a16d6b69d1008d06490534e. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adopted the new typed IPC contracts across the app, replacing IpcClient with namespaced ipc methods. Restored app output streaming via events, added maxOutputTokens for custom model flows, migrated custom theme operations to ipc.template, and fixed chat stream cancellation lifecycle. - **Refactors** - Replaced IpcClient usage with ipc.* namespaces (system, app, chat, github, git, version, languageModel, neon, visualEditing, context, prompt, mcp, misc, capacitor, template, events, upgrade, agent, proposal). - Moved type imports from "@/ipc/ipc_types" to "@/ipc/types"; adjusted renamed types and paths (e.g., ListedApp under "@/ipc/types/app", security types under "@/ipc/types/security"). - Updated method signatures to use object params and new return shapes (e.g., getAppVersion returns { version }). - Subscribed to ipc.events.misc.onAppOutput in useRunApp to process app output, restore preview URL updates, and handle HMR after API changes. - Added missing maxOutputTokens in create/edit custom model dialogs; small UI and hooks changes to align with new contracts. - Fixed DeepLinkData typing by re-exporting a discriminated union for correct type narrowing. - Migrated custom themes and theme generation to ipc.template.*; added CustomTheme and theme generation types. - Moved methods to correct namespaces (e.g., takeScreenshot/restartDyad → system; checkProblems → misc). - Emit chat:stream:end on stream cancellation for consistent renderer cleanup. - **Migration** - Replace any remaining IpcClient references with ipc.*. - Update type imports to "@/ipc/types" and adjust for changed/optional fields (e.g., App lists use ListedApp; Collaborator.permissions and VercelProject.framework may be optional). - Verify callers for new parameter objects and result shapes; replace runApp/restartApp callbacks with ipc.events.misc.onAppOutput subscriptions. <sup>Written for commit 7c574fe53296ba5c9a16d6b69d1008d06490534e. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2276"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> --------- Co-authored-by: 's avatarClaude Opus 4.5 <noreply@anthropic.com>
上级 e9a079fb
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { createProblemFixPrompt } from "../shared/problem_prompt"; import { createProblemFixPrompt } from "../shared/problem_prompt";
import type { ProblemReport } from "../ipc/ipc_types"; import type { ProblemReport } from "@/ipc/types";
const snippet = `SNIPPET`; const snippet = `SNIPPET`;
......
...@@ -12,9 +12,9 @@ import { useDeepLink } from "@/contexts/DeepLinkContext"; ...@@ -12,9 +12,9 @@ import { useDeepLink } from "@/contexts/DeepLinkContext";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { DyadProSuccessDialog } from "@/components/DyadProSuccessDialog"; import { DyadProSuccessDialog } from "@/components/DyadProSuccessDialog";
import { useTheme } from "@/contexts/ThemeContext"; import { useTheme } from "@/contexts/ThemeContext";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo"; import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
import { UserBudgetInfo } from "@/ipc/ipc_types"; import type { UserBudgetInfo } from "@/ipc/types";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
...@@ -35,7 +35,7 @@ export const TitleBar = () => { ...@@ -35,7 +35,7 @@ export const TitleBar = () => {
// Check if we're running on Windows // Check if we're running on Windows
const checkPlatform = async () => { const checkPlatform = async () => {
try { try {
const platform = await IpcClient.getInstance().getSystemPlatform(); const platform = await ipc.system.getSystemPlatform();
setShowWindowControls(platform !== "darwin"); setShowWindowControls(platform !== "darwin");
} catch (error) { } catch (error) {
console.error("Failed to get platform info:", error); console.error("Failed to get platform info:", error);
...@@ -115,18 +115,17 @@ export const TitleBar = () => { ...@@ -115,18 +115,17 @@ export const TitleBar = () => {
function WindowsControls() { function WindowsControls() {
const { isDarkMode } = useTheme(); const { isDarkMode } = useTheme();
const ipcClient = IpcClient.getInstance();
const minimizeWindow = () => { const minimizeWindow = () => {
ipcClient.minimizeWindow(); ipc.system.minimizeWindow();
}; };
const maximizeWindow = () => { const maximizeWindow = () => {
ipcClient.maximizeWindow(); ipc.system.maximizeWindow();
}; };
const closeWindow = () => { const closeWindow = () => {
ipcClient.closeWindow(); ipc.system.closeWindow();
}; };
return ( return (
...@@ -225,7 +224,11 @@ export function DyadProButton({ ...@@ -225,7 +224,11 @@ export function DyadProButton({
); );
} }
export function AICreditStatus({ userBudget }: { userBudget: UserBudgetInfo }) { export function AICreditStatus({
userBudget,
}: {
userBudget: NonNullable<UserBudgetInfo>;
}) {
const remaining = Math.round( const remaining = Math.round(
userBudget.totalCredits - userBudget.usedCredits, userBudget.totalCredits - userBudget.usedCredits,
); );
......
import { atom } from "jotai"; import { atom } from "jotai";
import type { App, Version, ConsoleEntry } from "@/ipc/ipc_types"; import type { App, Version, ConsoleEntry } from "@/ipc/types";
import type { ListedApp } from "@/ipc/types/app";
import type { UserSettings } from "@/lib/schemas"; import type { UserSettings } from "@/lib/schemas";
export const currentAppAtom = atom<App | null>(null); export const currentAppAtom = atom<App | null>(null);
export const selectedAppIdAtom = atom<number | null>(null); export const selectedAppIdAtom = atom<number | null>(null);
export const appsListAtom = atom<App[]>([]); export const appsListAtom = atom<ListedApp[]>([]);
export const versionsListAtom = atom<Version[]>([]); export const versionsListAtom = atom<Version[]>([]);
export const previewModeAtom = atom< export const previewModeAtom = atom<
"preview" | "code" | "problems" | "configure" | "publish" | "security" "preview" | "code" | "problems" | "configure" | "publish" | "security"
......
import type { FileAttachment, Message, AgentTodo } from "@/ipc/ipc_types"; import type { FileAttachment, Message, AgentTodo } from "@/ipc/types";
import { atom } from "jotai"; import { atom } from "jotai";
// Per-chat atoms implemented with maps keyed by chatId // Per-chat atoms implemented with maps keyed by chatId
......
import { atom } from "jotai"; import { atom } from "jotai";
import { type LocalModel } from "@/ipc/ipc_types"; import { type LocalModel } from "@/ipc/types";
export const localModelsAtom = atom<LocalModel[]>([]); export const localModelsAtom = atom<LocalModel[]>([]);
export const localModelsLoadingAtom = atom<boolean>(false); export const localModelsLoadingAtom = atom<boolean>(false);
......
import { ComponentSelection, VisualEditingChange } from "@/ipc/ipc_types"; import { ComponentSelection, VisualEditingChange } from "@/ipc/types";
import { atom } from "jotai"; import { atom } from "jotai";
export const selectedComponentsPreviewAtom = atom<ComponentSelection[]>([]); export const selectedComponentsPreviewAtom = atom<ComponentSelection[]>([]);
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { getAppPort } from "../../shared/ports"; import { getAppPort } from "../../shared/ports";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
...@@ -11,13 +11,13 @@ export async function neonTemplateHook({ ...@@ -11,13 +11,13 @@ export async function neonTemplateHook({
appName: string; appName: string;
}) { }) {
console.log("Creating Neon project"); console.log("Creating Neon project");
const neonProject = await IpcClient.getInstance().createNeonProject({ const neonProject = await ipc.neon.createProject({
name: appName, name: appName,
appId: appId, appId: appId,
}); });
console.log("Neon project created", neonProject); console.log("Neon project created", neonProject);
await IpcClient.getInstance().setAppEnvVars({ await ipc.misc.setAppEnvVars({
appId: appId, appId: appId,
envVars: [ envVars: [
{ {
......
...@@ -5,15 +5,12 @@ import { Textarea } from "@/components/ui/textarea"; ...@@ -5,15 +5,12 @@ import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Loader2, Upload, X, Sparkles, Lock } from "lucide-react"; import { Loader2, Upload, X, Sparkles, Lock } from "lucide-react";
import { useGenerateThemePrompt } from "@/hooks/useCustomThemes"; import { useGenerateThemePrompt } from "@/hooks/useCustomThemes";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
import { toast } from "sonner"; import { toast } from "sonner";
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo"; import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
import { AiAccessBanner } from "./ProBanner"; import { AiAccessBanner } from "./ProBanner";
import type { import type { ThemeGenerationMode, ThemeGenerationModel } from "@/ipc/types";
ThemeGenerationMode,
ThemeGenerationModel,
} from "@/ipc/ipc_types";
// Image upload constants // Image upload constants
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB per image (raw file size) const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB per image (raw file size)
...@@ -79,7 +76,7 @@ export function AIGeneratorTab({ ...@@ -79,7 +76,7 @@ export function AIGeneratorTab({
const paths = images.map((img) => img.path); const paths = images.map((img) => img.path);
if (paths.length > 0) { if (paths.length > 0) {
try { try {
await IpcClient.getInstance().cleanupThemeImages({ paths }); await ipc.template.cleanupThemeImages({ paths });
} catch { } catch {
if (showErrors) { if (showErrors) {
showError("Failed to cleanup temporary image files"); showError("Failed to cleanup temporary image files");
...@@ -165,7 +162,7 @@ export function AIGeneratorTab({ ...@@ -165,7 +162,7 @@ export function AIGeneratorTab({
}); });
// Save to temp file via IPC // Save to temp file via IPC
const result = await IpcClient.getInstance().saveThemeImage({ const result = await ipc.template.saveThemeImage({
data: base64Data, data: base64Data,
filename: file.name, filename: file.name,
}); });
......
...@@ -2,9 +2,8 @@ import { Button } from "@/components/ui/button"; ...@@ -2,9 +2,8 @@ import { Button } from "@/components/ui/button";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Terminal } from "lucide-react"; import { Terminal } from "lucide-react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type AppUpgrade } from "@/ipc/types";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AppUpgrade } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export function AppUpgrades({ appId }: { appId: number | null }) { export function AppUpgrades({ appId }: { appId: number | null }) {
...@@ -20,7 +19,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) { ...@@ -20,7 +19,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) {
if (!appId) { if (!appId) {
return Promise.resolve([]); return Promise.resolve([]);
} }
return IpcClient.getInstance().getAppUpgrades({ appId }); return ipc.upgrade.getAppUpgrades({ appId });
}, },
enabled: !!appId, enabled: !!appId,
}); });
...@@ -35,7 +34,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) { ...@@ -35,7 +34,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) {
if (!appId) { if (!appId) {
throw new Error("appId is not set"); throw new Error("appId is not set");
} }
return IpcClient.getInstance().executeAppUpgrade({ return ipc.upgrade.executeAppUpgrade({
appId, appId,
upgradeId, upgradeId,
}); });
...@@ -132,7 +131,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) { ...@@ -132,7 +131,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) {
<a <a
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
upgrade.manualUpgradeUrl ?? "https://dyad.sh/docs", upgrade.manualUpgradeUrl ?? "https://dyad.sh/docs",
); );
}} }}
......
...@@ -2,7 +2,7 @@ import { useSettings } from "@/hooks/useSettings"; ...@@ -2,7 +2,7 @@ import { useSettings } from "@/hooks/useSettings";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { toast } from "sonner"; import { toast } from "sonner";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
export function AutoUpdateSwitch() { export function AutoUpdateSwitch() {
const { settings, updateSettings } = useSettings(); const { settings, updateSettings } = useSettings();
...@@ -24,7 +24,7 @@ export function AutoUpdateSwitch() { ...@@ -24,7 +24,7 @@ export function AutoUpdateSwitch() {
action: { action: {
label: "Restart Dyad", label: "Restart Dyad",
onClick: () => { onClick: () => {
IpcClient.getInstance().restartDyad(); ipc.system.restartDyad();
}, },
}, },
}); });
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { Dialog, DialogTitle } from "@radix-ui/react-dialog"; import { Dialog, DialogTitle } from "@radix-ui/react-dialog";
import { DialogContent, DialogHeader } from "./ui/dialog"; import { DialogContent, DialogHeader } from "./ui/dialog";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
...@@ -26,7 +26,7 @@ export function BugScreenshotDialog({ ...@@ -26,7 +26,7 @@ export function BugScreenshotDialog({
onClose(); onClose();
setTimeout(async () => { setTimeout(async () => {
try { try {
await IpcClient.getInstance().takeScreenshot(); await ipc.system.takeScreenshot();
setIsScreenshotSuccessOpen(true); setIsScreenshotSuccessOpen(true);
} catch (error) { } catch (error) {
setScreenshotError( setScreenshotError(
......
import { useState } from "react"; import { useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { showSuccess } from "@/lib/toast"; import { showSuccess } from "@/lib/toast";
import { import {
Smartphone, Smartphone,
...@@ -44,7 +44,7 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) { ...@@ -44,7 +44,7 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) {
// Check if Capacitor is installed // Check if Capacitor is installed
const { data: isCapacitor, isLoading } = useQuery({ const { data: isCapacitor, isLoading } = useQuery({
queryKey: queryKeys.appUpgrades.isCapacitor({ appId }), queryKey: queryKeys.appUpgrades.isCapacitor({ appId }),
queryFn: () => IpcClient.getInstance().isCapacitor({ appId }), queryFn: () => ipc.capacitor.isCapacitor({ appId }),
enabled: appId !== undefined && appId !== null, enabled: appId !== undefined && appId !== null,
}); });
...@@ -59,10 +59,10 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) { ...@@ -59,10 +59,10 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) {
mutationFn: async () => { mutationFn: async () => {
setIosStatus("syncing"); setIosStatus("syncing");
// First sync // First sync
await IpcClient.getInstance().syncCapacitor({ appId }); await ipc.capacitor.syncCapacitor({ appId });
setIosStatus("opening"); setIosStatus("opening");
// Then open iOS // Then open iOS
await IpcClient.getInstance().openIos({ appId }); await ipc.capacitor.openIos({ appId });
}, },
onSuccess: () => { onSuccess: () => {
setIosStatus("idle"); setIosStatus("idle");
...@@ -79,10 +79,10 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) { ...@@ -79,10 +79,10 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) {
mutationFn: async () => { mutationFn: async () => {
setAndroidStatus("syncing"); setAndroidStatus("syncing");
// First sync // First sync
await IpcClient.getInstance().syncCapacitor({ appId }); await ipc.capacitor.syncCapacitor({ appId });
setAndroidStatus("opening"); setAndroidStatus("opening");
// Then open Android // Then open Android
await IpcClient.getInstance().openAndroid({ appId }); await ipc.capacitor.openAndroid({ appId });
}, },
onSuccess: () => { onSuccess: () => {
setAndroidStatus("idle"); setAndroidStatus("idle");
...@@ -136,7 +136,7 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) { ...@@ -136,7 +136,7 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) {
size="sm" size="sm"
onClick={() => { onClick={() => {
// TODO: Add actual help link // TODO: Add actual help link
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://dyad.sh/docs/guides/mobile-app#troubleshooting", "https://dyad.sh/docs/guides/mobile-app#troubleshooting",
); );
}} }}
......
...@@ -7,7 +7,7 @@ import { useAtom } from "jotai"; ...@@ -7,7 +7,7 @@ import { useAtom } from "jotai";
import { selectedChatIdAtom } from "@/atoms/chatAtoms"; import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { dropdownOpenAtom } from "@/atoms/uiAtoms"; import { dropdownOpenAtom } from "@/atoms/uiAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { getEffectiveDefaultChatMode } from "@/lib/schemas"; import { getEffectiveDefaultChatMode } from "@/lib/schemas";
...@@ -88,7 +88,7 @@ export function ChatList({ show }: { show?: boolean }) { ...@@ -88,7 +88,7 @@ export function ChatList({ show }: { show?: boolean }) {
if (selectedAppId) { if (selectedAppId) {
try { try {
// Create a new chat with an empty title for now // Create a new chat with an empty title for now
const chatId = await IpcClient.getInstance().createChat(selectedAppId); const chatId = await ipc.chat.createChat(selectedAppId);
// Set the default chat mode for the new chat // Set the default chat mode for the new chat
if (settings) { if (settings) {
...@@ -117,7 +117,7 @@ export function ChatList({ show }: { show?: boolean }) { ...@@ -117,7 +117,7 @@ export function ChatList({ show }: { show?: boolean }) {
const handleDeleteChat = async (chatId: number) => { const handleDeleteChat = async (chatId: number) => {
try { try {
await IpcClient.getInstance().deleteChat(chatId); await ipc.chat.deleteChat(chatId);
showSuccess("Chat deleted successfully"); showSuccess("Chat deleted successfully");
// If the deleted chat was selected, navigate to home // If the deleted chat was selected, navigate to home
......
...@@ -5,7 +5,7 @@ import { ...@@ -5,7 +5,7 @@ import {
chatStreamCountByIdAtom, chatStreamCountByIdAtom,
isStreamingByIdAtom, isStreamingByIdAtom,
} from "../atoms/chatAtoms"; } from "../atoms/chatAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { ChatHeader } from "./chat/ChatHeader"; import { ChatHeader } from "./chat/ChatHeader";
import { MessagesList } from "./chat/MessagesList"; import { MessagesList } from "./chat/MessagesList";
...@@ -123,7 +123,7 @@ export function ChatPanel({ ...@@ -123,7 +123,7 @@ export function ChatPanel({
// no-op when no chat // no-op when no chat
return; return;
} }
const chat = await IpcClient.getInstance().getChat(chatId); const chat = await ipc.chat.getChat(chatId);
setMessagesById((prev) => { setMessagesById((prev) => {
const next = new Map(prev); const next = new Map(prev);
next.set(chatId, chat.messages); next.set(chatId, chat.messages);
......
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
...@@ -33,8 +33,6 @@ export function CreateCustomModelDialog({ ...@@ -33,8 +33,6 @@ export function CreateCustomModelDialog({
const [maxOutputTokens, setMaxOutputTokens] = useState<string>(""); const [maxOutputTokens, setMaxOutputTokens] = useState<string>("");
const [contextWindow, setContextWindow] = useState<string>(""); const [contextWindow, setContextWindow] = useState<string>("");
const ipcClient = IpcClient.getInstance();
const mutation = useMutation({ const mutation = useMutation({
mutationFn: async () => { mutationFn: async () => {
const params = { const params = {
...@@ -56,7 +54,14 @@ export function CreateCustomModelDialog({ ...@@ -56,7 +54,14 @@ export function CreateCustomModelDialog({
if (contextWindow && isNaN(params.contextWindow ?? NaN)) if (contextWindow && isNaN(params.contextWindow ?? NaN))
throw new Error("Context Window must be a valid number"); throw new Error("Context Window must be a valid number");
await ipcClient.createCustomLanguageModel(params); await ipc.languageModel.createCustomModel({
providerId: params.providerId,
displayName: params.displayName,
apiName: params.apiName,
description: params.description,
maxOutputTokens: params.maxOutputTokens,
contextWindow: params.contextWindow,
});
}, },
onSuccess: () => { onSuccess: () => {
showSuccess("Custom model created successfully!"); showSuccess("Custom model created successfully!");
......
...@@ -11,7 +11,7 @@ import { Input } from "@/components/ui/input"; ...@@ -11,7 +11,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { useCustomLanguageModelProvider } from "@/hooks/useCustomLanguageModelProvider"; import { useCustomLanguageModelProvider } from "@/hooks/useCustomLanguageModelProvider";
import type { LanguageModelProvider } from "@/ipc/ipc_types"; import type { LanguageModelProvider } from "@/ipc/types";
interface CreateCustomProviderDialogProps { interface CreateCustomProviderDialogProps {
isOpen: boolean; isOpen: boolean;
......
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
...@@ -47,8 +47,6 @@ export function EditCustomModelDialog({ ...@@ -47,8 +47,6 @@ export function EditCustomModelDialog({
const [contextWindow, setContextWindow] = useState<string>(""); const [contextWindow, setContextWindow] = useState<string>("");
const { settings, updateSettings } = useSettings(); const { settings, updateSettings } = useSettings();
const ipcClient = IpcClient.getInstance();
useEffect(() => { useEffect(() => {
if (model) { if (model) {
setApiName(model.apiName); setApiName(model.apiName);
...@@ -83,13 +81,20 @@ export function EditCustomModelDialog({ ...@@ -83,13 +81,20 @@ export function EditCustomModelDialog({
throw new Error("Context Window must be a valid number"); throw new Error("Context Window must be a valid number");
// First delete the old model // First delete the old model
await ipcClient.deleteCustomModel({ await ipc.languageModel.deleteModel({
providerId, providerId,
modelApiName: model.apiName, modelApiName: model.apiName,
}); });
// Then create the new model // Then create the new model
await ipcClient.createCustomLanguageModel(newParams); await ipc.languageModel.createCustomModel({
providerId: newParams.providerId,
displayName: newParams.displayName,
apiName: newParams.apiName,
description: newParams.description,
maxOutputTokens: newParams.maxOutputTokens,
contextWindow: newParams.contextWindow,
});
}, },
onSuccess: async () => { onSuccess: async () => {
if ( if (
......
...@@ -19,7 +19,7 @@ import { ...@@ -19,7 +19,7 @@ import {
import { Save, Edit2, Loader2 } from "lucide-react"; import { Save, Edit2, Loader2 } from "lucide-react";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
import { toast } from "sonner"; import { toast } from "sonner";
import type { CustomTheme } from "@/ipc/ipc_types"; import type { CustomTheme } from "@/ipc/types";
interface EditThemeDialogProps { interface EditThemeDialogProps {
theme: CustomTheme; theme: CustomTheme;
......
...@@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button"; ...@@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button";
import { LightbulbIcon } from "lucide-react"; import { LightbulbIcon } from "lucide-react";
import { ErrorComponentProps } from "@tanstack/react-router"; import { ErrorComponentProps } from "@tanstack/react-router";
import { usePostHog } from "posthog-js/react"; import { usePostHog } from "posthog-js/react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
export function ErrorBoundary({ error }: ErrorComponentProps) { export function ErrorBoundary({ error }: ErrorComponentProps) {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
...@@ -18,7 +18,7 @@ export function ErrorBoundary({ error }: ErrorComponentProps) { ...@@ -18,7 +18,7 @@ export function ErrorBoundary({ error }: ErrorComponentProps) {
setIsLoading(true); setIsLoading(true);
try { try {
// Get system debug info // Get system debug info
const debugInfo = await IpcClient.getInstance().getSystemDebugInfo(); const debugInfo = await ipc.system.getSystemDebugInfo();
// Create a formatted issue body with the debug info and error information // Create a formatted issue body with the debug info and error information
const issueBody = ` const issueBody = `
...@@ -62,13 +62,11 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"} ...@@ -62,13 +62,11 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=bug,filed-from-app,client-error&body=${encodedBody}`; const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=bug,filed-from-app,client-error&body=${encodedBody}`;
// Open the pre-filled GitHub issue page // Open the pre-filled GitHub issue page
await IpcClient.getInstance().openExternalUrl(githubIssueUrl); await ipc.system.openExternalUrl(githubIssueUrl);
} catch (err) { } catch (err) {
console.error("Failed to prepare bug report:", err); console.error("Failed to prepare bug report:", err);
// Fallback to opening the regular GitHub issue page // Fallback to opening the regular GitHub issue page
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://github.com/dyad-sh/dyad/issues/new");
"https://github.com/dyad-sh/dyad/issues/new",
);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
......
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { import {
ChevronsDownUp, ChevronsDownUp,
ChevronsUpDown, ChevronsUpDown,
...@@ -110,10 +110,8 @@ export function GithubBranchManager({ ...@@ -110,10 +110,8 @@ export function GithubBranchManager({
setIsLoading(true); setIsLoading(true);
try { try {
const [localResult, remoteBranches] = await Promise.all([ const [localResult, remoteBranches] = await Promise.all([
IpcClient.getInstance().listLocalGithubBranches(appId), ipc.github.listLocalBranches({ appId }),
IpcClient.getInstance() ipc.github.listRemoteBranches({ appId }).catch(() => []),
.listRemoteGithubBranches(appId)
.catch(() => []),
]); ]);
// Merge local and remote branches, removing duplicates // Merge local and remote branches, removing duplicates
...@@ -137,11 +135,11 @@ export function GithubBranchManager({ ...@@ -137,11 +135,11 @@ export function GithubBranchManager({
setIsCreating(true); setIsCreating(true);
const branchName = newBranchName.trim(); const branchName = newBranchName.trim();
try { try {
await IpcClient.getInstance().createGithubBranch( await ipc.github.createBranch({
appId, appId,
branchName, branch: branchName,
sourceBranch || undefined, from: sourceBranch || undefined,
); });
showSuccess(`Branch '${branchName}' created`); showSuccess(`Branch '${branchName}' created`);
setNewBranchName(""); setNewBranchName("");
setSourceBranch(""); // Reset source branch selection setSourceBranch(""); // Reset source branch selection
...@@ -162,7 +160,7 @@ export function GithubBranchManager({ ...@@ -162,7 +160,7 @@ export function GithubBranchManager({
setIsSwitching(true); setIsSwitching(true);
try { try {
const switchBranch = async () => const switchBranch = async () =>
await IpcClient.getInstance().switchGithubBranch(appId, branch); await ipc.github.switchBranch({ appId, branch });
try { try {
await switchBranch(); await switchBranch();
...@@ -181,7 +179,7 @@ export function GithubBranchManager({ ...@@ -181,7 +179,7 @@ export function GithubBranchManager({
| undefined; | undefined;
if (!errorCode) { if (!errorCode) {
try { try {
const state = await IpcClient.getInstance().getGithubState(appId); const state = await ipc.github.getGitState({ appId });
if (state.rebaseInProgress) inferredCode = "REBASE_IN_PROGRESS"; if (state.rebaseInProgress) inferredCode = "REBASE_IN_PROGRESS";
else if (state.mergeInProgress) inferredCode = "MERGE_IN_PROGRESS"; else if (state.mergeInProgress) inferredCode = "MERGE_IN_PROGRESS";
} catch { } catch {
...@@ -197,8 +195,7 @@ export function GithubBranchManager({ ...@@ -197,8 +195,7 @@ export function GithubBranchManager({
// Check if there are unresolved conflicts // Check if there are unresolved conflicts
let hasConflicts = false; let hasConflicts = false;
try { try {
const conflicts = const conflicts = await ipc.github.getConflicts({ appId });
await IpcClient.getInstance().getGithubMergeConflicts(appId);
hasConflicts = conflicts.length > 0; hasConflicts = conflicts.length > 0;
} catch { } catch {
// If we can't get conflicts, assume there might be conflicts to be safe // If we can't get conflicts, assume there might be conflicts to be safe
...@@ -219,8 +216,7 @@ export function GithubBranchManager({ ...@@ -219,8 +216,7 @@ export function GithubBranchManager({
// Check if there are unresolved conflicts // Check if there are unresolved conflicts
let hasConflicts = false; let hasConflicts = false;
try { try {
const conflicts = const conflicts = await ipc.github.getConflicts({ appId });
await IpcClient.getInstance().getGithubMergeConflicts(appId);
hasConflicts = conflicts.length > 0; hasConflicts = conflicts.length > 0;
} catch { } catch {
// If we can't get conflicts, assume there might be conflicts to be safe // If we can't get conflicts, assume there might be conflicts to be safe
...@@ -255,14 +251,14 @@ export function GithubBranchManager({ ...@@ -255,14 +251,14 @@ export function GithubBranchManager({
try { try {
// Abort the operation - both methods throw on error // Abort the operation - both methods throw on error
if (operationType === "rebase") { if (operationType === "rebase") {
await IpcClient.getInstance().abortGithubRebase(appId); await ipc.github.rebaseAbort({ appId });
} else { } else {
await IpcClient.getInstance().abortGithubMerge(appId); await ipc.github.mergeAbort({ appId });
} }
// Now switch to the target branch // Now switch to the target branch
try { try {
await IpcClient.getInstance().switchGithubBranch(appId, targetBranch); await ipc.github.switchBranch({ appId, branch: targetBranch });
showSuccess( showSuccess(
`Aborted ongoing ${operationType} and switched to branch '${targetBranch}'`, `Aborted ongoing ${operationType} and switched to branch '${targetBranch}'`,
); );
...@@ -291,7 +287,7 @@ export function GithubBranchManager({ ...@@ -291,7 +287,7 @@ export function GithubBranchManager({
setIsDeleting(true); setIsDeleting(true);
try { try {
await IpcClient.getInstance().deleteGithubBranch(appId, branchToDelete); await ipc.github.deleteBranch({ appId, branch: branchToDelete });
showSuccess(`Branch '${branchToDelete}' deleted`); showSuccess(`Branch '${branchToDelete}' deleted`);
setBranchToDelete(null); setBranchToDelete(null);
await loadBranches(); await loadBranches();
...@@ -307,11 +303,11 @@ export function GithubBranchManager({ ...@@ -307,11 +303,11 @@ export function GithubBranchManager({
setIsRenaming(true); setIsRenaming(true);
try { try {
const trimmedNewName = renameBranchName.trim(); const trimmedNewName = renameBranchName.trim();
await IpcClient.getInstance().renameGithubBranch( await ipc.github.renameBranch({
appId, appId,
branchToRename, oldBranch: branchToRename,
trimmedNewName, newBranch: trimmedNewName,
); });
showSuccess(`Renamed '${branchToRename}' to '${trimmedNewName}'`); showSuccess(`Renamed '${branchToRename}' to '${trimmedNewName}'`);
setBranchToRename(null); setBranchToRename(null);
setRenameBranchName(""); setRenameBranchName("");
...@@ -328,7 +324,7 @@ export function GithubBranchManager({ ...@@ -328,7 +324,7 @@ export function GithubBranchManager({
setIsMerging(true); setIsMerging(true);
setConflicts([]); // Clear conflicts when starting a new merge operation setConflicts([]); // Clear conflicts when starting a new merge operation
try { try {
await IpcClient.getInstance().mergeGithubBranch(appId, branchToMerge); await ipc.github.mergeBranch({ appId, branch: branchToMerge });
showSuccess(`Merged '${branchToMerge}' into '${currentBranch}'`); showSuccess(`Merged '${branchToMerge}' into '${currentBranch}'`);
setConflicts([]); // Clear conflicts on successful merge setConflicts([]); // Clear conflicts on successful merge
setBranchToMerge(null); setBranchToMerge(null);
...@@ -343,8 +339,7 @@ export function GithubBranchManager({ ...@@ -343,8 +339,7 @@ export function GithubBranchManager({
showInfo("Merge conflict detected. Please resolve them in the editor."); showInfo("Merge conflict detected. Please resolve them in the editor.");
// Show conflicts dialog // Show conflicts dialog
try { try {
const conflicts = const conflicts = await ipc.github.getConflicts({ appId });
await IpcClient.getInstance().getGithubMergeConflicts(appId);
if (conflicts.length > 0) { if (conflicts.length > 0) {
setConflicts(conflicts); setConflicts(conflicts);
......
...@@ -9,7 +9,7 @@ import { ...@@ -9,7 +9,7 @@ import {
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { SimpleAvatar } from "@/components/ui/SimpleAvatar"; import { SimpleAvatar } from "@/components/ui/SimpleAvatar";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { import {
Trash2, Trash2,
UserPlus, UserPlus,
...@@ -32,7 +32,7 @@ import { ...@@ -32,7 +32,7 @@ import {
interface Collaborator { interface Collaborator {
login: string; login: string;
avatar_url: string; avatar_url: string;
permissions: { permissions?: {
admin: boolean; admin: boolean;
push: boolean; push: boolean;
pull: boolean; pull: boolean;
...@@ -56,7 +56,7 @@ export function GithubCollaboratorManager({ appId }: CollaboratorManagerProps) { ...@@ -56,7 +56,7 @@ export function GithubCollaboratorManager({ appId }: CollaboratorManagerProps) {
const loadCollaborators = useCallback(async () => { const loadCollaborators = useCallback(async () => {
setIsLoading(true); setIsLoading(true);
try { try {
const collabs = await IpcClient.getInstance().listCollaborators(appId); const collabs = await ipc.github.listCollaborators({ appId });
setCollaborators(collabs); setCollaborators(collabs);
} catch (error: any) { } catch (error: any) {
console.error("Failed to load collaborators:", error); console.error("Failed to load collaborators:", error);
...@@ -78,7 +78,7 @@ export function GithubCollaboratorManager({ appId }: CollaboratorManagerProps) { ...@@ -78,7 +78,7 @@ export function GithubCollaboratorManager({ appId }: CollaboratorManagerProps) {
setIsInviting(true); setIsInviting(true);
try { try {
await IpcClient.getInstance().inviteCollaborator(appId, trimmedUsername); await ipc.github.inviteCollaborator({ appId, username: trimmedUsername });
showSuccess(`Invited ${trimmedUsername} to the project.`); showSuccess(`Invited ${trimmedUsername} to the project.`);
setInviteUsername(""); setInviteUsername("");
// Reload list (though they might be pending) // Reload list (though they might be pending)
...@@ -94,10 +94,10 @@ export function GithubCollaboratorManager({ appId }: CollaboratorManagerProps) { ...@@ -94,10 +94,10 @@ export function GithubCollaboratorManager({ appId }: CollaboratorManagerProps) {
if (!collaboratorToDelete) return; if (!collaboratorToDelete) return;
try { try {
await IpcClient.getInstance().removeCollaborator( await ipc.github.removeCollaborator({
appId, appId,
collaboratorToDelete, username: collaboratorToDelete,
); });
showSuccess(`Removed ${collaboratorToDelete} from the project.`); showSuccess(`Removed ${collaboratorToDelete} from the project.`);
loadCollaborators(); loadCollaborators();
} catch (error: any) { } catch (error: any) {
...@@ -193,9 +193,9 @@ export function GithubCollaboratorManager({ appId }: CollaboratorManagerProps) { ...@@ -193,9 +193,9 @@ export function GithubCollaboratorManager({ appId }: CollaboratorManagerProps) {
<div> <div>
<p className="text-sm font-medium">{collab.login}</p> <p className="text-sm font-medium">{collab.login}</p>
<p className="text-xs text-gray-500"> <p className="text-xs text-gray-500">
{collab.permissions.admin {collab.permissions?.admin
? "Admin" ? "Admin"
: collab.permissions.push : collab.permissions?.push
? "Editor" ? "Editor"
: "Viewer"} : "Viewer"}
</p> </p>
......
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { LoadingBlock, VanillaMarkdownParser } from "@/components/LoadingBlock"; import { LoadingBlock, VanillaMarkdownParser } from "@/components/LoadingBlock";
...@@ -75,59 +75,62 @@ export function HelpBotDialog({ isOpen, onClose }: HelpBotDialogProps) { ...@@ -75,59 +75,62 @@ export function HelpBotDialog({ isOpen, onClose }: HelpBotDialogProps) {
setInput(""); setInput("");
setStreaming(true); setStreaming(true);
IpcClient.getInstance().startHelpChat(sessionId, trimmed, { ipc.helpStream.start(
onChunk: (delta) => { { sessionId, message: trimmed },
// Buffer assistant content; UI will flush on interval for smoothness {
assistantBufferRef.current += delta; onChunk: (data) => {
}, // Buffer assistant content; UI will flush on interval for smoothness
onEnd: () => { assistantBufferRef.current += data.delta;
// Final flush then stop streaming },
setMessages((prev) => { onEnd: () => {
const next = [...prev]; // Final flush then stop streaming
const lastIdx = next.length - 1; setMessages((prev) => {
if (lastIdx >= 0 && next[lastIdx].role === "assistant") { const next = [...prev];
next[lastIdx] = { const lastIdx = next.length - 1;
...next[lastIdx], if (lastIdx >= 0 && next[lastIdx].role === "assistant") {
content: assistantBufferRef.current, next[lastIdx] = {
reasoning: reasoningBufferRef.current, ...next[lastIdx],
}; content: assistantBufferRef.current,
reasoning: reasoningBufferRef.current,
};
}
return next;
});
setStreaming(false);
if (flushTimerRef.current) {
window.clearInterval(flushTimerRef.current);
flushTimerRef.current = null;
} }
return next; },
}); onError: (data) => {
setStreaming(false); setError(data.error);
if (flushTimerRef.current) { setStreaming(false);
window.clearInterval(flushTimerRef.current);
flushTimerRef.current = null;
}
},
onError: (errorMessage: string) => {
setError(errorMessage);
setStreaming(false);
// Clear the flush timer // Clear the flush timer
if (flushTimerRef.current) { if (flushTimerRef.current) {
window.clearInterval(flushTimerRef.current); window.clearInterval(flushTimerRef.current);
flushTimerRef.current = null; flushTimerRef.current = null;
} }
// Clear the buffers // Clear the buffers
assistantBufferRef.current = ""; assistantBufferRef.current = "";
reasoningBufferRef.current = ""; reasoningBufferRef.current = "";
// Remove the empty assistant message that was added optimistically // Remove the empty assistant message that was added optimistically
setMessages((prev) => { setMessages((prev) => {
const next = [...prev]; const next = [...prev];
if ( if (
next.length > 0 && next.length > 0 &&
next[next.length - 1].role === "assistant" && next[next.length - 1].role === "assistant" &&
!next[next.length - 1].content !next[next.length - 1].content
) { ) {
next.pop(); next.pop();
} }
return next; return next;
}); });
},
}, },
}); );
// Start smooth flush interval // Start smooth flush interval
if (flushTimerRef.current) { if (flushTimerRef.current) {
......
...@@ -17,11 +17,11 @@ import { ...@@ -17,11 +17,11 @@ import {
FileIcon, FileIcon,
SparklesIcon, SparklesIcon,
} from "lucide-react"; } from "lucide-react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { selectedChatIdAtom } from "@/atoms/chatAtoms"; import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { ChatLogsData } from "@/ipc/ipc_types"; import { ChatLogsData } from "@/ipc/types";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
import { HelpBotDialog } from "./HelpBotDialog"; import { HelpBotDialog } from "./HelpBotDialog";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
...@@ -73,7 +73,7 @@ export function HelpDialog({ isOpen, onClose }: HelpDialogProps) { ...@@ -73,7 +73,7 @@ export function HelpDialog({ isOpen, onClose }: HelpDialogProps) {
setIsLoading(true); setIsLoading(true);
try { try {
// Get system debug info // Get system debug info
const debugInfo = await IpcClient.getInstance().getSystemDebugInfo(); const debugInfo = await ipc.system.getSystemDebugInfo();
// Create a formatted issue body with the debug info // Create a formatted issue body with the debug info
const issueBody = ` const issueBody = `
...@@ -112,13 +112,11 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"} ...@@ -112,13 +112,11 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=${labels}&body=${encodedBody}`; const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=${labels}&body=${encodedBody}`;
// Open the pre-filled GitHub issue page // Open the pre-filled GitHub issue page
IpcClient.getInstance().openExternalUrl(githubIssueUrl); ipc.system.openExternalUrl(githubIssueUrl);
} catch (error) { } catch (error) {
console.error("Failed to prepare bug report:", error); console.error("Failed to prepare bug report:", error);
// Fallback to opening the regular GitHub issue page // Fallback to opening the regular GitHub issue page
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://github.com/dyad-sh/dyad/issues/new");
"https://github.com/dyad-sh/dyad/issues/new",
);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
...@@ -133,8 +131,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"} ...@@ -133,8 +131,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
setIsUploading(true); setIsUploading(true);
try { try {
// Get chat logs (includes debug info, chat data, and codebase) // Get chat logs (includes debug info, chat data, and codebase)
const chatLogs = const chatLogs = await ipc.misc.getChatLogs(selectedChatId);
await IpcClient.getInstance().getChatLogs(selectedChatId);
// Store data for review and switch to review mode // Store data for review and switch to review mode
setChatLogsData(chatLogs); setChatLogsData(chatLogs);
...@@ -183,11 +180,11 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"} ...@@ -183,11 +180,11 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
const { uploadUrl, filename } = await response.json(); const { uploadUrl, filename } = await response.json();
await IpcClient.getInstance().uploadToSignedUrl( await ipc.system.uploadToSignedUrl({
uploadUrl, url: uploadUrl,
"application/json", contentType: "application/json",
chatLogsJson, data: chatLogsJson,
); });
// Extract session ID (filename without extension) // Extract session ID (filename without extension)
const sessionId = filename.replace(".json", ""); const sessionId = filename.replace(".json", "");
...@@ -233,7 +230,7 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"} ...@@ -233,7 +230,7 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"}
} }
const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=${labels}&body=${encodedBody}`; const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=${labels}&body=${encodedBody}`;
IpcClient.getInstance().openExternalUrl(githubIssueUrl); ipc.system.openExternalUrl(githubIssueUrl);
handleClose(); handleClose();
}; };
...@@ -403,9 +400,7 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"} ...@@ -403,9 +400,7 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"}
<Button <Button
variant="outline" variant="outline"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://www.dyad.sh/docs");
"https://www.dyad.sh/docs",
);
}} }}
className="w-full py-6 bg-(--background-lightest)" className="w-full py-6 bg-(--background-lightest)"
> >
......
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
DialogFooter, DialogFooter,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
import { Folder, X, Loader2, Info } from "lucide-react"; import { Folder, X, Loader2, Info } from "lucide-react";
...@@ -19,7 +19,7 @@ import { Checkbox } from "@/components/ui/checkbox"; ...@@ -19,7 +19,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@radix-ui/react-label"; import { Label } from "@radix-ui/react-label";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { useStreamChat } from "@/hooks/useStreamChat"; import { useStreamChat } from "@/hooks/useStreamChat";
import type { GithubRepository } from "@/ipc/ipc_types"; import type { GithubRepository } from "@/ipc/types";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
...@@ -90,7 +90,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -90,7 +90,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
const fetchRepos = async () => { const fetchRepos = async () => {
setLoading(true); setLoading(true);
try { try {
const fetchedRepos = await IpcClient.getInstance().listGithubRepos(); const fetchedRepos = await ipc.github.listRepos();
setRepos(fetchedRepos); setRepos(fetchedRepos);
} catch (err: unknown) { } catch (err: unknown) {
showError("Failed to fetch repositories.: " + (err as any).toString()); showError("Failed to fetch repositories.: " + (err as any).toString());
...@@ -105,7 +105,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -105,7 +105,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
setGithubAppName(repoName); setGithubAppName(repoName);
setIsCheckingGithubName(true); setIsCheckingGithubName(true);
try { try {
const result = await IpcClient.getInstance().checkAppName({ const result = await ipc.import.checkAppName({
appName: repoName, appName: repoName,
}); });
setGithubNameExists(result.exists); setGithubNameExists(result.exists);
...@@ -126,7 +126,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -126,7 +126,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
const match = extractRepoNameFromUrl(url); const match = extractRepoNameFromUrl(url);
const repoName = match ? match[2] : ""; const repoName = match ? match[2] : "";
const appName = githubAppName.trim() || repoName; const appName = githubAppName.trim() || repoName;
const result = await IpcClient.getInstance().cloneRepoFromUrl({ const result = await ipc.github.cloneRepoFromUrl({
url, url,
installCommand: installCommand.trim() || undefined, installCommand: installCommand.trim() || undefined,
startCommand: startCommand.trim() || undefined, startCommand: startCommand.trim() || undefined,
...@@ -139,7 +139,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -139,7 +139,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
} }
setSelectedAppId(result.app.id); setSelectedAppId(result.app.id);
showSuccess(`Successfully imported ${result.app.name}`); showSuccess(`Successfully imported ${result.app.name}`);
const chatId = await IpcClient.getInstance().createChat(result.app.id); const chatId = await ipc.chat.createChat(result.app.id);
navigate({ to: "/chat", search: { id: chatId } }); navigate({ to: "/chat", search: { id: chatId } });
if (!result.hasAiRules) { if (!result.hasAiRules) {
streamMessage({ streamMessage({
...@@ -160,7 +160,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -160,7 +160,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
try { try {
const appName = githubAppName.trim() || repo.name; const appName = githubAppName.trim() || repo.name;
const result = await IpcClient.getInstance().cloneRepoFromUrl({ const result = await ipc.github.cloneRepoFromUrl({
url: `https://github.com/${repo.full_name}.git`, url: `https://github.com/${repo.full_name}.git`,
installCommand: installCommand.trim() || undefined, installCommand: installCommand.trim() || undefined,
startCommand: startCommand.trim() || undefined, startCommand: startCommand.trim() || undefined,
...@@ -173,7 +173,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -173,7 +173,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
} }
setSelectedAppId(result.app.id); setSelectedAppId(result.app.id);
showSuccess(`Successfully imported ${result.app.name}`); showSuccess(`Successfully imported ${result.app.name}`);
const chatId = await IpcClient.getInstance().createChat(result.app.id); const chatId = await ipc.chat.createChat(result.app.id);
navigate({ to: "/chat", search: { id: chatId } }); navigate({ to: "/chat", search: { id: chatId } });
if (!result.hasAiRules) { if (!result.hasAiRules) {
streamMessage({ streamMessage({
...@@ -197,7 +197,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -197,7 +197,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
if (newName.trim()) { if (newName.trim()) {
setIsCheckingGithubName(true); setIsCheckingGithubName(true);
try { try {
const result = await IpcClient.getInstance().checkAppName({ const result = await ipc.import.checkAppName({
appName: newName, appName: newName,
}); });
setGithubNameExists(result.exists); setGithubNameExists(result.exists);
...@@ -218,7 +218,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -218,7 +218,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
}): Promise<void> => { }): Promise<void> => {
setIsCheckingName(true); setIsCheckingName(true);
try { try {
const result = await IpcClient.getInstance().checkAppName({ const result = await ipc.import.checkAppName({
appName: name, appName: name,
skipCopy, skipCopy,
}); });
...@@ -231,12 +231,12 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -231,12 +231,12 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
}; };
const selectFolderMutation = useMutation({ const selectFolderMutation = useMutation({
mutationFn: async () => { mutationFn: async () => {
const result = await IpcClient.getInstance().selectAppFolder(); const result = await ipc.system.selectAppFolder();
if (!result.path || !result.name) { if (!result.path || !result.name) {
// User cancelled the folder selection dialog // User cancelled the folder selection dialog
return null; return null;
} }
const aiRulesCheck = await IpcClient.getInstance().checkAiRules({ const aiRulesCheck = await ipc.import.checkAiRules({
path: result.path, path: result.path,
}); });
setHasAiRules(aiRulesCheck.exists); setHasAiRules(aiRulesCheck.exists);
...@@ -255,7 +255,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) { ...@@ -255,7 +255,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
const importAppMutation = useMutation({ const importAppMutation = useMutation({
mutationFn: async () => { mutationFn: async () => {
if (!selectedPath) throw new Error("No folder selected"); if (!selectedPath) throw new Error("No folder selected");
return IpcClient.getInstance().importApp({ return ipc.import.importApp({
path: selectedPath, path: selectedPath,
appName: customAppName, appName: customAppName,
installCommand: installCommand || undefined, installCommand: installCommand || undefined,
......
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
const customLink = ({ const customLink = ({
node: _node, node: _node,
...@@ -15,7 +15,7 @@ const customLink = ({ ...@@ -15,7 +15,7 @@ const customLink = ({
const url = props.href; const url = props.href;
if (url) { if (url) {
e.preventDefault(); e.preventDefault();
IpcClient.getInstance().openExternalUrl(url); ipc.system.openExternalUrl(url);
} }
}} }}
/> />
......
...@@ -21,7 +21,7 @@ import { useLocalModels } from "@/hooks/useLocalModels"; ...@@ -21,7 +21,7 @@ import { useLocalModels } from "@/hooks/useLocalModels";
import { useLocalLMSModels } from "@/hooks/useLMStudioModels"; import { useLocalLMSModels } from "@/hooks/useLMStudioModels";
import { useLanguageModelsByProviders } from "@/hooks/useLanguageModelsByProviders"; import { useLanguageModelsByProviders } from "@/hooks/useLanguageModelsByProviders";
import { LocalModel } from "@/ipc/ipc_types"; import { LocalModel } from "@/ipc/types";
import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders"; import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { PriceBadge } from "@/components/PriceBadge"; import { PriceBadge } from "@/components/PriceBadge";
......
import { useEffect } from "react"; import { useEffect } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { toast } from "sonner"; import { toast } from "sonner";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
...@@ -34,9 +34,7 @@ export function NeonConnector() { ...@@ -34,9 +34,7 @@ export function NeonConnector() {
<Button <Button
variant="outline" variant="outline"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://console.neon.tech/");
"https://console.neon.tech/",
);
}} }}
className="ml-2 px-2 py-1 h-8 mb-2" className="ml-2 px-2 py-1 h-8 mb-2"
style={{ display: "inline-flex", alignItems: "center" }} style={{ display: "inline-flex", alignItems: "center" }}
...@@ -67,9 +65,9 @@ export function NeonConnector() { ...@@ -67,9 +65,9 @@ export function NeonConnector() {
<div <div
onClick={async () => { onClick={async () => {
if (settings?.isTestMode) { if (settings?.isTestMode) {
await IpcClient.getInstance().fakeHandleNeonConnect(); await ipc.neon.fakeConnect();
} else { } else {
await IpcClient.getInstance().openExternalUrl( await ipc.system.openExternalUrl(
"https://oauth.dyad.sh/api/integrations/neon/login", "https://oauth.dyad.sh/api/integrations/neon/login",
); );
} }
......
...@@ -3,7 +3,7 @@ import { Label } from "@/components/ui/label"; ...@@ -3,7 +3,7 @@ import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { FolderOpen, RotateCcw, CheckCircle, AlertCircle } from "lucide-react"; import { FolderOpen, RotateCcw, CheckCircle, AlertCircle } from "lucide-react";
export function NodePathSelector() { export function NodePathSelector() {
...@@ -26,7 +26,7 @@ export function NodePathSelector() { ...@@ -26,7 +26,7 @@ export function NodePathSelector() {
const fetchSystemPath = async () => { const fetchSystemPath = async () => {
try { try {
const debugInfo = await IpcClient.getInstance().getSystemDebugInfo(); const debugInfo = await ipc.system.getSystemDebugInfo();
setSystemPath(debugInfo.nodePath || "System PATH (not available)"); setSystemPath(debugInfo.nodePath || "System PATH (not available)");
} catch (err) { } catch (err) {
console.error("Failed to fetch system path:", err); console.error("Failed to fetch system path:", err);
...@@ -43,7 +43,7 @@ export function NodePathSelector() { ...@@ -43,7 +43,7 @@ export function NodePathSelector() {
if (!settings) return; if (!settings) return;
setIsCheckingNode(true); setIsCheckingNode(true);
try { try {
const status = await IpcClient.getInstance().getNodejsStatus(); const status = await ipc.system.getNodejsStatus();
setNodeStatus({ setNodeStatus({
version: status.nodeVersion, version: status.nodeVersion,
isValid: !!status.nodeVersion, isValid: !!status.nodeVersion,
...@@ -59,12 +59,12 @@ export function NodePathSelector() { ...@@ -59,12 +59,12 @@ export function NodePathSelector() {
setIsSelectingPath(true); setIsSelectingPath(true);
try { try {
// Call the IPC method to select folder // Call the IPC method to select folder
const result = await IpcClient.getInstance().selectNodeFolder(); const result = await ipc.system.selectNodeFolder();
if (result.path) { if (result.path) {
// Save the custom path to settings // Save the custom path to settings
await updateSettings({ customNodePath: result.path }); await updateSettings({ customNodePath: result.path });
// Update the environment PATH // Update the environment PATH
await IpcClient.getInstance().reloadEnvPath(); await ipc.system.reloadEnvPath();
// Recheck Node.js status // Recheck Node.js status
await checkNodeStatus(); await checkNodeStatus();
showSuccess("Node.js path updated successfully"); showSuccess("Node.js path updated successfully");
...@@ -84,7 +84,7 @@ export function NodePathSelector() { ...@@ -84,7 +84,7 @@ export function NodePathSelector() {
// Clear the custom path // Clear the custom path
await updateSettings({ customNodePath: null }); await updateSettings({ customNodePath: null });
// Reload environment to use system PATH // Reload environment to use system PATH
await IpcClient.getInstance().reloadEnvPath(); await ipc.system.reloadEnvPath();
// Recheck Node.js status // Recheck Node.js status
await fetchSystemPath(); await fetchSystemPath();
await checkNodeStatus(); await checkNodeStatus();
......
import { useState } from "react"; import { useState } from "react";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { ExternalLink, Database, Loader2 } from "lucide-react"; import { ExternalLink, Database, Loader2 } from "lucide-react";
...@@ -17,8 +17,7 @@ export const PortalMigrate = ({ appId }: PortalMigrateProps) => { ...@@ -17,8 +17,7 @@ export const PortalMigrate = ({ appId }: PortalMigrateProps) => {
const migrateMutation = useMutation({ const migrateMutation = useMutation({
mutationFn: async () => { mutationFn: async () => {
const ipcClient = IpcClient.getInstance(); return ipc.misc.portalMigrateCreate({ appId });
return ipcClient.portalMigrateCreate({ appId });
}, },
onSuccess: (result) => { onSuccess: (result) => {
setOutput(result.output); setOutput(result.output);
...@@ -41,8 +40,7 @@ export const PortalMigrate = ({ appId }: PortalMigrateProps) => { ...@@ -41,8 +40,7 @@ export const PortalMigrate = ({ appId }: PortalMigrateProps) => {
}; };
const openDocs = () => { const openDocs = () => {
const ipcClient = IpcClient.getInstance(); ipc.system.openExternalUrl(
ipcClient.openExternalUrl(
"https://www.dyad.sh/docs/templates/portal#create-a-database-migration", "https://www.dyad.sh/docs/templates/portal#create-a-database-migration",
); );
}; };
......
...@@ -4,7 +4,7 @@ import openAiLogo from "../../assets/ai-logos/openai-logo.svg"; ...@@ -4,7 +4,7 @@ import openAiLogo from "../../assets/ai-logos/openai-logo.svg";
import googleLogo from "../../assets/ai-logos/google-logo.svg"; import googleLogo from "../../assets/ai-logos/google-logo.svg";
// @ts-ignore // @ts-ignore
import anthropicLogo from "../../assets/ai-logos/anthropic-logo.svg"; import anthropicLogo from "../../assets/ai-logos/anthropic-logo.svg";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useState } from "react"; import { useState } from "react";
import { ArrowUpRight, KeyRound, Wallet } from "lucide-react"; import { ArrowUpRight, KeyRound, Wallet } from "lucide-react";
...@@ -48,9 +48,7 @@ export function ManageDyadProButton({ className }: { className?: string }) { ...@@ -48,9 +48,7 @@ export function ManageDyadProButton({ className }: { className?: string }) {
className, className,
)} )}
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://academy.dyad.sh/subscription");
"https://academy.dyad.sh/subscription",
);
}} }}
> >
<Wallet aria-hidden="true" className="w-5 h-5" /> <Wallet aria-hidden="true" className="w-5 h-5" />
...@@ -67,9 +65,7 @@ export function SetupDyadProButton() { ...@@ -67,9 +65,7 @@ export function SetupDyadProButton() {
size="lg" size="lg"
className="cursor-pointer w-full mt-4 bg-(--background-lighter) text-primary" className="cursor-pointer w-full mt-4 bg-(--background-lighter) text-primary"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://academy.dyad.sh/settings");
"https://academy.dyad.sh/settings",
);
}} }}
> >
<KeyRound aria-hidden="true" /> <KeyRound aria-hidden="true" />
...@@ -83,7 +79,7 @@ export function AiAccessBanner() { ...@@ -83,7 +79,7 @@ export function AiAccessBanner() {
<div <div
className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-white via-indigo-50 to-sky-100 dark:from-indigo-700 dark:via-indigo-700 dark:to-indigo-900 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-black/5 dark:ring-white/10 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]" className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-white via-indigo-50 to-sky-100 dark:from-indigo-700 dark:via-indigo-700 dark:to-indigo-900 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-black/5 dark:ring-white/10 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-ai-access", "https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-ai-access",
); );
}} }}
...@@ -149,7 +145,7 @@ export function SmartContextBanner() { ...@@ -149,7 +145,7 @@ export function SmartContextBanner() {
<div <div
className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-emerald-50 via-emerald-100 to-emerald-200 dark:from-emerald-700 dark:via-emerald-700 dark:to-emerald-900 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-emerald-900/10 dark:ring-white/10 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]" className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-emerald-50 via-emerald-100 to-emerald-200 dark:from-emerald-700 dark:via-emerald-700 dark:to-emerald-900 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-emerald-900/10 dark:ring-white/10 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-smart-context", "https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-smart-context",
); );
}} }}
...@@ -190,7 +186,7 @@ export function TurboBanner() { ...@@ -190,7 +186,7 @@ export function TurboBanner() {
<div <div
className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-rose-50 via-rose-100 to-rose-200 dark:from-rose-800 dark:via-fuchsia-800 dark:to-rose-800 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-rose-900/10 dark:ring-white/5 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]" className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-rose-50 via-rose-100 to-rose-200 dark:from-rose-800 dark:via-fuchsia-800 dark:to-rose-800 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-rose-900/10 dark:ring-white/5 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-turbo", "https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-turbo",
); );
}} }}
......
...@@ -13,7 +13,7 @@ import { Switch } from "@/components/ui/switch"; ...@@ -13,7 +13,7 @@ import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Sparkles, Info } from "lucide-react"; import { Sparkles, Info } from "lucide-react";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { hasDyadProKey, type UserSettings } from "@/lib/schemas"; import { hasDyadProKey, type UserSettings } from "@/lib/schemas";
export function ProModeSelector() { export function ProModeSelector() {
...@@ -93,9 +93,7 @@ export function ProModeSelector() { ...@@ -93,9 +93,7 @@ export function ProModeSelector() {
<a <a
className="inline-flex items-center justify-center gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm font-medium text-primary shadow-sm transition-colors hover:bg-primary/20 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring cursor-pointer" className="inline-flex items-center justify-center gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm font-medium text-primary shadow-sm transition-colors hover:bg-primary/20 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring cursor-pointer"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://dyad.sh/pro#ai");
"https://dyad.sh/pro#ai",
);
}} }}
> >
Unlock Pro modes Unlock Pro modes
......
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
} from "@/components/ui/card"; } from "@/components/ui/card";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { providerSettingsRoute } from "@/routes/settings/providers/$provider"; import { providerSettingsRoute } from "@/routes/settings/providers/$provider";
import type { LanguageModelProvider } from "@/ipc/ipc_types"; import type { LanguageModelProvider } from "@/ipc/types";
import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders"; import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
import { useCustomLanguageModelProvider } from "@/hooks/useCustomLanguageModelProvider"; import { useCustomLanguageModelProvider } from "@/hooks/useCustomLanguageModelProvider";
......
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { toast } from "sonner"; import { toast } from "sonner";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import type { ReleaseChannel } from "@/lib/schemas"; import type { ReleaseChannel } from "@/lib/schemas";
export function ReleaseChannelSelector() { export function ReleaseChannelSelector() {
...@@ -27,7 +27,7 @@ export function ReleaseChannelSelector() { ...@@ -27,7 +27,7 @@ export function ReleaseChannelSelector() {
action: { action: {
label: "Download Stable", label: "Download Stable",
onClick: () => { onClick: () => {
IpcClient.getInstance().openExternalUrl("https://dyad.sh/download"); ipc.system.openExternalUrl("https://dyad.sh/download");
}, },
}, },
}); });
...@@ -38,7 +38,7 @@ export function ReleaseChannelSelector() { ...@@ -38,7 +38,7 @@ export function ReleaseChannelSelector() {
action: { action: {
label: "Restart Dyad", label: "Restart Dyad",
onClick: () => { onClick: () => {
IpcClient.getInstance().restartDyad(); ipc.system.restartDyad();
}, },
}, },
}); });
......
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
export function RuntimeModeSelector() { export function RuntimeModeSelector() {
const { settings, updateSettings } = useSettings(); const { settings, updateSettings } = useSettings();
...@@ -59,7 +59,7 @@ export function RuntimeModeSelector() { ...@@ -59,7 +59,7 @@ export function RuntimeModeSelector() {
type="button" type="button"
className="underline font-medium cursor-pointer" className="underline font-medium cursor-pointer"
onClick={() => onClick={() =>
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://www.docker.com/products/docker-desktop/", "https://www.docker.com/products/docker-desktop/",
) )
} }
......
...@@ -15,7 +15,7 @@ import { providerSettingsRoute } from "@/routes/settings/providers/$provider"; ...@@ -15,7 +15,7 @@ import { providerSettingsRoute } from "@/routes/settings/providers/$provider";
import SetupProviderCard from "@/components/SetupProviderCard"; import SetupProviderCard from "@/components/SetupProviderCard";
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, NodeSystemInfo } from "@/ipc/types";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
...@@ -24,7 +24,6 @@ import { ...@@ -24,7 +24,6 @@ import {
} from "@/components/ui/accordion"; } from "@/components/ui/accordion";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { NodeSystemInfo } from "@/ipc/ipc_types";
import { usePostHog } from "posthog-js/react"; import { usePostHog } from "posthog-js/react";
import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders"; import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo"; import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo";
...@@ -55,7 +54,7 @@ export function SetupBanner() { ...@@ -55,7 +54,7 @@ export function SetupBanner() {
const checkNode = useCallback(async () => { const checkNode = useCallback(async () => {
try { try {
setNodeCheckError(false); setNodeCheckError(false);
const status = await IpcClient.getInstance().getNodejsStatus(); const status = await ipc.system.getNodejsStatus();
setNodeSystemInfo(status); setNodeSystemInfo(status);
} catch (error) { } catch (error) {
console.error("Failed to check Node.js status:", error); console.error("Failed to check Node.js status:", error);
...@@ -71,10 +70,10 @@ export function SetupBanner() { ...@@ -71,10 +70,10 @@ export function SetupBanner() {
const handleManualNodeConfig = useCallback(async () => { const handleManualNodeConfig = useCallback(async () => {
setIsSelectingPath(true); setIsSelectingPath(true);
try { try {
const result = await IpcClient.getInstance().selectNodeFolder(); const result = await ipc.system.selectNodeFolder();
if (result.path) { if (result.path) {
await updateSettings({ customNodePath: result.path }); await updateSettings({ customNodePath: result.path });
await IpcClient.getInstance().reloadEnvPath(); await ipc.system.reloadEnvPath();
await checkNode(); await checkNode();
setNodeInstallStep("finished-checking"); setNodeInstallStep("finished-checking");
setShowManualConfig(false); setShowManualConfig(false);
...@@ -116,7 +115,7 @@ export function SetupBanner() { ...@@ -116,7 +115,7 @@ export function SetupBanner() {
}; };
const handleDyadProSetupClick = () => { const handleDyadProSetupClick = () => {
posthog.capture("setup-flow:ai-provider-setup:dyad:click"); posthog.capture("setup-flow:ai-provider-setup:dyad:click");
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=setup-banner", "https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=setup-banner",
); );
}; };
...@@ -129,13 +128,13 @@ export function SetupBanner() { ...@@ -129,13 +128,13 @@ export function SetupBanner() {
const handleNodeInstallClick = useCallback(async () => { const handleNodeInstallClick = useCallback(async () => {
posthog.capture("setup-flow:start-node-install-click"); posthog.capture("setup-flow:start-node-install-click");
setNodeInstallStep("waiting-for-continue"); setNodeInstallStep("waiting-for-continue");
IpcClient.getInstance().openExternalUrl(nodeSystemInfo!.nodeDownloadUrl); ipc.system.openExternalUrl(nodeSystemInfo!.nodeDownloadUrl);
}, [nodeSystemInfo, setNodeInstallStep]); }, [nodeSystemInfo, setNodeInstallStep]);
const finishNodeInstall = useCallback(async () => { const finishNodeInstall = useCallback(async () => {
posthog.capture("setup-flow:continue-node-install-click"); posthog.capture("setup-flow:continue-node-install-click");
setNodeInstallStep("continue-processing"); setNodeInstallStep("continue-processing");
await IpcClient.getInstance().reloadEnvPath(); await ipc.system.reloadEnvPath();
await checkNode(); await checkNode();
setNodeInstallStep("finished-checking"); setNodeInstallStep("finished-checking");
}, [checkNode, setNodeInstallStep]); }, [checkNode, setNodeInstallStep]);
...@@ -236,7 +235,7 @@ export function SetupBanner() { ...@@ -236,7 +235,7 @@ export function SetupBanner() {
<a <a
className="text-blue-500 dark:text-blue-400 hover:underline" className="text-blue-500 dark:text-blue-400 hover:underline"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://nodejs.org/en/download", "https://nodejs.org/en/download",
); );
}} }}
...@@ -392,9 +391,7 @@ function NodeJsHelpCallout() { ...@@ -392,9 +391,7 @@ function NodeJsHelpCallout() {
If you run into issues, read our{" "} If you run into issues, read our{" "}
<a <a
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://www.dyad.sh/docs/help/nodejs");
"https://www.dyad.sh/docs/help/nodejs",
);
}} }}
className="text-blue-600 dark:text-blue-400 hover:underline font-medium" className="text-blue-600 dark:text-blue-400 hover:underline font-medium"
> >
......
...@@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button"; ...@@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type SupabaseProject } from "@/ipc/types";
import { toast } from "sonner"; import { toast } from "sonner";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { useSupabase } from "@/hooks/useSupabase"; import { useSupabase } from "@/hooks/useSupabase";
...@@ -39,7 +39,6 @@ import connectSupabaseLight from "../../assets/supabase/connect-supabase-light.s ...@@ -39,7 +39,6 @@ import connectSupabaseLight from "../../assets/supabase/connect-supabase-light.s
import { ExternalLink, Plus, RefreshCw, Trash2 } from "lucide-react"; import { ExternalLink, Plus, RefreshCw, Trash2 } from "lucide-react";
import { useTheme } from "@/contexts/ThemeContext"; import { useTheme } from "@/contexts/ThemeContext";
import type { SupabaseProject } from "@/ipc/ipc_types";
import { isSupabaseConnected } from "@/lib/schemas"; import { isSupabaseConnected } from "@/lib/schemas";
export function SupabaseConnector({ appId }: { appId: number }) { export function SupabaseConnector({ appId }: { appId: number }) {
...@@ -132,12 +131,12 @@ export function SupabaseConnector({ appId }: { appId: number }) { ...@@ -132,12 +131,12 @@ export function SupabaseConnector({ appId }: { appId: number }) {
const handleAddAccount = async () => { const handleAddAccount = async () => {
if (settings?.isTestMode) { if (settings?.isTestMode) {
await IpcClient.getInstance().fakeHandleSupabaseConnect({ await ipc.supabase.fakeConnectAndSetProject({
appId, appId,
fakeProjectId: "fake-project-id", fakeProjectId: "fake-project-id",
}); });
} else { } else {
await IpcClient.getInstance().openExternalUrl( await ipc.system.openExternalUrl(
"https://supabase-oauth.dyad.sh/api/connect-supabase/login", "https://supabase-oauth.dyad.sh/api/connect-supabase/login",
); );
} }
...@@ -173,7 +172,7 @@ export function SupabaseConnector({ appId }: { appId: number }) { ...@@ -173,7 +172,7 @@ export function SupabaseConnector({ appId }: { appId: number }) {
<Button <Button
variant="outline" variant="outline"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
`https://supabase.com/dashboard/project/${app.supabaseProjectId}`, `https://supabase.com/dashboard/project/${app.supabaseProjectId}`,
); );
}} }}
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import React from "react"; import React from "react";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { atom, useAtom } from "jotai"; import { atom, useAtom } from "jotai";
...@@ -32,7 +32,7 @@ export function PrivacyBanner() { ...@@ -32,7 +32,7 @@ export function PrivacyBanner() {
</em> </em>
<a <a
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://dyad.sh/docs/policies/privacy-policy", "https://dyad.sh/docs/policies/privacy-policy",
); );
}} }}
......
import React, { useState } from "react"; import React, { useState } from "react";
import { ArrowLeft } from "lucide-react"; import { ArrowLeft } from "lucide-react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { CommunityCodeConsentDialog } from "./CommunityCodeConsentDialog"; import { CommunityCodeConsentDialog } from "./CommunityCodeConsentDialog";
import type { Template } from "@/shared/templates"; import type { Template } from "@/shared/templates";
...@@ -59,7 +59,7 @@ export const TemplateCard: React.FC<TemplateCardProps> = ({ ...@@ -59,7 +59,7 @@ export const TemplateCard: React.FC<TemplateCardProps> = ({
const handleGithubClick = (e: React.MouseEvent) => { const handleGithubClick = (e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
if (template.githubUrl) { if (template.githubUrl) {
IpcClient.getInstance().openExternalUrl(template.githubUrl); ipc.system.openExternalUrl(template.githubUrl);
} }
}; };
......
import { useState, useEffect, useCallback, useRef } from "react"; import { useState, useEffect, useCallback, useRef } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Globe } from "lucide-react"; import { Globe } from "lucide-react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, App } from "@/ipc/types";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { useLoadApp } from "@/hooks/useLoadApp"; import { useLoadApp } from "@/hooks/useLoadApp";
import { useVercelDeployments } from "@/hooks/useVercelDeployments"; import { useVercelDeployments } from "@/hooks/useVercelDeployments";
...@@ -15,7 +15,6 @@ import { ...@@ -15,7 +15,6 @@ import {
import {} from "@/components/ui/dialog"; import {} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { App } from "@/ipc/ipc_types";
interface VercelConnectorProps { interface VercelConnectorProps {
appId: number | null; appId: number | null;
...@@ -25,7 +24,7 @@ interface VercelConnectorProps { ...@@ -25,7 +24,7 @@ interface VercelConnectorProps {
interface VercelProject { interface VercelProject {
id: string; id: string;
name: string; name: string;
framework: string | null; framework?: string | null;
} }
interface ConnectedVercelConnectorProps { interface ConnectedVercelConnectorProps {
...@@ -88,7 +87,7 @@ function ConnectedVercelConnector({ ...@@ -88,7 +87,7 @@ function ConnectedVercelConnector({
<a <a
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
`https://vercel.com/${app.vercelTeamSlug}/${app.vercelProjectName}`, `https://vercel.com/${app.vercelTeamSlug}/${app.vercelProjectName}`,
); );
}} }}
...@@ -106,9 +105,7 @@ function ConnectedVercelConnector({ ...@@ -106,9 +105,7 @@ function ConnectedVercelConnector({
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
if (app.vercelDeploymentUrl) { if (app.vercelDeploymentUrl) {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(app.vercelDeploymentUrl);
app.vercelDeploymentUrl,
);
} }
}} }}
className="cursor-pointer text-blue-600 hover:underline dark:text-blue-400 font-mono" className="cursor-pointer text-blue-600 hover:underline dark:text-blue-400 font-mono"
...@@ -193,9 +190,7 @@ function ConnectedVercelConnector({ ...@@ -193,9 +190,7 @@ function ConnectedVercelConnector({
<a <a
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(`https://${deployment.url}`);
`https://${deployment.url}`,
);
}} }}
className="cursor-pointer text-blue-600 hover:underline dark:text-blue-400 text-sm" className="cursor-pointer text-blue-600 hover:underline dark:text-blue-400 text-sm"
target="_blank" target="_blank"
...@@ -277,7 +272,7 @@ function UnconnectedVercelConnector({ ...@@ -277,7 +272,7 @@ function UnconnectedVercelConnector({
const loadAvailableProjects = async () => { const loadAvailableProjects = async () => {
setIsLoadingProjects(true); setIsLoadingProjects(true);
try { try {
const projects = await IpcClient.getInstance().listVercelProjects(); const projects = await ipc.vercel.listProjects();
setAvailableProjects(projects); setAvailableProjects(projects);
} catch (error) { } catch (error) {
console.error("Failed to load Vercel projects:", error); console.error("Failed to load Vercel projects:", error);
...@@ -295,7 +290,7 @@ function UnconnectedVercelConnector({ ...@@ -295,7 +290,7 @@ function UnconnectedVercelConnector({
setTokenSuccess(false); setTokenSuccess(false);
try { try {
await IpcClient.getInstance().saveVercelAccessToken({ await ipc.vercel.saveToken({
token: accessToken.trim(), token: accessToken.trim(),
}); });
setTokenSuccess(true); setTokenSuccess(true);
...@@ -314,7 +309,7 @@ function UnconnectedVercelConnector({ ...@@ -314,7 +309,7 @@ function UnconnectedVercelConnector({
if (!name) return; if (!name) return;
setIsCheckingProject(true); setIsCheckingProject(true);
try { try {
const result = await IpcClient.getInstance().isVercelProjectAvailable({ const result = await ipc.vercel.isProjectAvailable({
name, name,
}); });
setProjectAvailable(result.available); setProjectAvailable(result.available);
...@@ -352,12 +347,12 @@ function UnconnectedVercelConnector({ ...@@ -352,12 +347,12 @@ function UnconnectedVercelConnector({
try { try {
if (projectSetupMode === "create") { if (projectSetupMode === "create") {
await IpcClient.getInstance().createVercelProject({ await ipc.vercel.createProject({
name: projectName, name: projectName,
appId, appId,
}); });
} else { } else {
await IpcClient.getInstance().connectToExistingVercelProject({ await ipc.vercel.connectExistingProject({
projectId: selectedProject, projectId: selectedProject,
appId, appId,
}); });
...@@ -398,9 +393,7 @@ function UnconnectedVercelConnector({ ...@@ -398,9 +393,7 @@ function UnconnectedVercelConnector({
<div className="flex gap-2 mt-3"> <div className="flex gap-2 mt-3">
<Button <Button
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://vercel.com/signup");
"https://vercel.com/signup",
);
}} }}
variant="outline" variant="outline"
className="flex-1" className="flex-1"
...@@ -409,7 +402,7 @@ function UnconnectedVercelConnector({ ...@@ -409,7 +402,7 @@ function UnconnectedVercelConnector({
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://vercel.com/account/settings/tokens", "https://vercel.com/account/settings/tokens",
); );
}} }}
......
...@@ -2,7 +2,7 @@ import { formatDistanceToNow } from "date-fns"; ...@@ -2,7 +2,7 @@ import { formatDistanceToNow } from "date-fns";
import { Star } from "lucide-react"; import { Star } from "lucide-react";
import { SidebarMenuItem } from "@/components/ui/sidebar"; import { SidebarMenuItem } from "@/components/ui/sidebar";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { App } from "@/ipc/ipc_types"; import type { ListedApp } from "@/ipc/types/app";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
...@@ -11,7 +11,7 @@ import { ...@@ -11,7 +11,7 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
type AppItemProps = { type AppItemProps = {
app: App; app: ListedApp;
handleAppClick: (id: number) => void; handleAppClick: (id: number) => void;
selectedAppId: number | null; selectedAppId: number | null;
handleToggleFavorite: (appId: number, e: React.MouseEvent) => void; handleToggleFavorite: (appId: number, e: React.MouseEvent) => void;
......
import { FileText, X, MessageSquare, Upload } from "lucide-react"; import { FileText, X, MessageSquare, Upload } from "lucide-react";
import type { FileAttachment } from "@/ipc/ipc_types"; import type { FileAttachment } from "@/ipc/types";
interface AttachmentsListProps { interface AttachmentsListProps {
attachments: FileAttachment[]; attachments: FileAttachment[];
......
...@@ -39,7 +39,7 @@ import { useThemes } from "@/hooks/useThemes"; ...@@ -39,7 +39,7 @@ import { useThemes } from "@/hooks/useThemes";
import { useAppTheme } from "@/hooks/useAppTheme"; import { useAppTheme } from "@/hooks/useAppTheme";
import { useCustomThemes } from "@/hooks/useCustomThemes"; import { useCustomThemes } from "@/hooks/useCustomThemes";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -104,7 +104,7 @@ export function AuxiliaryActionsMenu({ ...@@ -104,7 +104,7 @@ export function AuxiliaryActionsMenu({
const handleThemeSelect = async (themeId: string | null) => { const handleThemeSelect = async (themeId: string | null) => {
if (appId != null) { if (appId != null) {
// Update app-specific theme // Update app-specific theme
await IpcClient.getInstance().setAppTheme({ await ipc.template.setAppTheme({
appId, appId,
themeId, themeId,
}); });
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { AI_STREAMING_ERROR_MESSAGE_PREFIX } from "@/shared/texts"; import { AI_STREAMING_ERROR_MESSAGE_PREFIX } from "@/shared/texts";
import { import {
X, X,
...@@ -156,7 +156,7 @@ function ExternalLink({ ...@@ -156,7 +156,7 @@ function ExternalLink({
return ( return (
<a <a
className={`${baseClasses} ${variant === "primary" ? primaryClasses : secondaryClasses}`} className={`${baseClasses} ${variant === "primary" ? primaryClasses : secondaryClasses}`}
onClick={() => IpcClient.getInstance().openExternalUrl(href)} onClick={() => ipc.system.openExternalUrl(href)}
> >
<span>{children}</span> <span>{children}</span>
{iconElement} {iconElement}
...@@ -191,7 +191,7 @@ function ChatErrorContainer({ ...@@ -191,7 +191,7 @@ function ChatErrorContainer({
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
if (props.href) { if (props.href) {
IpcClient.getInstance().openExternalUrl(props.href); ipc.system.openExternalUrl(props.href);
} }
}} }}
className="text-blue-500 hover:text-blue-700" className="text-blue-500 hover:text-blue-700"
......
...@@ -16,7 +16,7 @@ import { ...@@ -16,7 +16,7 @@ import {
TooltipProvider, TooltipProvider,
TooltipTrigger, TooltipTrigger,
} from "../ui/tooltip"; } from "../ui/tooltip";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useRouter } from "@tanstack/react-router"; import { useRouter } from "@tanstack/react-router";
import { selectedChatIdAtom } from "@/atoms/chatAtoms"; import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { useChats } from "@/hooks/useChats"; import { useChats } from "@/hooks/useChats";
...@@ -84,7 +84,7 @@ export function ChatHeader({ ...@@ -84,7 +84,7 @@ export function ChatHeader({
const handleNewChat = async () => { const handleNewChat = async () => {
if (appId) { if (appId) {
try { try {
const chatId = await IpcClient.getInstance().createChat(appId); const chatId = await ipc.chat.createChat(appId);
setSelectedChatId(chatId); setSelectedChatId(chatId);
navigate({ navigate({
to: "/chat", to: "/chat",
......
...@@ -21,7 +21,7 @@ import type React from "react"; ...@@ -21,7 +21,7 @@ import type React from "react";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { import {
chatInputValueAtom, chatInputValueAtom,
chatMessagesByIdAtom, chatMessagesByIdAtom,
...@@ -173,7 +173,7 @@ export function ChatInput({ chatId }: { chatId?: number }) { ...@@ -173,7 +173,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
if (!chatId) { if (!chatId) {
return; return;
} }
const chat = await IpcClient.getInstance().getChat(chatId); const chat = await ipc.chat.getChat(chatId);
setMessagesById((prev) => { setMessagesById((prev) => {
const next = new Map(prev); const next = new Map(prev);
next.set(chatId, chat.messages); next.set(chatId, chat.messages);
...@@ -222,7 +222,7 @@ export function ChatInput({ chatId }: { chatId?: number }) { ...@@ -222,7 +222,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
const handleCancel = () => { const handleCancel = () => {
if (chatId) { if (chatId) {
IpcClient.getInstance().cancelChatStream(chatId); ipc.chat.cancelStream(chatId);
} }
setIsStreaming(false); setIsStreaming(false);
}; };
...@@ -240,7 +240,7 @@ export function ChatInput({ chatId }: { chatId?: number }) { ...@@ -240,7 +240,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
setIsApproving(true); setIsApproving(true);
posthog.capture("chat:approve"); posthog.capture("chat:approve");
try { try {
const result = await IpcClient.getInstance().approveProposal({ const result = await ipc.proposal.approveProposal({
chatId, chatId,
messageId, messageId,
}); });
...@@ -277,7 +277,7 @@ export function ChatInput({ chatId }: { chatId?: number }) { ...@@ -277,7 +277,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
setIsRejecting(true); setIsRejecting(true);
posthog.capture("chat:reject"); posthog.capture("chat:reject");
try { try {
await IpcClient.getInstance().rejectProposal({ await ipc.proposal.rejectProposal({
chatId, chatId,
messageId, messageId,
}); });
...@@ -333,7 +333,7 @@ export function ChatInput({ chatId }: { chatId?: number }) { ...@@ -333,7 +333,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
consent={pendingAgentConsent} consent={pendingAgentConsent}
queueTotal={consentsForThisChat.length} queueTotal={consentsForThisChat.length}
onDecision={(decision) => { onDecision={(decision) => {
IpcClient.getInstance().respondToAgentConsentRequest({ ipc.agent.respondToConsent({
requestId: pendingAgentConsent.requestId, requestId: pendingAgentConsent.requestId,
decision, decision,
}); });
...@@ -345,7 +345,7 @@ export function ChatInput({ chatId }: { chatId?: number }) { ...@@ -345,7 +345,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
); );
}} }}
onClose={() => { onClose={() => {
IpcClient.getInstance().respondToAgentConsentRequest({ ipc.agent.respondToConsent({
requestId: pendingAgentConsent.requestId, requestId: pendingAgentConsent.requestId,
decision: "decline", decision: "decline",
}); });
...@@ -413,9 +413,7 @@ export function ChatInput({ chatId }: { chatId?: number }) { ...@@ -413,9 +413,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl("https://dyad.sh/pro");
"https://dyad.sh/pro",
);
}} }}
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors cursor-pointer" className="flex items-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors cursor-pointer"
> >
...@@ -873,7 +871,7 @@ function ChatInputActions({ ...@@ -873,7 +871,7 @@ function ChatInputActions({
key={index} key={index}
className="flex items-center space-x-2" className="flex items-center space-x-2"
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
`https://www.npmjs.com/package/${pkg}`, `https://www.npmjs.com/package/${pkg}`,
); );
}} }}
......
import type { Message } from "@/ipc/ipc_types"; import type { Message } from "@/ipc/types";
import { import {
DyadMarkdownParser, DyadMarkdownParser,
VanillaMarkdownParser, VanillaMarkdownParser,
......
...@@ -2,7 +2,7 @@ import type React from "react"; ...@@ -2,7 +2,7 @@ import type React from "react";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { useState } from "react"; import { useState } from "react";
import { IpcClient } from "../../ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { Package, ChevronsUpDown, ChevronsDownUp } from "lucide-react"; import { Package, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight"; import { CodeHighlight } from "./CodeHighlight";
...@@ -45,7 +45,7 @@ export const DyadAddDependency: React.FC<DyadAddDependencyProps> = ({ ...@@ -45,7 +45,7 @@ export const DyadAddDependency: React.FC<DyadAddDependencyProps> = ({
className="cursor-pointer text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" className="cursor-pointer text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
key={p} key={p}
onClick={() => { onClick={() => {
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
`https://www.npmjs.com/package/${p}`, `https://www.npmjs.com/package/${p}`,
); );
}} }}
......
...@@ -19,7 +19,7 @@ import { isStreamingByIdAtom, selectedChatIdAtom } from "@/atoms/chatAtoms"; ...@@ -19,7 +19,7 @@ import { isStreamingByIdAtom, selectedChatIdAtom } from "@/atoms/chatAtoms";
import { CustomTagState } from "./stateTypes"; import { CustomTagState } from "./stateTypes";
import { DyadOutput } from "./DyadOutput"; import { DyadOutput } from "./DyadOutput";
import { DyadProblemSummary } from "./DyadProblemSummary"; import { DyadProblemSummary } from "./DyadProblemSummary";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { DyadMcpToolCall } from "./DyadMcpToolCall"; import { DyadMcpToolCall } from "./DyadMcpToolCall";
import { DyadMcpToolResult } from "./DyadMcpToolResult"; import { DyadMcpToolResult } from "./DyadMcpToolResult";
import { DyadWebSearchResult } from "./DyadWebSearchResult"; import { DyadWebSearchResult } from "./DyadWebSearchResult";
...@@ -99,7 +99,7 @@ const customLink = ({ ...@@ -99,7 +99,7 @@ const customLink = ({
const url = props.href; const url = props.href;
if (url) { if (url) {
e.preventDefault(); e.preventDefault();
IpcClient.getInstance().openExternalUrl(url); ipc.system.openExternalUrl(url);
} }
}} }}
/> />
......
...@@ -5,7 +5,7 @@ import { ...@@ -5,7 +5,7 @@ import {
AlertTriangle, AlertTriangle,
FileText, FileText,
} from "lucide-react"; } from "lucide-react";
import type { Problem } from "@/ipc/ipc_types"; import type { Problem } from "@/ipc/types";
type ProblemWithoutSnippet = Omit<Problem, "snippet">; type ProblemWithoutSnippet = Omit<Problem, "snippet">;
......
import React from "react"; import React from "react";
import type { Message } from "@/ipc/ipc_types"; import type { Message } from "@/ipc/types";
import { forwardRef, useState, useCallback, useMemo } from "react"; import { forwardRef, useState, useCallback, useMemo } from "react";
import { Virtuoso } from "react-virtuoso"; import { Virtuoso } from "react-virtuoso";
import ChatMessage from "./ChatMessage"; import ChatMessage from "./ChatMessage";
...@@ -13,7 +13,7 @@ import { Button } from "@/components/ui/button"; ...@@ -13,7 +13,7 @@ import { Button } from "@/components/ui/button";
import { useVersions } from "@/hooks/useVersions"; import { useVersions } from "@/hooks/useVersions";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { showError, showWarning } from "@/lib/toast"; import { showError, showWarning } from "@/lib/toast";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { chatMessagesByIdAtom } from "@/atoms/chatAtoms"; import { chatMessagesByIdAtom } from "@/atoms/chatAtoms";
import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders"; import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
...@@ -121,8 +121,7 @@ function FooterComponent({ context }: { context?: FooterContext }) { ...@@ -121,8 +121,7 @@ function FooterComponent({ context }: { context?: FooterContext }) {
} }
: undefined, : undefined,
}); });
const chat = const chat = await ipc.chat.getChat(selectedChatId);
await IpcClient.getInstance().getChat(selectedChatId);
setMessagesById((prev) => { setMessagesById((prev) => {
const next = new Map(prev); const next = new Map(prev);
next.set(selectedChatId, chat.messages); next.set(selectedChatId, chat.messages);
...@@ -182,8 +181,7 @@ function FooterComponent({ context }: { context?: FooterContext }) { ...@@ -182,8 +181,7 @@ function FooterComponent({ context }: { context?: FooterContext }) {
}); });
shouldRedo = false; shouldRedo = false;
} else { } else {
const chat = const chat = await ipc.chat.getChat(selectedChatId);
await IpcClient.getInstance().getChat(selectedChatId);
if (chat.initialCommitHash) { if (chat.initialCommitHash) {
console.debug( console.debug(
"Reverting to initial commit hash", "Reverting to initial commit hash",
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import React from "react"; import React from "react";
// Types for the message system // Types for the message system
...@@ -36,7 +36,7 @@ export function Message({ spans }: MessageConfig) { ...@@ -36,7 +36,7 @@ export function Message({ spans }: MessageConfig) {
if (span.action) { if (span.action) {
span.action(); span.action();
} else if (span.url) { } else if (span.url) {
IpcClient.getInstance().openExternalUrl(span.url); ipc.system.openExternalUrl(span.url);
} }
}} }}
className="text-blue-600 hover:text-blue-800 underline cursor-pointer" className="text-blue-600 hover:text-blue-800 underline cursor-pointer"
......
import { useState } from "react"; import { useState } from "react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
import { import {
Dialog, Dialog,
...@@ -46,7 +46,7 @@ export function RenameChatDialog({ ...@@ -46,7 +46,7 @@ export function RenameChatDialog({
} }
try { try {
await IpcClient.getInstance().updateChat({ await ipc.chat.updateChat({
chatId, chatId,
title: newTitle.trim(), title: newTitle.trim(),
}); });
......
...@@ -3,7 +3,7 @@ import { useAtomValue } from "jotai"; ...@@ -3,7 +3,7 @@ import { useAtomValue } from "jotai";
import { selectedChatIdAtom } from "@/atoms/chatAtoms"; import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { useStreamChat } from "@/hooks/useStreamChat"; import { useStreamChat } from "@/hooks/useStreamChat";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
export function useSummarizeInNewChat() { export function useSummarizeInNewChat() {
...@@ -22,7 +22,7 @@ export function useSummarizeInNewChat() { ...@@ -22,7 +22,7 @@ export function useSummarizeInNewChat() {
return; return;
} }
try { try {
const newChatId = await IpcClient.getInstance().createChat(appId); const newChatId = await ipc.chat.createChat(appId);
// navigate to new chat // navigate to new chat
await navigate({ to: "/chat", search: { id: newChatId } }); await navigate({ to: "/chat", search: { id: newChatId } });
await streamMessage({ await streamMessage({
......
import React, { useState } from "react"; import React, { useState } from "react";
import type { AgentTodo } from "@/ipc/ipc_types"; import type { AgentTodo } from "@/ipc/types";
import { import {
CheckCircle2, CheckCircle2,
Circle, Circle,
......
...@@ -16,7 +16,7 @@ import { ...@@ -16,7 +16,7 @@ import {
import { chatInputValueAtom } from "@/atoms/chatAtoms"; import { chatInputValueAtom } from "@/atoms/chatAtoms";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
interface TokenBarProps { interface TokenBarProps {
chatId?: number; chatId?: number;
...@@ -136,12 +136,10 @@ export function TokenBar({ chatId }: TokenBarProps) { ...@@ -136,12 +136,10 @@ export function TokenBar({ chatId }: TokenBarProps) {
<a <a
onClick={() => onClick={() =>
settings?.enableDyadPro settings?.enableDyadPro
? IpcClient.getInstance().openExternalUrl( ? ipc.system.openExternalUrl(
"https://www.dyad.sh/docs/guides/ai-models/pro-modes#smart-context", "https://www.dyad.sh/docs/guides/ai-models/pro-modes#smart-context",
) )
: IpcClient.getInstance().openExternalUrl( : ipc.system.openExternalUrl("https://dyad.sh/pro#ai")
"https://dyad.sh/pro#ai",
)
} }
className="text-blue-500 dark:text-blue-400 cursor-pointer hover:underline" className="text-blue-500 dark:text-blue-400 cursor-pointer hover:underline"
> >
......
...@@ -3,7 +3,7 @@ import { selectedAppIdAtom, selectedVersionIdAtom } from "@/atoms/appAtoms"; ...@@ -3,7 +3,7 @@ import { selectedAppIdAtom, selectedVersionIdAtom } from "@/atoms/appAtoms";
import { useVersions } from "@/hooks/useVersions"; import { useVersions } from "@/hooks/useVersions";
import { formatDistanceToNow } from "date-fns"; import { formatDistanceToNow } from "date-fns";
import { RotateCcw, X, Database, Loader2 } from "lucide-react"; import { RotateCcw, X, Database, Loader2 } from "lucide-react";
import type { Version } from "@/ipc/ipc_types"; import type { Version } from "@/ipc/types";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useCheckoutVersion } from "@/hooks/useCheckoutVersion"; import { useCheckoutVersion } from "@/hooks/useCheckoutVersion";
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { Play } from "lucide-react"; import { Play } from "lucide-react";
export const OnboardingBanner = ({ export const OnboardingBanner = ({
...@@ -16,7 +16,7 @@ export const OnboardingBanner = ({ ...@@ -16,7 +16,7 @@ export const OnboardingBanner = ({
<a <a
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://www.youtube.com/watch?v=rgdNoHLaRN4", "https://www.youtube.com/watch?v=rgdNoHLaRN4",
); );
setIsVisible(false); setIsVisible(false);
......
import { useAtom, useAtomValue } from "jotai"; import { useAtom, useAtomValue } from "jotai";
import { previewModeAtom, selectedAppIdAtom } from "../../atoms/appAtoms"; import { previewModeAtom, selectedAppIdAtom } from "../../atoms/appAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { import {
Eye, Eye,
...@@ -87,8 +87,7 @@ export const ActionHeader = () => { ...@@ -87,8 +87,7 @@ export const ActionHeader = () => {
const useClearSessionData = () => { const useClearSessionData = () => {
return useMutation({ return useMutation({
mutationFn: () => { mutationFn: () => {
const ipcClient = IpcClient.getInstance(); return ipc.system.clearSessionData();
return ipcClient.clearSessionData();
}, },
onSuccess: async () => { onSuccess: async () => {
await refreshAppIframe(); await refreshAppIframe();
......
import { Lock, ArrowLeft } from "lucide-react"; import { Lock, ArrowLeft } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
interface AnnotatorOnlyForProProps { interface AnnotatorOnlyForProProps {
onGoBack: () => void; onGoBack: () => void;
...@@ -8,7 +8,7 @@ interface AnnotatorOnlyForProProps { ...@@ -8,7 +8,7 @@ interface AnnotatorOnlyForProProps {
export const AnnotatorOnlyForPro = ({ onGoBack }: AnnotatorOnlyForProProps) => { export const AnnotatorOnlyForPro = ({ onGoBack }: AnnotatorOnlyForProProps) => {
const handleGetPro = () => { const handleGetPro = () => {
IpcClient.getInstance().openExternalUrl("https://dyad.sh/pro"); ipc.system.openExternalUrl("https://dyad.sh/pro");
}; };
return ( return (
......
...@@ -21,7 +21,7 @@ import { ...@@ -21,7 +21,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { NeonConfigure } from "./NeonConfigure"; import { NeonConfigure } from "./NeonConfigure";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -66,8 +66,7 @@ export const ConfigurePanel = () => { ...@@ -66,8 +66,7 @@ export const ConfigurePanel = () => {
queryKey: queryKeys.appEnvVars.byApp({ appId: selectedAppId }), queryKey: queryKeys.appEnvVars.byApp({ appId: selectedAppId }),
queryFn: async () => { queryFn: async () => {
if (!selectedAppId) return []; if (!selectedAppId) return [];
const ipcClient = IpcClient.getInstance(); return await ipc.misc.getAppEnvVars({ appId: selectedAppId });
return await ipcClient.getAppEnvVars({ appId: selectedAppId });
}, },
enabled: !!selectedAppId, enabled: !!selectedAppId,
}); });
...@@ -76,8 +75,7 @@ export const ConfigurePanel = () => { ...@@ -76,8 +75,7 @@ export const ConfigurePanel = () => {
const saveEnvVarsMutation = useMutation({ const saveEnvVarsMutation = useMutation({
mutationFn: async (newEnvVars: { key: string; value: string }[]) => { mutationFn: async (newEnvVars: { key: string; value: string }[]) => {
if (!selectedAppId) throw new Error("No app selected"); if (!selectedAppId) throw new Error("No app selected");
const ipcClient = IpcClient.getInstance(); return await ipc.misc.setAppEnvVars({
return await ipcClient.setAppEnvVars({
appId: selectedAppId, appId: selectedAppId,
envVars: newEnvVars, envVars: newEnvVars,
}); });
......
import { appConsoleEntriesAtom, selectedAppIdAtom } from "@/atoms/appAtoms"; import { appConsoleEntriesAtom, selectedAppIdAtom } from "@/atoms/appAtoms";
import type { ConsoleEntry } from "@/ipc/ipc_types"; import type { ConsoleEntry } from "@/ipc/types";
import { useAtomValue, useSetAtom } from "jotai"; import { useAtomValue, useSetAtom } from "jotai";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useEffect, useRef, useState, useMemo, useCallback, memo } from "react"; import { useEffect, useRef, useState, useMemo, useCallback, memo } from "react";
import { Virtuoso, VirtuosoHandle } from "react-virtuoso"; import { Virtuoso, VirtuosoHandle } from "react-virtuoso";
import { ConsoleEntryComponent } from "./ConsoleEntry"; import { ConsoleEntryComponent } from "./ConsoleEntry";
...@@ -107,7 +107,7 @@ export const Console = () => { ...@@ -107,7 +107,7 @@ export const Console = () => {
if (selectedAppId) { if (selectedAppId) {
try { try {
// Clear logs from backend store // Clear logs from backend store
await IpcClient.getInstance().clearLogs(selectedAppId); await ipc.misc.clearLogs({ appId: selectedAppId });
// Clear logs from UI // Clear logs from UI
setConsoleEntries([]); setConsoleEntries([]);
} catch (error) { } catch (error) {
......
...@@ -4,7 +4,7 @@ import { useLoadAppFile } from "@/hooks/useLoadAppFile"; ...@@ -4,7 +4,7 @@ import { useLoadAppFile } from "@/hooks/useLoadAppFile";
import { useTheme } from "@/contexts/ThemeContext"; import { useTheme } from "@/contexts/ThemeContext";
import { ChevronRight, Circle, Save } from "lucide-react"; import { ChevronRight, Circle, Save } from "lucide-react";
import "@/components/chat/monaco"; import "@/components/chat/monaco";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { showError, showSuccess, showWarning } from "@/lib/toast"; import { showError, showSuccess, showWarning } from "@/lib/toast";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
...@@ -190,12 +190,11 @@ export const FileEditor = ({ ...@@ -190,12 +190,11 @@ export const FileEditor = ({
isSavingRef.current = true; isSavingRef.current = true;
setIsSaving(true); setIsSaving(true);
const ipcClient = IpcClient.getInstance(); const { warning } = await ipc.app.editAppFile({
const { warning } = await ipcClient.editAppFile(
appId, appId,
filePath, filePath,
currentValueRef.current, content: currentValueRef.current,
); });
await queryClient.invalidateQueries({ await queryClient.invalidateQueries({
queryKey: queryKeys.versions.list({ appId }), queryKey: queryKeys.versions.list({ appId }),
}); });
......
...@@ -11,7 +11,7 @@ import { ...@@ -11,7 +11,7 @@ import {
import { selectedFileAtom } from "@/atoms/viewAtoms"; import { selectedFileAtom } from "@/atoms/viewAtoms";
import { useSetAtom } from "jotai"; import { useSetAtom } from "jotai";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import type { AppFileSearchResult } from "@/ipc/ipc_types"; import type { AppFileSearchResult } from "@/ipc/types";
import { useSearchAppFiles } from "@/hooks/useSearchAppFiles"; import { useSearchAppFiles } from "@/hooks/useSearchAppFiles";
interface FileTreeProps { interface FileTreeProps {
......
...@@ -7,8 +7,8 @@ import { Badge } from "@/components/ui/badge"; ...@@ -7,8 +7,8 @@ import { Badge } from "@/components/ui/badge";
import { Database, GitBranch } from "lucide-react"; import { Database, GitBranch } from "lucide-react";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { useLoadApp } from "@/hooks/useLoadApp"; import { useLoadApp } from "@/hooks/useLoadApp";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import type { GetNeonProjectResponse, NeonBranch } from "@/ipc/ipc_types"; import type { GetNeonProjectResponse, NeonBranch } from "@/ipc/types";
import { NeonDisconnectButton } from "@/components/NeonDisconnectButton"; import { NeonDisconnectButton } from "@/components/NeonDisconnectButton";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -44,8 +44,7 @@ export const NeonConfigure = () => { ...@@ -44,8 +44,7 @@ export const NeonConfigure = () => {
queryKey: queryKeys.neon.project({ appId: selectedAppId }), queryKey: queryKeys.neon.project({ appId: selectedAppId }),
queryFn: async () => { queryFn: async () => {
if (!selectedAppId) throw new Error("No app selected"); if (!selectedAppId) throw new Error("No app selected");
const ipcClient = IpcClient.getInstance(); return await ipc.neon.getProject({ appId: selectedAppId });
return await ipcClient.getNeonProject({ appId: selectedAppId });
}, },
enabled: !!selectedAppId && !!app?.neonProjectId, enabled: !!selectedAppId && !!app?.neonProjectId,
meta: { showErrorToast: true }, meta: { showErrorToast: true },
......
...@@ -27,7 +27,7 @@ import { ...@@ -27,7 +27,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import { selectedChatIdAtom } from "@/atoms/chatAtoms"; import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { CopyErrorMessage } from "@/components/CopyErrorMessage"; import { CopyErrorMessage } from "@/components/CopyErrorMessage";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useParseRouter } from "@/hooks/useParseRouter"; import { useParseRouter } from "@/hooks/useParseRouter";
import { import {
...@@ -46,7 +46,7 @@ import { ...@@ -46,7 +46,7 @@ import {
screenshotDataUrlAtom, screenshotDataUrlAtom,
pendingVisualChangesAtom, pendingVisualChangesAtom,
} from "@/atoms/previewAtoms"; } from "@/atoms/previewAtoms";
import { ComponentSelection } from "@/ipc/ipc_types"; import { ComponentSelection } from "@/ipc/types";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
...@@ -231,7 +231,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => { ...@@ -231,7 +231,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
if (!componentId || !selectedAppId) return; if (!componentId || !selectedAppId) return;
try { try {
const result = await IpcClient.getInstance().analyzeComponent({ const result = await ipc.visualEditing.analyzeComponent({
appId: selectedAppId, appId: selectedAppId,
componentId, componentId,
}); });
...@@ -362,7 +362,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => { ...@@ -362,7 +362,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
}; };
// Send to central log store // Send to central log store
IpcClient.getInstance().addLog(logEntry); ipc.misc.addLog(logEntry);
// Also update UI state // Also update UI state
setConsoleEntries((prev) => [...prev, logEntry]); setConsoleEntries((prev) => [...prev, logEntry]);
...@@ -382,7 +382,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => { ...@@ -382,7 +382,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
}; };
// Send to central log store // Send to central log store
IpcClient.getInstance().addLog(logEntry); ipc.misc.addLog(logEntry);
// Also update UI state // Also update UI state
setConsoleEntries((prev) => [...prev, logEntry]); setConsoleEntries((prev) => [...prev, logEntry]);
...@@ -404,7 +404,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => { ...@@ -404,7 +404,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
}; };
// Send to central log store // Send to central log store
IpcClient.getInstance().addLog(logEntry); ipc.misc.addLog(logEntry);
// Also update UI state // Also update UI state
setConsoleEntries((prev) => [...prev, logEntry]); setConsoleEntries((prev) => [...prev, logEntry]);
...@@ -425,7 +425,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => { ...@@ -425,7 +425,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
}; };
// Send to central log store // Send to central log store
IpcClient.getInstance().addLog(logEntry); ipc.misc.addLog(logEntry);
// Also update UI state // Also update UI state
setConsoleEntries((prev) => [...prev, logEntry]); setConsoleEntries((prev) => [...prev, logEntry]);
...@@ -573,7 +573,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => { ...@@ -573,7 +573,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
}; };
// Send to central log store // Send to central log store
IpcClient.getInstance().addLog(logEntry); ipc.misc.addLog(logEntry);
// Also update UI state // Also update UI state
setConsoleEntries((prev) => [...prev, logEntry]); setConsoleEntries((prev) => [...prev, logEntry]);
...@@ -590,7 +590,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => { ...@@ -590,7 +590,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
}; };
// Send to central log store // Send to central log store
IpcClient.getInstance().addLog(logEntry); ipc.misc.addLog(logEntry);
// Also update UI state // Also update UI state
setConsoleEntries((prev) => [...prev, logEntry]); setConsoleEntries((prev) => [...prev, logEntry]);
...@@ -950,7 +950,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => { ...@@ -950,7 +950,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
data-testid="preview-open-browser-button" data-testid="preview-open-browser-button"
onClick={() => { onClick={() => {
if (originalUrl) { if (originalUrl) {
IpcClient.getInstance().openExternalUrl(originalUrl); ipc.system.openExternalUrl(originalUrl);
} }
}} }}
className="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed dark:text-gray-300" className="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed dark:text-gray-300"
......
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
RefreshCw, RefreshCw,
Check, Check,
} from "lucide-react"; } from "lucide-react";
import { Problem, ProblemReport } from "@/ipc/ipc_types"; import { Problem, ProblemReport } from "@/ipc/types";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
......
...@@ -4,7 +4,7 @@ import { useLoadApp } from "@/hooks/useLoadApp"; ...@@ -4,7 +4,7 @@ import { useLoadApp } from "@/hooks/useLoadApp";
import { GitHubConnector } from "@/components/GitHubConnector"; import { GitHubConnector } from "@/components/GitHubConnector";
import { VercelConnector } from "@/components/VercelConnector"; import { VercelConnector } from "@/components/VercelConnector";
import { PortalMigrate } from "@/components/PortalMigrate"; import { PortalMigrate } from "@/components/PortalMigrate";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { GithubCollaboratorManager } from "@/components/GithubCollaboratorManager"; import { GithubCollaboratorManager } from "@/components/GithubCollaboratorManager";
...@@ -120,8 +120,7 @@ export const PublishPanel = () => { ...@@ -120,8 +120,7 @@ export const PublishPanel = () => {
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2">
<button <button
onClick={() => { onClick={() => {
const ipcClient = IpcClient.getInstance(); ipc.system.openExternalUrl("https://vercel.com/dashboard");
ipcClient.openExternalUrl("https://vercel.com/dashboard");
}} }}
className="flex items-center gap-2 hover:text-blue-600 dark:hover:text-blue-400 transition-colors cursor-pointer bg-transparent border-none p-0" className="flex items-center gap-2 hover:text-blue-600 dark:hover:text-blue-400 transition-colors cursor-pointer bg-transparent border-none p-0"
> >
......
...@@ -2,7 +2,7 @@ import { useAtomValue, useSetAtom } from "jotai"; ...@@ -2,7 +2,7 @@ import { useAtomValue, useSetAtom } from "jotai";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { selectedChatIdAtom } from "@/atoms/chatAtoms"; import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { useSecurityReview } from "@/hooks/useSecurityReview"; import { useSecurityReview } from "@/hooks/useSecurityReview";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
...@@ -28,7 +28,10 @@ import { useStreamChat } from "@/hooks/useStreamChat"; ...@@ -28,7 +28,10 @@ import { useStreamChat } from "@/hooks/useStreamChat";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import type { SecurityFinding, SecurityReviewResult } from "@/ipc/ipc_types"; import type {
SecurityFinding,
SecurityReviewResult,
} from "@/ipc/types/security";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { VanillaMarkdownParser } from "@/components/chat/DyadMarkdownParser"; import { VanillaMarkdownParser } from "@/components/chat/DyadMarkdownParser";
import { showSuccess, showWarning } from "@/lib/toast"; import { showSuccess, showWarning } from "@/lib/toast";
...@@ -251,7 +254,7 @@ function SecurityHeader({ ...@@ -251,7 +254,7 @@ function SecurityHeader({
<a <a
className="text-blue-600 dark:text-blue-400 hover:underline cursor-pointer" className="text-blue-600 dark:text-blue-400 hover:underline cursor-pointer"
onClick={() => onClick={() =>
IpcClient.getInstance().openExternalUrl( ipc.system.openExternalUrl(
"https://www.dyad.sh/docs/guides/security-review", "https://www.dyad.sh/docs/guides/security-review",
) )
} }
...@@ -742,12 +745,11 @@ export const SecurityPanel = () => { ...@@ -742,12 +745,11 @@ export const SecurityPanel = () => {
try { try {
setIsSaving(true); setIsSaving(true);
const ipcClient = IpcClient.getInstance(); const { warning } = await ipc.app.editAppFile({
const { warning } = await ipcClient.editAppFile( appId: selectedAppId,
selectedAppId, filePath: "SECURITY_RULES.md",
"SECURITY_RULES.md", content: rulesContent,
rulesContent, });
);
await queryClient.invalidateQueries({ await queryClient.invalidateQueries({
queryKey: queryKeys.versions.list({ appId: selectedAppId }), queryKey: queryKeys.versions.list({ appId: selectedAppId }),
}); });
...@@ -780,7 +782,7 @@ export const SecurityPanel = () => { ...@@ -780,7 +782,7 @@ export const SecurityPanel = () => {
setIsRunningReview(true); setIsRunningReview(true);
// Create a new chat // Create a new chat
const chatId = await IpcClient.getInstance().createChat(selectedAppId); const chatId = await ipc.chat.createChat(selectedAppId);
// Navigate to the new chat // Navigate to the new chat
setSelectedChatId(chatId); setSelectedChatId(chatId);
...@@ -811,7 +813,7 @@ export const SecurityPanel = () => { ...@@ -811,7 +813,7 @@ export const SecurityPanel = () => {
const key = createFindingKey(finding); const key = createFindingKey(finding);
setFixingFindingKey(key); setFixingFindingKey(key);
const chatId = await IpcClient.getInstance().createChat(selectedAppId); const chatId = await ipc.chat.createChat(selectedAppId);
// Navigate to the new chat // Navigate to the new chat
setSelectedChatId(chatId); setSelectedChatId(chatId);
...@@ -880,7 +882,7 @@ ${finding.description}`; ...@@ -880,7 +882,7 @@ ${finding.description}`;
); );
// Create a new chat // Create a new chat
const chatId = await IpcClient.getInstance().createChat(selectedAppId); const chatId = await ipc.chat.createChat(selectedAppId);
// Navigate to the new chat // Navigate to the new chat
setSelectedChatId(chatId); setSelectedChatId(chatId);
......
import { useAtom, useAtomValue } from "jotai"; import { useAtom, useAtomValue } from "jotai";
import { pendingVisualChangesAtom } from "@/atoms/previewAtoms"; import { pendingVisualChangesAtom } from "@/atoms/previewAtoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { Check, X } from "lucide-react"; import { Check, X } from "lucide-react";
import { useState, useEffect, useRef } from "react"; import { useState, useEffect, useRef } from "react";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
...@@ -66,7 +66,7 @@ export function VisualEditingChangesDialog({ ...@@ -66,7 +66,7 @@ export function VisualEditingChangesDialog({
return change; return change;
}); });
await IpcClient.getInstance().applyVisualEditingChanges({ await ipc.visualEditing.applyChanges({
appId: selectedAppId!, appId: selectedAppId!,
changes: updatedChanges, changes: updatedChanges,
}); });
...@@ -130,7 +130,7 @@ export function VisualEditingChangesDialog({ ...@@ -130,7 +130,7 @@ export function VisualEditingChangesDialog({
setAllResponsesReceived(true); setAllResponsesReceived(true);
} }
} else { } else {
await IpcClient.getInstance().applyVisualEditingChanges({ await ipc.visualEditing.applyChanges({
appId: selectedAppId!, appId: selectedAppId!,
changes: changesToSave, changes: changesToSave,
}); });
......
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { X, Move, Square, Palette, Type } from "lucide-react"; import { X, Move, Square, Palette, Type } from "lucide-react";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { ComponentSelection } from "@/ipc/ipc_types"; import { ComponentSelection } from "@/ipc/types";
import { useSetAtom, useAtomValue } from "jotai"; import { useSetAtom, useAtomValue } from "jotai";
import { import {
pendingVisualChangesAtom, pendingVisualChangesAtom,
......
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { import {
Popover, Popover,
PopoverContent, PopoverContent,
...@@ -53,7 +53,7 @@ export function ProviderSettingsHeader({ ...@@ -53,7 +53,7 @@ export function ProviderSettingsHeader({
const handleGetApiKeyClick = (e: React.MouseEvent<HTMLButtonElement>) => { const handleGetApiKeyClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault(); e.preventDefault();
if (providerWebsiteUrl) { if (providerWebsiteUrl) {
IpcClient.getInstance().openExternalUrl(providerWebsiteUrl); ipc.system.openExternalUrl(providerWebsiteUrl);
} }
}; };
......
import React, { createContext, useContext, useEffect, useState } from "react"; import React, { createContext, useContext, useEffect, useState } from "react";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { IpcClient } from "../ipc/ipc_client"; import { ipc, DeepLinkData } from "../ipc/types";
import { DeepLinkData } from "../ipc/deep_link_data";
import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo"; import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo";
type DeepLinkContextType = { type DeepLinkContextType = {
...@@ -24,8 +23,7 @@ export function DeepLinkProvider({ children }: { children: React.ReactNode }) { ...@@ -24,8 +23,7 @@ export function DeepLinkProvider({ children }: { children: React.ReactNode }) {
block: "start", block: "start",
}); });
useEffect(() => { useEffect(() => {
const ipcClient = IpcClient.getInstance(); const unsubscribe = ipc.events.misc.onDeepLinkReceived((data) => {
const unsubscribe = ipcClient.onDeepLinkReceived((data) => {
// Update with timestamp to ensure state change even if same type comes twice // Update with timestamp to ensure state change even if same type comes twice
setLastDeepLink({ ...data, timestamp: Date.now() }); setLastDeepLink({ ...data, timestamp: Date.now() });
if (data.type === "add-mcp-server") { if (data.type === "add-mcp-server") {
......
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { appsListAtom } from "@/atoms/appAtoms"; import { appsListAtom } from "@/atoms/appAtoms";
...@@ -9,7 +9,7 @@ export function useAddAppToFavorite() { ...@@ -9,7 +9,7 @@ export function useAddAppToFavorite() {
const mutation = useMutation<boolean, Error, number>({ const mutation = useMutation<boolean, Error, number>({
mutationFn: async (appId: number): Promise<boolean> => { mutationFn: async (appId: number): Promise<boolean> => {
const result = await IpcClient.getInstance().addAppToFavorite(appId); const result = await ipc.app.addToFavorite({ appId });
return result.isFavorite; return result.isFavorite;
}, },
onSuccess: (newIsFavorite, appId) => { onSuccess: (newIsFavorite, appId) => {
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
*/ */
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import type { AgentToolName } from "../pro/main/ipc/handlers/local_agent/tool_definitions"; import type { AgentToolName } from "../pro/main/ipc/handlers/local_agent/tool_definitions";
import type { AgentTool } from "@/ipc/ipc_types"; import type { AgentTool } from "@/ipc/types";
import { AgentToolConsent } from "@/lib/schemas"; import { AgentToolConsent } from "@/lib/schemas";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -18,8 +18,7 @@ export function useAgentTools() { ...@@ -18,8 +18,7 @@ export function useAgentTools() {
const toolsQuery = useQuery({ const toolsQuery = useQuery({
queryKey: queryKeys.agentTools.all, queryKey: queryKeys.agentTools.all,
queryFn: async () => { queryFn: async () => {
const ipcClient = IpcClient.getInstance(); return ipc.agent.getTools();
return ipcClient.getAgentTools();
}, },
}); });
...@@ -28,8 +27,7 @@ export function useAgentTools() { ...@@ -28,8 +27,7 @@ export function useAgentTools() {
toolName: AgentToolName; toolName: AgentToolName;
consent: AgentToolConsent; consent: AgentToolConsent;
}) => { }) => {
const ipcClient = IpcClient.getInstance(); return ipc.agent.setConsent(params);
return ipcClient.setAgentToolConsent(params);
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.agentTools.all }); queryClient.invalidateQueries({ queryKey: queryKeys.agentTools.all });
......
import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useQuery, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export function useAppTheme(appId: number | undefined) { export function useAppTheme(appId: number | undefined) {
...@@ -8,7 +8,7 @@ export function useAppTheme(appId: number | undefined) { ...@@ -8,7 +8,7 @@ export function useAppTheme(appId: number | undefined) {
const query = useQuery({ const query = useQuery({
queryKey: queryKeys.appTheme.byApp({ appId }), queryKey: queryKeys.appTheme.byApp({ appId }),
queryFn: async (): Promise<string | null> => { queryFn: async (): Promise<string | null> => {
return IpcClient.getInstance().getAppTheme({ appId: appId! }); return ipc.template.getAppTheme({ appId: appId! });
}, },
enabled: !!appId, enabled: !!appId,
}); });
......
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
export function useAppVersion() { export function useAppVersion() {
const [appVersion, setAppVersion] = useState<string | null>(null); const [appVersion, setAppVersion] = useState<string | null>(null);
...@@ -7,8 +7,8 @@ export function useAppVersion() { ...@@ -7,8 +7,8 @@ export function useAppVersion() {
useEffect(() => { useEffect(() => {
const fetchVersion = async () => { const fetchVersion = async () => {
try { try {
const version = await IpcClient.getInstance().getAppVersion(); const result = await ipc.system.getAppVersion();
setAppVersion(version); setAppVersion(result.version);
} catch { } catch {
setAppVersion(null); setAppVersion(null);
} }
......
import React, { useRef, useState } from "react"; import React, { useRef, useState } from "react";
import type { FileAttachment } from "@/ipc/ipc_types"; import type { FileAttachment } from "@/ipc/types";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { attachmentsAtom } from "@/atoms/chatAtoms"; import { attachmentsAtom } from "@/atoms/chatAtoms";
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import type { ChatSummary } from "@/lib/schemas"; import type { ChatSummary } from "@/lib/schemas";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useQuery, useQueryClient } from "@tanstack/react-query";
...@@ -9,7 +9,7 @@ export function useChats(appId: number | null) { ...@@ -9,7 +9,7 @@ export function useChats(appId: number | null) {
const { data, isLoading } = useQuery<ChatSummary[]>({ const { data, isLoading } = useQuery<ChatSummary[]>({
queryKey: queryKeys.chats.list({ appId }), queryKey: queryKeys.chats.list({ appId }),
queryFn: async () => { queryFn: async () => {
return IpcClient.getInstance().getChats(appId ?? undefined); return ipc.chat.getChats(appId ?? undefined);
}, },
}); });
......
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export const useCheckName = (appName: string) => { export const useCheckName = (appName: string) => {
return useQuery({ return useQuery({
queryKey: queryKeys.appName.check({ name: appName }), queryKey: queryKeys.appName.check({ name: appName }),
queryFn: async () => { queryFn: async () => {
const result = await IpcClient.getInstance().checkAppName({ appName }); const result = await ipc.app.checkAppName({ appName });
return result; return result;
}, },
enabled: !!appName && !!appName.trim(), enabled: !!appName && !!appName.trim(),
......
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type ProblemReport } from "@/ipc/types";
import type { ProblemReport } from "@/ipc/ipc_types";
import { useSettings } from "./useSettings"; import { useSettings } from "./useSettings";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -17,8 +16,7 @@ export function useCheckProblems(appId: number | null) { ...@@ -17,8 +16,7 @@ export function useCheckProblems(appId: number | null) {
if (!appId) { if (!appId) {
throw new Error("App ID is required"); throw new Error("App ID is required");
} }
const ipcClient = IpcClient.getInstance(); return ipc.misc.checkProblems({ appId });
return ipcClient.checkProblems({ appId });
}, },
enabled: !!appId && settings?.enableAutoFixProblems, enabled: !!appId && settings?.enableAutoFixProblems,
// DO NOT SHOW ERROR TOAST. // DO NOT SHOW ERROR TOAST.
......
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useSetAtom } from "jotai"; import { useSetAtom } from "jotai";
import { activeCheckoutCounterAtom } from "@/store/appAtoms"; import { activeCheckoutCounterAtom } from "@/store/appAtoms";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -20,10 +20,9 @@ export function useCheckoutVersion() { ...@@ -20,10 +20,9 @@ export function useCheckoutVersion() {
// Should be caught by UI logic before calling, but as a safeguard. // Should be caught by UI logic before calling, but as a safeguard.
throw new Error("App ID is null, cannot checkout version."); throw new Error("App ID is null, cannot checkout version.");
} }
const ipcClient = IpcClient.getInstance();
setActiveCheckouts((prev) => prev + 1); // Increment counter setActiveCheckouts((prev) => prev + 1); // Increment counter
try { try {
await ipcClient.checkoutVersion({ appId, versionId }); await ipc.version.checkoutVersion({ appId, versionId });
} finally { } finally {
setActiveCheckouts((prev) => prev - 1); // Decrement counter setActiveCheckouts((prev) => prev - 1); // Decrement counter
} }
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { showError, showSuccess } from "@/lib/toast"; import { showError, showSuccess } from "@/lib/toast";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -14,8 +14,7 @@ export function useCommitChanges() { ...@@ -14,8 +14,7 @@ export function useCommitChanges() {
appId: number; appId: number;
message: string; message: string;
}) => { }) => {
const ipcClient = IpcClient.getInstance(); return ipc.git.commitChanges({ appId, message });
return ipcClient.commitChanges({ appId, message });
}, },
onSuccess: (_, { appId }) => { onSuccess: (_, { appId }) => {
showSuccess("Changes committed successfully"); showSuccess("Changes committed successfully");
......
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { GlobPath, ContextPathResults } from "@/lib/schemas"; import { GlobPath, ContextPathResults } from "@/lib/schemas";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -22,8 +22,7 @@ export function useContextPaths() { ...@@ -22,8 +22,7 @@ export function useContextPaths() {
smartContextAutoIncludes: [], smartContextAutoIncludes: [],
excludePaths: [], excludePaths: [],
}; };
const ipcClient = IpcClient.getInstance(); return ipc.context.getContextPaths({ appId });
return ipcClient.getChatContextResults({ appId });
}, },
enabled: !!appId, enabled: !!appId,
}); });
...@@ -43,8 +42,7 @@ export function useContextPaths() { ...@@ -43,8 +42,7 @@ export function useContextPaths() {
excludePaths, excludePaths,
}) => { }) => {
if (!appId) throw new Error("No app selected"); if (!appId) throw new Error("No app selected");
const ipcClient = IpcClient.getInstance(); return ipc.context.setContextPaths({
return ipcClient.setChatContext({
appId, appId,
chatContext: { chatContext: {
contextPaths, contextPaths,
......
...@@ -3,8 +3,7 @@ import { ...@@ -3,8 +3,7 @@ import {
useQuery, useQuery,
useQueryClient, useQueryClient,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type TokenCountResult } from "@/ipc/types";
import type { TokenCountResult } from "@/ipc/ipc_types";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
...@@ -37,7 +36,7 @@ export function useCountTokens(chatId: number | null, input: string = "") { ...@@ -37,7 +36,7 @@ export function useCountTokens(chatId: number | null, input: string = "") {
queryKey: queryKeys.tokenCount.forChat({ chatId, input: debouncedInput }), queryKey: queryKeys.tokenCount.forChat({ chatId, input: debouncedInput }),
queryFn: async () => { queryFn: async () => {
if (chatId === null) return null; if (chatId === null) return null;
return IpcClient.getInstance().countTokens({ return ipc.chat.countTokens({
chatId, chatId,
input: debouncedInput, input: debouncedInput,
}); });
......
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import type { CreateAppParams, CreateAppResult } from "@/ipc/types";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
import type { CreateAppParams, CreateAppResult } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export function useCreateApp() { export function useCreateApp() {
...@@ -13,8 +13,7 @@ export function useCreateApp() { ...@@ -13,8 +13,7 @@ export function useCreateApp() {
throw new Error("App name is required"); throw new Error("App name is required");
} }
const ipcClient = IpcClient.getInstance(); return ipc.app.createApp(params);
return ipcClient.createApp(params);
}, },
onSuccess: () => { onSuccess: () => {
// Invalidate apps list to trigger refetch // Invalidate apps list to trigger refetch
......
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type BranchResult } from "@/ipc/types";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import type { BranchResult } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export function useCurrentBranch(appId: number | null) { export function useCurrentBranch(appId: number | null) {
...@@ -16,8 +15,7 @@ export function useCurrentBranch(appId: number | null) { ...@@ -16,8 +15,7 @@ export function useCurrentBranch(appId: number | null) {
// but as a safeguard, and to ensure queryFn always has a valid appId if called. // but as a safeguard, and to ensure queryFn always has a valid appId if called.
throw new Error("appId is null, cannot fetch current branch."); throw new Error("appId is null, cannot fetch current branch.");
} }
const ipcClient = IpcClient.getInstance(); return ipc.version.getCurrentBranch({ appId });
return ipcClient.getCurrentBranch(appId);
}, },
enabled: appId !== null, enabled: appId !== null,
meta: { showErrorToast: true }, meta: { showErrorToast: true },
......
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import {
import type { ipc,
CreateCustomLanguageModelProviderParams, type CreateCustomLanguageModelProviderParams,
LanguageModelProvider, type LanguageModelProvider,
} from "@/ipc/ipc_types"; } from "@/ipc/types";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export function useCustomLanguageModelProvider() { export function useCustomLanguageModelProvider() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const ipcClient = IpcClient.getInstance();
const createProviderMutation = useMutation({ const createProviderMutation = useMutation({
mutationFn: async ( mutationFn: async (
...@@ -25,7 +24,7 @@ export function useCustomLanguageModelProvider() { ...@@ -25,7 +24,7 @@ export function useCustomLanguageModelProvider() {
throw new Error("API base URL is required"); throw new Error("API base URL is required");
} }
return ipcClient.createCustomLanguageModelProvider({ return ipc.languageModel.createCustomProvider({
id: params.id.trim(), id: params.id.trim(),
name: params.name.trim(), name: params.name.trim(),
apiBaseUrl: params.apiBaseUrl.trim(), apiBaseUrl: params.apiBaseUrl.trim(),
...@@ -57,7 +56,7 @@ export function useCustomLanguageModelProvider() { ...@@ -57,7 +56,7 @@ export function useCustomLanguageModelProvider() {
throw new Error("API base URL is required"); throw new Error("API base URL is required");
} }
return ipcClient.editCustomLanguageModelProvider({ return ipc.languageModel.editCustomProvider({
id: params.id.trim(), id: params.id.trim(),
name: params.name.trim(), name: params.name.trim(),
apiBaseUrl: params.apiBaseUrl.trim(), apiBaseUrl: params.apiBaseUrl.trim(),
...@@ -81,7 +80,7 @@ export function useCustomLanguageModelProvider() { ...@@ -81,7 +80,7 @@ export function useCustomLanguageModelProvider() {
throw new Error("Provider ID is required"); throw new Error("Provider ID is required");
} }
return ipcClient.deleteCustomLanguageModelProvider(providerId); return ipc.languageModel.deleteCustomProvider({ providerId });
}, },
onSuccess: () => { onSuccess: () => {
// Invalidate and refetch // Invalidate and refetch
......
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import type { import type {
CustomTheme, CustomTheme,
CreateCustomThemeParams, CreateCustomThemeParams,
UpdateCustomThemeParams, UpdateCustomThemeParams,
GenerateThemePromptParams, GenerateThemePromptParams,
GenerateThemePromptResult, GenerateThemePromptResult,
} from "@/ipc/ipc_types"; } from "@/ipc/types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
/** /**
...@@ -16,8 +16,7 @@ export function useCustomThemes() { ...@@ -16,8 +16,7 @@ export function useCustomThemes() {
const query = useQuery({ const query = useQuery({
queryKey: queryKeys.customThemes.all, queryKey: queryKeys.customThemes.all,
queryFn: async (): Promise<CustomTheme[]> => { queryFn: async (): Promise<CustomTheme[]> => {
const ipcClient = IpcClient.getInstance(); return ipc.template.getCustomThemes();
return ipcClient.getCustomThemes();
}, },
meta: { meta: {
showErrorToast: true, showErrorToast: true,
...@@ -39,8 +38,7 @@ export function useCreateCustomTheme() { ...@@ -39,8 +38,7 @@ export function useCreateCustomTheme() {
mutationFn: async ( mutationFn: async (
params: CreateCustomThemeParams, params: CreateCustomThemeParams,
): Promise<CustomTheme> => { ): Promise<CustomTheme> => {
const ipcClient = IpcClient.getInstance(); return ipc.template.createCustomTheme(params);
return ipcClient.createCustomTheme(params);
}, },
onSuccess: () => { onSuccess: () => {
// Invalidate all custom theme queries using prefix matching // Invalidate all custom theme queries using prefix matching
...@@ -58,8 +56,7 @@ export function useUpdateCustomTheme() { ...@@ -58,8 +56,7 @@ export function useUpdateCustomTheme() {
mutationFn: async ( mutationFn: async (
params: UpdateCustomThemeParams, params: UpdateCustomThemeParams,
): Promise<CustomTheme> => { ): Promise<CustomTheme> => {
const ipcClient = IpcClient.getInstance(); return ipc.template.updateCustomTheme(params);
return ipcClient.updateCustomTheme(params);
}, },
onSuccess: () => { onSuccess: () => {
// Invalidate all custom theme queries using prefix matching // Invalidate all custom theme queries using prefix matching
...@@ -75,8 +72,7 @@ export function useDeleteCustomTheme() { ...@@ -75,8 +72,7 @@ export function useDeleteCustomTheme() {
return useMutation({ return useMutation({
mutationFn: async (id: number): Promise<void> => { mutationFn: async (id: number): Promise<void> => {
const ipcClient = IpcClient.getInstance(); await ipc.template.deleteCustomTheme({ id });
await ipcClient.deleteCustomTheme({ id });
}, },
onSuccess: () => { onSuccess: () => {
// Invalidate all custom theme queries using prefix matching // Invalidate all custom theme queries using prefix matching
...@@ -92,8 +88,7 @@ export function useGenerateThemePrompt() { ...@@ -92,8 +88,7 @@ export function useGenerateThemePrompt() {
mutationFn: async ( mutationFn: async (
params: GenerateThemePromptParams, params: GenerateThemePromptParams,
): Promise<GenerateThemePromptResult> => { ): Promise<GenerateThemePromptResult> => {
const ipcClient = IpcClient.getInstance(); return ipc.template.generateThemePrompt(params);
return ipcClient.generateThemePrompt(params);
}, },
}); });
} }
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
interface DeleteCustomModelParams { interface DeleteCustomModelParams {
...@@ -23,9 +23,7 @@ export function useDeleteCustomModel({ ...@@ -23,9 +23,7 @@ export function useDeleteCustomModel({
"Provider ID and Model API Name are required for deletion.", "Provider ID and Model API Name are required for deletion.",
); );
} }
const ipcClient = IpcClient.getInstance(); await ipc.languageModel.deleteModel(params);
// This method will be added to IpcClient next
await ipcClient.deleteCustomModel(params);
}, },
onSuccess: (data, params: DeleteCustomModelParams) => { onSuccess: (data, params: DeleteCustomModelParams) => {
// Invalidate queries related to language models for the specific provider // Invalidate queries related to language models for the specific provider
......
...@@ -5,22 +5,21 @@ import { ...@@ -5,22 +5,21 @@ import {
lmStudioModelsLoadingAtom, lmStudioModelsLoadingAtom,
lmStudioModelsErrorAtom, lmStudioModelsErrorAtom,
} from "@/atoms/localModelsAtoms"; } from "@/atoms/localModelsAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
export function useLocalLMSModels() { export function useLocalLMSModels() {
const [models, setModels] = useAtom(lmStudioModelsAtom); const [models, setModels] = useAtom(lmStudioModelsAtom);
const [loading, setLoading] = useAtom(lmStudioModelsLoadingAtom); const [loading, setLoading] = useAtom(lmStudioModelsLoadingAtom);
const [error, setError] = useAtom(lmStudioModelsErrorAtom); const [error, setError] = useAtom(lmStudioModelsErrorAtom);
const ipcClient = IpcClient.getInstance();
/** /**
* Load local models from Ollama * Load local models from LMStudio
*/ */
const loadModels = useCallback(async () => { const loadModels = useCallback(async () => {
setLoading(true); setLoading(true);
try { try {
const modelList = await ipcClient.listLocalLMStudioModels(); const { models: modelList } =
await ipc.languageModel.listLMStudioModels();
setModels(modelList); setModels(modelList);
setError(null); setError(null);
...@@ -32,7 +31,7 @@ export function useLocalLMSModels() { ...@@ -32,7 +31,7 @@ export function useLocalLMSModels() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [ipcClient, setModels, setError, setLoading]); }, [setModels, setError, setLoading]);
return { return {
models, models,
......
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type LanguageModelProvider } from "@/ipc/types";
import type { LanguageModelProvider } from "@/ipc/ipc_types";
import { useSettings } from "./useSettings"; import { useSettings } from "./useSettings";
import { import {
cloudProviders, cloudProviders,
...@@ -10,13 +9,12 @@ import { ...@@ -10,13 +9,12 @@ import {
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export function useLanguageModelProviders() { export function useLanguageModelProviders() {
const ipcClient = IpcClient.getInstance();
const { settings, envVars } = useSettings(); const { settings, envVars } = useSettings();
const queryResult = useQuery<LanguageModelProvider[], Error>({ const queryResult = useQuery<LanguageModelProvider[], Error>({
queryKey: queryKeys.languageModels.providers, queryKey: queryKeys.languageModels.providers,
queryFn: async () => { queryFn: async () => {
return ipcClient.getLanguageModelProviders(); return ipc.languageModel.getProviders();
}, },
}); });
......
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type LanguageModel } from "@/ipc/types";
import type { LanguageModel } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
/** /**
...@@ -9,12 +8,10 @@ import { queryKeys } from "@/lib/queryKeys"; ...@@ -9,12 +8,10 @@ import { queryKeys } from "@/lib/queryKeys";
* @returns TanStack Query result object for the language models organized by provider. * @returns TanStack Query result object for the language models organized by provider.
*/ */
export function useLanguageModelsByProviders() { export function useLanguageModelsByProviders() {
const ipcClient = IpcClient.getInstance();
return useQuery<Record<string, LanguageModel[]>, Error>({ return useQuery<Record<string, LanguageModel[]>, Error>({
queryKey: queryKeys.languageModels.byProviders, queryKey: queryKeys.languageModels.byProviders,
queryFn: async () => { queryFn: async () => {
return ipcClient.getLanguageModelsByProviders(); return ipc.languageModel.getModelsByProviders();
}, },
}); });
} }
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type LanguageModel } from "@/ipc/types";
import type { LanguageModel } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
/** /**
...@@ -10,8 +9,6 @@ import { queryKeys } from "@/lib/queryKeys"; ...@@ -10,8 +9,6 @@ import { queryKeys } from "@/lib/queryKeys";
* @returns TanStack Query result object for the language models. * @returns TanStack Query result object for the language models.
*/ */
export function useLanguageModelsForProvider(providerId: string | undefined) { export function useLanguageModelsForProvider(providerId: string | undefined) {
const ipcClient = IpcClient.getInstance();
return useQuery< return useQuery<
LanguageModel[], LanguageModel[],
Error // Specify Error type for better error handling Error // Specify Error type for better error handling
...@@ -25,7 +22,7 @@ export function useLanguageModelsForProvider(providerId: string | undefined) { ...@@ -25,7 +22,7 @@ export function useLanguageModelsForProvider(providerId: string | undefined) {
// Return an empty array as it's a query, not an error state // Return an empty array as it's a query, not an error state
return []; return [];
} }
return ipcClient.getLanguageModels({ providerId }); return ipc.languageModel.getModels({ providerId });
}, },
enabled: !!providerId, enabled: !!providerId,
}); });
......
import { useEffect } from "react"; import { useEffect } from "react";
import { useQuery, QueryClient } from "@tanstack/react-query"; import { useQuery, QueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc, type App } from "@/ipc/types";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { currentAppAtom } from "@/atoms/appAtoms"; import { currentAppAtom } from "@/atoms/appAtoms";
import { App } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export function useLoadApp(appId: number | null) { export function useLoadApp(appId: number | null) {
...@@ -20,8 +19,7 @@ export function useLoadApp(appId: number | null) { ...@@ -20,8 +19,7 @@ export function useLoadApp(appId: number | null) {
if (appId === null) { if (appId === null) {
return null; return null;
} }
const ipcClient = IpcClient.getInstance(); return ipc.app.getApp(appId);
return ipcClient.getApp(appId);
}, },
enabled: appId !== null, enabled: appId !== null,
// Deliberately not showing error toast here because // Deliberately not showing error toast here because
......
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
export function useLoadAppFile(appId: number | null, filePath: string | null) { export function useLoadAppFile(appId: number | null, filePath: string | null) {
const [content, setContent] = useState<string | null>(null); const [content, setContent] = useState<string | null>(null);
...@@ -16,8 +16,7 @@ export function useLoadAppFile(appId: number | null, filePath: string | null) { ...@@ -16,8 +16,7 @@ export function useLoadAppFile(appId: number | null, filePath: string | null) {
setLoading(true); setLoading(true);
try { try {
const ipcClient = IpcClient.getInstance(); const fileContent = await ipc.app.readAppFile({ appId, filePath });
const fileContent = await ipcClient.readAppFile(appId, filePath);
setContent(fileContent); setContent(fileContent);
setError(null); setError(null);
...@@ -43,8 +42,7 @@ export function useLoadAppFile(appId: number | null, filePath: string | null) { ...@@ -43,8 +42,7 @@ export function useLoadAppFile(appId: number | null, filePath: string | null) {
setLoading(true); setLoading(true);
try { try {
const ipcClient = IpcClient.getInstance(); const fileContent = await ipc.app.readAppFile({ appId, filePath });
const fileContent = await ipcClient.readAppFile(appId, filePath);
setContent(fileContent); setContent(fileContent);
setError(null); setError(null);
} catch (error) { } catch (error) {
......
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { appsListAtom } from "@/atoms/appAtoms"; import { appsListAtom } from "@/atoms/appAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
export function useLoadApps() { export function useLoadApps() {
const [apps, setApps] = useAtom(appsListAtom); const [apps, setApps] = useAtom(appsListAtom);
...@@ -11,8 +11,7 @@ export function useLoadApps() { ...@@ -11,8 +11,7 @@ export function useLoadApps() {
const refreshApps = useCallback(async () => { const refreshApps = useCallback(async () => {
setLoading(true); setLoading(true);
try { try {
const ipcClient = IpcClient.getInstance(); const appListResponse = await ipc.app.listApps();
const appListResponse = await ipcClient.listApps();
setApps(appListResponse.apps); setApps(appListResponse.apps);
setError(null); setError(null);
} catch (error) { } catch (error) {
......
...@@ -5,22 +5,20 @@ import { ...@@ -5,22 +5,20 @@ import {
localModelsLoadingAtom, localModelsLoadingAtom,
localModelsErrorAtom, localModelsErrorAtom,
} from "@/atoms/localModelsAtoms"; } from "@/atoms/localModelsAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
export function useLocalModels() { export function useLocalModels() {
const [models, setModels] = useAtom(localModelsAtom); const [models, setModels] = useAtom(localModelsAtom);
const [loading, setLoading] = useAtom(localModelsLoadingAtom); const [loading, setLoading] = useAtom(localModelsLoadingAtom);
const [error, setError] = useAtom(localModelsErrorAtom); const [error, setError] = useAtom(localModelsErrorAtom);
const ipcClient = IpcClient.getInstance();
/** /**
* Load local models from Ollama * Load local models from Ollama
*/ */
const loadModels = useCallback(async () => { const loadModels = useCallback(async () => {
setLoading(true); setLoading(true);
try { try {
const modelList = await ipcClient.listLocalOllamaModels(); const { models: modelList } = await ipc.languageModel.listOllamaModels();
setModels(modelList); setModels(modelList);
setError(null); setError(null);
...@@ -32,7 +30,7 @@ export function useLocalModels() { ...@@ -32,7 +30,7 @@ export function useLocalModels() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [ipcClient, setModels, setError, setLoading]); }, [setModels, setError, setLoading]);
return { return {
models, models,
......
import { useMemo } from "react"; import { useMemo } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import type { import type {
McpServer, McpServer,
McpServerUpdate, McpServerUpdate,
McpTool, McpTool,
McpToolConsent, McpToolConsent,
CreateMcpServer, CreateMcpServer,
} from "@/ipc/ipc_types"; } from "@/ipc/types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export type Transport = "stdio" | "http"; export type Transport = "stdio" | "http";
...@@ -18,8 +18,7 @@ export function useMcp() { ...@@ -18,8 +18,7 @@ export function useMcp() {
const serversQuery = useQuery<McpServer[], Error>({ const serversQuery = useQuery<McpServer[], Error>({
queryKey: queryKeys.mcp.servers, queryKey: queryKeys.mcp.servers,
queryFn: async () => { queryFn: async () => {
const ipc = IpcClient.getInstance(); const list = await ipc.mcp.listServers();
const list = await ipc.listMcpServers();
return (list || []) as McpServer[]; return (list || []) as McpServer[];
}, },
meta: { showErrorToast: true }, meta: { showErrorToast: true },
...@@ -34,9 +33,8 @@ export function useMcp() { ...@@ -34,9 +33,8 @@ export function useMcp() {
queryKey: queryKeys.mcp.toolsByServer.list({ serverIds }), queryKey: queryKeys.mcp.toolsByServer.list({ serverIds }),
enabled: serverIds.length > 0, enabled: serverIds.length > 0,
queryFn: async () => { queryFn: async () => {
const ipc = IpcClient.getInstance();
const entries = await Promise.all( const entries = await Promise.all(
serverIds.map(async (id) => [id, await ipc.listMcpTools(id)] as const), serverIds.map(async (id) => [id, await ipc.mcp.listTools(id)] as const),
); );
return Object.fromEntries(entries) as Record<number, McpTool[]>; return Object.fromEntries(entries) as Record<number, McpTool[]>;
}, },
...@@ -46,8 +44,7 @@ export function useMcp() { ...@@ -46,8 +44,7 @@ export function useMcp() {
const consentsQuery = useQuery<McpToolConsent[], Error>({ const consentsQuery = useQuery<McpToolConsent[], Error>({
queryKey: queryKeys.mcp.consents, queryKey: queryKeys.mcp.consents,
queryFn: async () => { queryFn: async () => {
const ipc = IpcClient.getInstance(); const list = await ipc.mcp.getToolConsents();
const list = await ipc.getMcpToolConsents();
return (list || []) as McpToolConsent[]; return (list || []) as McpToolConsent[];
}, },
meta: { showErrorToast: true }, meta: { showErrorToast: true },
...@@ -63,8 +60,7 @@ export function useMcp() { ...@@ -63,8 +60,7 @@ export function useMcp() {
const createServerMutation = useMutation({ const createServerMutation = useMutation({
mutationFn: async (params: CreateMcpServer) => { mutationFn: async (params: CreateMcpServer) => {
const ipc = IpcClient.getInstance(); return ipc.mcp.createServer(params);
return ipc.createMcpServer(params);
}, },
onSuccess: async () => { onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers }); await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers });
...@@ -77,8 +73,7 @@ export function useMcp() { ...@@ -77,8 +73,7 @@ export function useMcp() {
const updateServerMutation = useMutation({ const updateServerMutation = useMutation({
mutationFn: async (params: McpServerUpdate) => { mutationFn: async (params: McpServerUpdate) => {
const ipc = IpcClient.getInstance(); return ipc.mcp.updateServer(params);
return ipc.updateMcpServer(params);
}, },
onSuccess: async () => { onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers }); await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers });
...@@ -91,8 +86,7 @@ export function useMcp() { ...@@ -91,8 +86,7 @@ export function useMcp() {
const deleteServerMutation = useMutation({ const deleteServerMutation = useMutation({
mutationFn: async (id: number) => { mutationFn: async (id: number) => {
const ipc = IpcClient.getInstance(); return ipc.mcp.deleteServer(id);
return ipc.deleteMcpServer(id);
}, },
onSuccess: async () => { onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers }); await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers });
...@@ -109,8 +103,7 @@ export function useMcp() { ...@@ -109,8 +103,7 @@ export function useMcp() {
toolName: string; toolName: string;
consent: McpToolConsent["consent"]; consent: McpToolConsent["consent"];
}) => { }) => {
const ipc = IpcClient.getInstance(); return ipc.mcp.setToolConsent(params);
return ipc.setMcpToolConsent(params);
}, },
onSuccess: async () => { onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.consents }); await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.consents });
......
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { ipc } from "@/ipc/types";
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
export interface PromptItem { export interface PromptItem {
...@@ -16,8 +16,7 @@ export function usePrompts() { ...@@ -16,8 +16,7 @@ export function usePrompts() {
const listQuery = useQuery({ const listQuery = useQuery({
queryKey: queryKeys.prompts.all, queryKey: queryKeys.prompts.all,
queryFn: async (): Promise<PromptItem[]> => { queryFn: async (): Promise<PromptItem[]> => {
const ipc = IpcClient.getInstance(); return ipc.prompt.list();
return ipc.listPrompts();
}, },
meta: { showErrorToast: true }, meta: { showErrorToast: true },
}); });
...@@ -28,8 +27,7 @@ export function usePrompts() { ...@@ -28,8 +27,7 @@ export function usePrompts() {
description?: string; description?: string;
content: string; content: string;
}): Promise<PromptItem> => { }): Promise<PromptItem> => {
const ipc = IpcClient.getInstance(); return ipc.prompt.create(params);
return ipc.createPrompt(params);
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all }); queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all });
...@@ -46,8 +44,7 @@ export function usePrompts() { ...@@ -46,8 +44,7 @@ export function usePrompts() {
description?: string; description?: string;
content: string; content: string;
}): Promise<void> => { }): Promise<void> => {
const ipc = IpcClient.getInstance(); return ipc.prompt.update(params);
return ipc.updatePrompt(params);
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all }); queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all });
...@@ -59,8 +56,7 @@ export function usePrompts() { ...@@ -59,8 +56,7 @@ export function usePrompts() {
const deleteMutation = useMutation({ const deleteMutation = useMutation({
mutationFn: async (id: number): Promise<void> => { mutationFn: async (id: number): Promise<void> => {
const ipc = IpcClient.getInstance(); return ipc.prompt.delete(id);
return ipc.deletePrompt(id);
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all }); queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all });
......
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论