refactor: vscode / kilo-ui style

This commit is contained in:
Catriel Müller
2026-03-04 19:36:27 -03:00
parent 9a66d6756f
commit d6ce061731
16 changed files with 373 additions and 138 deletions
+84 -23
View File
@@ -1,26 +1,26 @@
/* Kilo Button overrides – sized to match @vscode/webview-ui-toolkit */
/* Kilo Button overrides – styled to match Figma design */
[data-component="button"] {
font-family: var(--vscode-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
font-size: 13px;
line-height: normal;
line-height: 16px;
border-radius: 2px;
cursor: pointer;
border-color: transparent;
box-shadow: none;
&:active:not([data-disabled]) {
opacity: 0.8;
&:focus-visible {
outline: 1px solid var(--border-focus);
outline-offset: 1px;
}
&[data-variant="primary"] {
background-color: var(--surface-interactive-base);
color: var(--text-on-interactive-base, white);
border-color: transparent;
box-shadow: none;
font-weight: 500;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
&:hover:not([data-disabled]) {
background-color: var(--surface-interactive-hover);
}
&:active:not(:disabled) {
opacity: 0.8;
}
/* Full-width variant for "Start New Task" style buttons */
@@ -28,35 +28,96 @@
width: 100%;
}
&[data-variant="secondary"] {
&[data-variant="primary"] {
background-color: var(--surface-brand-base);
color: #ffffff;
border-color: transparent;
box-shadow: none;
border: 1px solid var(--border-weak-base);
font-weight: 400;
[data-slot="icon-svg"] {
color: #ffffff;
}
&:hover:not(:disabled) {
background-color: var(--surface-brand-hover);
}
&:disabled {
background-color: var(--surface-brand-base);
opacity: 0.5;
}
}
&[data-variant="secondary"] {
background-color: var(--button-secondary-base);
color: var(--text-strong);
border-color: transparent;
box-shadow: none;
font-weight: 400;
&:hover:not(:disabled) {
background-color: var(--button-secondary-hover);
}
&:disabled {
background-color: var(--button-secondary-base);
opacity: 0.5;
}
[data-slot="icon-svg"] {
color: var(--icon-base);
}
}
&[data-variant="ghost"] {
background: transparent;
border-color: transparent;
box-shadow: none;
font-weight: 400;
&:hover:not(:disabled) {
background-color: rgba(255, 255, 255, 0.03);
background-color: var(--surface-base-hover);
}
&:disabled {
opacity: 0.5;
}
}
&[data-size="normal"] {
height: 26px;
padding: 4px 11px;
height: 32px;
padding: 6px 16px;
gap: 6px;
font-size: 13px;
line-height: 16px;
&[data-icon] {
padding: 6px 16px 6px 12px;
}
}
&[data-size="large"] {
height: 30px;
padding: 6px 11px;
height: 32px;
padding: 6px 16px;
gap: 6px;
font-size: 13px;
line-height: 16px;
&[data-icon] {
padding: 6px 16px 6px 12px;
}
}
&[data-size="small"] {
height: 22px;
padding: 2px 8px;
font-size: 11px;
height: 24px;
padding: 4px 12px;
gap: 4px;
font-size: 13px;
line-height: 16px;
&[data-icon] {
padding: 4px 12px 4px 8px;
}
}
}
+31 -23
View File
@@ -1,51 +1,59 @@
/* Kilo Select overrides – styled to match legacy SelectDropdown look */
/* Kilo Select overrides – styled to match Figma design */
/* Select trigger (renders as a Button, override the button sizing) */
[data-component="select"] [data-slot="select-select-trigger"] {
height: auto !important;
min-height: 22px;
border-radius: 3px;
font-size: 12px;
line-height: normal;
padding: 1px 6px !important;
height: 32px;
min-height: 32px;
border-radius: 2px;
font-size: 13px;
line-height: 16px;
padding: 6px 12px !important;
cursor: pointer;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.08);
background: var(--background-weak);
border: 1px solid var(--border-base);
box-shadow: none;
opacity: 0.9;
gap: 6px;
gap: 8px;
white-space: nowrap;
transition: all 150ms;
color: var(--text-base, var(--vscode-foreground));
transition: border-color 150ms;
color: var(--icon-weak-base);
justify-content: space-between;
&:hover:not(:disabled) {
opacity: 1;
background: rgba(255, 255, 255, 0.03);
border-color: rgba(255, 255, 255, 0.15);
background: var(--background-weak);
border-color: var(--border-hover);
}
&:focus-visible {
outline: none;
border-color: var(--border-hover);
box-shadow: 0 0 0 1px var(--border-focus);
}
&[data-expanded] {
background: rgba(255, 255, 255, 0.03);
border-color: rgba(255, 255, 255, 0.15);
background: var(--background-weak);
border-color: var(--border-hover);
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
[data-slot="select-select-trigger-value"] {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
font-size: 13px;
line-height: 16px;
color: var(--text-base);
}
[data-slot="select-select-trigger-icon"] {
width: 12px;
height: 12px;
opacity: 0.8;
width: 10px;
height: 20px;
flex-shrink: 0;
color: var(--icon-weak-base);
background-color: transparent;
}
}
@@ -82,7 +90,7 @@
box-sizing: border-box;
border-radius: 0;
padding: 6px 12px;
font-size: 12px;
font-size: 13px;
line-height: 16px;
cursor: pointer;
gap: 8px;
@@ -4,4 +4,76 @@
[data-slot="switch-label"] {
font-size: 13px;
}
[data-slot="switch-control"] {
display: flex;
flex-direction: row;
align-items: center;
box-sizing: border-box;
width: 36px;
height: 20px;
padding: 2px;
border-radius: 9999px;
border: none;
background: var(--border-base);
transition: background-color 150ms;
}
[data-slot="switch-thumb"] {
width: 16px;
height: 16px;
box-sizing: border-box;
border-radius: 9999px;
border: none;
background: #ffffff;
box-shadow:
0px 10px 15px -3px rgba(0, 0, 0, 0.1),
0px 4px 6px -2px rgba(0, 0, 0, 0.05);
transform: translateX(0);
transition: transform 150ms;
}
&:hover:not([data-disabled], [data-readonly]) [data-slot="switch-control"] {
border: none;
background-color: var(--border-hover);
}
&:not([data-readonly]) [data-slot="switch-input"]:focus-visible ~ [data-slot="switch-control"] {
border: none;
box-shadow: none;
outline: 2px solid var(--border-focus);
outline-offset: 2px;
}
&[data-checked] [data-slot="switch-control"] {
box-sizing: border-box;
border: none;
background-color: var(--border-focus);
}
&[data-checked] [data-slot="switch-thumb"] {
border: none;
transform: translateX(16px);
background: #ffffff;
}
&[data-checked]:hover:not([data-disabled], [data-readonly]) [data-slot="switch-control"] {
border: none;
background-color: var(--border-focus);
}
&[data-disabled] [data-slot="switch-control"] {
border: none;
background-color: var(--border-base);
opacity: 0.5;
}
&[data-disabled] [data-slot="switch-thumb"] {
background: #ffffff;
}
&[data-invalid] [data-slot="switch-control"] {
border: none;
box-shadow: 0 0 0 1px var(--border-error);
}
}
+50 -15
View File
@@ -4,6 +4,25 @@
font-size: 13px;
line-height: normal;
[data-slot="tabs-list"] {
padding: 8px;
}
[data-slot="tabs-trigger-wrapper"] {
padding: 8px;
border-radius: 2px;
&:has([data-selected]) {
background-color: var(--input-base);
}
}
[data-slot="tabs-trigger"] {
font-size: 12px;
line-height: normal;
padding: 0px;
}
/* Pill variant: closest to legacy tab style */
&[data-variant="pill"] [data-slot="tabs-list"] {
background: var(--input-base);
@@ -17,23 +36,39 @@
font-size: 13px;
}
/* Default/alt variant adjustments */
[data-slot="tabs-trigger"] {
height: 48px;
padding: 4px 0;
font-size: 13px;
line-height: normal;
}
&[data-variant="settings"] {
container-type: inline-size;
/* Settings variant: content area padding and header styling */
&[data-variant="settings"] [data-slot="tabs-content"] {
padding: 16px 20px;
[data-slot="tabs-list"] {
width: 180px;
min-width: 180px;
}
> h3 {
font-size: 18px;
font-weight: 600;
margin: 0 0 16px 0;
color: var(--vscode-foreground, var(--text-strong));
@container (max-width: 500px) {
[data-slot="tabs-list"] {
width: auto;
min-width: auto;
}
[data-slot="tabs-trigger"] .label {
display: none;
}
}
[data-slot="tabs-content"] {
background-color: var(--background-base);
padding: 8px 16px;
> h3 {
font-size: 16px;
font-weight: 400;
margin: 16px 0;
color: var(--vscode-foreground, var(--text-strong));
}
[data-component="card"] {
padding: 0;
border: none;
}
}
}
}
+44 -7
View File
@@ -1,26 +1,63 @@
/* Kilo Input / TextField overrides – sized to match @vscode/webview-ui-toolkit */
/* Kilo Input / TextField overrides – styled to match Figma design */
[data-component="input"] {
&[data-variant="normal"] [data-slot="input-label"] {
font-size: 12px;
line-height: 14px;
font-weight: var(--font-weight-regular);
color: var(--text-base);
}
&[data-variant="normal"] [data-slot="input-wrapper"] {
border-radius: 2px;
border: 1px solid var(--border-base);
background: var(--background-weak);
padding-right: 0;
transition: border-color 150ms;
&:hover:not(:has([data-readonly])):not(:has(:disabled)) {
border-color: var(--border-hover);
}
&:focus-within:not(:has([data-readonly])) {
border-color: var(--border-focus);
border-color: var(--border-hover);
box-shadow: 0 0 0 1px var(--border-focus);
}
&:has([data-invalid]) {
background: var(--background-weak);
border: 1px solid var(--border-critical-base);
box-shadow: none;
}
&:has(:disabled) {
opacity: 0.5;
}
}
&[data-variant="normal"] [data-slot="input-input"] {
height: 26px;
padding: 0 9px;
height: 32px;
padding: 6px 12px;
font-size: 13px;
line-height: normal;
line-height: 16px;
font-family: var(--vscode-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
color: var(--text-base);
&::placeholder {
color: var(--icon-weak-base);
}
}
&[data-variant="normal"] textarea[data-slot="input-input"] {
height: auto;
min-height: 26px;
padding: 9px;
min-height: 32px;
padding: 6px 12px;
}
&[data-variant="normal"] [data-slot="input-error"] {
font-size: 12px;
line-height: 14px;
font-weight: var(--font-weight-regular);
color: var(--text-on-critical-base);
}
}
+1 -1
View File
@@ -2016,7 +2016,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
iconsBaseUri: webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "assets", "icons")),
title: "Kilo Code",
port: this.connectionService.getServerInfo()?.port,
extraStyles: `.container { height: 100%; display: flex; flex-direction: column; height: 100vh; }`,
extraStyles: `.container { height: 100%; display: flex; flex-direction: column; height: 100vh; border-right: 1px solid var(--border-weak-base); }`,
})
}
@@ -430,7 +430,7 @@ export const PromptInput: Component = () => {
fallback={
<Tooltip value={language.t("prompt.action.send")} placement="top">
<Button
variant="primary"
variant="ghost"
size="small"
onClick={handleSend}
disabled={!canSend()}
@@ -182,19 +182,24 @@ const AgentBehaviourTab: Component = () => {
</SettingsRow>
</Card>
{/* Agent selector */}
<div style={{ "margin-bottom": "12px" }}>
<Select
options={agentSelectorOptions()}
current={agentSelectorOptions().find((o) => o.value === selectedAgent())}
value={(o) => o.value}
label={(o) => o.label}
onSelect={(o) => o && setSelectedAgent(o.value)}
variant="secondary"
size="small"
triggerVariant="settings"
/>
</div>
<Card style={{ "margin-bottom": "12px" }}>
<SettingsRow
title={language.t("settings.agentBehaviour.selectAgent.title")}
description={language.t("settings.agentBehaviour.selectAgent.description")}
last
>
<Select
options={agentSelectorOptions()}
current={agentSelectorOptions().find((o) => o.value === selectedAgent())}
value={(o) => o.value}
label={(o) => o.label}
onSelect={(o) => o && setSelectedAgent(o.value)}
variant="secondary"
size="small"
triggerVariant="settings"
/>
</SettingsRow>
</Card>
<Show when={selectedAgent()}>
<Card>
@@ -313,7 +318,7 @@ const AgentBehaviourTab: Component = () => {
<div style={{ "font-weight": "500" }}>{name}</div>
<div
style={{
"font-size": "11px",
"font-size": "12px",
color: "var(--text-weak-base, var(--vscode-descriptionForeground))",
"margin-top": "4px",
"font-family": "var(--vscode-editor-font-family, monospace)",
@@ -361,7 +366,7 @@ const AgentBehaviourTab: Component = () => {
}}
/>
</div>
<Button size="small" onClick={addSkillPath}>
<Button variant="secondary" onClick={addSkillPath}>
{language.t("common.add")}
</Button>
</div>
@@ -412,7 +417,7 @@ const AgentBehaviourTab: Component = () => {
}}
/>
</div>
<Button size="small" onClick={addSkillUrl}>
<Button variant="secondary" onClick={addSkillUrl}>
{language.t("common.add")}
</Button>
</div>
@@ -455,7 +460,7 @@ const AgentBehaviourTab: Component = () => {
<div style={{ "font-weight": "500" }}>{language.t("settings.agentBehaviour.instructionFiles")}</div>
<div
style={{
"font-size": "11px",
"font-size": "12px",
color: "var(--text-weak-base, var(--vscode-descriptionForeground))",
"margin-top": "2px",
}}
@@ -484,7 +489,7 @@ const AgentBehaviourTab: Component = () => {
}}
/>
</div>
<Button size="small" onClick={addInstruction}>
<Button variant="secondary" onClick={addInstruction}>
{language.t("common.add")}
</Button>
</div>
@@ -102,13 +102,14 @@ const AutoApproveTab: Component = () => {
style={{
"font-family": "var(--vscode-editor-font-family, monospace)",
"font-size": "12px",
"text-transform": "capitalize",
}}
>
{tool}
</div>
<div
style={{
"font-size": "11px",
"font-size": "12px",
color: "var(--text-weak-base, var(--vscode-descriptionForeground))",
"margin-top": "2px",
}}
@@ -69,7 +69,7 @@ const ContextTab: Component = () => {
<Card>
<div
style={{
"font-size": "11px",
"font-size": "12px",
color: "var(--text-weak-base, var(--vscode-descriptionForeground))",
"padding-bottom": "8px",
"border-bottom": patterns().length > 0 || newPattern() ? "1px solid var(--border-weak-base)" : "none",
@@ -98,7 +98,7 @@ const ContextTab: Component = () => {
}}
/>
</div>
<Button size="small" onClick={addPattern}>
<Button variant="secondary" onClick={addPattern}>
{language.t("common.add")}
</Button>
</div>
@@ -104,7 +104,7 @@ const ProvidersTab: Component = () => {
<Card>
<div
style={{
"font-size": "11px",
"font-size": "12px",
color: "var(--text-weak-base, var(--vscode-descriptionForeground))",
"padding-bottom": "8px",
"border-bottom": "1px solid var(--border-weak-base)",
@@ -129,13 +129,12 @@ const ProvidersTab: Component = () => {
label={(o) => o.label}
onSelect={(o) => setNewDisabled(o)}
variant="secondary"
size="small"
triggerVariant="settings"
placeholder="Select provider…"
/>
</div>
<Button
size="small"
variant="secondary"
onClick={() => {
if (newDisabled()) {
addToList("disabled_providers", newDisabled()!.value)
@@ -159,12 +158,7 @@ const ProvidersTab: Component = () => {
}}
>
<span style={{ "font-size": "12px" }}>{id}</span>
<IconButton
size="small"
variant="ghost"
icon="close"
onClick={() => removeFromList("disabled_providers", index())}
/>
<IconButton variant="ghost" icon="close" onClick={() => removeFromList("disabled_providers", index())} />
</div>
)}
</For>
@@ -175,7 +169,7 @@ const ProvidersTab: Component = () => {
<Card>
<div
style={{
"font-size": "11px",
"font-size": "12px",
color: "var(--text-weak-base, var(--vscode-descriptionForeground))",
"padding-bottom": "8px",
"border-bottom": "1px solid var(--border-weak-base)",
@@ -200,13 +194,12 @@ const ProvidersTab: Component = () => {
label={(o) => o.label}
onSelect={(o) => setNewEnabled(o)}
variant="secondary"
size="small"
triggerVariant="settings"
placeholder="Select provider…"
/>
</div>
<Button
size="small"
variant="secondary"
onClick={() => {
if (newEnabled()) {
addToList("enabled_providers", newEnabled()!.value)
@@ -229,12 +222,7 @@ const ProvidersTab: Component = () => {
}}
>
<span style={{ "font-size": "12px" }}>{id}</span>
<IconButton
size="small"
variant="ghost"
icon="close"
onClick={() => removeFromList("enabled_providers", index())}
/>
<IconButton variant="ghost" icon="close" onClick={() => removeFromList("enabled_providers", index())} />
</div>
)}
</For>
@@ -54,59 +54,59 @@ const Settings: Component<SettingsProps> = (props) => {
<Tabs.List>
<Tabs.Trigger value="providers">
<Icon name="providers" />
{language.t("settings.providers.title")}
<span class="label">{language.t("settings.providers.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="agentBehaviour">
<Icon name="brain" />
{language.t("settings.agentBehaviour.title")}
<span class="label">{language.t("settings.agentBehaviour.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="autoApprove">
<Icon name="checklist" />
{language.t("settings.autoApprove.title")}
<span class="label">{language.t("settings.autoApprove.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="browser">
<Icon name="window-cursor" />
{language.t("settings.browser.title")}
<span class="label">{language.t("settings.browser.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="checkpoints">
<Icon name="branch" />
{language.t("settings.checkpoints.title")}
<span class="label">{language.t("settings.checkpoints.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="display">
<Icon name="eye" />
{language.t("settings.display.title")}
<span class="label">{language.t("settings.display.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="autocomplete">
<Icon name="code-lines" />
{language.t("settings.autocomplete.title")}
<span class="label">{language.t("settings.autocomplete.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="notifications">
<Icon name="circle-check" />
{language.t("settings.notifications.title")}
<span class="label">{language.t("settings.notifications.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="context">
<Icon name="server" />
{language.t("settings.context.title")}
<span class="label">{language.t("settings.context.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="terminal">
<Icon name="console" />
{language.t("settings.terminal.title")}
<span class="label">{language.t("settings.terminal.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="prompts">
<Icon name="comment" />
{language.t("settings.prompts.title")}
<span class="label">{language.t("settings.prompts.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="experimental">
<Icon name="settings-gear" />
{language.t("settings.experimental.title")}
<span class="label">{language.t("settings.experimental.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="language">
<Icon name="speech-bubble" />
{language.t("settings.language.title")}
<span class="label">{language.t("settings.language.title")}</span>
</Tabs.Trigger>
<Tabs.Trigger value="aboutKiloCode">
<Icon name="help" />
{language.t("settings.aboutKiloCode.title")}
<span class="label">{language.t("settings.aboutKiloCode.title")}</span>
</Tabs.Trigger>
</Tabs.List>
@@ -6,22 +6,16 @@ const SettingsRow: Component<{ title: string; description: string; last?: boolea
<div
data-slot="settings-row"
style={{
display: "flex",
"flex-wrap": "wrap",
"align-items": "center",
"justify-content": "space-between",
padding: "12px 0",
gap: "8px",
"margin-bottom": props.last ? "0" : "8px",
"padding-bottom": props.last ? "0" : "8px",
"border-bottom": props.last ? "none" : "1px solid var(--border-weak-base)",
}}
>
<div style={{ "min-width": "150px", flex: "1 1 200px" }}>
<div style={{ "font-weight": "500" }}>{props.title}</div>
<div style={{ "font-size": "11px", color: "var(--text-weak-base, var(--vscode-descriptionForeground))" }}>
{props.description}
</div>
<div data-slot="settings-row-label">
<div data-slot="settings-row-label-title">{props.title}</div>
<div data-slot="settings-row-label-subtitle">{props.description}</div>
</div>
{props.children}
<div data-slot="settings-row-input">{props.children}</div>
</div>
)
@@ -191,7 +191,7 @@ export const ModelSelectorBase: Component<ModelSelectorBaseProps> = (props) => {
triggerAs={Button}
triggerProps={{
variant: "ghost",
size: "small",
size: "normal",
disabled: !hasProviders(),
title: selectedModel()?.id,
}}
@@ -924,7 +924,8 @@ export const dict = {
"settings.agentBehaviour.defaultAgent.title": "Default Agent",
"settings.agentBehaviour.defaultAgent.description": "Agent to use when none is specified",
"settings.agentBehaviour.selectAgent": "Select an agent to configure…",
"settings.agentBehaviour.selectAgent.title": "Agent",
"settings.agentBehaviour.selectAgent.description": "Select an agent to configure…",
"settings.agentBehaviour.modelOverride.title": "Model Override",
"settings.agentBehaviour.modelOverride.description": "Override the default model for this agent",
"settings.agentBehaviour.prompt.title": "Custom Prompt",
@@ -998,7 +999,6 @@ export const dict = {
// Agent Manager strings live in webview-ui/agent-manager/i18n/en.ts
"question.summary": "{{n}} of {{total}} questions",
"common.review": "Review",
@@ -1221,3 +1221,37 @@
.enhance-spinner {
animation: enhance-spin 1s linear infinite;
}
[data-slot="settings-row"] {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
[data-slot="settings-row-label"] {
flex: 1 1 0%;
min-width: 150px;
[data-slot="settings-row-label-title"] {
font-weight: 500;
min-height: 16px;
margin-bottom: 4px;
}
[data-slot="settings-row-label-subtitle"] {
font-size: 12px;
min-height: 14px;
color: var(--text-weak-base, var(--vscode-descriptionForeground));
}
}
[data-slot="settings-row-input"] {
flex: 0 0 auto;
max-width: 160px;
& > * {
max-width: 100%;
}
}
}