-
} className={headingClasses}>
- {environment.name ?? environment.id}
+
}
+ className={cn(headingClasses, 'flex min-w-0 items-baseline gap-1.5')}
+ >
+
{environment.name ?? environment.id}
+ {owners && (
+
+ {localize('com_ui_code_workspace_used_by', { 0: owners })}
+
+ )}
- {requiredBy != null && requiredBy.length > 0 && (
-
- {localize('com_ui_code_workspace_used_by', {
- 0: requiredBy.map(({ id, name }) => name || id).join(', '),
- })}
-
- )}
{workspaces.length === 0 && (
-
{emptyLabel}
+
{emptyLabel}
)}
{workspaces.map((descriptor) => {
const selected = isSelected(descriptor.id);
+ /** A sibling with the same name keeps its id visible, so the two rows never read alike. */
+ const source = [
+ descriptor.environment?.repo,
+ descriptor.environment?.ref,
+ sharedNames.has(descriptor.name ?? descriptor.id) ? descriptor.id : undefined,
+ ]
+ .filter(Boolean)
+ .join(' · ');
return (
-
-
-
- {descriptor.name ?? descriptor.id}
-
- {descriptor.name && (
-
{descriptor.id}
- )}
- {descriptor.instructions !== undefined && (
-
- {descriptor.instructions.length === 0
- ? localize('com_ui_repository_instructions_none')
- : descriptor.instructions
- .map(
- (file) =>
- `${file.path} · ${(file.bytes / 1024).toFixed(1)} KB${file.truncated ? ` · ${localize('com_ui_repository_instructions_truncated')}` : ''}`,
- )
- .join(', ')}
-
- )}
- {(descriptor.environment?.repo || descriptor.environment?.ref) && (
-
- {[descriptor.environment.repo, descriptor.environment.ref]
- .filter(Boolean)
- .join(' · ')}
-
- )}
-
+
+
+ {descriptor.name ?? descriptor.id}
+
+ {source}
{selected && (
-
+
)}
{selected &&
allowCheckoutSelection &&
- environment.configSchema?.workspaces?.allowCheckoutSelection === true && (
- <>
- } className={headingClasses}>
- {localize('com_ui_code_checkout_mode')}
-
- {(['isolated', 'source'] as const)
- .filter(
- (mode) =>
- mode === 'source' ||
- descriptor.workspaceInstances?.includes('git_worktree'),
- )
- .map((mode) => (
-
- onSelect({
+ environment.configSchema?.workspaces?.allowCheckoutSelection === true &&
+ checkoutModes
+ .filter(
+ ({ value }) =>
+ value === 'source' ||
+ (value === 'auto' && allowAutoCheckout) ||
+ (value === 'isolated' &&
+ descriptor.workspaceInstances?.includes('git_worktree')),
+ )
+ .map(({ value, icon: ModeIcon, label }) => (
+
+ value === 'auto'
+ ? onSelect(
+ { environmentId: environment.id, workspaceId: descriptor.id },
+ false,
+ )
+ : onSelect({
environmentId: environment.id,
workspaceId: descriptor.id,
- checkout: mode,
+ checkout: value,
})
- }
- >
-
-
- {localize(
- mode === 'isolated'
- ? 'com_ui_code_checkout_isolated'
- : 'com_ui_code_checkout_source',
- )}
-
-
- {localize(
- mode === 'isolated'
- ? 'com_ui_code_checkout_isolated_info'
- : 'com_ui_code_checkout_source_info',
- )}
-
-
- {checkout === mode && (
-
- )}
-
- ))}
- >
- )}
+ }
+ >
+
+
+ {localize(label)}
+
+ {currentCheckout === value && (
+
+ )}
+
+ ))}
);
})}
@@ -288,30 +294,34 @@ function GitContext({
target: CodeWorkspaceEnvironmentResult;
disabled: boolean;
locked: boolean;
- onSelect?: (selection: CodeWorkspaceSelection) => void;
+ onSelect?: (selection: CodeWorkspaceSelection, inheritCheckout?: boolean) => void;
}) {
const localize = useLocalize();
+ const checkoutStore = Ariakit.useMenuStore({ focusLoop: true, placement: 'top-start' });
+ const checkoutOpen = checkoutStore.useState('open');
const descriptor = target.workspaces.find(({ id }) => id === target.selected?.workspaceId);
if (descriptor == null) return null;
const supportsWorktree = descriptor.workspaceInstances?.includes('git_worktree') === true;
const checkout = target.selected?.checkout;
const checkoutSelectionAllowed =
target.environment.configSchema?.workspaces?.allowCheckoutSelection === true;
- const checkoutEditable = !locked && supportsWorktree && checkoutSelectionAllowed;
+ /** The registered checkout needs no worker capability, so a workspace without worktrees still
+ * lets the reader override Auto with it; only the isolated option depends on support. */
+ const checkoutEditable = !locked && checkoutSelectionAllowed;
const usesIsolation = checkout !== 'source' && supportsWorktree;
const showLinkedWorktrees =
!usesIsolation &&
descriptor.workspaceScopes?.includes('git_linked_worktree') &&
isLinkedWorktreeRoutingAllowed(target.environment.configSchema?.workspaces?.linkedWorktrees) &&
isCodeWorkspaceCheckoutAvailable({ checkout }, descriptor, checkoutSelectionAllowed);
- const worktreeInfo =
- checkout == null
- ? localize('com_ui_code_checkout_automatic_info')
- : localize(
- checkout === 'isolated'
- ? 'com_ui_code_checkout_isolated_info'
- : 'com_ui_code_checkout_source_info',
- );
+ const current = checkoutModes.find(({ value }) => value === (checkout ?? 'auto'));
+ const options = checkoutModes.filter(({ value }) => value !== 'isolated' || supportsWorktree);
+ const chooseCheckout = (choice: CheckoutChoice) => {
+ if (target.selected == null || !checkoutEditable || disabled) return;
+ const { checkout: _previous, ...selection } = target.selected;
+ onSelect?.(choice === 'auto' ? selection : { ...selection, checkout: choice }, false);
+ };
+ const CurrentIcon = current?.icon ?? WandSparkles;
return (
<>
{descriptor.environment?.ref && (
@@ -329,36 +339,75 @@ function GitContext({
)}
- {(supportsWorktree || checkout != null) && (
-
{
- if (target.selected == null || !checkoutEditable) return;
- onSelect?.({
- ...target.selected,
- checkout: checkout === 'isolated' ? 'source' : 'isolated',
- });
- }}
- />
- }
- >
-
- {localize('com_ui_code_worktree')}
- {checkout == null && (
-
- {localize('com_ui_code_checkout_automatic')}
-
- )}
-
+ {(supportsWorktree || checkout != null || checkoutEditable) && current != null && (
+
+
+ }
+ >
+
+ {localize(current.chip)}
+ {checkoutEditable && (
+
+ )}
+
+
+ } className={headingClasses}>
+ {localize('com_ui_code_checkout_mode')}
+
+ {options.map(({ value, icon: ModeIcon, label, info }) => {
+ const selected = value === current.value;
+ return (
+ chooseCheckout(value)}
+ className={cn(menuItemClasses(selected), 'items-start')}
+ >
+
+
+
+ {localize(label)}
+
+ {localize(info)}
+
+ {selected && (
+
+ )}
+
+ );
+ })}
+
+
)}
{showLinkedWorktrees && (
environment.id));
- const selectWorkspace = (selection: CodeWorkspaceSelection) => {
+ const selectWorkspace = (selection: CodeWorkspaceSelection, inheritCheckout = true) => {
if (disabled || workspace.locked) return;
const previous = workspace.environments.find(
({ environment }) => environment.id === selection.environmentId,
)?.selected;
- if (selection.checkout == null && previous?.checkout != null) {
+ if (inheritCheckout && selection.checkout == null && previous?.checkout != null) {
selection = { ...selection, checkout: previous.checkout };
}
workspace.rememberSelection(selection);
@@ -792,15 +841,12 @@ export default function CodeWorkspaceMenu({
menuStore.show();
}}
>
-
+
{candidate.name ?? candidate.id}
{workspace.mode === 'attached' && environmentIds.has(candidate.id) && (
-
+
)}
))}
@@ -850,7 +896,13 @@ export default function CodeWorkspaceMenu({
-