diff --git a/e2e-tests/auto_update.spec.ts b/e2e-tests/auto_update.spec.ts new file mode 100644 index 0000000..f03b7ac --- /dev/null +++ b/e2e-tests/auto_update.spec.ts @@ -0,0 +1,15 @@ +import { expect } from "@playwright/test"; +import { test } from "./helpers/test_helper"; + +test("auto update - disable and enable", async ({ po }) => { + await po.goToSettingsTab(); + + await po.toggleAutoUpdate(); + await expect( + po.page.getByRole("button", { name: "Restart Dyad" }), + ).toBeVisible(); + await po.snapshotSettings(); + + await po.toggleAutoUpdate(); + await po.snapshotSettings(); +}); diff --git a/e2e-tests/helpers/test_helper.ts b/e2e-tests/helpers/test_helper.ts index 3fbf366..4453015 100644 --- a/e2e-tests/helpers/test_helper.ts +++ b/e2e-tests/helpers/test_helper.ts @@ -833,6 +833,10 @@ export class PageObject { expect(sanitizedSettingsContent).toMatchSnapshot(); } + async toggleAutoUpdate() { + await this.page.getByRole("switch", { name: "Auto-update" }).click(); + } + async clickTelemetryAccept() { await this.page.getByTestId("telemetry-accept-button").click(); } diff --git a/e2e-tests/snapshots/auto_update.spec.ts_auto-update---disable-and-enable-1.txt b/e2e-tests/snapshots/auto_update.spec.ts_auto-update---disable-and-enable-1.txt new file mode 100644 index 0000000..da4fa25 --- /dev/null +++ b/e2e-tests/snapshots/auto_update.spec.ts_auto-update---disable-and-enable-1.txt @@ -0,0 +1,18 @@ +{ + "selectedModel": { + "name": "auto", + "provider": "auto" + }, + "providerSettings": {}, + "telemetryConsent": "unset", + "telemetryUserId": "[UUID]", + "hasRunBefore": true, + "experiments": {}, + "lastShownReleaseNotesVersion": "[scrubbed]", + "enableProLazyEditsMode": true, + "enableProSmartFilesContextMode": true, + "selectedChatMode": "build", + "enableAutoFixProblems": false, + "enableAutoUpdate": false, + "isTestMode": true +} \ No newline at end of file diff --git a/e2e-tests/snapshots/auto_update.spec.ts_auto-update---disable-and-enable-2.txt b/e2e-tests/snapshots/auto_update.spec.ts_auto-update---disable-and-enable-2.txt new file mode 100644 index 0000000..2e1bf3b --- /dev/null +++ b/e2e-tests/snapshots/auto_update.spec.ts_auto-update---disable-and-enable-2.txt @@ -0,0 +1,18 @@ +{ + "selectedModel": { + "name": "auto", + "provider": "auto" + }, + "providerSettings": {}, + "telemetryConsent": "unset", + "telemetryUserId": "[UUID]", + "hasRunBefore": true, + "experiments": {}, + "lastShownReleaseNotesVersion": "[scrubbed]", + "enableProLazyEditsMode": true, + "enableProSmartFilesContextMode": true, + "selectedChatMode": "build", + "enableAutoFixProblems": false, + "enableAutoUpdate": true, + "isTestMode": true +} \ No newline at end of file diff --git a/e2e-tests/snapshots/context_window.spec.ts_context-window-4.txt b/e2e-tests/snapshots/context_window.spec.ts_context-window-4.txt index 0fa9f0d..fd7eebb 100644 --- a/e2e-tests/snapshots/context_window.spec.ts_context-window-4.txt +++ b/e2e-tests/snapshots/context_window.spec.ts_context-window-4.txt @@ -15,5 +15,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---accept-1.txt b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---accept-1.txt index 74df747..b19297a 100644 --- a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---accept-1.txt +++ b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---accept-1.txt @@ -12,5 +12,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---accept-2.txt b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---accept-2.txt index 971922d..840f152 100644 --- a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---accept-2.txt +++ b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---accept-2.txt @@ -13,5 +13,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---later-1.txt b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---later-1.txt index 74df747..b19297a 100644 --- a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---later-1.txt +++ b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---later-1.txt @@ -12,5 +12,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---later-2.txt b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---later-2.txt index 435a6b2..2e1bf3b 100644 --- a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---later-2.txt +++ b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---later-2.txt @@ -13,5 +13,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---reject-1.txt b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---reject-1.txt index 74df747..b19297a 100644 --- a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---reject-1.txt +++ b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---reject-1.txt @@ -12,5 +12,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---reject-2.txt b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---reject-2.txt index d21a48f..74242ef 100644 --- a/e2e-tests/snapshots/telemetry.spec.ts_telemetry---reject-2.txt +++ b/e2e-tests/snapshots/telemetry.spec.ts_telemetry---reject-2.txt @@ -13,5 +13,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-1.txt b/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-1.txt index bf5a86c..679208c 100644 --- a/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-1.txt +++ b/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-1.txt @@ -22,5 +22,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-3.txt b/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-3.txt index 0564e79..639ac63 100644 --- a/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-3.txt +++ b/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-3.txt @@ -22,5 +22,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-5.txt b/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-5.txt index b215448..d245e7a 100644 --- a/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-5.txt +++ b/e2e-tests/snapshots/thinking_budget.spec.ts_thinking-budget-5.txt @@ -22,5 +22,6 @@ "enableProSmartFilesContextMode": true, "selectedChatMode": "build", "enableAutoFixProblems": false, + "enableAutoUpdate": true, "isTestMode": true } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a1e46a7..168c2e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dyad", - "version": "0.8.0", + "version": "0.11.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dyad", - "version": "0.8.0", + "version": "0.11.1", "license": "MIT", "dependencies": { "@ai-sdk/anthropic": "^1.2.8", @@ -24,6 +24,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.7", "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-popover": "^1.1.7", + "@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-select": "^2.2.2", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.2.2", @@ -4680,6 +4681,78 @@ } } }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.9.tgz", + "integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-select": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.2.tgz", diff --git a/package.json b/package.json index 7fa6660..faac659 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.7", "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-popover": "^1.1.7", + "@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-select": "^2.2.2", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.2.2", diff --git a/src/components/AutoUpdateSwitch.tsx b/src/components/AutoUpdateSwitch.tsx new file mode 100644 index 0000000..1e60206 --- /dev/null +++ b/src/components/AutoUpdateSwitch.tsx @@ -0,0 +1,36 @@ +import { useSettings } from "@/hooks/useSettings"; +import { Label } from "@/components/ui/label"; +import { Switch } from "@/components/ui/switch"; +import { toast } from "sonner"; +import { IpcClient } from "@/ipc/ipc_client"; + +export function AutoUpdateSwitch() { + const { settings, updateSettings } = useSettings(); + + if (!settings) { + return null; + } + + return ( +
+ { + updateSettings({ enableAutoUpdate: checked }); + toast("Auto-update settings changed", { + description: + "You will need to restart Dyad for your settings to take effect.", + action: { + label: "Restart Dyad", + onClick: () => { + IpcClient.getInstance().restartDyad(); + }, + }, + }); + }} + /> + +
+ ); +} diff --git a/src/components/ProviderSettings.tsx b/src/components/ProviderSettings.tsx index 5fe55dd..0292ca3 100644 --- a/src/components/ProviderSettings.tsx +++ b/src/components/ProviderSettings.tsx @@ -68,7 +68,7 @@ export function ProviderSettingsGrid() { if (isLoading) { return (
-

AI Providers

+

AI Providers

{[1, 2, 3, 4, 5].map((i) => ( @@ -86,7 +86,7 @@ export function ProviderSettingsGrid() { if (error) { return (
-

AI Providers

+

AI Providers

Error @@ -100,7 +100,7 @@ export function ProviderSettingsGrid() { return (
-

AI Providers

+

AI Providers

{providers ?.filter((p) => p.type !== "local") @@ -116,7 +116,7 @@ export function ProviderSettingsGrid() { className="p-4 cursor-pointer" onClick={() => handleProviderClick(provider.id)} > - + {provider.name} {isProviderSetup(provider.id) ? ( @@ -178,8 +178,8 @@ export function ProviderSettingsGrid() { onClick={() => setIsDialogOpen(true)} > - - + + Add custom provider diff --git a/src/components/SettingsList.tsx b/src/components/SettingsList.tsx new file mode 100644 index 0000000..95610f1 --- /dev/null +++ b/src/components/SettingsList.tsx @@ -0,0 +1,88 @@ +import { ScrollArea } from "@/components/ui/scroll-area"; +import { cn } from "@/lib/utils"; +import { useNavigate } from "@tanstack/react-router"; +import { useEffect, useState } from "react"; + +const SETTINGS_SECTIONS = [ + { id: "general-settings", label: "General" }, + { id: "workflow-settings", label: "Workflow" }, + { id: "ai-settings", label: "AI" }, + { id: "provider-settings", label: "Model Providers" }, + { id: "telemetry", label: "Telemetry" }, + { id: "integrations", label: "Integrations" }, + { id: "experiments", label: "Experiments" }, + { id: "danger-zone", label: "Danger Zone" }, +]; + +export function SettingsList({ show }: { show: boolean }) { + const navigate = useNavigate(); + const [activeSection, setActiveSection] = useState( + "general-settings", + ); + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + setActiveSection(entry.target.id); + return; + } + } + }, + { rootMargin: "-20% 0px -80% 0px", threshold: 0 }, + ); + + for (const section of SETTINGS_SECTIONS) { + const el = document.getElementById(section.id); + if (el) { + observer.observe(el); + } + } + + return () => { + observer.disconnect(); + }; + }, []); + + if (!show) { + return null; + } + + const handleScrollAndNavigateTo = async (id: string) => { + await navigate({ + to: "/settings", + }); + const element = document.getElementById(id); + if (element) { + element.scrollIntoView({ behavior: "smooth", block: "start" }); + setActiveSection(id); + } + }; + + return ( +
+
+

Settings

+
+ +
+ {SETTINGS_SECTIONS.map((section) => ( + + ))} +
+
+
+ ); +} diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index 21eb4c1..41df181 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -20,6 +20,7 @@ import { import { ChatList } from "./ChatList"; import { AppList } from "./AppList"; import { HelpDialog } from "./HelpDialog"; // Import the new dialog +import { SettingsList } from "./SettingsList"; // Menu items. const items = [ @@ -49,6 +50,7 @@ const items = [ type HoverState = | "start-hover:app" | "start-hover:chat" + | "start-hover:settings" | "clear-hover" | "no-hover"; @@ -60,10 +62,7 @@ export function AppSidebar() { const [isDropdownOpen] = useAtom(dropdownOpenAtom); useEffect(() => { - if ( - (hoverState === "start-hover:app" || hoverState === "start-hover:chat") && - state === "collapsed" - ) { + if (hoverState.startsWith("start-hover") && state === "collapsed") { expandedByHover.current = true; toggleSidebar(); } @@ -84,17 +83,22 @@ export function AppSidebar() { routerState.location.pathname === "/" || routerState.location.pathname.startsWith("/app-details"); const isChatRoute = routerState.location.pathname === "/chat"; + const isSettingsRoute = routerState.location.pathname.startsWith("/settings"); let selectedItem: string | null = null; if (hoverState === "start-hover:app") { selectedItem = "Apps"; } else if (hoverState === "start-hover:chat") { selectedItem = "Chat"; + } else if (hoverState === "start-hover:settings") { + selectedItem = "Settings"; } else if (state === "expanded") { if (isAppRoute) { selectedItem = "Apps"; } else if (isChatRoute) { selectedItem = "Chat"; + } else if (isSettingsRoute) { + selectedItem = "Settings"; } } @@ -122,6 +126,7 @@ export function AppSidebar() {
+
@@ -188,6 +193,8 @@ function AppIcons({ onHoverChange("start-hover:app"); } else if (item.title === "Chat") { onHoverChange("start-hover:chat"); + } else if (item.title === "Settings") { + onHoverChange("start-hover:settings"); } }} > diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000..80b954a --- /dev/null +++ b/src/components/ui/scroll-area.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; + +import { cn } from "@/lib/utils"; + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)); +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)); +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; + +export { ScrollArea, ScrollBar }; diff --git a/src/ipc/handlers/app_handlers.ts b/src/ipc/handlers/app_handlers.ts index ad22f04..1545df6 100644 --- a/src/ipc/handlers/app_handlers.ts +++ b/src/ipc/handlers/app_handlers.ts @@ -1,4 +1,4 @@ -import { ipcMain } from "electron"; +import { ipcMain, app } from "electron"; import { db, getDatabasePath } from "../../db"; import { apps, chats } from "../../db/schema"; import { desc, eq } from "drizzle-orm"; @@ -186,6 +186,11 @@ async function killProcessOnPort(port: number): Promise { } export function registerAppHandlers() { + handle("restart-dyad", async () => { + app.relaunch(); + app.quit(); + }); + handle( "create-app", async ( diff --git a/src/ipc/ipc_client.ts b/src/ipc/ipc_client.ts index aa43a99..0fb4fb0 100644 --- a/src/ipc/ipc_client.ts +++ b/src/ipc/ipc_client.ts @@ -165,6 +165,10 @@ export class IpcClient { return IpcClient.instance; } + public async restartDyad(): Promise { + await this.ipcRenderer.invoke("restart-dyad"); + } + public async reloadEnvPath(): Promise { await this.ipcRenderer.invoke("reload-env-path"); } diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts index 82c5a7a..4965ed7 100644 --- a/src/lib/schemas.ts +++ b/src/lib/schemas.ts @@ -151,6 +151,7 @@ export const UserSettingsSchema = z.object({ enableAutoFixProblems: z.boolean().optional(), enableNativeGit: z.boolean().optional(), + enableAutoUpdate: z.boolean(), //////////////////////////////// // E2E TESTING ONLY. diff --git a/src/main.ts b/src/main.ts index bc90ad0..af3e4f0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,7 +17,11 @@ log.scope.labelPadding = false; const logger = log.scope("main"); -updateElectronApp(); // additional configuration options available +// Check settings before enabling auto-update +const settings = readSettings(); +if (settings.enableAutoUpdate) { + updateElectronApp({ logger }); // additional configuration options available +} // Load environment variables from .env file dotenv.config(); diff --git a/src/main/settings.ts b/src/main/settings.ts index 2b79ddf..fc7f376 100644 --- a/src/main/settings.ts +++ b/src/main/settings.ts @@ -21,6 +21,7 @@ const DEFAULT_SETTINGS: UserSettings = { enableProSmartFilesContextMode: true, selectedChatMode: "build", enableAutoFixProblems: false, + enableAutoUpdate: true, }; const SETTINGS_FILE = "user-settings.json"; diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 3f285c4..6c279e3 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -18,9 +18,9 @@ import { SupabaseIntegration } from "@/components/SupabaseIntegration"; import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; import { AutoFixProblemsSwitch } from "@/components/AutoFixProblemsSwitch"; +import { AutoUpdateSwitch } from "@/components/AutoUpdateSwitch"; export default function SettingsPage() { - const { theme, setTheme } = useTheme(); const [isResetDialogOpen, setIsResetDialogOpen] = useState(false); const [isResetting, setIsResetting] = useState(false); const appVersion = useAppVersion(); @@ -60,108 +60,25 @@ export default function SettingsPage() {

Settings

- - {/* App Version Section */} -
- App Version: - - {appVersion ? appVersion : "-"} - -
-
-

- General Settings -

+ + + -
-
- - -
- {(["system", "light", "dark"] as const).map((option) => ( - - ))} -
-
-
- -
- -
- This will automatically approve code changes and run them. -
-
- -
- -
- This will automatically fix TypeScript errors. -
-
- -
-
- { - updateSettings({ - enableNativeGit: checked, - }); - }} - /> - -
- -
- -
- -
- -
- -
-
- -
+
-
+

Telemetry

@@ -182,7 +99,10 @@ export default function SettingsPage() {
{/* Integrations Section */} -
+

Integrations

@@ -193,41 +113,74 @@ export default function SettingsPage() {
{/* Experiments Section */} -
+

Experiments

- {/* Enable File Editing Experiment */} -
- - { - updateSettings({ - experiments: { - ...settings?.experiments, - enableFileEditing: checked, - }, - }); - }} - /> +
+
+ { + updateSettings({ + enableNativeGit: checked, + }); + }} + /> + +
+ +
+ {/* Enable File Editing Experiment */} +
+
+ + updateSettings({ + experiments: { + ...settings?.experiments, + enableFileEditing: checked, + }, + }) + } + /> + +
+
+ File editing is not reliable and requires you to manually + commit changes and update Supabase edge functions. +
-

- File editing is not reliable and requires you to manually commit - changes and update Supabase edge functions. -

{/* Danger Zone */} -
+

Danger Zone

@@ -268,3 +221,108 @@ export default function SettingsPage() {
); } + +export function GeneralSettings({ appVersion }: { appVersion: string | null }) { + const { theme, setTheme } = useTheme(); + + return ( +
+

+ General Settings +

+ +
+
+ + +
+ {(["system", "light", "dark"] as const).map((option) => ( + + ))} +
+
+
+ +
+ +
+ This will automatically update the app when new versions are + available. +
+
+ +
+ App Version: + + {appVersion ? appVersion : "-"} + +
+
+ ); +} + +export function WorkflowSettings() { + return ( +
+

+ Workflow Settings +

+ +
+ +
+ This will automatically approve code changes and run them. +
+
+ +
+ +
+ This will automatically fix TypeScript errors. +
+
+
+ ); +} +export function AISettings() { + return ( +
+

+ AI Settings +

+ +
+ +
+ +
+ +
+
+ ); +} diff --git a/src/preload.ts b/src/preload.ts index 51456a8..4394445 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -89,6 +89,7 @@ const validInvokeChannels = [ "open-ios", "open-android", "check-problems", + "restart-dyad", // Test-only channels // These should ALWAYS be guarded with IS_TEST_BUILD in the main process. // We can't detect with IS_TEST_BUILD in the preload script because