diff --git a/apps/desktop/src/lib/trpc/routers/external/index.ts b/apps/desktop/src/lib/trpc/routers/external/index.ts
index f35bca75dff..7ea6cb9f5da 100644
--- a/apps/desktop/src/lib/trpc/routers/external/index.ts
+++ b/apps/desktop/src/lib/trpc/routers/external/index.ts
@@ -1,5 +1,6 @@
-import { EXTERNAL_APPS, settings } from "@superset/local-db";
+import { EXTERNAL_APPS, projects } from "@superset/local-db";
import { TRPCError } from "@trpc/server";
+import { eq } from "drizzle-orm";
import { clipboard, shell } from "electron";
import { localDb } from "main/lib/local-db";
import { z } from "zod";
@@ -75,17 +76,17 @@ export const createExternalRouter = () => {
z.object({
path: z.string(),
app: ExternalAppSchema,
+ projectId: z.string().optional(),
}),
)
.mutation(async ({ input }) => {
- localDb
- .insert(settings)
- .values({ id: 1, lastUsedApp: input.app })
- .onConflictDoUpdate({
- target: settings.id,
- set: { lastUsedApp: input.app },
- })
- .run();
+ if (input.projectId) {
+ localDb
+ .update(projects)
+ .set({ defaultApp: input.app })
+ .where(eq(projects.id, input.projectId))
+ .run();
+ }
await openPathInApp(input.path, input.app);
}),
@@ -100,12 +101,20 @@ export const createExternalRouter = () => {
line: z.number().optional(),
column: z.number().optional(),
cwd: z.string().optional(),
+ projectId: z.string().optional(),
}),
)
.mutation(async ({ input }) => {
const filePath = resolvePath(input.path, input.cwd);
- const settingsRow = localDb.select().from(settings).get();
- const app = settingsRow?.lastUsedApp ?? "cursor";
+ let app: ExternalApp = "cursor";
+ if (input.projectId) {
+ const project = localDb
+ .select()
+ .from(projects)
+ .where(eq(projects.id, input.projectId))
+ .get();
+ app = project?.defaultApp ?? "cursor";
+ }
await openPathInApp(filePath, app);
}),
});
diff --git a/apps/desktop/src/lib/trpc/routers/projects/projects.ts b/apps/desktop/src/lib/trpc/routers/projects/projects.ts
index 0bb54a27b10..7bca2aa2fe5 100644
--- a/apps/desktop/src/lib/trpc/routers/projects/projects.ts
+++ b/apps/desktop/src/lib/trpc/routers/projects/projects.ts
@@ -3,6 +3,7 @@ import { access } from "node:fs/promises";
import { basename, join } from "node:path";
import {
BRANCH_PREFIX_MODES,
+ EXTERNAL_APPS,
projects,
type SelectProject,
settings,
@@ -313,6 +314,18 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => {
return project;
}),
+ getDefaultApp: publicProcedure
+ .input(z.object({ projectId: z.string() }))
+ .query(({ input }) => {
+ const project = localDb
+ .select()
+ .from(projects)
+ .where(eq(projects.id, input.projectId))
+ .get();
+
+ return project?.defaultApp ?? "cursor";
+ }),
+
getRecents: publicProcedure.query((): Project[] => {
return localDb
.select()
@@ -800,6 +813,7 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => {
branchPrefixMode: z.enum(BRANCH_PREFIX_MODES).nullable().optional(),
branchPrefixCustom: z.string().nullable().optional(),
hideImage: z.boolean().optional(),
+ defaultApp: z.enum(EXTERNAL_APPS).nullable().optional(),
}),
}),
)
@@ -829,6 +843,9 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => {
...(input.patch.hideImage !== undefined && {
hideImage: input.patch.hideImage,
}),
+ ...(input.patch.defaultApp !== undefined && {
+ defaultApp: input.patch.defaultApp,
+ }),
lastOpenedAt: Date.now(),
})
.where(eq(projects.id, input.id))
diff --git a/apps/desktop/src/lib/trpc/routers/settings/index.ts b/apps/desktop/src/lib/trpc/routers/settings/index.ts
index a4a0416c3eb..ea21f579afb 100644
--- a/apps/desktop/src/lib/trpc/routers/settings/index.ts
+++ b/apps/desktop/src/lib/trpc/routers/settings/index.ts
@@ -100,10 +100,6 @@ export function getPresetsForTrigger(
export const createSettingsRouter = () => {
return router({
- getLastUsedApp: publicProcedure.query(() => {
- const row = getSettings();
- return row.lastUsedApp ?? "cursor";
- }),
getTerminalPresets: publicProcedure.query(() => {
const row = getSettings();
if (!row.terminalPresetsInitialized) {
diff --git a/apps/desktop/src/renderer/components/ConfigFilePreview/ConfigFilePreview.tsx b/apps/desktop/src/renderer/components/ConfigFilePreview/ConfigFilePreview.tsx
index 5f0ec5fd70e..077ee427b81 100644
--- a/apps/desktop/src/renderer/components/ConfigFilePreview/ConfigFilePreview.tsx
+++ b/apps/desktop/src/renderer/components/ConfigFilePreview/ConfigFilePreview.tsx
@@ -49,7 +49,11 @@ export function ConfigFilePreview({
{projectName}/{PROJECT_SUPERSET_DIR_NAME}/{CONFIG_FILE_NAME}
-
+
diff --git a/apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx b/apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx
index 542ecc7a0d8..be12e96dbd0 100644
--- a/apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx
+++ b/apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx
@@ -108,12 +108,15 @@ export interface OpenInButtonProps {
label?: string;
/** Show keyboard shortcut hints */
showShortcuts?: boolean;
+ /** Project ID for per-project default app */
+ projectId?: string;
}
export function OpenInButton({
path,
label,
showShortcuts = false,
+ projectId,
}: OpenInButtonProps) {
const [isOpen, setIsOpen] = useState(false);
const utils = electronTrpc.useUtils();
@@ -123,19 +126,26 @@ export function OpenInButton({
const showCopyPathShortcut =
showShortcuts && copyPathShortcut !== "Unassigned";
- const { data: lastUsedApp = "cursor" } =
- electronTrpc.settings.getLastUsedApp.useQuery();
+ const { data: defaultApp = "cursor" } =
+ electronTrpc.projects.getDefaultApp.useQuery(
+ { projectId: projectId as string },
+ { enabled: !!projectId },
+ );
const openInApp = electronTrpc.external.openInApp.useMutation({
- onSuccess: () => utils.settings.getLastUsedApp.invalidate(),
+ onSuccess: () => {
+ if (projectId) {
+ utils.projects.getDefaultApp.invalidate({ projectId });
+ }
+ },
});
const copyPath = electronTrpc.external.copyPath.useMutation();
- const currentApp = getAppOption(lastUsedApp);
+ const currentApp = getAppOption(defaultApp);
const handleOpenIn = (app: ExternalApp) => {
if (!path) return;
- openInApp.mutate({ path, app });
+ openInApp.mutate({ path, app, projectId });
setIsOpen(false);
};
@@ -147,7 +157,7 @@ export function OpenInButton({
const handleOpenLastUsed = () => {
if (!path) return;
- openInApp.mutate({ path, app: lastUsedApp });
+ openInApp.mutate({ path, app: defaultApp, projectId });
};
return (
@@ -204,7 +214,7 @@ export function OpenInButton({
/>
{app.label}
- {showOpenInShortcut && app.id === lastUsedApp && (
+ {showOpenInShortcut && app.id === defaultApp && (
{openInShortcut}
@@ -235,7 +245,7 @@ export function OpenInButton({
/>
{app.label}
- {showShortcuts && app.id === lastUsedApp && (
+ {showShortcuts && app.id === defaultApp && (
⌘O
)}
@@ -266,7 +276,7 @@ export function OpenInButton({
/>
{app.label}
- {showOpenInShortcut && app.id === lastUsedApp && (
+ {showOpenInShortcut && app.id === defaultApp && (
{openInShortcut}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx
index cf6aa6e39af..b35c4ce10f0 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx
@@ -52,6 +52,7 @@ export function TopBar() {
)}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OpenInMenuButton/OpenInMenuButton.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OpenInMenuButton/OpenInMenuButton.tsx
index 14e6b026e83..9b2e40ef39c 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OpenInMenuButton/OpenInMenuButton.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OpenInMenuButton/OpenInMenuButton.tsx
@@ -30,19 +30,26 @@ import { useHotkeyText } from "renderer/stores/hotkeys";
interface OpenInMenuButtonProps {
worktreePath: string;
branch?: string;
+ projectId?: string;
}
export const OpenInMenuButton = memo(function OpenInMenuButton({
worktreePath,
branch,
+ projectId,
}: OpenInMenuButtonProps) {
const utils = electronTrpc.useUtils();
- const { data: lastUsedApp = "cursor" } =
- electronTrpc.settings.getLastUsedApp.useQuery(undefined, {
- staleTime: 30000,
- });
+ const { data: defaultApp = "cursor" } =
+ electronTrpc.projects.getDefaultApp.useQuery(
+ { projectId: projectId as string },
+ { enabled: !!projectId, staleTime: 30000 },
+ );
const openInApp = electronTrpc.external.openInApp.useMutation({
- onSuccess: () => utils.settings.getLastUsedApp.invalidate(),
+ onSuccess: () => {
+ if (projectId) {
+ utils.projects.getDefaultApp.invalidate({ projectId });
+ }
+ },
onError: (error) => toast.error(`Failed to open: ${error.message}`),
});
const copyPath = electronTrpc.external.copyPath.useMutation({
@@ -50,7 +57,7 @@ export const OpenInMenuButton = memo(function OpenInMenuButton({
onError: (error) => toast.error(`Failed to copy path: ${error.message}`),
});
- const currentApp = useMemo(() => getAppOption(lastUsedApp), [lastUsedApp]);
+ const currentApp = useMemo(() => getAppOption(defaultApp), [defaultApp]);
const openInShortcut = useHotkeyText("OPEN_IN_APP");
const copyPathShortcut = useHotkeyText("COPY_PATH");
const showOpenInShortcut = openInShortcut !== "Unassigned";
@@ -59,15 +66,15 @@ export const OpenInMenuButton = memo(function OpenInMenuButton({
const handleOpenInEditor = useCallback(() => {
if (openInApp.isPending || copyPath.isPending) return;
- openInApp.mutate({ path: worktreePath, app: lastUsedApp });
- }, [worktreePath, lastUsedApp, openInApp, copyPath.isPending]);
+ openInApp.mutate({ path: worktreePath, app: defaultApp, projectId });
+ }, [worktreePath, defaultApp, projectId, openInApp, copyPath.isPending]);
const handleOpenInOtherApp = useCallback(
(appId: ExternalApp) => {
if (openInApp.isPending || copyPath.isPending) return;
- openInApp.mutate({ path: worktreePath, app: appId });
+ openInApp.mutate({ path: worktreePath, app: appId, projectId });
},
- [worktreePath, openInApp, copyPath.isPending],
+ [worktreePath, projectId, openInApp, copyPath.isPending],
);
const handleCopyPath = useCallback(() => {
@@ -159,7 +166,7 @@ export const OpenInMenuButton = memo(function OpenInMenuButton({
className="size-4 object-contain mr-2"
/>
{app.label}
- {app.id === lastUsedApp && showOpenInShortcut && (
+ {app.id === defaultApp && showOpenInShortcut && (
{openInShortcut}
)}
@@ -185,7 +192,7 @@ export const OpenInMenuButton = memo(function OpenInMenuButton({
className="size-4 object-contain mr-2"
/>
{app.label}
- {app.id === lastUsedApp && showOpenInShortcut && (
+ {app.id === defaultApp && showOpenInShortcut && (
{openInShortcut}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx
index 1bab4ac2208..c18afd7351f 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx
@@ -299,21 +299,33 @@ function WorkspacePage() {
);
// Open in last used app shortcut
- const { data: lastUsedApp = "cursor" } =
- electronTrpc.settings.getLastUsedApp.useQuery();
- const openInApp = electronTrpc.external.openInApp.useMutation();
+ const projectId = workspace?.projectId;
+ const { data: defaultApp = "cursor" } =
+ electronTrpc.projects.getDefaultApp.useQuery(
+ { projectId: projectId as string },
+ { enabled: !!projectId },
+ );
+ const utils = electronTrpc.useUtils();
+ const openInApp = electronTrpc.external.openInApp.useMutation({
+ onSuccess: () => {
+ if (projectId) {
+ utils.projects.getDefaultApp.invalidate({ projectId });
+ }
+ },
+ });
useAppHotkey(
"OPEN_IN_APP",
() => {
if (workspace?.worktreePath) {
openInApp.mutate({
path: workspace.worktreePath,
- app: lastUsedApp,
+ app: defaultApp,
+ projectId,
});
}
},
undefined,
- [workspace?.worktreePath, lastUsedApp],
+ [workspace?.worktreePath, defaultApp, projectId],
);
// Copy path shortcut
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/components/ClickablePath/ClickablePath.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/components/ClickablePath/ClickablePath.tsx
index eeb2dc4a1f8..5288a5bd4a9 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/settings/components/ClickablePath/ClickablePath.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/settings/components/ClickablePath/ClickablePath.tsx
@@ -27,17 +27,12 @@ interface ClickablePathProps {
className?: string;
}
+const defaultApp: ExternalApp = "cursor";
+
export function ClickablePath({ path, className }: ClickablePathProps) {
const [isOpen, setIsOpen] = useState(false);
- const utils = electronTrpc.useUtils();
-
- const { data: lastUsedApp = "cursor" } =
- electronTrpc.settings.getLastUsedApp.useQuery(undefined, {
- staleTime: 30000,
- });
const openInApp = electronTrpc.external.openInApp.useMutation({
- onSuccess: () => utils.settings.getLastUsedApp.invalidate(),
onError: (error) => toast.error(`Failed to open: ${error.message}`),
});
@@ -81,7 +76,7 @@ export function ClickablePath({ path, className }: ClickablePathProps) {
>
{app.label}
- {app.id === lastUsedApp && (
+ {app.id === defaultApp && (
Default
@@ -106,7 +101,7 @@ export function ClickablePath({ path, className }: ClickablePathProps) {
>
{app.label}
- {app.id === lastUsedApp && (
+ {app.id === defaultApp && (
Default
@@ -133,7 +128,7 @@ export function ClickablePath({ path, className }: ClickablePathProps) {
>
{app.label}
- {app.id === lastUsedApp && (
+ {app.id === defaultApp && (
Default
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx
index 31fc7b58e30..755f81f9bb7 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx
@@ -40,6 +40,7 @@ export function ChangesView({ onFileOpen, isExpandedView }: ChangesViewProps) {
{ enabled: !!workspaceId },
);
const worktreePath = workspace?.worktreePath;
+ const projectId = workspace?.projectId;
const { status, isLoading, effectiveBaseBranch, refetch } =
useGitChangesStatus({
@@ -366,6 +367,7 @@ export function ChangesView({ onFileOpen, isExpandedView }: ChangesViewProps) {
selectedCommitHash={selectedCommitHash}
onFileSelect={(file) => handleFileSelect(file, "against-base")}
worktreePath={worktreePath}
+ projectId={projectId}
category="against-base"
isExpandedView={isExpandedView}
/>
@@ -388,6 +390,7 @@ export function ChangesView({ onFileOpen, isExpandedView }: ChangesViewProps) {
onFileSelect={handleCommitFileSelect}
viewMode={fileListViewMode}
worktreePath={worktreePath}
+ projectId={projectId}
isExpandedView={isExpandedView}
/>
))}
@@ -451,6 +454,7 @@ export function ChangesView({ onFileOpen, isExpandedView }: ChangesViewProps) {
}
isActioning={unstageFileMutation.isPending}
worktreePath={worktreePath}
+ projectId={projectId}
category="staged"
isExpandedView={isExpandedView}
/>
@@ -518,6 +522,7 @@ export function ChangesView({ onFileOpen, isExpandedView }: ChangesViewProps) {
deleteUntrackedMutation.isPending
}
worktreePath={worktreePath}
+ projectId={projectId}
onDiscard={handleDiscard}
category="unstaged"
isExpandedView={isExpandedView}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/CommitItem/CommitItem.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/CommitItem/CommitItem.tsx
index b178784ec9c..6ce979b6780 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/CommitItem/CommitItem.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/CommitItem/CommitItem.tsx
@@ -14,6 +14,7 @@ interface CommitItemProps {
viewMode: ChangesViewMode;
worktreePath: string;
isExpandedView?: boolean;
+ projectId?: string;
}
function CommitHeader({
@@ -48,6 +49,7 @@ export function CommitItem({
viewMode,
worktreePath,
isExpandedView,
+ projectId,
}: CommitItemProps) {
const hasFiles = commit.files.length > 0;
@@ -79,6 +81,7 @@ export function CommitItem({
selectedCommitHash={selectedCommitHash}
onFileSelect={handleFileSelect}
worktreePath={worktreePath}
+ projectId={projectId}
category="committed"
commitHash={commit.hash}
isExpandedView={isExpandedView}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx
index 7eae4406ec3..54b354fde87 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx
@@ -47,6 +47,7 @@ interface FileItemProps {
commitHash?: string;
/** Expanded view uses scroll-sync highlighting; collapsed view uses selection highlighting */
isExpandedView?: boolean;
+ projectId?: string;
}
function LevelIndicators({ level }: { level: number }) {
@@ -80,6 +81,7 @@ export function FileItem({
category,
commitHash,
isExpandedView = false,
+ projectId,
}: FileItemProps) {
const [showDiscardDialog, setShowDiscardDialog] = useState(false);
const { activeFileKey } = useScrollContext();
@@ -104,6 +106,7 @@ export function FileItem({
absolutePath,
relativePath: file.path,
cwd: worktreePath,
+ projectId,
});
const fileDragProps = useFileDrag({ absolutePath });
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileList.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileList.tsx
index 4ae5c679c07..72bb308b687 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileList.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileList.tsx
@@ -18,6 +18,7 @@ interface FileListProps {
category?: ChangeCategory;
commitHash?: string;
isExpandedView?: boolean;
+ projectId?: string;
}
export function FileList({
@@ -35,6 +36,7 @@ export function FileList({
category,
commitHash,
isExpandedView,
+ projectId,
}: FileListProps) {
if (files.length === 0) {
return null;
@@ -56,6 +58,7 @@ export function FileList({
category={category}
commitHash={commitHash}
isExpandedView={isExpandedView}
+ projectId={projectId}
/>
);
}
@@ -75,6 +78,7 @@ export function FileList({
category={category}
commitHash={commitHash}
isExpandedView={isExpandedView}
+ projectId={projectId}
/>
);
}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileListGrouped.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileListGrouped.tsx
index 3775839798e..8c260f058a1 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileListGrouped.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileListGrouped.tsx
@@ -17,6 +17,7 @@ interface FileListGroupedProps {
category?: ChangeCategory;
commitHash?: string;
isExpandedView?: boolean;
+ projectId?: string;
}
interface FolderGroup {
@@ -71,6 +72,7 @@ interface FolderGroupItemProps {
category?: ChangeCategory;
commitHash?: string;
isExpandedView?: boolean;
+ projectId?: string;
}
function FolderGroupItem({
@@ -86,6 +88,7 @@ function FolderGroupItem({
category,
commitHash,
isExpandedView,
+ projectId,
}: FolderGroupItemProps) {
const [isExpanded, setIsExpanded] = useState(true);
const displayName = group.folderPath || "Root Path";
@@ -120,6 +123,7 @@ function FolderGroupItem({
variant="grouped"
folderPath={group.folderPath}
worktreePath={worktreePath}
+ projectId={projectId}
onStageAll={onStage ? handleStageAll : undefined}
onUnstageAll={onUnstage ? handleUnstageAll : undefined}
onDiscardAll={onDiscard ? handleDiscardAll : undefined}
@@ -136,6 +140,7 @@ function FolderGroupItem({
onUnstage={onUnstage ? () => onUnstage(file) : undefined}
isActioning={isActioning}
worktreePath={worktreePath}
+ projectId={projectId}
onDiscard={onDiscard ? () => onDiscard(file) : undefined}
category={category}
commitHash={commitHash}
@@ -159,6 +164,7 @@ export function FileListGrouped({
category,
commitHash,
isExpandedView,
+ projectId,
}: FileListGroupedProps) {
const groups = groupFilesByFolder(files);
@@ -179,6 +185,7 @@ export function FileListGrouped({
category={category}
commitHash={commitHash}
isExpandedView={isExpandedView}
+ projectId={projectId}
/>
))}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileListTree.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileListTree.tsx
index 6ffb6955884..89b8376b538 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileListTree.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileList/FileListTree.tsx
@@ -42,6 +42,7 @@ interface FileListTreeProps {
category?: ChangeCategory;
commitHash?: string;
isExpandedView?: boolean;
+ projectId?: string;
}
function buildFileTree(files: ChangedFile[]): FileTreeNode[] {
@@ -111,6 +112,7 @@ interface TreeNodeComponentProps {
category?: ChangeCategory;
commitHash?: string;
isExpandedView?: boolean;
+ projectId?: string;
}
function TreeNodeComponent({
@@ -128,6 +130,7 @@ function TreeNodeComponent({
category,
commitHash,
isExpandedView,
+ projectId,
}: TreeNodeComponentProps) {
const [isExpanded, setIsExpanded] = useState(true);
const hasChildren = node.children && node.children.length > 0;
@@ -168,6 +171,7 @@ function TreeNodeComponent({
variant="tree"
folderPath={node.path}
worktreePath={worktreePath}
+ projectId={projectId}
onStageAll={onStage ? handleStageAll : undefined}
onUnstageAll={onUnstage ? handleUnstageAll : undefined}
onDiscardAll={onDiscard ? handleDiscardAll : undefined}
@@ -190,6 +194,7 @@ function TreeNodeComponent({
category={category}
commitHash={commitHash}
isExpandedView={isExpandedView}
+ projectId={projectId}
/>
))}
@@ -209,6 +214,7 @@ function TreeNodeComponent({
onUnstage={onUnstage ? () => onUnstage(file) : undefined}
isActioning={isActioning}
worktreePath={worktreePath}
+ projectId={projectId}
onDiscard={onDiscard ? () => onDiscard(file) : undefined}
category={category}
commitHash={commitHash}
@@ -234,6 +240,7 @@ export function FileListTree({
category,
commitHash,
isExpandedView,
+ projectId,
}: FileListTreeProps) {
const tree = buildFileTree(files);
@@ -255,6 +262,7 @@ export function FileListTree({
category={category}
commitHash={commitHash}
isExpandedView={isExpandedView}
+ projectId={projectId}
/>
))}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FolderRow/FolderRow.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FolderRow/FolderRow.tsx
index ede28c180d5..2da5a5a5305 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FolderRow/FolderRow.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FolderRow/FolderRow.tsx
@@ -37,6 +37,7 @@ interface FolderRowProps {
onUnstageAll?: () => void;
onDiscardAll?: () => void;
isActioning?: boolean;
+ projectId?: string;
}
function LevelIndicators({ level }: { level: number }) {
@@ -112,6 +113,7 @@ export function FolderRow({
onUnstageAll,
onDiscardAll,
isActioning = false,
+ projectId,
}: FolderRowProps) {
const isGrouped = variant === "grouped";
const isRoot = folderPath === "";
@@ -121,6 +123,7 @@ export function FolderRow({
usePathActions({
absolutePath,
relativePath: folderPath || undefined,
+ projectId,
});
const triggerContent = (
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/usePathActions.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/usePathActions.ts
index b2f3eb09a05..e858db5ce58 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/usePathActions.ts
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/usePathActions.ts
@@ -6,19 +6,25 @@ interface UsePathActionsProps {
relativePath?: string;
/** For files: pass cwd to use openFileInEditor. For folders: omit to use openInApp */
cwd?: string;
+ /** Project ID for per-project default app resolution */
+ projectId?: string;
}
export function usePathActions({
absolutePath,
relativePath,
cwd,
+ projectId,
}: UsePathActionsProps) {
const openInFinderMutation = electronTrpc.external.openInFinder.useMutation();
const openInAppMutation = electronTrpc.external.openInApp.useMutation();
const openFileInEditorMutation =
electronTrpc.external.openFileInEditor.useMutation();
- const { data: lastUsedApp = "cursor" } =
- electronTrpc.settings.getLastUsedApp.useQuery();
+ const { data: defaultApp = "cursor" } =
+ electronTrpc.projects.getDefaultApp.useQuery(
+ { projectId: projectId as string },
+ { enabled: !!projectId },
+ );
const copyPath = useCallback(async () => {
if (absolutePath) {
@@ -42,14 +48,19 @@ export function usePathActions({
if (!absolutePath) return;
if (cwd) {
- openFileInEditorMutation.mutate({ path: absolutePath, cwd });
+ openFileInEditorMutation.mutate({ path: absolutePath, cwd, projectId });
} else {
- openInAppMutation.mutate({ path: absolutePath, app: lastUsedApp });
+ openInAppMutation.mutate({
+ path: absolutePath,
+ app: defaultApp,
+ projectId,
+ });
}
}, [
absolutePath,
cwd,
- lastUsedApp,
+ projectId,
+ defaultApp,
openInAppMutation,
openFileInEditorMutation,
]);
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx
index a7f288e1075..cb231c36166 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx
@@ -170,9 +170,13 @@ export function FilesView() {
const handleOpenInEditor = useCallback(
(entry: DirectoryEntry) => {
if (!worktreePath) return;
- openFileInEditorMutation.mutate({ path: entry.path, cwd: worktreePath });
+ openFileInEditorMutation.mutate({
+ path: entry.path,
+ cwd: worktreePath,
+ projectId,
+ });
},
- [worktreePath, openFileInEditorMutation],
+ [worktreePath, projectId, openFileInEditorMutation],
);
const handleNewFile = useCallback(
@@ -351,6 +355,7 @@ export function FilesView() {
key={entry.id}
entry={entry}
worktreePath={worktreePath}
+ projectId={projectId}
onActivate={handleFileActivate}
onOpenInEditor={handleOpenInEditor}
onNewFile={handleNewFile}
@@ -394,6 +399,7 @@ export function FilesView() {
rowHeight={ROW_HEIGHT}
indent={TREE_INDENT}
worktreePath={worktreePath}
+ projectId={projectId}
onActivate={handleFileActivate}
onOpenInEditor={handleOpenInEditor}
onNewFile={handleNewFile}
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileSearchResultItem/FileSearchResultItem.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileSearchResultItem/FileSearchResultItem.tsx
index e4867e63e52..63e4670a639 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileSearchResultItem/FileSearchResultItem.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileSearchResultItem/FileSearchResultItem.tsx
@@ -24,6 +24,7 @@ import { getFileIcon } from "../../utils";
interface FileSearchResultItemProps {
entry: DirectoryEntry;
worktreePath: string;
+ projectId?: string;
onActivate: (entry: DirectoryEntry) => void;
onOpenInEditor: (entry: DirectoryEntry) => void;
onNewFile: (parentPath: string) => void;
@@ -54,6 +55,7 @@ function truncatePathStart(value: string, maxLength: number): string {
export function FileSearchResultItem({
entry,
worktreePath,
+ projectId,
onActivate,
onOpenInEditor,
onNewFile,
@@ -81,6 +83,7 @@ export function FileSearchResultItem({
absolutePath: entry.path,
relativePath: entry.relativePath,
cwd: worktreePath,
+ projectId,
});
const fileDragProps = useFileDrag({ absolutePath: entry.path });
diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeItem/FileTreeItem.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeItem/FileTreeItem.tsx
index 2d0e07163f5..21f72413223 100644
--- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeItem/FileTreeItem.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeItem/FileTreeItem.tsx
@@ -29,6 +29,7 @@ interface FileTreeItemProps {
rowHeight: number;
indent: number;
worktreePath: string;
+ projectId?: string;
onActivate: (entry: DirectoryEntry) => void;
onOpenInEditor: (entry: DirectoryEntry) => void;
onNewFile: (parentPath: string) => void;
@@ -43,6 +44,7 @@ export function FileTreeItem({
rowHeight,
indent,
worktreePath,
+ projectId,
onActivate,
onOpenInEditor,
onNewFile,
@@ -64,6 +66,7 @@ export function FileTreeItem({
absolutePath: entry.path,
relativePath: entry.relativePath,
cwd: worktreePath,
+ projectId,
});
const fileDragProps = useFileDrag({ absolutePath: entry.path });
diff --git a/packages/local-db/drizzle/0027_per_project_default_app.sql b/packages/local-db/drizzle/0027_per_project_default_app.sql
new file mode 100644
index 00000000000..9b979203e96
--- /dev/null
+++ b/packages/local-db/drizzle/0027_per_project_default_app.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `projects` ADD `default_app` text;--> statement-breakpoint
+ALTER TABLE `settings` DROP COLUMN `last_used_app`;
\ No newline at end of file
diff --git a/packages/local-db/drizzle/meta/0027_snapshot.json b/packages/local-db/drizzle/meta/0027_snapshot.json
new file mode 100644
index 00000000000..74b2c5a02e5
--- /dev/null
+++ b/packages/local-db/drizzle/meta/0027_snapshot.json
@@ -0,0 +1,1210 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "0a13d4fc-215b-41bb-9a42-29becaf02803",
+ "prevId": "f9a3301f-7c42-4e88-a95d-bca4ce39da98",
+ "tables": {
+ "browser_history": {
+ "name": "browser_history",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "favicon_url": {
+ "name": "favicon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_visited_at": {
+ "name": "last_visited_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "visit_count": {
+ "name": "visit_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ }
+ },
+ "indexes": {
+ "browser_history_url_unique": {
+ "name": "browser_history_url_unique",
+ "columns": [
+ "url"
+ ],
+ "isUnique": true
+ },
+ "browser_history_url_idx": {
+ "name": "browser_history_url_idx",
+ "columns": [
+ "url"
+ ],
+ "isUnique": false
+ },
+ "browser_history_last_visited_at_idx": {
+ "name": "browser_history_last_visited_at_idx",
+ "columns": [
+ "last_visited_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "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
+ },
+ "github_owner": {
+ "name": "github_owner",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch_prefix_mode": {
+ "name": "branch_prefix_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch_prefix_custom": {
+ "name": "branch_prefix_custom",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "hide_image": {
+ "name": "hide_image",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "neon_project_id": {
+ "name": "neon_project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "default_app": {
+ "name": "default_app",
+ "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
+ },
+ "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
+ },
+ "terminal_link_behavior": {
+ "name": "terminal_link_behavior",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "persist_terminal": {
+ "name": "persist_terminal",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "auto_apply_default_preset": {
+ "name": "auto_apply_default_preset",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch_prefix_mode": {
+ "name": "branch_prefix_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch_prefix_custom": {
+ "name": "branch_prefix_custom",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "notification_sounds_muted": {
+ "name": "notification_sounds_muted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "delete_local_branch": {
+ "name": "delete_local_branch",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "file_open_mode": {
+ "name": "file_open_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "show_presets_bar": {
+ "name": "show_presets_bar",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_font_family": {
+ "name": "terminal_font_family",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "terminal_font_size": {
+ "name": "terminal_font_size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "editor_font_family": {
+ "name": "editor_font_family",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "editor_font_size": {
+ "name": "editor_font_size",
+ "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
+ },
+ "is_unread": {
+ "name": "is_unread",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_unnamed": {
+ "name": "is_unnamed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "deleting_at": {
+ "name": "deleting_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "port_base": {
+ "name": "port_base",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "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 6dd08f6a403..6b5f80ae961 100644
--- a/packages/local-db/drizzle/meta/_journal.json
+++ b/packages/local-db/drizzle/meta/_journal.json
@@ -190,6 +190,13 @@
"when": 1771214717763,
"tag": "0026_browser_history",
"breakpoints": true
+ },
+ {
+ "idx": 27,
+ "version": "6",
+ "when": 1771524526706,
+ "tag": "0027_per_project_default_app",
+ "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 ff4a1f5546b..38e7a6259ed 100644
--- a/packages/local-db/src/schema/schema.ts
+++ b/packages/local-db/src/schema/schema.ts
@@ -41,6 +41,7 @@ export const projects = sqliteTable(
hideImage: integer("hide_image", { mode: "boolean" }),
iconUrl: text("icon_url"),
neonProjectId: text("neon_project_id"),
+ defaultApp: text("default_app").$type(),
},
(table) => [
index("projects_main_repo_path_idx").on(table.mainRepoPath),
@@ -138,7 +139,6 @@ export type SelectWorkspace = typeof workspaces.$inferSelect;
export const settings = sqliteTable("settings", {
id: integer("id").primaryKey().default(1),
lastActiveWorkspaceId: text("last_active_workspace_id"),
- lastUsedApp: text("last_used_app").$type(),
terminalPresets: text("terminal_presets", { mode: "json" }).$type<
TerminalPreset[]
>(),