- Detect and route jj repositories alongside git - Thread VCS kind through status and UI labels - Add jj driver contract coverage and shared freshness helper
Showing the changes to apps/web/src (12 of 30 files). Show all
Unified · Split
| @@ -23,6 +23,7 @@ import { cn } from "../lib/utils"; · 22 unmodified lines | ||
| 23 | 23 | import { parsePullRequestReference } from "../pullRequestReference"; |
| 24 | 24 | import { useStore } from "../store"; |
| 25 | 25 | import { createProjectSelectorByRef, createThreadSelectorByRef } from "../storeSelectors"; |
| 26 | +import { resolveVcsTerms } from "../vcsPresentation"; | |
| 26 | 27 | import { |
| 27 | 28 | deriveLocalBranchNameFromRemoteRef, |
| 28 | 29 | resolveBranchSelectionTarget, |
| @@ -66,10 +67,11 @@ function getBranchTriggerLabel(input: { · 37 unmodified lines | ||
| 66 | 67 | activeWorktreePath: string | null; |
| 67 | 68 | effectiveEnvMode: "local" | "worktree"; |
| 68 | 69 | resolvedActiveBranch: string | null; |
| 70 | + refNoun: string; | |
| 69 | 71 | }): string { |
| 70 | − const { activeWorktreePath, effectiveEnvMode, resolvedActiveBranch } = input; | |
| 72 | + const { activeWorktreePath, effectiveEnvMode, resolvedActiveBranch, refNoun } = input; | |
| 71 | 73 | if (!resolvedActiveBranch) { |
| 72 | − return "Select ref"; | |
| 74 | + return `Select ${refNoun}`; | |
| 73 | 75 | } |
| 74 | 76 | if (effectiveEnvMode === "worktree" && !activeWorktreePath) { |
| 75 | 77 | return `From ${resolvedActiveBranch}`; |
| @@ -201,6 +203,7 @@ export function BranchToolbarBranchSelector({ · 125 unmodified lines | ||
| 201 | 203 | const deferredBranchQuery = useDeferredValue(branchQuery); |
| 202 | 204 | |
| 203 | 205 | const branchStatusQuery = useGitStatus({ environmentId, cwd: branchCwd }); |
| 206 | + const vcsTerms = resolveVcsTerms(branchStatusQuery.data?.kind); | |
| 204 | 207 | const trimmedBranchQuery = branchQuery.trim(); |
| 205 | 208 | const deferredTrimmedBranchQuery = deferredBranchQuery.trim(); |
| 206 | 209 | |
| @@ -289,11 +292,11 @@ export function BranchToolbarBranchSelector({ · 82 unmodified lines | ||
| 289 | 292 | const shouldVirtualizeBranchList = filteredBranchPickerItems.length > 40; |
| 290 | 293 | const totalBranchCount = branchesSearchData?.pages[0]?.totalCount ?? 0; |
| 291 | 294 | const branchStatusText = isBranchesSearchPending |
| 292 | − ? "Loading refs..." | |
| 295 | + ? `Loading ${vcsTerms.refNounPlural}...` | |
| 293 | 296 | : isFetchingNextPage |
| 294 | − ? "Loading more refs..." | |
| 297 | + ? `Loading more ${vcsTerms.refNounPlural}...` | |
| 295 | 298 | : hasNextPage |
| 296 | − ? `Showing ${refs.length} of ${totalBranchCount} refs` | |
| 299 | + ? `Showing ${refs.length} of ${totalBranchCount} ${vcsTerms.refNounPlural}` | |
| 297 | 300 | : null; |
| 298 | 301 | |
| 299 | 302 | // --------------------------------------------------------------------------- |
| @@ -357,7 +360,7 @@ export function BranchToolbarBranchSelector({ · 57 unmodified lines | ||
| 357 | 360 | toastManager.add( |
| 358 | 361 | stackedThreadToast({ |
| 359 | 362 | type: "error", |
| 360 | − title: "Failed to switch ref.", | |
| 363 | + title: `Failed to switch ${vcsTerms.refNoun}.`, | |
| 361 | 364 | description: toBranchActionErrorMessage(error), |
| 362 | 365 | }), |
| 363 | 366 | ); |
| @@ -389,7 +392,7 @@ export function BranchToolbarBranchSelector({ · 25 unmodified lines | ||
| 389 | 392 | toastManager.add( |
| 390 | 393 | stackedThreadToast({ |
| 391 | 394 | type: "error", |
| 392 | − title: "Failed to create and switch ref.", | |
| 395 | + title: `Failed to create and switch ${vcsTerms.refNoun}.`, | |
| 393 | 396 | description: toBranchActionErrorMessage(error), |
| 394 | 397 | }), |
| 395 | 398 | ); |
| @@ -488,6 +491,7 @@ export function BranchToolbarBranchSelector({ · 92 unmodified lines | ||
| 488 | 491 | activeWorktreePath, |
| 489 | 492 | effectiveEnvMode, |
| 490 | 493 | resolvedActiveBranch, |
| 494 | + refNoun: vcsTerms.refNoun, | |
| 491 | 495 | }); |
| 492 | 496 | |
| 493 | 497 | function renderPickerItem(itemValue: string, index: number) { |
| @@ -524,7 +528,9 @@ export function BranchToolbarBranchSelector({ · 30 unmodified lines | ||
| 524 | 528 | value={itemValue} |
| 525 | 529 | onClick={() => createRef(trimmedBranchQuery)} |
| 526 | 530 | > |
| 527 | − <span className="truncate">Create new ref "{trimmedBranchQuery}"</span> | |
| 531 | + <span className="truncate"> | |
| 532 | + Create new {vcsTerms.refNoun} "{trimmedBranchQuery}" | |
| 533 | + </span> | |
| 528 | 534 | </ComboboxItem> |
| 529 | 535 | ); |
| 530 | 536 | } |
| @@ -1458,7 +1458,8 @@ export default function ChatView(props: ChatViewProps) { · 1457 unmodified lines | ||
| 1458 | 1458 | ? terminalLaunchContext |
| 1459 | 1459 | : (storeServerTerminalLaunchContext ?? null); |
| 1460 | 1460 | // Default true while loading to avoid toolbar flicker. |
| 1461 | − const isGitRepo = gitStatusQuery.data?.isRepo ?? true; | |
| 1461 | + const vcsKind = gitStatusQuery.data?.kind ?? null; | |
| 1462 | + const isVcsRepo = gitStatusQuery.data?.isRepo ?? true; | |
| 1462 | 1463 | const terminalShortcutLabelOptions = useMemo( |
| 1463 | 1464 | () => ({ |
| 1464 | 1465 | context: { |
| @@ -2136,7 +2137,7 @@ export default function ChatView(props: ChatViewProps) { · 671 unmodified lines | ||
| 2136 | 2137 | : (activeThread?.branch ?? null); |
| 2137 | 2138 | const sendEnvMode = resolveSendEnvMode({ |
| 2138 | 2139 | requestedEnvMode: envMode, |
| 2139 | − isGitRepo, | |
| 2140 | + isGitRepo: isVcsRepo, | |
| 2140 | 2141 | }); |
| 2141 | 2142 | |
| 2142 | 2143 | useEffect(() => { |
| @@ -3297,7 +3298,8 @@ export default function ChatView(props: ChatViewProps) { · 1154 unmodified lines | ||
| 3297 | 3298 | {...(routeKind === "draft" && draftId ? { draftId } : {})} |
| 3298 | 3299 | activeThreadTitle={activeThread.title} |
| 3299 | 3300 | activeProjectName={activeProject?.name} |
| 3300 | − isGitRepo={isGitRepo} | |
| 3301 | + isVcsRepo={isVcsRepo} | |
| 3302 | + vcsKind={vcsKind} | |
| 3301 | 3303 | openInCwd={gitCwd} |
| 3302 | 3304 | activeProjectScripts={activeProject?.scripts} |
| 3303 | 3305 | preferredScriptId={ |
| @@ -3377,7 +3379,7 @@ export default function ChatView(props: ChatViewProps) { · 73 unmodified lines | ||
| 3377 | 3379 | <div |
| 3378 | 3380 | className={cn( |
| 3379 | 3381 | "pl-[calc(env(safe-area-inset-left)+0.75rem)] pr-[calc(env(safe-area-inset-right)+0.75rem)] pt-1.5 sm:pl-[calc(env(safe-area-inset-left)+1.25rem)] sm:pr-[calc(env(safe-area-inset-right)+1.25rem)] sm:pt-2", |
| 3380 | − isGitRepo | |
| 3382 | + isVcsRepo | |
| 3381 | 3383 | ? "pb-[calc(env(safe-area-inset-bottom)+0.25rem)]" |
| 3382 | 3384 | : "pb-[calc(env(safe-area-inset-bottom)+0.75rem)] sm:pb-[calc(env(safe-area-inset-bottom)+1rem)]", |
| 3383 | 3385 | )} |
| @@ -3450,7 +3452,7 @@ export default function ChatView(props: ChatViewProps) { · 66 unmodified lines | ||
| 3450 | 3452 | setThreadError={setThreadError} |
| 3451 | 3453 | onExpandImage={onExpandTimelineImage} |
| 3452 | 3454 | /> |
| 3453 | − {isGitRepo && ( | |
| 3455 | + {isVcsRepo && ( | |
| 3454 | 3456 | <BranchToolbar |
| 3455 | 3457 | environmentId={activeThread.environmentId} |
| 3456 | 3458 | threadId={activeThread.id} |
| @@ -35,6 +35,7 @@ import { createThreadSelectorByRef } from "../storeSelectors"; · 34 unmodified lines | ||
| 35 | 35 | import { buildThreadRouteParams, resolveThreadRouteRef } from "../threadRoutes"; |
| 36 | 36 | import { useSettings } from "../hooks/useSettings"; |
| 37 | 37 | import { formatShortTimestamp } from "../timestampFormat"; |
| 38 | +import { resolveVcsTerms } from "../vcsPresentation"; | |
| 38 | 39 | import { DiffPanelLoadingState, DiffPanelShell, type DiffPanelMode } from "./DiffPanelShell"; |
| 39 | 40 | import { ToggleGroup, Toggle } from "./ui/toggle-group"; |
| 40 | 41 | |
| @@ -201,7 +202,8 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) { · 160 unmodified lines | ||
| 201 | 202 | environmentId: activeThread?.environmentId ?? null, |
| 202 | 203 | cwd: activeCwd ?? null, |
| 203 | 204 | }); |
| 204 | − const isGitRepo = gitStatusQuery.data?.isRepo ?? true; | |
| 205 | + const isVcsRepo = gitStatusQuery.data?.isRepo ?? true; | |
| 206 | + const vcsTerms = resolveVcsTerms(gitStatusQuery.data?.kind); | |
| 205 | 207 | const { turnDiffSummaries, inferredCheckpointTurnCountByTurnId } = |
| 206 | 208 | useTurnDiffSummaries(activeThread); |
| 207 | 209 | const orderedTurnDiffSummaries = useMemo( |
| @@ -278,7 +280,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) { · 70 unmodified lines | ||
| 278 | 280 | fromTurnCount: activeCheckpointRange?.fromTurnCount ?? null, |
| 279 | 281 | toTurnCount: activeCheckpointRange?.toTurnCount ?? null, |
| 280 | 282 | cacheScope: selectedTurn ? `turn:${selectedTurn.turnId}` : conversationCacheScope, |
| 281 | − enabled: isGitRepo, | |
| 283 | + enabled: isVcsRepo, | |
| 282 | 284 | }), |
| 283 | 285 | ); |
| 284 | 286 | const selectedTurnCheckpointDiff = selectedTurn |
| @@ -561,9 +563,9 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) { · 276 unmodified lines | ||
| 561 | 563 | <div className="flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70"> |
| 562 | 564 | Select a thread to inspect turn diffs. |
| 563 | 565 | </div> |
| 564 | − ) : !isGitRepo ? ( | |
| 566 | + ) : !isVcsRepo ? ( | |
| 565 | 567 | <div className="flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70"> |
| 566 | − Turn diffs are unavailable because this project is not a git repository. | |
| 568 | + Turn diffs are unavailable because this project is not a {vcsTerms.repositoryNoun}. | |
| 567 | 569 | </div> |
| 568 | 570 | ) : orderedTurnDiffSummaries.length === 0 ? ( |
| 569 | 571 | <div className="flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70"> |
| @@ -113,6 +113,7 @@ vi.mock("~/lib/gitStatusState", () => ({ · 112 unmodified lines | ||
| 113 | 113 | resetGitStatusStateForTests: () => undefined, |
| 114 | 114 | useGitStatus: vi.fn(() => ({ |
| 115 | 115 | data: { |
| 116 | + kind: "git", | |
| 116 | 117 | isRepo: true, |
| 117 | 118 | sourceControlProvider: { |
| 118 | 119 | kind: "github", |
| @@ -13,6 +13,7 @@ import { · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | function status(overrides: Partial<VcsStatusResult> = {}): VcsStatusResult { |
| 15 | 15 | return { |
| 16 | + kind: "git", | |
| 16 | 17 | isRepo: true, |
| 17 | 18 | hasPrimaryRemote: true, |
| 18 | 19 | isDefaultRef: false, |
| @@ -54,6 +54,7 @@ import { readEnvironmentApi } from "~/environmentApi"; · 53 unmodified lines | ||
| 54 | 54 | import { readLocalApi } from "~/localApi"; |
| 55 | 55 | import { useStore } from "~/store"; |
| 56 | 56 | import { createThreadSelectorByRef } from "~/storeSelectors"; |
| 57 | +import { resolveVcsActionPresentation, resolveVcsTerms } from "~/vcsPresentation"; | |
| 57 | 58 | |
| 58 | 59 | interface GitActionsControlProps { |
| 59 | 60 | gitCwd: string | null; |
| @@ -324,8 +325,12 @@ export default function GitActionsControl({ · 264 unmodified lines | ||
| 324 | 325 | }); |
| 325 | 326 | // Default to true while loading so we don't flash init controls. |
| 326 | 327 | const isRepo = gitStatus?.isRepo ?? true; |
| 328 | + const vcsKind = gitStatus?.kind ?? null; | |
| 329 | + const vcsTerms = resolveVcsTerms(vcsKind); | |
| 330 | + const vcsActionPresentation = resolveVcsActionPresentation(vcsKind); | |
| 331 | + const supportsGitWorkflowActions = !isRepo || vcsActionPresentation.supportsGitWorkflowActions; | |
| 327 | 332 | const hasPrimaryRemote = gitStatus?.hasPrimaryRemote ?? false; |
| 328 | − const gitStatusForActions = gitStatus; | |
| 333 | + const gitStatusForActions = supportsGitWorkflowActions ? gitStatus : null; | |
| 329 | 334 | |
| 330 | 335 | const allFiles = gitStatusForActions?.workingTree.files ?? []; |
| 331 | 336 | const selectedFiles = allFiles.filter((f) => !excludedFiles.has(f.path)); |
| @@ -874,6 +879,25 @@ export default function GitActionsControl({ · 542 unmodified lines | ||
| 874 | 879 | > |
| 875 | 880 | {initMutation.isPending ? "Initializing..." : "Initialize Git"} |
| 876 | 881 | </Button> |
| 882 | + ) : !supportsGitWorkflowActions ? ( | |
| 883 | + <Popover> | |
| 884 | + <PopoverTrigger | |
| 885 | + openOnHover | |
| 886 | + render={ | |
| 887 | + <Button | |
| 888 | + aria-disabled="true" | |
| 889 | + className="cursor-not-allowed opacity-70" | |
| 890 | + size="xs" | |
| 891 | + variant="outline" | |
| 892 | + /> | |
| 893 | + } | |
| 894 | + > | |
| 895 | + {vcsTerms.systemName} {vcsTerms.repositoryNoun} | |
| 896 | + </PopoverTrigger> | |
| 897 | + <PopoverPopup tooltipStyle side="bottom" align="end"> | |
| 898 | + {vcsActionPresentation.unsupportedGitWorkflowDescription} | |
| 899 | + </PopoverPopup> | |
| 900 | + </Popover> | |
| 877 | 901 | ) : ( |
| 878 | 902 | <Group aria-label="Git actions" className="shrink-0"> |
| 879 | 903 | {quickActionDisabledReason ? ( |
| @@ -17,6 +17,8 @@ import { Toggle } from "../ui/toggle"; · 16 unmodified lines | ||
| 17 | 17 | import { SidebarTrigger } from "../ui/sidebar"; |
| 18 | 18 | import { OpenInPicker } from "./OpenInPicker"; |
| 19 | 19 | import { usePrimaryEnvironmentId } from "../../environments/primary"; |
| 20 | +import { resolveVcsTerms } from "../../vcsPresentation"; | |
| 21 | +import type { VcsDriverKind } from "@t3tools/contracts"; | |
| 20 | 22 | |
| 21 | 23 | interface ChatHeaderProps { |
| 22 | 24 | activeThreadEnvironmentId: EnvironmentId; |
| @@ -24,7 +26,8 @@ interface ChatHeaderProps { · 1 unmodified line | ||
| 24 | 26 | draftId?: DraftId; |
| 25 | 27 | activeThreadTitle: string; |
| 26 | 28 | activeProjectName: string | undefined; |
| 27 | − isGitRepo: boolean; | |
| 29 | + isVcsRepo: boolean; | |
| 30 | + vcsKind: VcsDriverKind | null; | |
| 28 | 31 | openInCwd: string | null; |
| 29 | 32 | activeProjectScripts: ProjectScript[] | undefined; |
| 30 | 33 | preferredScriptId: string | null; |
| @@ -50,7 +53,8 @@ export const ChatHeader = memo(function ChatHeader({ · 19 unmodified lines | ||
| 50 | 53 | draftId, |
| 51 | 54 | activeThreadTitle, |
| 52 | 55 | activeProjectName, |
| 53 | − isGitRepo, | |
| 56 | + isVcsRepo, | |
| 57 | + vcsKind, | |
| 54 | 58 | openInCwd, |
| 55 | 59 | activeProjectScripts, |
| 56 | 60 | preferredScriptId, |
| @@ -72,6 +76,7 @@ export const ChatHeader = memo(function ChatHeader({ · 15 unmodified lines | ||
| 72 | 76 | const primaryEnvironmentId = usePrimaryEnvironmentId(); |
| 73 | 77 | const isRemoteEnvironment = |
| 74 | 78 | primaryEnvironmentId !== null && activeThreadEnvironmentId !== primaryEnvironmentId; |
| 79 | + const vcsTerms = resolveVcsTerms(vcsKind); | |
| 75 | 80 | |
| 76 | 81 | return ( |
| 77 | 82 | <div className="@container/header-actions flex min-w-0 flex-1 items-center gap-2"> |
| @@ -88,9 +93,9 @@ export const ChatHeader = memo(function ChatHeader({ · 10 unmodified lines | ||
| 88 | 93 | <span className="min-w-0 truncate">{activeProjectName}</span> |
| 89 | 94 | </Badge> |
| 90 | 95 | )} |
| 91 | − {activeProjectName && !isGitRepo && ( | |
| 96 | + {activeProjectName && !isVcsRepo && ( | |
| 92 | 97 | <Badge variant="outline" className="shrink-0 text-[10px] text-amber-700"> |
| 93 | − No Git | |
| 98 | + No repository | |
| 94 | 99 | </Badge> |
| 95 | 100 | )} |
| 96 | 101 | </div> |
| @@ -154,15 +159,15 @@ export const ChatHeader = memo(function ChatHeader({ · 57 unmodified lines | ||
| 154 | 159 | aria-label="Toggle diff panel" |
| 155 | 160 | variant="outline" |
| 156 | 161 | size="xs" |
| 157 | − disabled={!isGitRepo && !diffOpen} | |
| 162 | + disabled={!isVcsRepo && !diffOpen} | |
| 158 | 163 | > |
| 159 | 164 | <DiffIcon className="size-3" /> |
| 160 | 165 | </Toggle> |
| 161 | 166 | } |
| 162 | 167 | /> |
| 163 | 168 | <TooltipPopup side="bottom"> |
| 164 | − {!isGitRepo && !diffOpen | |
| 165 | − ? "Diff panel is unavailable because this project is not a git repository." | |
| 169 | + {!isVcsRepo && !diffOpen | |
| 170 | + ? `Diff panel is unavailable because this project is not a supported ${vcsTerms.repositoryNoun}.` | |
| 166 | 171 | : diffToggleShortcutLabel |
| 167 | 172 | ? `Toggle diff panel (${diffToggleShortcutLabel})` |
| 168 | 173 | : "Toggle diff panel"} |
| @@ -40,6 +40,7 @@ const TARGET = { environmentId: ENVIRONMENT_ID, cwd: "/repo" } as const; · 39 unmodified lines | ||
| 40 | 40 | const FRESH_TARGET = { environmentId: ENVIRONMENT_ID, cwd: "/fresh" } as const; |
| 41 | 41 | |
| 42 | 42 | const BASE_STATUS: VcsStatusResult = { |
| 43 | + kind: "git", | |
| 43 | 44 | isRepo: true, |
| 44 | 45 | hasPrimaryRemote: true, |
| 45 | 46 | isDefaultRef: false, |
| @@ -64,7 +64,7 @@ function normalizeCheckpointErrorMessage(error: unknown): string { · 63 unmodified lines | ||
| 64 | 64 | |
| 65 | 65 | const lower = message.toLowerCase(); |
| 66 | 66 | if (lower.includes("not a git repository")) { |
| 67 | − return "Turn diffs are unavailable because this project is not a git repository."; | |
| 67 | + return "Turn diffs are unavailable because this project is not a repository."; | |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | 70 | if ( |
| @@ -262,6 +262,7 @@ const baseServerConfig: ServerConfig = { · 261 unmodified lines | ||
| 262 | 262 | }; |
| 263 | 263 | |
| 264 | 264 | const baseGitStatus: VcsStatusResult = { |
| 265 | + kind: "git", | |
| 265 | 266 | isRepo: true, |
| 266 | 267 | hasPrimaryRemote: true, |
| 267 | 268 | isDefaultRef: false, |
| @@ -19,6 +19,7 @@ import { createWsRpcClient } from "./wsRpcClient"; · 18 unmodified lines | ||
| 19 | 19 | import { type WsTransport } from "./wsTransport"; |
| 20 | 20 | |
| 21 | 21 | const baseLocalStatus: VcsStatusLocalResult = { |
| 22 | + kind: "git", | |
| 22 | 23 | isRepo: true, |
| 23 | 24 | hasPrimaryRemote: true, |
| 24 | 25 | isDefaultRef: false, |
| @@ -0,0 +1,67 @@ | ||
| 1 | +import type { VcsDriverKind } from "@t3tools/contracts"; | |
| 2 | + | |
| 3 | +export interface VcsTerms { | |
| 4 | + readonly systemName: string; | |
| 5 | + readonly repositoryNoun: string; | |
| 6 | + readonly refNoun: string; | |
| 7 | + readonly refNounPlural: string; | |
| 8 | + readonly currentRefFallback: string; | |
| 9 | +} | |
| 10 | + | |
| 11 | +export interface VcsActionPresentation { | |
| 12 | + readonly supportsGitWorkflowActions: boolean; | |
| 13 | + readonly unsupportedGitWorkflowDescription: string; | |
| 14 | +} | |
| 15 | + | |
| 16 | +const gitLikeTerms: VcsTerms = { | |
| 17 | + systemName: "Git", | |
| 18 | + repositoryNoun: "repository", | |
| 19 | + refNoun: "branch", | |
| 20 | + refNounPlural: "branches", | |
| 21 | + currentRefFallback: "checkout", | |
| 22 | +}; | |
| 23 | + | |
| 24 | +const unknownTerms: VcsTerms = { | |
| 25 | + systemName: "VCS", | |
| 26 | + repositoryNoun: "repository", | |
| 27 | + refNoun: "ref", | |
| 28 | + refNounPlural: "refs", | |
| 29 | + currentRefFallback: "checkout", | |
| 30 | +}; | |
| 31 | + | |
| 32 | +export function resolveVcsTerms(kind: VcsDriverKind | null | undefined): VcsTerms { | |
| 33 | + if (kind === "jj") { | |
| 34 | + return { | |
| 35 | + systemName: "JJ", | |
| 36 | + repositoryNoun: "workspace", | |
| 37 | + refNoun: "bookmark", | |
| 38 | + refNounPlural: "bookmarks", | |
| 39 | + currentRefFallback: "working copy", | |
| 40 | + }; | |
| 41 | + } | |
| 42 | + | |
| 43 | + if (kind === "sapling") { | |
| 44 | + return { | |
| 45 | + ...gitLikeTerms, | |
| 46 | + systemName: "Sapling", | |
| 47 | + }; | |
| 48 | + } | |
| 49 | + | |
| 50 | + if (kind === "git") { | |
| 51 | + return gitLikeTerms; | |
| 52 | + } | |
| 53 | + | |
| 54 | + return unknownTerms; | |
| 55 | +} | |
| 56 | + | |
| 57 | +export function resolveVcsActionPresentation( | |
| 58 | + kind: VcsDriverKind | null | undefined, | |
| 59 | +): VcsActionPresentation { | |
| 60 | + const terms = resolveVcsTerms(kind); | |
| 61 | + const supportsGitWorkflowActions = kind === undefined || kind === null || kind === "git"; | |
| 62 | + | |
| 63 | + return { | |
| 64 | + supportsGitWorkflowActions, | |
| 65 | + unsupportedGitWorkflowDescription: `Git commit, push, and PR actions are not available for this ${terms.systemName} ${terms.repositoryNoun}.`, | |
| 66 | + }; | |
| 67 | +} | |