diff --git a/apps/desktop/src/lib/trpc/routers/settings/index.ts b/apps/desktop/src/lib/trpc/routers/settings/index.ts
index 93418fa3d72..ce624581628 100644
--- a/apps/desktop/src/lib/trpc/routers/settings/index.ts
+++ b/apps/desktop/src/lib/trpc/routers/settings/index.ts
@@ -1,5 +1,6 @@
import { settings, type TerminalPreset } from "@superset/local-db";
import { localDb } from "main/lib/local-db";
+import { DEFAULT_CONFIRM_ON_QUIT } from "shared/constants";
import { DEFAULT_RINGTONE_ID, RINGTONES } from "shared/ringtones";
import { z } from "zod";
import { publicProcedure, router } from "../..";
@@ -158,5 +159,26 @@ export const createSettingsRouter = () => {
return { success: true };
}),
+
+ getConfirmOnQuit: publicProcedure.query(() => {
+ const row = getSettings();
+ // Default to true (confirm on quit enabled by default)
+ return row.confirmOnQuit ?? DEFAULT_CONFIRM_ON_QUIT;
+ }),
+
+ setConfirmOnQuit: publicProcedure
+ .input(z.object({ enabled: z.boolean() }))
+ .mutation(({ input }) => {
+ localDb
+ .insert(settings)
+ .values({ id: 1, confirmOnQuit: input.enabled })
+ .onConflictDoUpdate({
+ target: settings.id,
+ set: { confirmOnQuit: input.enabled },
+ })
+ .run();
+
+ return { success: true };
+ }),
});
};
diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts
index ea5541318fc..10e3a9afb4f 100644
--- a/apps/desktop/src/main/index.ts
+++ b/apps/desktop/src/main/index.ts
@@ -3,9 +3,10 @@ import { initSentry } from "./lib/sentry";
initSentry();
import path from "node:path";
-import { app, BrowserWindow } from "electron";
+import { settings } from "@superset/local-db";
+import { app, BrowserWindow, dialog } from "electron";
import { makeAppSetup } from "lib/electron-app/factories/app/setup";
-import { PROTOCOL_SCHEME } from "shared/constants";
+import { DEFAULT_CONFIRM_ON_QUIT, PROTOCOL_SCHEME } from "shared/constants";
import { setupAgentHooks } from "./lib/agent-setup";
import { posthog } from "./lib/analytics";
import { initAppState } from "./lib/app-state";
@@ -91,10 +92,100 @@ app.on("open-url", async (event, url) => {
await processDeepLink(url);
});
-// Track when app is quitting to suppress expected termination errors
+type QuitState =
+ | "idle"
+ | "confirming"
+ | "confirmed"
+ | "cleaning"
+ | "ready-to-quit";
+let quitState: QuitState = "idle";
let isQuitting = false;
-app.on("before-quit", () => {
+let skipConfirmation = false;
+
+/**
+ * Check if the user has enabled the confirm-on-quit setting
+ */
+function getConfirmOnQuitSetting(): boolean {
+ try {
+ const row = localDb.select().from(settings).get();
+ return row?.confirmOnQuit ?? DEFAULT_CONFIRM_ON_QUIT;
+ } catch {
+ return DEFAULT_CONFIRM_ON_QUIT;
+ }
+}
+
+/**
+ * Skip the confirmation dialog for the next quit (e.g., auto-updater)
+ */
+export function setSkipQuitConfirmation(): void {
+ skipConfirmation = true;
+}
+
+/**
+ * Skip the confirmation dialog and quit immediately
+ */
+export function quitWithoutConfirmation(): void {
+ skipConfirmation = true;
+ app.quit();
+}
+
+app.on("before-quit", async (event) => {
isQuitting = true;
+
+ if (quitState === "ready-to-quit") return;
+ if (quitState === "cleaning" || quitState === "confirming") {
+ event.preventDefault();
+ return;
+ }
+
+ // Check if we need to show confirmation
+ if (quitState === "idle") {
+ const shouldConfirm = !skipConfirmation && getConfirmOnQuitSetting();
+
+ if (shouldConfirm) {
+ event.preventDefault();
+ quitState = "confirming";
+
+ try {
+ const { response } = await dialog.showMessageBox({
+ type: "question",
+ buttons: ["Quit", "Cancel"],
+ defaultId: 0,
+ cancelId: 1,
+ title: "Quit Superset",
+ message: "Are you sure you want to quit?",
+ });
+
+ if (response === 1) {
+ // User cancelled
+ quitState = "idle";
+ isQuitting = false;
+ return;
+ }
+ } catch (error) {
+ // Dialog failed - proceed with quit to avoid stuck state
+ console.error("[main] Quit confirmation dialog failed:", error);
+ }
+
+ // User confirmed or dialog failed, proceed with quit
+ quitState = "confirmed";
+ app.quit();
+ return;
+ }
+
+ // No confirmation needed
+ quitState = "confirmed";
+ }
+
+ event.preventDefault();
+ quitState = "cleaning";
+
+ try {
+ await Promise.all([terminalManager.cleanup(), posthog?.shutdown()]);
+ } finally {
+ quitState = "ready-to-quit";
+ app.quit();
+ }
});
process.on("uncaughtException", (error) => {
@@ -111,8 +202,8 @@ process.on("unhandledRejection", (reason) => {
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
- // Another instance is already running, quit this one
- app.quit();
+ // Another instance is already running, exit immediately without triggering before-quit
+ app.exit(0);
} else {
// Handle deep links when second instance is launched (Windows/Linux)
app.on("second-instance", async (_event, argv) => {
@@ -144,10 +235,5 @@ if (!gotTheLock) {
if (coldStartUrl) {
await processDeepLink(coldStartUrl);
}
-
- // Clean up all terminals and analytics when app is quitting
- app.on("before-quit", async () => {
- await Promise.all([terminalManager.cleanup(), posthog?.shutdown()]);
- });
})();
}
diff --git a/apps/desktop/src/main/lib/auto-updater.ts b/apps/desktop/src/main/lib/auto-updater.ts
index 0bc07423b84..739846884b7 100644
--- a/apps/desktop/src/main/lib/auto-updater.ts
+++ b/apps/desktop/src/main/lib/auto-updater.ts
@@ -2,6 +2,7 @@ import { EventEmitter } from "node:events";
import { app, dialog } from "electron";
import { autoUpdater } from "electron-updater";
import { env } from "main/env.main";
+import { setSkipQuitConfirmation } from "main/index";
import { AUTO_UPDATE_STATUS, type AutoUpdateStatus } from "shared/auto-update";
import { PLATFORM } from "shared/constants";
@@ -49,6 +50,8 @@ export function installUpdate(): void {
emitStatus(AUTO_UPDATE_STATUS.IDLE);
return;
}
+ // Skip confirmation dialog - quitAndInstall internally calls app.quit()
+ setSkipQuitConfirmation();
autoUpdater.quitAndInstall(false, true);
}
diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/BehaviorSettings.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/BehaviorSettings.tsx
new file mode 100644
index 00000000000..f21aff0a34f
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/BehaviorSettings.tsx
@@ -0,0 +1,64 @@
+import { Label } from "@superset/ui/label";
+import { Switch } from "@superset/ui/switch";
+import { trpc } from "renderer/lib/trpc";
+
+export function BehaviorSettings() {
+ const utils = trpc.useUtils();
+ const { data: confirmOnQuit, isLoading } =
+ trpc.settings.getConfirmOnQuit.useQuery();
+ const setConfirmOnQuit = trpc.settings.setConfirmOnQuit.useMutation({
+ onMutate: async ({ enabled }) => {
+ // Cancel outgoing fetches
+ await utils.settings.getConfirmOnQuit.cancel();
+ // Snapshot previous value
+ const previous = utils.settings.getConfirmOnQuit.getData();
+ // Optimistically update
+ utils.settings.getConfirmOnQuit.setData(undefined, enabled);
+ return { previous };
+ },
+ onError: (_err, _vars, context) => {
+ // Rollback on error
+ if (context?.previous !== undefined) {
+ utils.settings.getConfirmOnQuit.setData(undefined, context.previous);
+ }
+ },
+ onSettled: () => {
+ // Refetch to ensure sync with server
+ utils.settings.getConfirmOnQuit.invalidate();
+ },
+ });
+
+ const handleToggle = (enabled: boolean) => {
+ setConfirmOnQuit.mutate({ enabled });
+ };
+
+ return (
+
+
+
Behavior
+
+ Configure app behavior and preferences
+
+
+
+
+
+
+
+
+ Show a confirmation dialog when quitting the app
+
+
+
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsContent.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsContent.tsx
index 49d652901a2..2af699cd289 100644
--- a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsContent.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsContent.tsx
@@ -1,6 +1,7 @@
import type { SettingsSection } from "renderer/stores";
import { AccountSettings } from "./AccountSettings";
import { AppearanceSettings } from "./AppearanceSettings";
+import { BehaviorSettings } from "./BehaviorSettings";
import { KeyboardShortcutsSettings } from "./KeyboardShortcutsSettings";
import { PresetsSettings } from "./PresetsSettings";
import { ProjectSettings } from "./ProjectSettings";
@@ -21,6 +22,7 @@ export function SettingsContent({ activeSection }: SettingsContentProps) {
{activeSection === "ringtones" && }
{activeSection === "keyboard" && }
{activeSection === "presets" && }
+ {activeSection === "behavior" && }
);
}
diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/GeneralSettings.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/GeneralSettings.tsx
index 35882334afb..7803fa3b11f 100644
--- a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/GeneralSettings.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/GeneralSettings.tsx
@@ -1,5 +1,6 @@
import { cn } from "@superset/ui/utils";
import {
+ HiOutlineAdjustmentsHorizontal,
HiOutlineBell,
HiOutlineCog6Tooth,
HiOutlineCommandLine,
@@ -43,6 +44,11 @@ const GENERAL_SECTIONS: {
label: "Presets",
icon: ,
},
+ {
+ id: "behavior",
+ label: "Behavior",
+ icon: ,
+ },
];
export function GeneralSettings({
diff --git a/apps/desktop/src/renderer/stores/app-state.ts b/apps/desktop/src/renderer/stores/app-state.ts
index eb0d4c88579..296752c34d8 100644
--- a/apps/desktop/src/renderer/stores/app-state.ts
+++ b/apps/desktop/src/renderer/stores/app-state.ts
@@ -9,7 +9,8 @@ export type SettingsSection =
| "appearance"
| "keyboard"
| "presets"
- | "ringtones";
+ | "ringtones"
+ | "behavior";
interface AppState {
currentView: AppView;
diff --git a/apps/desktop/src/shared/constants.ts b/apps/desktop/src/shared/constants.ts
index 5c2ace1e0e8..6bc788cead4 100644
--- a/apps/desktop/src/shared/constants.ts
+++ b/apps/desktop/src/shared/constants.ts
@@ -43,3 +43,6 @@ export const NOTIFICATION_EVENTS = {
AGENT_COMPLETE: "agent-complete",
FOCUS_TAB: "focus-tab",
} as const;
+
+// Default user preference values
+export const DEFAULT_CONFIRM_ON_QUIT = true;
diff --git a/packages/local-db/drizzle/0003_add_confirm_on_quit_setting.sql b/packages/local-db/drizzle/0003_add_confirm_on_quit_setting.sql
new file mode 100644
index 00000000000..e3c8b3ef822
--- /dev/null
+++ b/packages/local-db/drizzle/0003_add_confirm_on_quit_setting.sql
@@ -0,0 +1 @@
+ALTER TABLE `settings` ADD `confirm_on_quit` integer;
\ No newline at end of file
diff --git a/packages/local-db/drizzle/meta/0003_snapshot.json b/packages/local-db/drizzle/meta/0003_snapshot.json
new file mode 100644
index 00000000000..89416d5efb6
--- /dev/null
+++ b/packages/local-db/drizzle/meta/0003_snapshot.json
@@ -0,0 +1,970 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "d5a52ac9-bc1e-4529-89bf-5748d4df5006",
+ "prevId": "1dcdfa5e-5ab6-4505-ad3f-dec56b23b38b",
+ "tables": {
+ "organization_members": {
+ "name": "organization_members",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organization_members_organization_id_idx": {
+ "name": "organization_members_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "organization_members_user_id_idx": {
+ "name": "organization_members_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "organization_members_organization_id_organizations_id_fk": {
+ "name": "organization_members_organization_id_organizations_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_user_id_users_id_fk": {
+ "name": "organization_members_user_id_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organizations": {
+ "name": "organizations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_org_id": {
+ "name": "clerk_org_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "github_org": {
+ "name": "github_org",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organizations_clerk_org_id_unique": {
+ "name": "organizations_clerk_org_id_unique",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_unique": {
+ "name": "organizations_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "organizations_slug_idx": {
+ "name": "organizations_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "organizations_clerk_org_id_idx": {
+ "name": "organizations_clerk_org_id_idx",
+ "columns": [
+ "clerk_org_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "projects": {
+ "name": "projects",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "main_repo_path": {
+ "name": "main_repo_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config_toast_dismissed": {
+ "name": "config_toast_dismissed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_branch": {
+ "name": "default_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "projects_main_repo_path_idx": {
+ "name": "projects_main_repo_path_idx",
+ "columns": [
+ "main_repo_path"
+ ],
+ "isUnique": false
+ },
+ "projects_last_opened_at_idx": {
+ "name": "projects_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "settings": {
+ "name": "settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "last_active_workspace_id": {
+ "name": "last_active_workspace_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used_app": {
+ "name": "last_used_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets": {
+ "name": "terminal_presets",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_presets_initialized": {
+ "name": "terminal_presets_initialized",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "selected_ringtone_id": {
+ "name": "selected_ringtone_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "confirm_on_quit": {
+ "name": "confirm_on_quit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "tasks": {
+ "name": "tasks",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status_color": {
+ "name": "status_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_type": {
+ "name": "status_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status_position": {
+ "name": "status_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "assignee_id": {
+ "name": "assignee_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "estimate": {
+ "name": "estimate",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "due_date": {
+ "name": "due_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "labels": {
+ "name": "labels",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_provider": {
+ "name": "external_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_synced_at": {
+ "name": "last_synced_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "tasks_slug_unique": {
+ "name": "tasks_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "tasks_slug_idx": {
+ "name": "tasks_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "tasks_organization_id_idx": {
+ "name": "tasks_organization_id_idx",
+ "columns": [
+ "organization_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_assignee_id_idx": {
+ "name": "tasks_assignee_id_idx",
+ "columns": [
+ "assignee_id"
+ ],
+ "isUnique": false
+ },
+ "tasks_status_idx": {
+ "name": "tasks_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "tasks_created_at_idx": {
+ "name": "tasks_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "tasks_organization_id_organizations_id_fk": {
+ "name": "tasks_organization_id_organizations_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tasks_assignee_id_users_id_fk": {
+ "name": "tasks_assignee_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assignee_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_creator_id_users_id_fk": {
+ "name": "tasks_creator_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "clerk_id": {
+ "name": "clerk_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_clerk_id_unique": {
+ "name": "users_clerk_id_unique",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": true
+ },
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_clerk_id_idx": {
+ "name": "users_clerk_id_idx",
+ "columns": [
+ "clerk_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "workspaces": {
+ "name": "workspaces",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "worktree_id": {
+ "name": "worktree_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "tab_order": {
+ "name": "tab_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_opened_at": {
+ "name": "last_opened_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "workspaces_project_id_idx": {
+ "name": "workspaces_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_worktree_id_idx": {
+ "name": "workspaces_worktree_id_idx",
+ "columns": [
+ "worktree_id"
+ ],
+ "isUnique": false
+ },
+ "workspaces_last_opened_at_idx": {
+ "name": "workspaces_last_opened_at_idx",
+ "columns": [
+ "last_opened_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "workspaces_project_id_projects_id_fk": {
+ "name": "workspaces_project_id_projects_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspaces_worktree_id_worktrees_id_fk": {
+ "name": "workspaces_worktree_id_worktrees_id_fk",
+ "tableFrom": "workspaces",
+ "tableTo": "worktrees",
+ "columnsFrom": [
+ "worktree_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "worktrees": {
+ "name": "worktrees",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_branch": {
+ "name": "base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "git_status": {
+ "name": "git_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_status": {
+ "name": "github_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "worktrees_project_id_idx": {
+ "name": "worktrees_project_id_idx",
+ "columns": [
+ "project_id"
+ ],
+ "isUnique": false
+ },
+ "worktrees_branch_idx": {
+ "name": "worktrees_branch_idx",
+ "columns": [
+ "branch"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "worktrees_project_id_projects_id_fk": {
+ "name": "worktrees_project_id_projects_id_fk",
+ "tableFrom": "worktrees",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/local-db/drizzle/meta/_journal.json b/packages/local-db/drizzle/meta/_journal.json
index ab1956cbcae..3117a6e2266 100644
--- a/packages/local-db/drizzle/meta/_journal.json
+++ b/packages/local-db/drizzle/meta/_journal.json
@@ -22,6 +22,13 @@
"when": 1766776955487,
"tag": "0002_add_active_organization_id",
"breakpoints": true
+ },
+ {
+ "idx": 3,
+ "version": "6",
+ "when": 1766932805546,
+ "tag": "0003_add_confirm_on_quit_setting",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/local-db/src/schema/schema.ts b/packages/local-db/src/schema/schema.ts
index a832e0982cd..9b0639805f0 100644
--- a/packages/local-db/src/schema/schema.ts
+++ b/packages/local-db/src/schema/schema.ts
@@ -126,6 +126,7 @@ export const settings = sqliteTable("settings", {
}),
selectedRingtoneId: text("selected_ringtone_id"),
activeOrganizationId: text("active_organization_id"),
+ confirmOnQuit: integer("confirm_on_quit", { mode: "boolean" }),
});
export type InsertSettings = typeof settings.$inferInsert;