mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
fix(vscode): group context controls in task header and use distinct icons (#12287)
* fix(vscode): group context controls in task header and use distinct icons * chore: update kilo-vscode visual regression baselines --------- Co-authored-by: kilo-maintainer[bot] <kilo-maintainer[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
64713d4816
commit
bb75928749
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@kilocode/kilo-ui": patch
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Use distinct icons for context, project memory, and code indexing, and group context controls to preserve task titles in narrow VS Code sidebars.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6a89d1a9a31f7de1780d772b7caf71d96f0b4b895df954c77dcd508a7febe7c9
|
||||
size 1950
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9a864b6a5c0e88bc00292f8a38685acdf2ecdd9eb5d880a663ba830152b7280b
|
||||
size 3523
|
||||
oid sha256:431a0c12cb844b5d4e535cc4c7bf5546fd9e66a178b8023b00978d07c291a531
|
||||
size 3919
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:08f1498ef61670dfec83a2de45f1b9d7008c6c9987705a39175eb990182ee5c2
|
||||
size 6156
|
||||
oid sha256:f68544cdf994abca3004e652911aa8edd77cf2bb3f91678e5dc6210022dff2f9
|
||||
size 6188
|
||||
|
||||
@@ -3,6 +3,10 @@ import { splitProps } from "solid-js"
|
||||
|
||||
const icons: Record<string, { path: string; viewBox: string }> = {
|
||||
memory: {
|
||||
viewBox: "0 0 24 24",
|
||||
path: `<path d="M2 7h4M2 12h4M2 17h4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><rect width="16" height="20" x="4" y="2" rx="2" stroke="currentColor" stroke-width="2"/>`,
|
||||
},
|
||||
database: {
|
||||
viewBox: "0 0 24 24",
|
||||
path: `<ellipse cx="12" cy="5" rx="7" ry="3" stroke="currentColor" stroke-width="2"/><path d="M5 5v7c0 1.66 3.13 3 7 3s7-1.34 7-3V5M5 12v7c0 1.66 3.13 3 7 3s7-1.34 7-3v-7" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>`,
|
||||
},
|
||||
|
||||
@@ -122,13 +122,6 @@ export const TaskHeader: Component<TaskHeaderProps> = (props) => {
|
||||
: []),
|
||||
]
|
||||
})
|
||||
const activitySummary = createMemo(() => {
|
||||
if (memory.error()) return memory.error()!
|
||||
if (!memory.enabled()) return language.t("chat.memory.project.disabled")
|
||||
const lines = activityLines()
|
||||
if (lines.length === 0) return language.t("chat.memory.activity.idle")
|
||||
return [language.t("chat.memory.status.active"), ...lines].join(" · ")
|
||||
})
|
||||
const activityItems = createMemo(() =>
|
||||
activity()
|
||||
.flatMap((item) => {
|
||||
@@ -157,6 +150,8 @@ export const TaskHeader: Component<TaskHeaderProps> = (props) => {
|
||||
)
|
||||
const activityTooltip = () => (
|
||||
<>
|
||||
<div data-slot="task-header-context-tooltip-title">{language.t("settings.context.title")}</div>
|
||||
<div data-slot="task-header-context-tooltip-status">{memoryStatus()}</div>
|
||||
{activitySummaryView()}
|
||||
<Show when={memoryVerbose() && activityItems().length > 0}>
|
||||
<div data-slot="task-header-memory-activity-list">
|
||||
@@ -318,107 +313,117 @@ export const TaskHeader: Component<TaskHeaderProps> = (props) => {
|
||||
<DeferredPopover
|
||||
placement="bottom-end"
|
||||
portal={false}
|
||||
class="task-header-memory-popover"
|
||||
class="task-header-context-popover"
|
||||
triggerAs="button"
|
||||
triggerProps={{
|
||||
type: "button",
|
||||
get class() {
|
||||
return `task-header-memory-trigger${memory.enabled() ? " task-header-memory-trigger--enabled" : ""}`
|
||||
},
|
||||
class: "task-header-context-trigger",
|
||||
get ["aria-label"]() {
|
||||
return activitySummary()
|
||||
return language.t("settings.context.title")
|
||||
},
|
||||
}}
|
||||
trigger={
|
||||
<>
|
||||
<Icon name="memory" size="small" />
|
||||
<Icon name="server" size="small" />
|
||||
<Show when={memoryActive()}>
|
||||
<span data-slot="task-header-memory-dot" />
|
||||
</Show>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div data-slot="task-header-memory-menu">
|
||||
<div data-slot="task-header-memory-status">{memoryStatus()}</div>
|
||||
{activitySummaryView()}
|
||||
<Show
|
||||
when={memory.enabled()}
|
||||
fallback={
|
||||
<>
|
||||
<div data-slot="task-header-context-menu">
|
||||
<div data-slot="task-header-context-title">{language.t("settings.context.title")}</div>
|
||||
<Show when={!props.readonly}>
|
||||
<section data-slot="task-header-context-section">
|
||||
<div data-slot="task-header-context-section-title">
|
||||
<Icon name="compress" size="small" />
|
||||
<span>{language.t("settings.context.compaction.title")}</span>
|
||||
</div>
|
||||
<div data-slot="task-header-context-actions">
|
||||
<button
|
||||
data-slot="task-header-memory-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.enable()}
|
||||
data-slot="task-header-context-action"
|
||||
disabled={!canCompact()}
|
||||
onClick={() => session.compact()}
|
||||
>
|
||||
{language.t("chat.memory.enable")}
|
||||
{language.t("command.session.compact")}
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div data-slot="task-header-memory-actions">
|
||||
<button
|
||||
data-slot="task-header-memory-action"
|
||||
disabled={memory.loading() || memory.pending()}
|
||||
onClick={() => memory.showMemory()}
|
||||
>
|
||||
{language.t("chat.memory.inspect")}
|
||||
</button>
|
||||
<button
|
||||
data-slot="task-header-memory-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.remember()}
|
||||
>
|
||||
{language.t("chat.memory.remember")}
|
||||
</button>
|
||||
<button
|
||||
data-slot="task-header-memory-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.forget()}
|
||||
>
|
||||
{language.t("chat.memory.forget")}
|
||||
</button>
|
||||
<button
|
||||
data-slot="task-header-memory-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.rebuild()}
|
||||
>
|
||||
{language.t("chat.memory.rebuild")}
|
||||
</button>
|
||||
<button
|
||||
data-slot="task-header-memory-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.disable()}
|
||||
>
|
||||
{language.t("chat.memory.disable")}
|
||||
</button>
|
||||
</div>
|
||||
<div data-slot="task-header-memory-verbose">
|
||||
<span>{language.t("chat.memory.verbose")}</span>
|
||||
<Switch
|
||||
checked={memoryVerbose()}
|
||||
disabled={memory.pending()}
|
||||
hideLabel
|
||||
onChange={(next) => memory.verbose(next ? "on" : "off")}
|
||||
>
|
||||
{language.t("chat.memory.verbose")}
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Show>
|
||||
<section data-slot="task-header-context-section">
|
||||
<div data-slot="task-header-context-section-title">
|
||||
<Icon name="memory" size="small" />
|
||||
<span>{language.t("settings.context.memory.title")}</span>
|
||||
</div>
|
||||
<div data-slot="task-header-memory-status">{memoryStatus()}</div>
|
||||
{activitySummaryView()}
|
||||
<div data-slot="task-header-context-actions">
|
||||
<Show
|
||||
when={memory.enabled()}
|
||||
fallback={
|
||||
<button
|
||||
data-slot="task-header-context-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.enable()}
|
||||
>
|
||||
{language.t("chat.memory.enable")}
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<button
|
||||
data-slot="task-header-context-action"
|
||||
disabled={memory.loading() || memory.pending()}
|
||||
onClick={() => memory.showMemory()}
|
||||
>
|
||||
{language.t("chat.memory.inspect")}
|
||||
</button>
|
||||
<button
|
||||
data-slot="task-header-context-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.remember()}
|
||||
>
|
||||
{language.t("chat.memory.remember")}
|
||||
</button>
|
||||
<button
|
||||
data-slot="task-header-context-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.forget()}
|
||||
>
|
||||
{language.t("chat.memory.forget")}
|
||||
</button>
|
||||
<button
|
||||
data-slot="task-header-context-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.rebuild()}
|
||||
>
|
||||
{language.t("chat.memory.rebuild")}
|
||||
</button>
|
||||
<button
|
||||
data-slot="task-header-context-action"
|
||||
disabled={memory.pending()}
|
||||
onClick={() => memory.disable()}
|
||||
>
|
||||
{language.t("chat.memory.disable")}
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={memory.enabled()}>
|
||||
<div data-slot="task-header-memory-verbose">
|
||||
<span>{language.t("chat.memory.verbose")}</span>
|
||||
<Switch
|
||||
checked={memoryVerbose()}
|
||||
disabled={memory.pending()}
|
||||
hideLabel
|
||||
onChange={(next) => memory.verbose(next ? "on" : "off")}
|
||||
>
|
||||
{language.t("chat.memory.verbose")}
|
||||
</Switch>
|
||||
</div>
|
||||
</Show>
|
||||
</section>
|
||||
</div>
|
||||
</DeferredPopover>
|
||||
</Tooltip>
|
||||
<Show when={!props.readonly}>
|
||||
<Tooltip value={language.t("command.session.compact")} placement="bottom">
|
||||
<IconButton
|
||||
icon="compress"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
disabled={!canCompact()}
|
||||
onClick={() => session.compact()}
|
||||
aria-label={language.t("command.session.compact")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Show>
|
||||
<Show when={hasMessages()}>
|
||||
<Tooltip value={language.t("chat.search.toggle")} placement="bottom">
|
||||
<IconButton
|
||||
|
||||
@@ -208,7 +208,7 @@ const Settings: Component<SettingsProps> = (props) => {
|
||||
</Tabs.Trigger>
|
||||
<Show when={features().indexing}>
|
||||
<Tabs.Trigger value="indexing" aria-label={language.t("settings.indexing.title")}>
|
||||
<Icon name="server" />
|
||||
<Icon name="database" />
|
||||
<span class="label">{language.t("settings.indexing.title")}</span>
|
||||
</Tabs.Trigger>
|
||||
</Show>
|
||||
|
||||
@@ -1037,31 +1037,40 @@ const mockMemory: MemoryContextValue = {
|
||||
forget: () => {},
|
||||
}
|
||||
|
||||
const memoryHeader = (width: string) => {
|
||||
const session = {
|
||||
...mockSessionValue({ id: SESSION_ID, status: "idle" }),
|
||||
messages: () => [{ id: "msg-001" }] as any[],
|
||||
contextUsage: () => ({ tokens: 34300, percentage: 17 }),
|
||||
costBreakdown: () => [{ label: "Session", cost: 0.64 }],
|
||||
currentSession: () => ({
|
||||
id: SESSION_ID,
|
||||
title: "Integrate project memory",
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}),
|
||||
}
|
||||
return (
|
||||
<StoryProviders sessionID={SESSION_ID} status="idle" noPadding>
|
||||
<SessionContext.Provider value={session as any}>
|
||||
<MemoryContext.Provider value={mockMemory}>
|
||||
<div style={{ width }}>
|
||||
<TaskHeader />
|
||||
</div>
|
||||
</MemoryContext.Provider>
|
||||
</SessionContext.Provider>
|
||||
</StoryProviders>
|
||||
)
|
||||
}
|
||||
|
||||
export const TaskHeaderWithMemory: Story = {
|
||||
name: "TaskHeader — with memory enabled",
|
||||
render: () => {
|
||||
const session = {
|
||||
...mockSessionValue({ id: SESSION_ID, status: "idle" }),
|
||||
messages: () => [{ id: "msg-001" }] as any[],
|
||||
currentSession: () => ({
|
||||
id: SESSION_ID,
|
||||
title: "Integrate project memory",
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}),
|
||||
}
|
||||
return (
|
||||
<StoryProviders sessionID={SESSION_ID} status="idle" noPadding>
|
||||
<SessionContext.Provider value={session as any}>
|
||||
<MemoryContext.Provider value={mockMemory}>
|
||||
<div style={{ width: "380px" }}>
|
||||
<TaskHeader />
|
||||
</div>
|
||||
</MemoryContext.Provider>
|
||||
</SessionContext.Provider>
|
||||
</StoryProviders>
|
||||
)
|
||||
},
|
||||
render: () => memoryHeader("380px"),
|
||||
}
|
||||
|
||||
export const TaskHeaderWithMemory200: Story = {
|
||||
name: "TaskHeader — with memory enabled 200",
|
||||
render: () => memoryHeader("200px"),
|
||||
}
|
||||
|
||||
const usageTokens = { input: 25_900_000, output: 52_000, reasoning: 4_100, cache: { read: 10_500_000, write: 80_000 } }
|
||||
|
||||
@@ -50,6 +50,12 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
[data-component="task-header"] {
|
||||
padding-inline: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Task Header Todos
|
||||
============================================ */
|
||||
@@ -467,7 +473,7 @@
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.task-header-memory-trigger {
|
||||
.task-header-context-trigger {
|
||||
all: unset;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
@@ -478,18 +484,10 @@
|
||||
border-radius: 3px;
|
||||
color: var(--vscode-foreground);
|
||||
cursor: pointer;
|
||||
opacity: 0.4;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.task-header-memory-trigger--enabled {
|
||||
color: var(--vscode-foreground);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.task-header-memory-trigger:hover {
|
||||
.task-header-context-trigger:hover {
|
||||
background: var(--vscode-toolbar-hoverBackground);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-dot"] {
|
||||
@@ -502,7 +500,7 @@
|
||||
background: var(--vscode-testing-iconPassed, #73c991);
|
||||
}
|
||||
|
||||
.task-header-memory-popover {
|
||||
.task-header-context-popover {
|
||||
width: min(280px, calc(100vw - 16px));
|
||||
max-width: calc(100vw - 16px);
|
||||
white-space: normal;
|
||||
@@ -515,20 +513,62 @@
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-menu"] {
|
||||
[data-slot="task-header-context-tooltip-title"] {
|
||||
margin-bottom: 4px;
|
||||
color: var(--vscode-foreground);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
[data-slot="task-header-context-tooltip-status"] {
|
||||
margin-bottom: 2px;
|
||||
color: var(--vscode-foreground);
|
||||
}
|
||||
|
||||
[data-slot="task-header-context-menu"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
[data-slot="task-header-context-title"] {
|
||||
padding: 2px 4px 8px;
|
||||
color: var(--vscode-foreground);
|
||||
font-size: var(--kilo-font-size-12);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
[data-slot="task-header-context-section"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
[data-slot="task-header-context-section"] + [data-slot="task-header-context-section"] {
|
||||
margin-top: 6px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--vscode-widget-border, var(--vscode-editorWidget-border));
|
||||
}
|
||||
|
||||
[data-slot="task-header-context-section-title"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 0 4px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: var(--kilo-font-size-11);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-status"] {
|
||||
padding: 0 4px;
|
||||
color: var(--vscode-foreground);
|
||||
font-size: var(--kilo-font-size-11);
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-activity"] {
|
||||
min-width: 0;
|
||||
padding: 0 4px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: var(--kilo-font-size-11);
|
||||
white-space: normal;
|
||||
@@ -558,13 +598,13 @@
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-actions"] {
|
||||
[data-slot="task-header-context-actions"] {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-action"] {
|
||||
[data-slot="task-header-context-action"] {
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
padding: 4px 6px;
|
||||
@@ -576,15 +616,15 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-action"]:last-child:nth-child(odd) {
|
||||
[data-slot="task-header-context-action"]:last-child:nth-child(odd) {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-action"]:hover:not(:disabled) {
|
||||
[data-slot="task-header-context-action"]:hover:not(:disabled) {
|
||||
background: var(--vscode-list-hoverBackground);
|
||||
}
|
||||
|
||||
[data-slot="task-header-memory-action"]:disabled {
|
||||
[data-slot="task-header-context-action"]:disabled {
|
||||
color: var(--vscode-disabledForeground);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user