diff --git a/apps/server/src/open.test.ts b/apps/server/src/open.test.ts index 1889c851562b340b1b1ed05670bdea10b7068472..3a55bde8569e8ff2abd82a360c9882a99be0cc25 100644 --- a/apps/server/src/open.test.ts +++ b/apps/server/src/open.test.ts @@ -33,6 +33,15 @@ describe("resolveEditorLaunch", () => { command: "code", args: ["/tmp/workspace"], }); + + const zedLaunch = yield* resolveEditorLaunch( + { cwd: "/tmp/workspace", editor: "zed" }, + "darwin", + ); + assert.deepEqual(zedLaunch, { + command: "zed", + args: ["/tmp/workspace"], + }); }), ); @@ -64,6 +73,15 @@ describe("resolveEditorLaunch", () => { command: "code", args: ["--goto", "/tmp/workspace/src/open.ts:71:5"], }); + + const zedLineAndColumn = yield* resolveEditorLaunch( + { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "zed" }, + "darwin", + ); + assert.deepEqual(zedLineAndColumn, { + command: "zed", + args: ["/tmp/workspace/src/open.ts:71:5"], + }); }), ); diff --git a/apps/web/src/components/BranchToolbar.tsx b/apps/web/src/components/BranchToolbar.tsx index 6d9e92ac62f380c5f3fdf32df5bff2263a254aef..422ec8038984373844ee1406b0e35ad8c056d6ae 100644 --- a/apps/web/src/components/BranchToolbar.tsx +++ b/apps/web/src/components/BranchToolbar.tsx @@ -1,6 +1,6 @@ import type { GitBranch, ThreadId } from "@t3tools/contracts"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { useOptimistic, useState } from "react"; +import { startTransition, useOptimistic, useState } from "react"; import { newCommandId } from "../lib/utils"; import { @@ -80,6 +80,11 @@ export default function BranchToolbar({ canonicalActiveBranch, (_currentBranch: string | null, optimisticBranch: string | null) => optimisticBranch, ); + const setOptimisticBranchInTransition = (branch: string | null) => { + startTransition(() => { + setOptimisticBranch(branch); + }); + }; const branchNames = branches.map((branch) => branch.name); const branchByName = new Map(branches.map((branch) => [branch.name, branch])); const trimmedBranchQuery = branchQuery.trim(); @@ -172,7 +177,7 @@ export default function BranchToolbar({ const selectedBranchName = branch.isRemote ? deriveLocalBranchNameFromRemoteRef(branch.name) : branch.name; - setOptimisticBranch(selectedBranchName); + setOptimisticBranchInTransition(selectedBranchName); checkoutMutation.mutate(branch.name, { onSuccess: async () => { @@ -184,13 +189,13 @@ export default function BranchToolbar({ nextBranchName = status.branch; } } - setOptimisticBranch(nextBranchName); + setOptimisticBranchInTransition(nextBranchName); setThreadBranch(nextBranchName, activeWorktreePath); setIsBranchMenuOpen(false); onComposerFocusRequest?.(); }, onError: (error) => { - setOptimisticBranch(canonicalActiveBranch); + setOptimisticBranchInTransition(canonicalActiveBranch); setThreadError(error instanceof Error ? error.message : "Failed to checkout branch."); setIsBranchMenuOpen(true); }, @@ -201,10 +206,10 @@ export default function BranchToolbar({ const name = rawName.trim(); const api = readNativeApi(); if (!api || !activeThreadId || !branchCwd || !name || createBranchMutation.isPending) return; - setOptimisticBranch(name); + setOptimisticBranchInTransition(name); createBranchMutation.mutate(name, { onSuccess: () => { - setOptimisticBranch(name); + setOptimisticBranchInTransition(name); setThreadError(null); setThreadBranch(name, activeWorktreePath); setBranchQuery(""); @@ -212,7 +217,7 @@ export default function BranchToolbar({ onComposerFocusRequest?.(); }, onError: (error) => { - setOptimisticBranch(canonicalActiveBranch); + setOptimisticBranchInTransition(canonicalActiveBranch); setThreadError(error instanceof Error ? error.message : "Failed to create branch."); }, }); diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index a651adf43fb79da52585ef3af14a237a5979dd5a..f6a6142f5c559d6cd574eda93a2e6a722d0ab619 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -100,7 +100,7 @@ import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "./u import { Separator } from "./ui/separator"; import { Group, GroupSeparator } from "./ui/group"; import { Menu, MenuItem, MenuPopup, MenuShortcut, MenuTrigger } from "./ui/menu"; -import { CursorIcon, Icon, VisualStudioCode } from "./Icons"; +import { CursorIcon, Icon, VisualStudioCode, Zed } from "./Icons"; import { cn, isMacPlatform, isWindowsPlatform } from "~/lib/utils"; import { Badge } from "./ui/badge"; import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; @@ -3693,6 +3693,11 @@ const OpenInPicker = memo(function OpenInPicker({ Icon: VisualStudioCode, value: "vscode", }, + { + label: "Zed", + Icon: Zed, + value: "zed", + }, { label: isMacPlatform(navigator.platform) ? "Finder" diff --git a/apps/web/src/components/Icons.tsx b/apps/web/src/components/Icons.tsx index 1bac0fddfb9fec764ae4bb019c59897f0d7b9f6d..c74d785cdae6a5ab44f9486cf1be14358cf0ae5c 100644 --- a/apps/web/src/components/Icons.tsx +++ b/apps/web/src/components/Icons.tsx @@ -119,3 +119,26 @@ export const VisualStudioCode: Icon = (props) => { ); }; + +export const Zed: Icon = (props) => { + const id = useId(); + const clipPathId = `${id}-zed-logo-a`; + + return ( + + + + + + + + + + + ); +}; diff --git a/packages/contracts/src/editor.ts b/packages/contracts/src/editor.ts index 14f61a896b22c56130e54fe1544d83011a06ecce..8d7e0292339930b08341a91d7d7076af8131ed5f 100644 --- a/packages/contracts/src/editor.ts +++ b/packages/contracts/src/editor.ts @@ -4,6 +4,7 @@ import { TrimmedNonEmptyString } from "./baseSchemas"; export const EDITORS = [ { id: "cursor", label: "Cursor", command: "cursor" }, { id: "vscode", label: "VS Code", command: "code" }, + { id: "zed", label: "Zed", command: "zed" }, { id: "file-manager", label: "File Manager", command: null }, ] as const;