| author | |
| committer | |
| log | de61c0c42b0797735a5cbe7ec6d4b3358fcf4295 |
| tree | f74abd294eb0468d9ec8b7359a3db49ebf80d2ec |
| parent | ecb4a1efa43ae762b4314895505371033170faae |
- Skip auto-rename when the active worktree branch no longer matches the temp branch
- Update reactor tests to cover branch-switch and first-turn rename behavior
- Show the actual current branch in the toolbar and use a VS Code icon in Open In picker5 files changed, 231 insertions(+), 76 deletions(-)
apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts+75-12| ... | ... | @@ -108,11 +108,27 @@ describe("ProviderCommandReactor", () => { |
| 108 | 108 | const interruptTurn = vi.fn((_: unknown) => Effect.void); |
| 109 | 109 | const respondToRequest = vi.fn((_: unknown) => Effect.void); |
| 110 | 110 | const stopSession = vi.fn((_: unknown) => Effect.void); |
| 111 | const statusBranchByCwd = new Map<string, string | null>(); | |
| 111 | 112 | const renameBranch = vi.fn((_: unknown) => |
| 112 | 113 | Effect.succeed({ |
| 113 | 114 | branch: "t3code/generated-name", |
| 114 | 115 | }), |
| 115 | 116 | ); |
| 117 | const statusDetails = vi.fn((cwd: string) => | |
| 118 | Effect.succeed({ | |
| 119 | branch: statusBranchByCwd.get(cwd) ?? null, | |
| 120 | upstreamRef: null, | |
| 121 | hasWorkingTreeChanges: false, | |
| 122 | workingTree: { | |
| 123 | files: [], | |
| 124 | insertions: 0, | |
| 125 | deletions: 0, | |
| 126 | }, | |
| 127 | hasUpstream: false, | |
| 128 | aheadCount: 0, | |
| 129 | behindCount: 0, | |
| 130 | }), | |
| 131 | ); | |
| 116 | 132 | const generateBranchName = vi.fn<TextGenerationShape["generateBranchName"]>(() => |
| 117 | 133 | Effect.succeed({ |
| 118 | 134 | branch: "generated-name", |
| ... | ... | @@ -141,7 +157,12 @@ describe("ProviderCommandReactor", () => { |
| 141 | 157 | const layer = ProviderCommandReactorLive.pipe( |
| 142 | 158 | Layer.provideMerge(orchestrationLayer), |
| 143 | 159 | Layer.provideMerge(Layer.succeed(ProviderService, service)), |
| 144 | Layer.provideMerge(Layer.succeed(GitCore, { renameBranch } as unknown as GitCoreShape)), | |
| 160 | Layer.provideMerge( | |
| 161 | Layer.succeed( | |
| 162 | GitCore, | |
| 163 | { renameBranch, statusDetails } as unknown as GitCoreShape, | |
| 164 | ), | |
| 165 | ), | |
| 145 | 166 | Layer.provideMerge( |
| 146 | 167 | Layer.succeed(TextGeneration, { generateBranchName } as unknown as TextGenerationShape), |
| 147 | 168 | ), |
| ... | ... | @@ -189,7 +210,11 @@ describe("ProviderCommandReactor", () => { |
| 189 | 210 | respondToRequest, |
| 190 | 211 | stopSession, |
| 191 | 212 | renameBranch, |
| 213 | statusDetails, | |
| 192 | 214 | generateBranchName, |
| 215 | setStatusBranch: (cwd: string, branch: string | null) => { | |
| 216 | statusBranchByCwd.set(cwd, branch); | |
| 217 | }, | |
| 193 | 218 | stateDir, |
| 194 | 219 | }; |
| 195 | 220 | } |
| ... | ... | @@ -235,6 +260,7 @@ describe("ProviderCommandReactor", () => { |
| 235 | 260 | it("generates and renames temporary worktree branch on first turn", async () => { |
| 236 | 261 | const harness = await createHarness(); |
| 237 | 262 | const now = new Date().toISOString(); |
| 263 | const worktreeCwd = "/tmp/provider-project/.t3/worktrees/t3code-89abc123"; | |
| 238 | 264 | |
| 239 | 265 | await Effect.runPromise( |
| 240 | 266 | harness.engine.dispatch({ |
| ... | ... | @@ -242,9 +268,10 @@ describe("ProviderCommandReactor", () => { |
| 242 | 268 | commandId: CommandId.makeUnsafe("cmd-thread-meta-set-temp-branch"), |
| 243 | 269 | threadId: ThreadId.makeUnsafe("thread-1"), |
| 244 | 270 | branch: "t3code/89abc123", |
| 245 | worktreePath: "/tmp/provider-project/.t3/worktrees/t3code-89abc123", | |
| 271 | worktreePath: worktreeCwd, | |
| 246 | 272 | }), |
| 247 | 273 | ); |
| 274 | harness.setStatusBranch(worktreeCwd, "t3code/89abc123"); | |
| 248 | 275 | |
| 249 | 276 | await Effect.runPromise( |
| 250 | 277 | harness.engine.dispatch({ |
| ... | ... | @@ -275,7 +302,7 @@ describe("ProviderCommandReactor", () => { |
| 275 | 302 | await waitFor(() => harness.renameBranch.mock.calls.length === 1); |
| 276 | 303 | |
| 277 | 304 | expect(harness.generateBranchName.mock.calls[0]?.[0]).toEqual({ |
| 278 | cwd: "/tmp/provider-project/.t3/worktrees/t3code-89abc123", | |
| 305 | cwd: worktreeCwd, | |
| 279 | 306 | message: "Fix visual bug from screenshot", |
| 280 | 307 | attachments: [ |
| 281 | 308 | { |
| ... | ... | @@ -288,18 +315,14 @@ describe("ProviderCommandReactor", () => { |
| 288 | 315 | ], |
| 289 | 316 | }); |
| 290 | 317 | expect(harness.renameBranch.mock.calls[0]?.[0]).toEqual({ |
| 291 | cwd: "/tmp/provider-project/.t3/worktrees/t3code-89abc123", | |
| 318 | cwd: worktreeCwd, | |
| 292 | 319 | oldBranch: "t3code/89abc123", |
| 293 | 320 | newBranch: "t3code/generated-name", |
| 294 | 321 | }); |
| 295 | 322 | |
| 296 | await waitFor(() => { | |
| 297 | const readModel = Effect.runSync(harness.engine.getReadModel()); | |
| 298 | const thread = readModel.threads.find( | |
| 299 | (entry) => entry.id === ThreadId.makeUnsafe("thread-1"), | |
| 300 | ); | |
| 301 | return thread?.branch === "t3code/generated-name"; | |
| 302 | }); | |
| 323 | const readModel = await Effect.runPromise(harness.engine.getReadModel()); | |
| 324 | const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1")); | |
| 325 | expect(thread?.branch).toBe("t3code/89abc123"); | |
| 303 | 326 | }); |
| 304 | 327 | |
| 305 | 328 | it("passes persisted attachment references to branch generation and turn start", async () => { |
| ... | ... | @@ -373,6 +396,7 @@ describe("ProviderCommandReactor", () => { |
| 373 | 396 | it("skips worktree branch generation after the first user turn", async () => { |
| 374 | 397 | const harness = await createHarness(); |
| 375 | 398 | const now = new Date().toISOString(); |
| 399 | const worktreeCwd = "/tmp/provider-project/.t3/worktrees/t3code-1234abcd"; | |
| 376 | 400 | |
| 377 | 401 | await Effect.runPromise( |
| 378 | 402 | harness.engine.dispatch({ |
| ... | ... | @@ -380,9 +404,10 @@ describe("ProviderCommandReactor", () => { |
| 380 | 404 | commandId: CommandId.makeUnsafe("cmd-thread-meta-set-temp-branch-2"), |
| 381 | 405 | threadId: ThreadId.makeUnsafe("thread-1"), |
| 382 | 406 | branch: "t3code/1234abcd", |
| 383 | worktreePath: "/tmp/provider-project/.t3/worktrees/t3code-1234abcd", | |
| 407 | worktreePath: worktreeCwd, | |
| 384 | 408 | }), |
| 385 | 409 | ); |
| 410 | harness.setStatusBranch(worktreeCwd, "t3code/1234abcd"); | |
| 386 | 411 | |
| 387 | 412 | await Effect.runPromise( |
| 388 | 413 | harness.engine.dispatch({ |
| ... | ... | @@ -425,6 +450,44 @@ describe("ProviderCommandReactor", () => { |
| 425 | 450 | expect(harness.renameBranch.mock.calls.length).toBe(1); |
| 426 | 451 | }); |
| 427 | 452 | |
| 453 | it("skips worktree rename when active branch already changed", async () => { | |
| 454 | const harness = await createHarness(); | |
| 455 | const now = new Date().toISOString(); | |
| 456 | const worktreeCwd = "/tmp/provider-project/.t3/worktrees/t3code-badf00d0"; | |
| 457 | ||
| 458 | await Effect.runPromise( | |
| 459 | harness.engine.dispatch({ | |
| 460 | type: "thread.meta.update", | |
| 461 | commandId: CommandId.makeUnsafe("cmd-thread-meta-set-temp-branch-switched"), | |
| 462 | threadId: ThreadId.makeUnsafe("thread-1"), | |
| 463 | branch: "t3code/badf00d0", | |
| 464 | worktreePath: worktreeCwd, | |
| 465 | }), | |
| 466 | ); | |
| 467 | harness.setStatusBranch(worktreeCwd, "feature/add-vscode-open-in-support"); | |
| 468 | ||
| 469 | await Effect.runPromise( | |
| 470 | harness.engine.dispatch({ | |
| 471 | type: "thread.turn.start", | |
| 472 | commandId: CommandId.makeUnsafe("cmd-turn-start-worktree-switched"), | |
| 473 | threadId: ThreadId.makeUnsafe("thread-1"), | |
| 474 | message: { | |
| 475 | messageId: asMessageId("user-message-worktree-switched"), | |
| 476 | role: "user", | |
| 477 | text: "Fix visual bug from screenshot", | |
| 478 | attachments: [], | |
| 479 | }, | |
| 480 | approvalPolicy: "on-request", | |
| 481 | sandboxMode: "workspace-write", | |
| 482 | createdAt: now, | |
| 483 | }), | |
| 484 | ); | |
| 485 | ||
| 486 | await waitFor(() => harness.generateBranchName.mock.calls.length === 1); | |
| 487 | await Effect.runPromise(Effect.sleep("20 millis")); | |
| 488 | expect(harness.renameBranch.mock.calls.length).toBe(0); | |
| 489 | }); | |
| 490 | ||
| 428 | 491 | it("skips worktree rename when branch-name generation fails", async () => { |
| 429 | 492 | const harness = await createHarness(); |
| 430 | 493 | const now = new Date().toISOString(); |
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts+36-11| ... | ... | @@ -330,17 +330,42 @@ const make = Effect.gen(function* () { |
| 330 | 330 | const targetBranch = buildGeneratedWorktreeBranchName(generated.branch); |
| 331 | 331 | if (targetBranch === oldBranch) return Effect.void; |
| 332 | 332 | |
| 333 | return Effect.flatMap( | |
| 334 | git.renameBranch({ cwd, oldBranch, newBranch: targetBranch }), | |
| 335 | (renamed) => | |
| 336 | orchestrationEngine.dispatch({ | |
| 337 | type: "thread.meta.update", | |
| 338 | commandId: serverCommandId("worktree-branch-rename"), | |
| 339 | threadId: input.threadId, | |
| 340 | branch: renamed.branch, | |
| 341 | worktreePath: cwd, | |
| 342 | }), | |
| 343 | ); | |
| 333 | return Effect.gen(function* () { | |
| 334 | const currentBranch = yield* git.statusDetails(cwd).pipe( | |
| 335 | Effect.map((status) => status.branch), | |
| 336 | Effect.catch((error) => | |
| 337 | Effect.logWarning( | |
| 338 | "provider command reactor failed to read active worktree branch", | |
| 339 | { | |
| 340 | threadId: input.threadId, | |
| 341 | cwd, | |
| 342 | oldBranch, | |
| 343 | targetBranch, | |
| 344 | reason: error.message, | |
| 345 | }, | |
| 346 | ).pipe(Effect.as(null)), | |
| 347 | ), | |
| 348 | ); | |
| 349 | ||
| 350 | if (currentBranch !== oldBranch) { | |
| 351 | return yield* Effect.logWarning( | |
| 352 | "provider command reactor skipped worktree branch rename because active branch changed", | |
| 353 | { | |
| 354 | threadId: input.threadId, | |
| 355 | cwd, | |
| 356 | oldBranch, | |
| 357 | targetBranch, | |
| 358 | currentBranch, | |
| 359 | }, | |
| 360 | ); | |
| 361 | } | |
| 362 | ||
| 363 | yield* git.renameBranch({ | |
| 364 | cwd, | |
| 365 | oldBranch, | |
| 366 | newBranch: targetBranch, | |
| 367 | }); | |
| 368 | }); | |
| 344 | 369 | }), |
| 345 | 370 | Effect.catchCause((cause) => |
| 346 | 371 | Effect.logWarning( |
apps/web/src/components/BranchToolbar.tsx+11-51| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | import type { GitBranch, ThreadId } from "@t3tools/contracts"; |
| 2 | 2 | import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; |
| 3 | import { useEffect, useState } from "react"; | |
| 3 | import { useState } from "react"; | |
| 4 | 4 | |
| 5 | 5 | import { newCommandId } from "../lib/utils"; |
| 6 | 6 | import { |
| ... | ... | @@ -14,7 +14,6 @@ import { useStore } from "../store"; |
| 14 | 14 | import { |
| 15 | 15 | dedupeRemoteBranchesWithLocalMatches, |
| 16 | 16 | deriveLocalBranchNameFromRemoteRef, |
| 17 | deriveSyncedLocalBranch, | |
| 18 | 17 | } from "./BranchToolbar.logic"; |
| 19 | 18 | import { Button } from "./ui/button"; |
| 20 | 19 | import { |
| ... | ... | @@ -72,6 +71,11 @@ export default function BranchToolbar({ |
| 72 | 71 | const branchesQuery = useQuery(gitBranchesQueryOptions(branchCwd)); |
| 73 | 72 | |
| 74 | 73 | const branches = dedupeRemoteBranchesWithLocalMatches(branchesQuery.data?.branches ?? []); |
| 74 | const currentGitBranch = branches.find((branch) => branch.current)?.name ?? null; | |
| 75 | const resolvedActiveBranch = | |
| 76 | effectiveEnvMode === "worktree" && !activeWorktreePath | |
| 77 | ? activeThreadBranch | |
| 78 | : (currentGitBranch ?? activeThreadBranch); | |
| 75 | 79 | const branchNames = branches.map((branch) => branch.name); |
| 76 | 80 | const branchByName = new Map(branches.map((branch) => [branch.name, branch])); |
| 77 | 81 | const trimmedBranchQuery = branchQuery.trim(); |
| ... | ... | @@ -92,50 +96,6 @@ export default function BranchToolbar({ |
| 92 | 96 | gitCreateBranchAndCheckoutMutationOptions({ cwd: branchCwd, queryClient }), |
| 93 | 97 | ); |
| 94 | 98 | |
| 95 | // ── Effects ─────────────────────────────────────────────────────────── | |
| 96 | ||
| 97 | // Keep thread branch synced to git current branch for local threads. | |
| 98 | const queryBranches = branchesQuery.data?.branches; | |
| 99 | useEffect(() => { | |
| 100 | const syncedBranch = deriveSyncedLocalBranch({ | |
| 101 | activeThreadId, | |
| 102 | activeWorktreePath, | |
| 103 | envMode: effectiveEnvMode, | |
| 104 | activeThreadBranch, | |
| 105 | queryBranches, | |
| 106 | }); | |
| 107 | if (!activeThreadId || !syncedBranch) return; | |
| 108 | const api = readNativeApi(); | |
| 109 | ||
| 110 | if (api && hasServerThread) { | |
| 111 | void api.orchestration.dispatchCommand({ | |
| 112 | type: "thread.meta.update", | |
| 113 | commandId: newCommandId(), | |
| 114 | threadId: activeThreadId, | |
| 115 | branch: syncedBranch, | |
| 116 | worktreePath: null, | |
| 117 | }); | |
| 118 | } | |
| 119 | if (hasServerThread) { | |
| 120 | setThreadBranchAction(activeThreadId, syncedBranch, null); | |
| 121 | return; | |
| 122 | } | |
| 123 | setDraftThreadContext(threadId, { | |
| 124 | branch: syncedBranch, | |
| 125 | worktreePath: null, | |
| 126 | }); | |
| 127 | }, [ | |
| 128 | activeThreadId, | |
| 129 | activeWorktreePath, | |
| 130 | activeThreadBranch, | |
| 131 | queryBranches, | |
| 132 | effectiveEnvMode, | |
| 133 | setThreadBranchAction, | |
| 134 | hasServerThread, | |
| 135 | setDraftThreadContext, | |
| 136 | threadId, | |
| 137 | ]); | |
| 138 | ||
| 139 | 99 | // ── Helpers ─────────────────────────────────────────────────────────── |
| 140 | 100 | |
| 141 | 101 | const setThreadError = (error: string | null) => { |
| ... | ... | @@ -282,7 +242,7 @@ export default function BranchToolbar({ |
| 282 | 242 | } |
| 283 | 243 | }} |
| 284 | 244 | open={isBranchMenuOpen} |
| 285 | value={activeThreadBranch} | |
| 245 | value={resolvedActiveBranch} | |
| 286 | 246 | > |
| 287 | 247 | <ComboboxTrigger |
| 288 | 248 | render={<Button variant="ghost" size="xs" />} |
| ... | ... | @@ -290,10 +250,10 @@ export default function BranchToolbar({ |
| 290 | 250 | disabled={branchesQuery.isLoading} |
| 291 | 251 | > |
| 292 | 252 | <span className="max-w-[240px] truncate"> |
| 293 | {activeThreadBranch | |
| 253 | {resolvedActiveBranch | |
| 294 | 254 | ? effectiveEnvMode === "worktree" && !activeWorktreePath |
| 295 | ? `From ${activeThreadBranch}` | |
| 296 | : activeThreadBranch | |
| 255 | ? `From ${resolvedActiveBranch}` | |
| 256 | : resolvedActiveBranch | |
| 297 | 257 | : "Select branch"} |
| 298 | 258 | </span> |
| 299 | 259 | <ChevronDownIcon /> |
| ... | ... | @@ -347,7 +307,7 @@ export default function BranchToolbar({ |
| 347 | 307 | key={itemValue} |
| 348 | 308 | value={itemValue} |
| 349 | 309 | className={ |
| 350 | itemValue === activeThreadBranch ? "bg-accent text-foreground" : undefined | |
| 310 | itemValue === resolvedActiveBranch ? "bg-accent text-foreground" : undefined | |
| 351 | 311 | } |
| 352 | 312 | onClick={() => selectBranch(branch)} |
| 353 | 313 | > |
apps/web/src/components/ChatView.tsx+2-2| ... | ... | @@ -100,7 +100,7 @@ import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "./u |
| 100 | 100 | import { Separator } from "./ui/separator"; |
| 101 | 101 | import { Group, GroupSeparator } from "./ui/group"; |
| 102 | 102 | import { Menu, MenuItem, MenuPopup, MenuShortcut, MenuTrigger } from "./ui/menu"; |
| 103 | import { CursorIcon, Icon } from "./Icons"; | |
| 103 | import { CursorIcon, Icon, VisualStudioCode } from "./Icons"; | |
| 104 | 104 | import { cn, isMacPlatform, isWindowsPlatform } from "~/lib/utils"; |
| 105 | 105 | import { Badge } from "./ui/badge"; |
| 106 | 106 | import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; |
| ... | ... | @@ -3681,7 +3681,7 @@ const OpenInPicker = memo(function OpenInPicker({ |
| 3681 | 3681 | }, |
| 3682 | 3682 | { |
| 3683 | 3683 | label: "VS Code", |
| 3684 | Icon: FileIcon, | |
| 3684 | Icon: VisualStudioCode, | |
| 3685 | 3685 | value: "vscode", |
| 3686 | 3686 | }, |
| 3687 | 3687 | { |
apps/web/src/components/Icons.tsx+107| ... | ... | @@ -19,3 +19,110 @@ export const CursorIcon: Icon = (props) => ( |
| 19 | 19 | <path d="M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z" /> |
| 20 | 20 | </svg> |
| 21 | 21 | ); |
| 22 | ||
| 23 | export const VisualStudioCode: Icon = (props) => ( | |
| 24 | <svg {...props} fill="none" viewBox="0 0 100 100"> | |
| 25 | <mask | |
| 26 | id="vscode__a" | |
| 27 | width="100" | |
| 28 | height="100" | |
| 29 | x="0" | |
| 30 | y="0" | |
| 31 | maskUnits="userSpaceOnUse" | |
| 32 | > | |
| 33 | <path | |
| 34 | fill="#fff" | |
| 35 | fillRule="evenodd" | |
| 36 | d="M70.912 99.317a6.223 6.223 0 0 0 4.96-.19l20.589-9.907A6.25 6.25 0 0 0 100 83.587V16.413a6.25 6.25 0 0 0-3.54-5.632L75.874.874a6.226 6.226 0 0 0-7.104 1.21L29.355 38.04 12.187 25.01a4.162 4.162 0 0 0-5.318.236l-5.506 5.009a4.168 4.168 0 0 0-.004 6.162L16.247 50 1.36 63.583a4.168 4.168 0 0 0 .004 6.162l5.506 5.01a4.162 4.162 0 0 0 5.318.236l17.168-13.032L68.77 97.917a6.217 6.217 0 0 0 2.143 1.4ZM75.015 27.3 45.11 50l29.906 22.701V27.3Z" | |
| 37 | clipRule="evenodd" | |
| 38 | /> | |
| 39 | </mask> | |
| 40 | <g mask="url(#vscode__a)"> | |
| 41 | <path | |
| 42 | fill="#0065A9" | |
| 43 | d="M96.461 10.796 75.857.876a6.23 6.23 0 0 0-7.107 1.207l-67.451 61.5a4.167 4.167 0 0 0 .004 6.162l5.51 5.009a4.167 4.167 0 0 0 5.32.236l81.228-61.62c2.725-2.067 6.639-.124 6.639 3.297v-.24a6.25 6.25 0 0 0-3.539-5.63Z" | |
| 44 | /> | |
| 45 | <g filter="url(#vscode__b)"> | |
| 46 | <path | |
| 47 | fill="#007ACC" | |
| 48 | d="m96.461 89.204-20.604 9.92a6.229 6.229 0 0 1-7.107-1.207l-67.451-61.5a4.167 4.167 0 0 1 .004-6.162l5.51-5.009a4.167 4.167 0 0 1 5.32-.236l81.228 61.62c2.725 2.067 6.639.124 6.639-3.297v.24a6.25 6.25 0 0 1-3.539 5.63Z" | |
| 49 | /> | |
| 50 | </g> | |
| 51 | <g filter="url(#vscode__c)"> | |
| 52 | <path | |
| 53 | fill="#1F9CF0" | |
| 54 | d="M75.858 99.126a6.232 6.232 0 0 1-7.108-1.21c2.306 2.307 6.25.674 6.25-2.588V4.672c0-3.262-3.944-4.895-6.25-2.589a6.232 6.232 0 0 1 7.108-1.21l20.6 9.908A6.25 6.25 0 0 1 100 16.413v67.174a6.25 6.25 0 0 1-3.541 5.633l-20.601 9.906Z" | |
| 55 | /> | |
| 56 | </g> | |
| 57 | <path | |
| 58 | fill="url(#vscode__d)" | |
| 59 | fillRule="evenodd" | |
| 60 | d="M70.851 99.317a6.224 6.224 0 0 0 4.96-.19L96.4 89.22a6.25 6.25 0 0 0 3.54-5.633V16.413a6.25 6.25 0 0 0-3.54-5.632L75.812.874a6.226 6.226 0 0 0-7.104 1.21L29.294 38.04 12.126 25.01a4.162 4.162 0 0 0-5.317.236l-5.507 5.009a4.168 4.168 0 0 0-.004 6.162L16.186 50 1.298 63.583a4.168 4.168 0 0 0 .004 6.162l5.507 5.009a4.162 4.162 0 0 0 5.317.236L29.294 61.96l39.414 35.958a6.218 6.218 0 0 0 2.143 1.4ZM74.954 27.3 45.048 50l29.906 22.701V27.3Z" | |
| 61 | clipRule="evenodd" | |
| 62 | opacity=".25" | |
| 63 | style={{ mixBlendMode: "overlay" }} | |
| 64 | /> | |
| 65 | </g> | |
| 66 | <defs> | |
| 67 | <filter | |
| 68 | id="vscode__b" | |
| 69 | width="116.727" | |
| 70 | height="92.246" | |
| 71 | x="-8.394" | |
| 72 | y="15.829" | |
| 73 | colorInterpolationFilters="sRGB" | |
| 74 | filterUnits="userSpaceOnUse" | |
| 75 | > | |
| 76 | <feFlood floodOpacity="0" result="BackgroundImageFix" /> | |
| 77 | <feColorMatrix | |
| 78 | in="SourceAlpha" | |
| 79 | values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" | |
| 80 | /> | |
| 81 | <feOffset /> | |
| 82 | <feGaussianBlur stdDeviation="4.167" /> | |
| 83 | <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" /> | |
| 84 | <feBlend | |
| 85 | in2="BackgroundImageFix" | |
| 86 | mode="overlay" | |
| 87 | result="effect1_dropShadow" | |
| 88 | /> | |
| 89 | <feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape" /> | |
| 90 | </filter> | |
| 91 | <filter | |
| 92 | id="vscode__c" | |
| 93 | width="47.917" | |
| 94 | height="116.151" | |
| 95 | x="60.417" | |
| 96 | y="-8.076" | |
| 97 | colorInterpolationFilters="sRGB" | |
| 98 | filterUnits="userSpaceOnUse" | |
| 99 | > | |
| 100 | <feFlood floodOpacity="0" result="BackgroundImageFix" /> | |
| 101 | <feColorMatrix | |
| 102 | in="SourceAlpha" | |
| 103 | values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" | |
| 104 | /> | |
| 105 | <feOffset /> | |
| 106 | <feGaussianBlur stdDeviation="4.167" /> | |
| 107 | <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" /> | |
| 108 | <feBlend | |
| 109 | in2="BackgroundImageFix" | |
| 110 | mode="overlay" | |
| 111 | result="effect1_dropShadow" | |
| 112 | /> | |
| 113 | <feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape" /> | |
| 114 | </filter> | |
| 115 | <linearGradient | |
| 116 | id="vscode__d" | |
| 117 | x1="49.939" | |
| 118 | x2="49.939" | |
| 119 | y1=".258" | |
| 120 | y2="99.742" | |
| 121 | gradientUnits="userSpaceOnUse" | |
| 122 | > | |
| 123 | <stop stopColor="#fff" /> | |
| 124 | <stop offset="1" stopColor="#fff" stopOpacity="0" /> | |
| 125 | </linearGradient> | |
| 126 | </defs> | |
| 127 | </svg> | |
| 128 | ); |