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[] >(),