| ... | ... | @@ -40,6 +40,7 @@ import BranchToolbar from "./BranchToolbar"; |
| 40 | 40 | import { isTerminalToggleShortcut } from "../terminal-shortcuts"; |
| 41 | 41 | import ChatMarkdown from "./ChatMarkdown"; |
| 42 | 42 | import ThreadTerminalDrawer from "./ThreadTerminalDrawer"; |
| 43 | import { CloudUploadIcon, GitCommitIcon, GithubIcon } from "lucide-react"; |
| 43 | 44 | |
| 44 | 45 | function formatMessageMeta(createdAt: string, duration: string | null): string { |
| 45 | 46 | if (!duration) return formatTimestamp(createdAt); |
| ... | ... | @@ -70,62 +71,14 @@ function GitActionIcon(props: { icon: GitActionMenuItem["icon"]; disabled: boole |
| 70 | 71 | const toneClass = props.disabled ? "text-muted-foreground/45" : "text-foreground/85"; |
| 71 | 72 | |
| 72 | 73 | if (props.icon === "commit") { |
| 73 | | return ( |
| 74 | | <svg |
| 75 | | viewBox="0 0 20 20" |
| 76 | | aria-hidden="true" |
| 77 | | className={`h-5 w-5 shrink-0 ${toneClass}`} |
| 78 | | fill="none" |
| 79 | | stroke="currentColor" |
| 80 | | strokeWidth="1.8" |
| 81 | | strokeLinecap="round" |
| 82 | | strokeLinejoin="round" |
| 83 | | > |
| 84 | | <circle cx="10" cy="10" r="4.2" /> |
| 85 | | <path d="M1.8 10h4.2" /> |
| 86 | | <path d="M14 10h4.2" /> |
| 87 | | </svg> |
| 88 | | ); |
| 74 | return <GitCommitIcon className={`h-5 w-5 shrink-0 ${toneClass}`} />; |
| 89 | 75 | } |
| 90 | 76 | |
| 91 | 77 | if (props.icon === "push") { |
| 92 | | return ( |
| 93 | | <svg |
| 94 | | viewBox="0 0 20 20" |
| 95 | | aria-hidden="true" |
| 96 | | className={`h-5 w-5 shrink-0 ${toneClass}`} |
| 97 | | fill="none" |
| 98 | | stroke="currentColor" |
| 99 | | strokeWidth="1.8" |
| 100 | | strokeLinecap="round" |
| 101 | | strokeLinejoin="round" |
| 102 | | > |
| 103 | | <path d="M5.2 16h9.6a3.2 3.2 0 0 0 .2-6.4h-.5A4.8 4.8 0 0 0 5 9 3.7 3.7 0 0 0 5.2 16Z" /> |
| 104 | | <path d="M10 5.8v6.2" /> |
| 105 | | <path d="m7.7 8.1 2.3-2.3 2.3 2.3" /> |
| 106 | | </svg> |
| 107 | | ); |
| 78 | return <CloudUploadIcon className={`h-5 w-5 shrink-0 ${toneClass}`} />; |
| 108 | 79 | } |
| 109 | 80 | |
| 110 | | return ( |
| 111 | | <svg |
| 112 | | viewBox="0 0 20 20" |
| 113 | | aria-hidden="true" |
| 114 | | className={`h-5 w-5 shrink-0 ${toneClass}`} |
| 115 | | fill="none" |
| 116 | | stroke="currentColor" |
| 117 | | strokeWidth="1.8" |
| 118 | | strokeLinecap="round" |
| 119 | | strokeLinejoin="round" |
| 120 | | > |
| 121 | | <circle cx="6" cy="5" r="1.7" /> |
| 122 | | <circle cx="6" cy="15" r="1.7" /> |
| 123 | | <circle cx="14" cy="10" r="1.7" /> |
| 124 | | <path d="M6 6.8v6.5" /> |
| 125 | | <path d="M7.8 6.1c2.9 0 3.8 1.4 5.1 2.9" /> |
| 126 | | <path d="M7.8 13.9c2.9 0 3.8-1.4 5.1-2.9" /> |
| 127 | | </svg> |
| 128 | | ); |
| 81 | return <GithubIcon className={`h-5 w-5 shrink-0 ${toneClass}`} />; |
| 129 | 82 | } |
| 130 | 83 | |
| 131 | 84 | function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string { |
| ... | ... | @@ -334,8 +287,7 @@ export default function ChatView() { |
| 334 | 287 | sandboxMode: "workspace-write", |
| 335 | 288 | } as const); |
| 336 | 289 | const gitCwd = activeThread?.worktreePath ?? activeProject?.cwd ?? null; |
| 337 | | const gitBaseDisabled = |
| 338 | | !api || !gitCwd || !gitStatus || isGitActionRunning; |
| 290 | const gitBaseDisabled = !api || !gitCwd || !gitStatus || isGitActionRunning; |
| 339 | 291 | const gitActionMenuItems = useMemo<GitActionMenuItem[]>(() => { |
| 340 | 292 | if (!gitStatus) return []; |
| 341 | 293 | |
| ... | ... | @@ -343,7 +295,9 @@ export default function ChatView() { |
| 343 | 295 | const hasOpenPr = gitStatus.openPr !== null; |
| 344 | 296 | const canCommit = !gitBaseDisabled && gitStatus.hasWorkingTreeChanges; |
| 345 | 297 | const canPush = |
| 346 | | !gitBaseDisabled && hasBranch && (gitStatus.hasWorkingTreeChanges || gitStatus.aheadCount > 0); |
| 298 | !gitBaseDisabled && |
| 299 | hasBranch && |
| 300 | (gitStatus.hasWorkingTreeChanges || gitStatus.aheadCount > 0); |
| 347 | 301 | const canViewPr = |
| 348 | 302 | !gitBaseDisabled && |
| 349 | 303 | hasBranch && |
| ... | ... | @@ -403,9 +357,7 @@ export default function ChatView() { |
| 403 | 357 | action, |
| 404 | 358 | }); |
| 405 | 359 | } catch (error) { |
| 406 | | setGitActionError( |
| 407 | | error instanceof Error ? error.message : "Git action failed.", |
| 408 | | ); |
| 360 | setGitActionError(error instanceof Error ? error.message : "Git action failed."); |
| 409 | 361 | } finally { |
| 410 | 362 | setIsGitActionRunning(false); |
| 411 | 363 | try { |
| ... | ... | @@ -425,9 +377,7 @@ export default function ChatView() { |
| 425 | 377 | (activeThread.messages.length > 0 || |
| 426 | 378 | (activeThread.session !== null && activeThread.session.status !== "closed")), |
| 427 | 379 | ); |
| 428 | | const terminalShortcutHint = navigator.platform.includes("Mac") |
| 429 | | ? "\u2318J" |
| 430 | | : "Ctrl+J"; |
| 380 | const terminalShortcutHint = navigator.platform.includes("Mac") ? "\u2318J" : "Ctrl+J"; |
| 431 | 381 | const focusComposer = useCallback(() => { |
| 432 | 382 | const textarea = textareaRef.current; |
| 433 | 383 | if (!textarea) return; |
| ... | ... | @@ -505,11 +455,7 @@ export default function ChatView() { |
| 505 | 455 | } catch (error) { |
| 506 | 456 | if (!cancelled) { |
| 507 | 457 | setGitStatus(null); |
| 508 | | setGitActionError( |
| 509 | | error instanceof Error |
| 510 | | ? error.message |
| 511 | | : "Failed to read git status.", |
| 512 | | ); |
| 458 | setGitActionError(error instanceof Error ? error.message : "Failed to read git status."); |
| 513 | 459 | } |
| 514 | 460 | } |
| 515 | 461 | }; |
| ... | ... | @@ -927,9 +873,7 @@ export default function ChatView() { |
| 927 | 873 | </button> |
| 928 | 874 | {isGitMenuOpen && ( |
| 929 | 875 | <div className="absolute right-0 top-full z-50 mt-1 w-[280px] rounded-3xl border border-border bg-popover p-3 shadow-xl"> |
| 930 | | <p className="px-3 pb-2 text-[13px] text-muted-foreground/75"> |
| 931 | | Git actions |
| 932 | | </p> |
| 876 | <p className="px-3 pb-2 text-[13px] text-muted-foreground/75">Git actions</p> |
| 933 | 877 | {gitActionMenuItems.map((item) => { |
| 934 | 878 | return ( |
| 935 | 879 | <button |
| ... | ... | @@ -1036,7 +980,7 @@ export default function ChatView() { |
| 1036 | 980 | </button> |
| 1037 | 981 | <button |
| 1038 | 982 | type="button" |
| 1039 | | className="rounded-md border border-sky-300/30 bg-sky-500/[0.15] px-2 py-1 text-[11px] text-sky-100 transition-colors duration-150 hover:bg-sky-500/[0.22] disabled:cursor-not-allowed disabled:opacity-50" |
| 983 | className="rounded-md border border-sky-300/30 bg-sky-500/15 px-2 py-1 text-[11px] text-sky-100 transition-colors duration-150 hover:bg-sky-500/22 disabled:cursor-not-allowed disabled:opacity-50" |
| 1040 | 984 | disabled={isResponding} |
| 1041 | 985 | onClick={() => void onRespondToApproval(approval.requestId, "acceptForSession")} |
| 1042 | 986 | > |
| ... | ... | @@ -1052,7 +996,7 @@ export default function ChatView() { |
| 1052 | 996 | </button> |
| 1053 | 997 | <button |
| 1054 | 998 | type="button" |
| 1055 | | className="rounded-md border border-rose-300/30 bg-rose-500/[0.12] px-2 py-1 text-[11px] text-rose-100 transition-colors duration-150 hover:bg-rose-500/[0.2] disabled:cursor-not-allowed disabled:opacity-50" |
| 999 | className="rounded-md border border-rose-300/30 bg-rose-500/12 px-2 py-1 text-[11px] text-rose-100 transition-colors duration-150 hover:bg-rose-500/20 disabled:cursor-not-allowed disabled:opacity-50" |
| 1056 | 1000 | disabled={isResponding} |
| 1057 | 1001 | onClick={() => void onRespondToApproval(approval.requestId, "cancel")} |
| 1058 | 1002 | > |
| ... | ... | @@ -1171,7 +1115,7 @@ export default function ChatView() { |
| 1171 | 1115 | <Fragment key={timelineEntry.id}> |
| 1172 | 1116 | <div className="flex justify-end"> |
| 1173 | 1117 | <div className="max-w-[80%] rounded-2xl rounded-br-sm border border-border bg-secondary px-4 py-3"> |
| 1174 | | <pre className="whitespace-pre-wrap break-words font-mono text-sm leading-relaxed text-foreground"> |
| 1118 | <pre className="whitespace-pre-wrap wrap-break-word font-mono text-sm leading-relaxed text-foreground"> |
| 1175 | 1119 | {timelineEntry.message.text} |
| 1176 | 1120 | </pre> |
| 1177 | 1121 | <p className="mt-1.5 text-right text-[10px] text-muted-foreground/30"> |