Merge branch 'main' into accidental-matrix

This commit is contained in:
Kirill Kalishev
2026-05-10 11:29:26 -04:00
committed by GitHub
13 changed files with 249 additions and 142 deletions
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Keep the Agent Manager new-tab button visible at the end of the tab row, separated by a divider, and show all tab tooltips above the tabs.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Move Agent Manager tab creation and search controls to the left side of the tab bar with clearer toolbar separators.
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:483e73147bed34050ee35e60607c33c04d7e726b8f746921ad27e582cb5b19ee
size 2886
oid sha256:b7ec87f1e046cf1a44688b952c0d994899119c6663bf627026688bf337b56127
size 2411
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3b4687ed34fe06adb7e72797798b1f3602c225e9a36223842ea1c66eebea4601
size 4794
oid sha256:7ee0856c65e445a230cf3e94bfe592c98306b7502e44a2602b5fac2f6d69937a
size 4601
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:980c9baf0edafa113956157a34eb00495aff55293b10fb4cbb86a8cb5b28e8c0
size 2865
oid sha256:f1ee2d880a055b03664d594598a6e8764ab27d443a578572fd01b37451cb0193
size 3193
@@ -2646,6 +2646,16 @@ const AgentManagerContent: Component = () => {
<DragDropSensors />
<ConstrainDragYAxis />
<div class="am-tab-bar" onPointerLeave={releaseTabs}>
<div class="am-tab-leading">
<CurrentTabsMenu
items={tabMenuItems}
label={t("agentManager.tabsMenu.label")}
searchLabel={t("agentManager.tabsMenu.search")}
emptyLabel={t("agentManager.tabsMenu.empty")}
activeId={visibleTabId}
onSelect={focusTab}
/>
</div>
<div class="am-tab-scroll-area">
<div class={`am-tab-fade am-tab-fade-left ${tabScroll.showLeft() ? "am-tab-fade-visible" : ""}`} />
<div class="am-tab-list-wrap">
@@ -2688,6 +2698,12 @@ const AgentManagerContent: Component = () => {
</For>
</SortableProvider>
</div>
</div>
<div class={`am-tab-fade am-tab-fade-right ${tabScroll.showRight() ? "am-tab-fade-visible" : ""}`} />
</div>
<Show when={selection() !== null}>
<div class="am-tab-add-wrap">
<div class="am-tab-add-separator" />
{renderNewTabButton({
contextSelected: () => selection() !== null,
kb,
@@ -2699,17 +2715,8 @@ const AgentManagerContent: Component = () => {
onNewTerminal: () => termHandlers.requestNew(),
})}
</div>
<div class={`am-tab-fade am-tab-fade-right ${tabScroll.showRight() ? "am-tab-fade-visible" : ""}`} />
</div>
</Show>
<div class="am-tab-actions">
<CurrentTabsMenu
items={tabMenuItems}
label={t("agentManager.tabsMenu.label")}
searchLabel={t("agentManager.tabsMenu.search")}
emptyLabel={t("agentManager.tabsMenu.empty")}
activeId={visibleTabId}
onSelect={focusTab}
/>
{(() => {
const sel = () => selection()
const isWorktree = () => typeof sel() === "string" && sel() !== LOCAL
@@ -231,13 +231,13 @@ export const CurrentTabsMenu: Component<CurrentTabsMenuProps> = (props) => {
return (
<Popover
placement="bottom-end"
placement="bottom-start"
open={open()}
onOpenChange={setOpen}
modal={false}
class="am-tabs-menu"
trigger={
<Tooltip value={props.label} placement="bottom" inactive={open()}>
<Tooltip value={props.label} placement="top" gutter={8} inactive={open()}>
<button class="am-tabs-menu-trigger" type="button" aria-label={props.label}>
<SearchIcon />
</button>
@@ -1028,10 +1028,19 @@ button.am-section-toggle:hover .am-section-label {
border-bottom: 1px solid var(--border-weak-base);
background: var(--surface-base);
height: 36px;
padding: 0 4px;
flex-shrink: 0;
}
.am-tab-leading {
display: flex;
align-items: center;
gap: 2px;
flex-shrink: 0;
align-self: stretch;
padding: 0 4px;
border-right: 1px solid var(--border-weak-base);
}
.am-tab-list {
--am-tab-max-width: 240px;
--am-tab-width: clamp(72px, calc(100% / var(--tab-count, 1)), var(--am-tab-max-width));
@@ -1050,7 +1059,6 @@ button.am-section-toggle:hover .am-section-label {
.am-tab-list-wrap {
display: flex;
align-items: stretch;
gap: 4px;
min-width: 0;
flex: 0 1 auto;
max-width: 100%;
@@ -1066,12 +1074,29 @@ button.am-section-toggle:hover .am-section-label {
position: relative;
display: flex;
min-width: 0;
flex: 1 1 auto;
flex: 0 1 auto;
align-items: stretch;
height: 100%;
overflow: hidden;
}
/* Fixed add-button area after the scrollable tab list */
.am-tab-add-wrap {
display: flex;
align-items: center;
flex-shrink: 0;
align-self: stretch;
padding-right: 4px;
}
.am-tab-add-separator {
width: 1px;
align-self: stretch;
margin-right: 4px;
background: var(--border-weak-base);
flex-shrink: 0;
}
/* Fade indicators for overflow */
.am-tab-fade {
position: absolute;
@@ -1109,18 +1134,21 @@ button.am-section-toggle:hover .am-section-label {
font-size: var(--kilo-font-size-12);
cursor: pointer;
white-space: nowrap;
border: none;
border-bottom: 2px solid transparent;
border: 1px solid transparent;
border-bottom-width: 2px;
border-radius: 0;
min-width: 0;
width: 100%;
height: 100%;
transition:
background-color 120ms ease,
background 120ms ease,
border-color 120ms ease,
color 120ms ease;
}
.am-tab:hover {
.am-tab:hover,
.am-tab:focus-visible {
background: var(--button-ghost-hover, var(--surface-base-hover, rgba(128, 128, 128, 0.2)));
color: var(--text-base);
}
@@ -1151,6 +1179,24 @@ button.am-section-toggle:hover .am-section-label {
white-space: nowrap;
}
.am-tab-title {
display: flex;
align-items: center;
gap: 5px;
min-width: 0;
width: 100%;
}
.am-tab-tooltip[data-component="tooltip-trigger"] {
display: flex;
align-items: center;
min-width: 0;
height: 100%;
flex: 1;
padding-right: 27px;
margin-right: -27px;
}
.am-tab-close-wrap {
display: inline-flex;
align-items: center;
@@ -1160,6 +1206,8 @@ button.am-section-toggle:hover .am-section-label {
flex-shrink: 0;
opacity: 0;
pointer-events: none;
position: relative;
z-index: 1;
transition: opacity 100ms ease;
}
@@ -1211,6 +1259,11 @@ button.am-section-toggle:hover .am-section-label {
width: 100%;
}
.am-tab-sortable > .am-tab,
.am-tab-sortable > [data-slot="context-menu-trigger"] > .am-tab {
height: 100%;
}
.am-tab-dragging {
opacity: 0.25;
}
@@ -1273,7 +1326,6 @@ body.am-wt-dragging-active * {
.am-tab-add-split {
flex-shrink: 0;
align-self: center;
margin-right: 2px;
}
.am-tab-actions {
@@ -1281,8 +1333,11 @@ body.am-wt-dragging-active * {
align-items: center;
gap: 2px;
flex-shrink: 0;
align-self: stretch;
margin-left: auto;
padding-left: 6px;
padding-right: 4px;
border-left: 1px solid var(--border-weak-base);
}
.am-tabs-menu-trigger {
@@ -53,7 +53,7 @@ export const dict = {
"agentManager.tab.terminal": "Terminal",
"agentManager.tab.openTerminal": "Open Terminal",
"agentManager.tab.newOptions": "More new-tab options",
"agentManager.tabsMenu.label": "Search open tabs",
"agentManager.tabsMenu.label": "Search and switch tabs",
"agentManager.tabsMenu.search": "Search tabs...",
"agentManager.tabsMenu.empty": "No matching tabs",
"agentManager.tabsMenu.status.waiting": "Wait",
@@ -66,45 +66,49 @@ export const SortableTab: Component<{
>
<ContextMenu>
<ContextMenu.Trigger as="div" style={{ display: "contents" }}>
<TooltipKeybind
title={props.tab.title || t("agentManager.session.untitled")}
keybind={props.keybind ?? ""}
placement="bottom"
inactive={props.active}
openDelay={0}
<div
class={`am-tab ${props.active ? "am-tab-active" : ""}`}
onClick={props.onSelect}
onMouseDown={props.onMiddleClick}
>
<div
class={`am-tab ${props.active ? "am-tab-active" : ""}`}
onClick={props.onSelect}
onMouseDown={props.onMiddleClick}
<TooltipKeybind
title={props.tab.title || t("agentManager.session.untitled")}
keybind={props.keybind ?? ""}
placement="bottom"
gutter={8}
class="am-tab-tooltip"
openDelay={0}
>
<Show when={props.busy}>
<span class="am-tab-icon">
<Spinner class="am-worktree-spinner" />
</span>
</Show>
<span class="am-tab-label">{props.tab.title || t("agentManager.session.untitled")}</span>
<TooltipKeybind
title={t("agentManager.tab.close")}
keybind={props.closeKeybind ?? ""}
placement="bottom"
class="am-tab-close-wrap"
openDelay={0}
>
<IconButton
icon="close-small"
size="small"
variant="ghost"
label={t("agentManager.tab.closeTab")}
class="am-tab-close"
onClick={(e) => {
e.stopPropagation()
props.onClose()
}}
/>
</TooltipKeybind>
</div>
</TooltipKeybind>
<span class="am-tab-title">
<Show when={props.busy}>
<span class="am-tab-icon">
<Spinner class="am-worktree-spinner" />
</span>
</Show>
<span class="am-tab-label">{props.tab.title || t("agentManager.session.untitled")}</span>
</span>
</TooltipKeybind>
<TooltipKeybind
title={t("agentManager.tab.close")}
keybind={props.closeKeybind ?? ""}
placement="top"
gutter={8}
class="am-tab-close-wrap"
openDelay={0}
>
<IconButton
icon="close-small"
size="small"
variant="ghost"
label={t("agentManager.tab.closeTab")}
class="am-tab-close"
onClick={(e) => {
e.stopPropagation()
props.onClose()
}}
/>
</TooltipKeybind>
</div>
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content class="am-ctx-menu">
@@ -156,40 +160,44 @@ export const SortableReviewTab: Component<{
class={`am-tab-sortable ${sortable.isActiveDraggable ? "am-tab-dragging" : ""}`}
data-tab-id={props.id}
>
<TooltipKeybind
title={props.tooltip}
keybind={props.keybind ?? ""}
placement="bottom"
inactive={props.active}
openDelay={0}
<div
class={`am-tab am-tab-review ${props.active ? "am-tab-active" : ""}`}
onClick={props.onSelect}
onMouseDown={props.onMiddleClick}
>
<div
class={`am-tab am-tab-review ${props.active ? "am-tab-active" : ""}`}
onClick={props.onSelect}
onMouseDown={props.onMiddleClick}
<TooltipKeybind
title={props.tooltip}
keybind={props.keybind ?? ""}
placement="bottom"
gutter={8}
class="am-tab-tooltip"
openDelay={0}
>
<span class="am-tab-icon">
<Icon name="layers" size="small" />
<span class="am-tab-title">
<span class="am-tab-icon">
<Icon name="layers" size="small" />
</span>
<span class="am-tab-label">{props.label}</span>
</span>
<span class="am-tab-label">{props.label}</span>
<TooltipKeybind
title={t("agentManager.tab.close")}
keybind={props.closeKeybind ?? ""}
placement="bottom"
class="am-tab-close-wrap"
openDelay={0}
>
<IconButton
icon="close-small"
size="small"
variant="ghost"
label={t("agentManager.tab.closeTab")}
class="am-tab-close"
onClick={props.onClose}
/>
</TooltipKeybind>
</div>
</TooltipKeybind>
</TooltipKeybind>
<TooltipKeybind
title={t("agentManager.tab.close")}
keybind={props.closeKeybind ?? ""}
placement="top"
gutter={8}
class="am-tab-close-wrap"
openDelay={0}
>
<IconButton
icon="close-small"
size="small"
variant="ghost"
label={t("agentManager.tab.closeTab")}
class="am-tab-close"
onClick={props.onClose}
/>
</TooltipKeybind>
</div>
</div>
)
}
@@ -174,7 +174,13 @@ export function renderNewTabButton(deps: NewTabButtonDeps): JSX.Element {
return (
<Show when={deps.contextSelected()}>
<div class="am-split-button am-tab-add-split">
<TooltipKeybind title={deps.newSessionLabel} keybind={deps.kb().newTab ?? ""} placement="bottom" openDelay={0}>
<TooltipKeybind
title={deps.newSessionLabel}
keybind={deps.kb().newTab ?? ""}
placement="top"
gutter={8}
openDelay={0}
>
<IconButton
icon="plus"
size="small"
@@ -45,40 +45,44 @@ export const SortableTerminalTab: Component<{
>
<ContextMenu>
<ContextMenu.Trigger as="div" style={{ display: "contents" }}>
<TooltipKeybind
title={props.tooltip}
keybind={props.keybind ?? ""}
placement="bottom"
inactive={props.active}
openDelay={0}
<div
class={`am-tab am-tab-terminal ${props.active ? "am-tab-active" : ""}`}
onClick={props.onSelect}
onMouseDown={props.onMiddleClick}
>
<div
class={`am-tab am-tab-terminal ${props.active ? "am-tab-active" : ""}`}
onClick={props.onSelect}
onMouseDown={props.onMiddleClick}
<TooltipKeybind
title={props.tooltip}
keybind={props.keybind ?? ""}
placement="bottom"
gutter={8}
class="am-tab-tooltip"
openDelay={0}
>
<span class="am-tab-icon">
<Icon name="console" size="small" />
<span class="am-tab-title">
<span class="am-tab-icon">
<Icon name="console" size="small" />
</span>
<span class="am-tab-label">{props.label}</span>
</span>
<span class="am-tab-label">{props.label}</span>
<TooltipKeybind
title={t("agentManager.tab.close")}
keybind={props.closeKeybind ?? ""}
placement="bottom"
class="am-tab-close-wrap"
openDelay={0}
>
<IconButton
icon="close-small"
size="small"
variant="ghost"
label={t("agentManager.tab.closeTab")}
class="am-tab-close"
onClick={props.onClose}
/>
</TooltipKeybind>
</div>
</TooltipKeybind>
</TooltipKeybind>
<TooltipKeybind
title={t("agentManager.tab.close")}
keybind={props.closeKeybind ?? ""}
placement="top"
gutter={8}
class="am-tab-close-wrap"
openDelay={0}
>
<IconButton
icon="close-small"
size="small"
variant="ghost"
label={t("agentManager.tab.closeTab")}
class="am-tab-close"
onClick={props.onClose}
/>
</TooltipKeybind>
</div>
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content class="am-ctx-menu">
@@ -563,11 +563,38 @@ const MockReviewTab = (props: { active?: boolean }) => (
</div>
)
const MockTabsSearchButton = () => (
<button class="am-tabs-menu-trigger" type="button" aria-label="Search open tabs">
<svg class="am-tabs-search-icon" viewBox="0 0 16 16" aria-hidden="true">
<circle cx="6.8" cy="6.8" r="4.3" />
<path d="M10.2 10.2L13.5 13.5" />
</svg>
</button>
)
const MockTabLeading = () => (
<div class="am-tab-leading">
<MockTabsSearchButton />
</div>
)
const MockTabAdd = () => (
<div class="am-tab-add-wrap">
<div class="am-tab-add-separator" />
<div class="am-split-button am-tab-add-split">
<TooltipKeybind title="New session" keybind="⌘T" placement="bottom">
<IconButton icon="plus" size="small" variant="ghost" label="New session" class="am-tab-add" />
</TooltipKeybind>
</div>
</div>
)
export const TabBarMultipleTabs: Story = {
name: "TabBar — multiple tabs with active",
render: () => (
<StoryProviders noPadding>
<div class="am-tab-bar">
<MockTabLeading />
<div class="am-tab-scroll-area">
<div class="am-tab-list-wrap">
<div class="am-tab-list" style={{ "--tab-count": "3" } as JSX.CSSProperties}>
@@ -575,13 +602,9 @@ export const TabBarMultipleTabs: Story = {
<MockTab title="Fix button styles" />
<MockTab title="Add unit tests" />
</div>
<div class="am-split-button am-tab-add-split">
<TooltipKeybind title="New session" keybind="⌘T" placement="bottom">
<IconButton icon="plus" size="small" variant="ghost" label="New session" class="am-tab-add" />
</TooltipKeybind>
</div>
</div>
</div>
<MockTabAdd />
<div class="am-tab-actions">
<button class="am-diff-toggle-btn am-diff-toggle-has-changes">
<Icon name="layers" size="small" />
@@ -603,19 +626,16 @@ export const TabBarWithReviewTab: Story = {
render: () => (
<StoryProviders noPadding>
<div class="am-tab-bar">
<MockTabLeading />
<div class="am-tab-scroll-area">
<div class="am-tab-list-wrap">
<div class="am-tab-list" style={{ "--tab-count": "2" } as JSX.CSSProperties}>
<MockTab title="Implement auth" />
<MockReviewTab active />
</div>
<div class="am-split-button am-tab-add-split">
<TooltipKeybind title="New session" keybind="⌘T" placement="bottom">
<IconButton icon="plus" size="small" variant="ghost" label="New session" class="am-tab-add" />
</TooltipKeybind>
</div>
</div>
</div>
<MockTabAdd />
<div class="am-tab-actions">
<IconButton icon="expand" size="small" variant="ghost" label="Review" class="am-tab-diff-btn-active" />
<IconButton icon="console" size="small" variant="ghost" label="Terminal" />
@@ -630,18 +650,15 @@ export const TabBarSingleTab: Story = {
render: () => (
<StoryProviders noPadding>
<div class="am-tab-bar">
<MockTabLeading />
<div class="am-tab-scroll-area">
<div class="am-tab-list-wrap">
<div class="am-tab-list" style={{ "--tab-count": "1" } as JSX.CSSProperties}>
<MockTab title="PR #6966 worktree checkout" active />
</div>
<div class="am-split-button am-tab-add-split">
<TooltipKeybind title="New session" keybind="⌘T" placement="bottom">
<IconButton icon="plus" size="small" variant="ghost" label="New session" class="am-tab-add" />
</TooltipKeybind>
</div>
</div>
</div>
<MockTabAdd />
<div class="am-tab-actions">
<button class="am-diff-toggle-btn am-diff-toggle-has-changes">
<Icon name="layers" size="small" />