diff --git a/apps/desktop/src/lib/trpc/routers/settings/index.ts b/apps/desktop/src/lib/trpc/routers/settings/index.ts index 4676a7d9ed9..d9ff55e10b7 100644 --- a/apps/desktop/src/lib/trpc/routers/settings/index.ts +++ b/apps/desktop/src/lib/trpc/routers/settings/index.ts @@ -1,6 +1,7 @@ import { BRANCH_PREFIX_MODES, EXECUTION_MODES, + FILE_OPEN_MODES, settings, TERMINAL_LINK_BEHAVIORS, type TerminalPreset, @@ -12,6 +13,7 @@ import { localDb } from "main/lib/local-db"; import { DEFAULT_AUTO_APPLY_DEFAULT_PRESET, DEFAULT_CONFIRM_ON_QUIT, + DEFAULT_FILE_OPEN_MODE, DEFAULT_TERMINAL_LINK_BEHAVIOR, } from "shared/constants"; import { DEFAULT_RINGTONE_ID, RINGTONES } from "shared/ringtones"; @@ -362,6 +364,26 @@ export const createSettingsRouter = () => { return { success: true }; }), + getFileOpenMode: publicProcedure.query(() => { + const row = getSettings(); + return row.fileOpenMode ?? DEFAULT_FILE_OPEN_MODE; + }), + + setFileOpenMode: publicProcedure + .input(z.object({ mode: z.enum(FILE_OPEN_MODES) })) + .mutation(({ input }) => { + localDb + .insert(settings) + .values({ id: 1, fileOpenMode: input.mode }) + .onConflictDoUpdate({ + target: settings.id, + set: { fileOpenMode: input.mode }, + }) + .run(); + + return { success: true }; + }), + getAutoApplyDefaultPreset: publicProcedure.query(() => { const row = getSettings(); return row.autoApplyDefaultPreset ?? DEFAULT_AUTO_APPLY_DEFAULT_PRESET; diff --git a/apps/desktop/src/renderer/hooks/useFileOpenMode/index.ts b/apps/desktop/src/renderer/hooks/useFileOpenMode/index.ts new file mode 100644 index 00000000000..9c7ae61741c --- /dev/null +++ b/apps/desktop/src/renderer/hooks/useFileOpenMode/index.ts @@ -0,0 +1 @@ +export { useFileOpenMode } from "./useFileOpenMode"; diff --git a/apps/desktop/src/renderer/hooks/useFileOpenMode/useFileOpenMode.ts b/apps/desktop/src/renderer/hooks/useFileOpenMode/useFileOpenMode.ts new file mode 100644 index 00000000000..e0c31713af7 --- /dev/null +++ b/apps/desktop/src/renderer/hooks/useFileOpenMode/useFileOpenMode.ts @@ -0,0 +1,6 @@ +import { electronTrpc } from "renderer/lib/electron-trpc"; + +export function useFileOpenMode() { + const { data } = electronTrpc.settings.getFileOpenMode.useQuery(); + return data ?? "split-pane"; +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/behavior/components/BehaviorSettings/BehaviorSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/behavior/components/BehaviorSettings/BehaviorSettings.tsx index f28e73c8ef1..81b9846a302 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/behavior/components/BehaviorSettings/BehaviorSettings.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/behavior/components/BehaviorSettings/BehaviorSettings.tsx @@ -1,4 +1,4 @@ -import type { BranchPrefixMode } from "@superset/local-db"; +import type { BranchPrefixMode, FileOpenMode } from "@superset/local-db"; import { Input } from "@superset/ui/input"; import { Label } from "@superset/ui/label"; import { @@ -40,6 +40,10 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) { SETTING_ITEM_ID.BEHAVIOR_TELEMETRY, visibleItems, ); + const showFileOpenMode = isItemVisible( + SETTING_ITEM_ID.BEHAVIOR_FILE_OPEN_MODE, + visibleItems, + ); const utils = electronTrpc.useUtils(); @@ -160,6 +164,25 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) { }); }; + const { data: fileOpenMode, isLoading: isFileOpenModeLoading } = + electronTrpc.settings.getFileOpenMode.useQuery(); + const setFileOpenMode = electronTrpc.settings.setFileOpenMode.useMutation({ + onMutate: async ({ mode }) => { + await utils.settings.getFileOpenMode.cancel(); + const previous = utils.settings.getFileOpenMode.getData(); + utils.settings.getFileOpenMode.setData(undefined, mode); + return { previous }; + }, + onError: (_err, _vars, context) => { + if (context?.previous !== undefined) { + utils.settings.getFileOpenMode.setData(undefined, context.previous); + } + }, + onSettled: () => { + utils.settings.getFileOpenMode.invalidate(); + }, + }); + const previewPrefix = resolveBranchPrefix({ mode: branchPrefix?.mode ?? "none", @@ -276,6 +299,32 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) { )} + {showFileOpenMode && ( +
+
+ +

+ Choose how files open when no preview pane exists +

+
+ +
+ )} + {false && showTelemetry && (
diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts index cb611ce0951..b69f8c9093e 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts @@ -24,6 +24,7 @@ export const SETTING_ITEM_ID = { BEHAVIOR_DELETE_LOCAL_BRANCH: "behavior-delete-local-branch", BEHAVIOR_BRANCH_PREFIX: "behavior-branch-prefix", BEHAVIOR_TELEMETRY: "behavior-telemetry", + BEHAVIOR_FILE_OPEN_MODE: "behavior-file-open-mode", TERMINAL_PRESETS: "terminal-presets", TERMINAL_QUICK_ADD: "terminal-quick-add", @@ -364,6 +365,25 @@ export const SETTINGS_ITEMS: SettingsItem[] = [ "disable", ], }, + { + id: SETTING_ITEM_ID.BEHAVIOR_FILE_OPEN_MODE, + section: "behavior", + title: "File open mode", + description: + "Choose how files open when clicked in the file tree or changes view", + keywords: [ + "file", + "open", + "mode", + "split", + "pane", + "tab", + "new tab", + "split pane", + "viewer", + "behavior", + ], + }, { id: SETTING_ITEM_ID.TERMINAL_PRESETS, section: "terminal", diff --git a/apps/desktop/src/renderer/screens/main/components/SidebarControl/SidebarControl.tsx b/apps/desktop/src/renderer/screens/main/components/SidebarControl/SidebarControl.tsx index 034c42ec157..b6f640764f6 100644 --- a/apps/desktop/src/renderer/screens/main/components/SidebarControl/SidebarControl.tsx +++ b/apps/desktop/src/renderer/screens/main/components/SidebarControl/SidebarControl.tsx @@ -5,6 +5,7 @@ import { useParams } from "@tanstack/react-router"; import { useCallback } from "react"; import { LuDiff } from "react-icons/lu"; import { HotkeyTooltipContent } from "renderer/components/HotkeyTooltipContent"; +import { useFileOpenMode } from "renderer/hooks/useFileOpenMode"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { useSidebarStore } from "renderer/stores"; import { useChangesStore } from "renderer/stores/changes"; @@ -46,6 +47,7 @@ export function SidebarControl() { ); const addFileViewerPane = useTabsStore((s) => s.addFileViewerPane); + const fileOpenMode = useFileOpenMode(); const trpcUtils = electronTrpc.useUtils(); const invalidateFileContent = useCallback( @@ -92,6 +94,7 @@ export function SidebarControl() { diffCategory: category, oldPath: firstFile.oldPath, isPinned: false, + openInNewTab: fileOpenMode === "new-tab", }); invalidateFileContent(firstFile.path); } @@ -102,6 +105,7 @@ export function SidebarControl() { selectFile, addFileViewerPane, invalidateFileContent, + fileOpenMode, ]); const handleClick = useCallback(() => { diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useFileLinkClick.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useFileLinkClick.ts index e1fdc40edb4..04c1beaa04b 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useFileLinkClick.ts +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useFileLinkClick.ts @@ -1,5 +1,6 @@ import { toast } from "@superset/ui/sonner"; import { useCallback } from "react"; +import { useFileOpenMode } from "renderer/hooks/useFileOpenMode"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { electronTrpcClient as trpcClient } from "renderer/lib/trpc-client"; import { useTabsStore } from "renderer/stores/tabs/store"; @@ -25,6 +26,7 @@ export function useFileLinkClick({ workspaceCwd, }: UseFileLinkClickOptions): UseFileLinkClickReturn { const addFileViewerPane = useTabsStore((s) => s.addFileViewerPane); + const fileOpenMode = useFileOpenMode(); // Query terminal link behavior setting const { data: terminalLinkBehavior } = @@ -85,12 +87,23 @@ export function useFileLinkClick({ }); return; } - addFileViewerPane(workspaceId, { filePath, line, column }); + addFileViewerPane(workspaceId, { + filePath, + line, + column, + openInNewTab: fileOpenMode === "new-tab", + }); } else { openInExternalEditor(); } }, - [terminalLinkBehavior, workspaceId, workspaceCwd, addFileViewerPane], + [ + terminalLinkBehavior, + workspaceId, + workspaceCwd, + addFileViewerPane, + fileOpenMode, + ], ); return { 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..49ddbf20d16 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 @@ -14,6 +14,7 @@ import { import { useParams } from "@tanstack/react-router"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { LuFile, LuFolder } from "react-icons/lu"; +import { useFileOpenMode } from "renderer/hooks/useFileOpenMode"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { useFileExplorerStore } from "renderer/stores/file-explorer"; import { useTabsStore } from "renderer/stores/tabs/store"; @@ -148,6 +149,7 @@ export function FilesView() { }); const addFileViewerPane = useTabsStore((s) => s.addFileViewerPane); + const fileOpenMode = useFileOpenMode(); const openFileInEditorMutation = electronTrpc.external.openFileInEditor.useMutation(); @@ -162,9 +164,10 @@ export function FilesView() { if (!workspaceId || !worktreePath || entry.isDirectory) return; addFileViewerPane(workspaceId, { filePath: entry.relativePath, + openInNewTab: fileOpenMode === "new-tab", }); }, - [workspaceId, worktreePath, addFileViewerPane], + [workspaceId, worktreePath, addFileViewerPane, fileOpenMode], ); const handleOpenInEditor = useCallback( diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx index 624a9b93bbc..9bad05da004 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx @@ -11,6 +11,7 @@ import { LuX, } from "react-icons/lu"; import { HotkeyTooltipContent } from "renderer/components/HotkeyTooltipContent"; +import { useFileOpenMode } from "renderer/hooks/useFileOpenMode"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { RightSidebarTab, @@ -101,6 +102,7 @@ export function RightSidebar() { }; const addFileViewerPane = useTabsStore((s) => s.addFileViewerPane); + const fileOpenMode = useFileOpenMode(); const trpcUtils = electronTrpc.useUtils(); const { scrollToFile } = useScrollContext(); @@ -135,10 +137,17 @@ export function RightSidebar() { diffCategory: category, commitHash, oldPath: file.oldPath, + openInNewTab: fileOpenMode === "new-tab", }); invalidateFileContent(file.path); }, - [workspaceId, worktreePath, addFileViewerPane, invalidateFileContent], + [ + workspaceId, + worktreePath, + addFileViewerPane, + invalidateFileContent, + fileOpenMode, + ], ); const handleFileScrollTo = useCallback( diff --git a/apps/desktop/src/renderer/stores/tabs/store.ts b/apps/desktop/src/renderer/stores/tabs/store.ts index a2da770dcaf..8304ebc90d4 100644 --- a/apps/desktop/src/renderer/stores/tabs/store.ts +++ b/apps/desktop/src/renderer/stores/tabs/store.ts @@ -682,6 +682,48 @@ export const useTabsStore = create()( } // No reusable pane found, create a new one + if (options.openInNewTab) { + const workspaceId = activeTab.workspaceId; + const newTabId = generateId("tab"); + const newPane = createFileViewerPane(newTabId, options); + + const newTab = { + id: newTabId, + workspaceId, + name: newPane.name, + layout: newPane.id as MosaicNode, + createdAt: Date.now(), + }; + + const currentActiveId = state.activeTabIds[workspaceId]; + const historyStack = state.tabHistoryStacks[workspaceId] || []; + const newHistoryStack = currentActiveId + ? [ + currentActiveId, + ...historyStack.filter((id) => id !== currentActiveId), + ] + : historyStack; + + set({ + tabs: [...state.tabs, newTab], + panes: { ...state.panes, [newPane.id]: newPane }, + activeTabIds: { + ...state.activeTabIds, + [workspaceId]: newTab.id, + }, + focusedPaneIds: { + ...state.focusedPaneIds, + [newTab.id]: newPane.id, + }, + tabHistoryStacks: { + ...state.tabHistoryStacks, + [workspaceId]: newHistoryStack, + }, + }); + + return newPane.id; + } + const newPane = createFileViewerPane(activeTab.id, options); const newLayout: MosaicNode = { diff --git a/apps/desktop/src/renderer/stores/tabs/types.ts b/apps/desktop/src/renderer/stores/tabs/types.ts index d0cd41fd981..3870758c7cb 100644 --- a/apps/desktop/src/renderer/stores/tabs/types.ts +++ b/apps/desktop/src/renderer/stores/tabs/types.ts @@ -57,6 +57,8 @@ export interface AddFileViewerPaneOptions { column?: number; /** If true, opens pinned (permanent). If false/undefined, opens in preview mode (can be replaced) */ isPinned?: boolean; + /** If true, opens in a new tab instead of splitting the current tab */ + openInNewTab?: boolean; } /** diff --git a/apps/desktop/src/shared/constants.ts b/apps/desktop/src/shared/constants.ts index 2eadec60637..c6186fc627b 100644 --- a/apps/desktop/src/shared/constants.ts +++ b/apps/desktop/src/shared/constants.ts @@ -53,6 +53,7 @@ export const MOCK_ORG_ID = "mock-org-id"; // Default user preference values export const DEFAULT_CONFIRM_ON_QUIT = true; export const DEFAULT_TERMINAL_LINK_BEHAVIOR = "external-editor" as const; +export const DEFAULT_FILE_OPEN_MODE = "split-pane" as const; export const DEFAULT_AUTO_APPLY_DEFAULT_PRESET = true; export const DEFAULT_TELEMETRY_ENABLED = true; diff --git a/packages/local-db/drizzle/0020_add_file_open_mode_setting.sql b/packages/local-db/drizzle/0020_add_file_open_mode_setting.sql new file mode 100644 index 00000000000..1866465ce0e --- /dev/null +++ b/packages/local-db/drizzle/0020_add_file_open_mode_setting.sql @@ -0,0 +1 @@ +ALTER TABLE `settings` ADD `file_open_mode` text; \ No newline at end of file diff --git a/packages/local-db/drizzle/meta/0020_snapshot.json b/packages/local-db/drizzle/meta/0020_snapshot.json new file mode 100644 index 00000000000..be74644589f --- /dev/null +++ b/packages/local-db/drizzle/meta/0020_snapshot.json @@ -0,0 +1,1078 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "3ff0af35-7fc1-4e36-8012-b113e7a91372", + "prevId": "732c942c-5f01-451f-a6cf-92c38b434076", + "tables": { + "organization_members": { + "name": "organization_members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "organization_members_organization_id_idx": { + "name": "organization_members_organization_id_idx", + "columns": [ + "organization_id" + ], + "isUnique": false + }, + "organization_members_user_id_idx": { + "name": "organization_members_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organization_members_organization_id_organizations_id_fk": { + "name": "organization_members_organization_id_organizations_id_fk", + "tableFrom": "organization_members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_members_user_id_users_id_fk": { + "name": "organization_members_user_id_users_id_fk", + "tableFrom": "organization_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "clerk_org_id": { + "name": "clerk_org_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_org": { + "name": "github_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "organizations_clerk_org_id_unique": { + "name": "organizations_clerk_org_id_unique", + "columns": [ + "clerk_org_id" + ], + "isUnique": true + }, + "organizations_slug_unique": { + "name": "organizations_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "organizations_slug_idx": { + "name": "organizations_slug_idx", + "columns": [ + "slug" + ], + "isUnique": false + }, + "organizations_clerk_org_id_idx": { + "name": "organizations_clerk_org_id_idx", + "columns": [ + "clerk_org_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "projects": { + "name": "projects", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "main_repo_path": { + "name": "main_repo_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tab_order": { + "name": "tab_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_opened_at": { + "name": "last_opened_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_toast_dismissed": { + "name": "config_toast_dismissed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "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 + } + }, + "indexes": { + "projects_main_repo_path_idx": { + "name": "projects_main_repo_path_idx", + "columns": [ + "main_repo_path" + ], + "isUnique": false + }, + "projects_last_opened_at_idx": { + "name": "projects_last_opened_at_idx", + "columns": [ + "last_opened_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "settings": { + "name": "settings", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "last_active_workspace_id": { + "name": "last_active_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_used_app": { + "name": "last_used_app", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_presets": { + "name": "terminal_presets", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_presets_initialized": { + "name": "terminal_presets_initialized", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "selected_ringtone_id": { + "name": "selected_ringtone_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "confirm_on_quit": { + "name": "confirm_on_quit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "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 + } + }, + "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 + } + }, + "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 d6ac4a2497b..60394526996 100644 --- a/packages/local-db/drizzle/meta/_journal.json +++ b/packages/local-db/drizzle/meta/_journal.json @@ -141,6 +141,13 @@ "when": 1770438863796, "tag": "0019_add_hide_image_to_projects", "breakpoints": true + }, + { + "idx": 20, + "version": "6", + "when": 1770754481921, + "tag": "0020_add_file_open_mode_setting", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/local-db/src/schema/schema.ts b/packages/local-db/src/schema/schema.ts index 74757311d4d..8ca558f826c 100644 --- a/packages/local-db/src/schema/schema.ts +++ b/packages/local-db/src/schema/schema.ts @@ -4,6 +4,7 @@ import { v4 as uuidv4 } from "uuid"; import type { BranchPrefixMode, ExternalApp, + FileOpenMode, GitHubStatus, GitStatus, TerminalLinkBehavior, @@ -157,6 +158,7 @@ export const settings = sqliteTable("settings", { mode: "boolean", }), deleteLocalBranch: integer("delete_local_branch", { mode: "boolean" }), + fileOpenMode: text("file_open_mode").$type(), }); export type InsertSettings = typeof settings.$inferInsert; diff --git a/packages/local-db/src/schema/zod.ts b/packages/local-db/src/schema/zod.ts index be05a5d5e74..59b1972f32e 100644 --- a/packages/local-db/src/schema/zod.ts +++ b/packages/local-db/src/schema/zod.ts @@ -128,3 +128,7 @@ export const BRANCH_PREFIX_MODES = [ ] as const; export type BranchPrefixMode = (typeof BRANCH_PREFIX_MODES)[number]; + +export const FILE_OPEN_MODES = ["split-pane", "new-tab"] as const; + +export type FileOpenMode = (typeof FILE_OPEN_MODES)[number];