diff --git a/apps/desktop/src/lib/trpc/routers/settings/index.ts b/apps/desktop/src/lib/trpc/routers/settings/index.ts index 72d40259b7f..e11650081ab 100644 --- a/apps/desktop/src/lib/trpc/routers/settings/index.ts +++ b/apps/desktop/src/lib/trpc/routers/settings/index.ts @@ -21,6 +21,7 @@ import { DEFAULT_AUTO_APPLY_DEFAULT_PRESET, DEFAULT_CONFIRM_ON_QUIT, DEFAULT_FILE_OPEN_MODE, + DEFAULT_OPTION_AS_META_KEY, DEFAULT_OPEN_LINKS_IN_APP, DEFAULT_SHOW_PRESETS_BAR, DEFAULT_SHOW_RESOURCE_MONITOR, @@ -658,30 +659,50 @@ export const createSettingsRouter = () => { return { success: true }; }), - getOpenLinksInApp: publicProcedure.query(() => { - const row = getSettings(); - return row.openLinksInApp ?? DEFAULT_OPEN_LINKS_IN_APP; - }), + getOpenLinksInApp: publicProcedure.query(() => { + const row = getSettings(); + return row.openLinksInApp ?? DEFAULT_OPEN_LINKS_IN_APP; + }), - setOpenLinksInApp: publicProcedure - .input(z.object({ enabled: z.boolean() })) - .mutation(({ input }) => { - localDb - .insert(settings) - .values({ id: 1, openLinksInApp: input.enabled }) - .onConflictDoUpdate({ - target: settings.id, - set: { openLinksInApp: input.enabled }, - }) - .run(); + setOpenLinksInApp: publicProcedure + .input(z.object({ enabled: z.boolean() })) + .mutation(({ input }) => { + localDb + .insert(settings) + .values({ id: 1, openLinksInApp: input.enabled }) + .onConflictDoUpdate({ + target: settings.id, + set: { openLinksInApp: input.enabled }, + }) + .run(); - return { success: true }; + return { success: true }; + }), + + getOptionAsMetaKey: publicProcedure.query(() => { + const row = getSettings(); + return row.optionAsMetaKey ?? DEFAULT_OPTION_AS_META_KEY; }), - getDefaultEditor: publicProcedure.query(() => { - const row = getSettings(); - return row.defaultEditor ?? null; - }), + setOptionAsMetaKey: publicProcedure + .input(z.object({ enabled: z.boolean() })) + .mutation(({ input }) => { + localDb + .insert(settings) + .values({ id: 1, optionAsMetaKey: input.enabled }) + .onConflictDoUpdate({ + target: settings.id, + set: { optionAsMetaKey: input.enabled }, + }) + .run(); + + return { success: true }; + }), + + getDefaultEditor: publicProcedure.query(() => { + const row = getSettings(); + return row.defaultEditor ?? null; + }), setDefaultEditor: publicProcedure .input( 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 f742a04e610..fee3c03a247 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 @@ -60,6 +60,13 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) { SETTING_ITEM_ID.BEHAVIOR_OPEN_LINKS_IN_APP, visibleItems, ); + const showOptionAsMetaKey = isItemVisible( + SETTING_ITEM_ID.BEHAVIOR_OPTION_AS_META_KEY, + visibleItems, + ); + const isMac = + typeof navigator !== "undefined" && + navigator.platform.toLowerCase().includes("mac"); const utils = electronTrpc.useUtils(); @@ -266,6 +273,28 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) { }, }, ); + const { data: optionAsMetaKey, isLoading: isOptionAsMetaKeyLoading } = + electronTrpc.settings.getOptionAsMetaKey.useQuery(); + const setOptionAsMetaKey = + electronTrpc.settings.setOptionAsMetaKey.useMutation({ + onMutate: async ({ enabled }) => { + await utils.settings.getOptionAsMetaKey.cancel(); + const previous = utils.settings.getOptionAsMetaKey.getData(); + utils.settings.getOptionAsMetaKey.setData(undefined, enabled); + return { previous }; + }, + onError: (_err, _vars, context) => { + if (context?.previous !== undefined) { + utils.settings.getOptionAsMetaKey.setData( + undefined, + context.previous, + ); + } + }, + onSettled: () => { + utils.settings.getOptionAsMetaKey.invalidate(); + }, + }); const previewPrefix = resolveBranchPrefix({ @@ -432,34 +461,63 @@ export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) { )} - {showOpenLinksInApp && ( -
-
- -

- Open links from chat and terminal in the built-in browser - instead of your default browser -

+ {showOpenLinksInApp && ( +
+
+ +

+ Open links from chat and terminal in the built-in browser + instead of your default browser +

+
+ + setOpenLinksInApp.mutate({ enabled }) + } + disabled={isOpenLinksInAppLoading || setOpenLinksInApp.isPending} + />
- - setOpenLinksInApp.mutate({ enabled }) - } - disabled={isOpenLinksInAppLoading || setOpenLinksInApp.isPending} - /> -
- )} + )} + + {showOptionAsMetaKey && ( +
+
+ +

+ On macOS, send Escape + letter for Option + letter in terminal + instead of inserting special characters +

+
+ + setOptionAsMetaKey.mutate({ enabled }) + } + disabled={ + !isMac || + isOptionAsMetaKeyLoading || + setOptionAsMetaKey.isPending + } + /> +
+ )} - {showWorktreeLocation && ( -
- + {showWorktreeLocation && ( +
+

Base directory for new worktrees

diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.test.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.test.ts index 5b860fb8946..312991558e6 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.test.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.test.ts @@ -62,4 +62,10 @@ describe("settings search - font settings", () => { expect(editorFont?.section).toBe("appearance"); expect(terminalFont?.section).toBe("appearance"); }); + + it('searching "option meta" returns the Option as Meta key setting', () => { + const results = searchSettings("option meta"); + const ids = getIds(results); + expect(ids).toContain(SETTING_ITEM_ID.BEHAVIOR_OPTION_AS_META_KEY); + }); }); 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 2e49f5f5ef0..77b834b0aa1 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 @@ -30,6 +30,7 @@ export const SETTING_ITEM_ID = { BEHAVIOR_RESOURCE_MONITOR: "behavior-resource-monitor", BEHAVIOR_WORKTREE_LOCATION: "behavior-worktree-location", BEHAVIOR_OPEN_LINKS_IN_APP: "behavior-open-links-in-app", + BEHAVIOR_OPTION_AS_META_KEY: "behavior-option-as-meta-key", TERMINAL_PRESETS: "terminal-presets", TERMINAL_QUICK_ADD: "terminal-quick-add", @@ -481,6 +482,26 @@ export const SETTINGS_ITEMS: SettingsItem[] = [ "url", ], }, + { + id: SETTING_ITEM_ID.BEHAVIOR_OPTION_AS_META_KEY, + section: "behavior", + title: "Use Option as Meta key", + description: + "On macOS, send Escape + letter for Option + letter in terminal", + keywords: [ + "option", + "option meta", + "alt", + "meta", + "escape", + "terminal", + "macos", + "shortcut", + "bindings", + "claude code", + "special characters", + ], + }, { id: SETTING_ITEM_ID.TERMINAL_PRESETS, section: "terminal", diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx index b42d2e6105a..e6eac8b18cb 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx @@ -6,6 +6,7 @@ import { useEffect, useRef, useState } from "react"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { useTabsStore } from "renderer/stores/tabs/store"; import { useTerminalTheme } from "renderer/stores/theme"; +import { DEFAULT_OPTION_AS_META_KEY } from "shared/constants"; import { SessionKilledOverlay } from "./components"; import { DEFAULT_TERMINAL_FONT_FAMILY, @@ -124,6 +125,8 @@ export const Terminal = ({ paneId, tabId, workspaceId }: TerminalProps) => { // URL click handler - opens in app browser or system browser based on setting const { data: openLinksInApp } = electronTrpc.settings.getOpenLinksInApp.useQuery(); + const { data: optionAsMetaKey } = + electronTrpc.settings.getOptionAsMetaKey.useQuery(); const openInBrowserPane = useTabsStore((s) => s.openInBrowserPane); const handleUrlClickRef = useRef<((url: string) => void) | undefined>( undefined, @@ -340,20 +343,21 @@ export const Terminal = ({ paneId, tabId, workspaceId }: TerminalProps) => { maybeApplyInitialState, flushPendingEvents, resetModes, - isAlternateScreenRef, - isBracketedPasteRef, - setPaneNameRef, - renameUnnamedWorkspaceRef, - handleTerminalFocusRef, - registerClearCallbackRef, - unregisterClearCallbackRef, - registerScrollToBottomCallbackRef, - unregisterScrollToBottomCallbackRef, - registerGetSelectionCallbackRef, - unregisterGetSelectionCallbackRef, - registerPasteCallbackRef, - unregisterPasteCallbackRef, - }); + isAlternateScreenRef, + isBracketedPasteRef, + setPaneNameRef, + renameUnnamedWorkspaceRef, + handleTerminalFocusRef, + registerClearCallbackRef, + unregisterClearCallbackRef, + registerScrollToBottomCallbackRef, + unregisterScrollToBottomCallbackRef, + registerGetSelectionCallbackRef, + unregisterGetSelectionCallbackRef, + registerPasteCallbackRef, + unregisterPasteCallbackRef, + useOptionAsMetaKey: optionAsMetaKey ?? DEFAULT_OPTION_AS_META_KEY, + }); useEffect(() => { const xterm = xtermRef.current; diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.test.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.test.ts index 0dd72c7e654..df78c67b69e 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.test.ts +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.test.ts @@ -161,6 +161,82 @@ describe("setupKeyboardHandler", () => { expect(onWrite).toHaveBeenCalledWith("\x1bf"); }); + it("maps Option+letter to Meta+letter on macOS when enabled", () => { + // @ts-expect-error - mocking navigator for tests + globalThis.navigator = { platform: "MacIntel" }; + + const captured: { handler: ((event: KeyboardEvent) => boolean) | null } = { + handler: null, + }; + const xterm = { + attachCustomKeyEventHandler: ( + next: (event: KeyboardEvent) => boolean, + ) => { + captured.handler = next; + }, + }; + + const onWrite = mock(() => {}); + setupKeyboardHandler(xterm as unknown as XTerm, { + onWrite, + isOptionAsMetaKeyEnabled: () => true, + }); + + captured.handler?.({ + type: "keydown", + key: "π", + code: "KeyP", + altKey: true, + metaKey: false, + ctrlKey: false, + shiftKey: false, + } as KeyboardEvent); + captured.handler?.({ + type: "keydown", + key: "Í", + code: "KeyF", + altKey: true, + metaKey: false, + ctrlKey: false, + shiftKey: true, + } as KeyboardEvent); + + expect(onWrite).toHaveBeenCalledWith("\x1bp"); + expect(onWrite).toHaveBeenCalledWith("\x1bF"); + }); + + it("does not map Option+letter on macOS when disabled", () => { + // @ts-expect-error - mocking navigator for tests + globalThis.navigator = { platform: "MacIntel" }; + + const captured: { handler: ((event: KeyboardEvent) => boolean) | null } = { + handler: null, + }; + const xterm = { + attachCustomKeyEventHandler: ( + next: (event: KeyboardEvent) => boolean, + ) => { + captured.handler = next; + }, + }; + + const onWrite = mock(() => {}); + setupKeyboardHandler(xterm as unknown as XTerm, { onWrite }); + + const result = captured.handler?.({ + type: "keydown", + key: "π", + code: "KeyP", + altKey: true, + metaKey: false, + ctrlKey: false, + shiftKey: false, + } as KeyboardEvent); + + expect(result).toBeTrue(); + expect(onWrite).not.toHaveBeenCalled(); + }); + it("maps Ctrl+Left/Right to Meta+B/F on Windows", () => { // @ts-expect-error - mocking navigator for tests globalThis.navigator = { platform: "Win32" }; diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts index 6e21a82bbe6..f005a1eb126 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts @@ -311,6 +311,7 @@ export interface KeyboardHandlerOptions { /** Callback for the configured clear terminal shortcut */ onClear?: () => void; onWrite?: (data: string) => void; + isOptionAsMetaKeyEnabled?: () => boolean; } export interface PasteHandlerOptions { @@ -626,6 +627,25 @@ export function setupKeyboardHandler( return false; } + const isOptionLetter = + (options.isOptionAsMetaKeyEnabled?.() ?? false) && + /^Key[A-Z]$/.test(event.code) && + event.altKey && + isMac && + !event.metaKey && + !event.ctrlKey; + + if (isOptionLetter) { + if (event.type === "keydown" && options.onWrite) { + const baseLetter = event.code.slice(3); + const letter = event.shiftKey + ? baseLetter + : baseLetter.toLowerCase(); + options.onWrite(`\x1b${letter}`); + } + return false; + } + // Ctrl+Left/Right (Windows): word navigation (Meta+B / Meta+F) const isCtrlLeft = event.key === "ArrowLeft" && diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useTerminalLifecycle.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useTerminalLifecycle.ts index 99dd86d5d60..772f7159fba 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useTerminalLifecycle.ts +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useTerminalLifecycle.ts @@ -132,6 +132,7 @@ export interface UseTerminalLifecycleOptions { (paneId: string, callback: (text: string) => void) => void >; unregisterPasteCallbackRef: MutableRefObject; + useOptionAsMetaKey: boolean; } export interface UseTerminalLifecycleReturn { @@ -188,9 +189,12 @@ export function useTerminalLifecycle({ unregisterGetSelectionCallbackRef, registerPasteCallbackRef, unregisterPasteCallbackRef, + useOptionAsMetaKey, }: UseTerminalLifecycleOptions): UseTerminalLifecycleReturn { const [xtermInstance, setXtermInstance] = useState(null); const restartTerminalRef = useRef<() => void>(() => {}); + const useOptionAsMetaKeyRef = useRef(useOptionAsMetaKey); + useOptionAsMetaKeyRef.current = useOptionAsMetaKey; const restartTerminal = useCallback(() => restartTerminalRef.current(), []); // biome-ignore lint/correctness/useExhaustiveDependencies: refs used intentionally @@ -488,11 +492,12 @@ export function useTerminalLifecycle({ writeRef.current({ paneId, data }); }; - const cleanupKeyboard = setupKeyboardHandler(xterm, { - onShiftEnter: () => handleWrite("\x1b\r"), - onClear: handleClear, - onWrite: handleWrite, - }); + const cleanupKeyboard = setupKeyboardHandler(xterm, { + onShiftEnter: () => handleWrite("\x1b\r"), + onClear: handleClear, + onWrite: handleWrite, + isOptionAsMetaKeyEnabled: () => useOptionAsMetaKeyRef.current, + }); const cleanupClickToMove = setupClickToMoveCursor(xterm, { onWrite: handleWrite, }); diff --git a/apps/desktop/src/shared/constants.ts b/apps/desktop/src/shared/constants.ts index 48ed97f5af1..60cdbdf984a 100644 --- a/apps/desktop/src/shared/constants.ts +++ b/apps/desktop/src/shared/constants.ts @@ -45,6 +45,7 @@ export const DEFAULT_USE_COMPACT_TERMINAL_ADD_BUTTON = true; export const DEFAULT_TELEMETRY_ENABLED = true; export const DEFAULT_SHOW_RESOURCE_MONITOR = false; export const DEFAULT_OPEN_LINKS_IN_APP = false; +export const DEFAULT_OPTION_AS_META_KEY = false; // External links (documentation, help resources, etc.) export const EXTERNAL_LINKS = { diff --git a/packages/local-db/drizzle/0035_add_option_as_meta_key_setting.sql b/packages/local-db/drizzle/0035_add_option_as_meta_key_setting.sql new file mode 100644 index 00000000000..021a9ec8240 --- /dev/null +++ b/packages/local-db/drizzle/0035_add_option_as_meta_key_setting.sql @@ -0,0 +1 @@ +ALTER TABLE `settings` ADD `option_as_meta_key` integer; \ No newline at end of file diff --git a/packages/local-db/drizzle/meta/0035_snapshot.json b/packages/local-db/drizzle/meta/0035_snapshot.json new file mode 100644 index 00000000000..0f6400cc7a7 --- /dev/null +++ b/packages/local-db/drizzle/meta/0035_snapshot.json @@ -0,0 +1,1266 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "f1b467f6-af24-4840-aded-a23459550710", + "prevId": "ba86e644-bfb3-445c-83c0-ac90043bbca6", + "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 + }, + "workspace_base_branch": { + "name": "workspace_base_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 + }, + "worktree_base_dir": { + "name": "worktree_base_dir", + "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 + }, + "use_compact_terminal_add_button": { + "name": "use_compact_terminal_add_button", + "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 + }, + "show_resource_monitor": { + "name": "show_resource_monitor", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "worktree_base_dir": { + "name": "worktree_base_dir", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "open_links_in_app": { + "name": "open_links_in_app", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "option_as_meta_key": { + "name": "option_as_meta_key", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_editor": { + "name": "default_editor", + "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 + }, + "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 1f8067d2c52..c384d11f32d 100644 --- a/packages/local-db/drizzle/meta/_journal.json +++ b/packages/local-db/drizzle/meta/_journal.json @@ -246,6 +246,13 @@ "when": 1771977629901, "tag": "0034_add_use_compact_terminal_add_button_setting", "breakpoints": true + }, + { + "idx": 35, + "version": "6", + "when": 1773635890112, + "tag": "0035_add_option_as_meta_key_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 a22b00d244f..aaea7505782 100644 --- a/packages/local-db/src/schema/schema.ts +++ b/packages/local-db/src/schema/schema.ts @@ -177,6 +177,7 @@ export const settings = sqliteTable("settings", { showResourceMonitor: integer("show_resource_monitor", { mode: "boolean" }), worktreeBaseDir: text("worktree_base_dir"), openLinksInApp: integer("open_links_in_app", { mode: "boolean" }), + optionAsMetaKey: integer("option_as_meta_key", { mode: "boolean" }), defaultEditor: text("default_editor").$type(), });