fix(dashboard): drop the border around the footer folder toggle

The status-bar folder-toggle button had a 1px border + radius that made it
look like a chip. Per request, render it as a bare icon — transparent
background, no border. Hover now only shifts the icon color, the active
state drops to color only, and focus-visible still draws a focus ring with
border-radius applied so it remains a clean halo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-25 10:55:06 -07:00
parent 8c632a47b3
commit e543dabe91
7 changed files with 614 additions and 9 deletions

View File

@@ -46,7 +46,7 @@
.model-combobox-trigger-icon {
display: inline-flex;
margin-right: 8px;
margin-right: var(--space-sm);
flex-shrink: 0;
}
@@ -160,6 +160,19 @@
background: rgba(88, 166, 255, 0.25);
}
.model-combobox-option-main {
display: inline-flex;
align-items: center;
gap: var(--space-sm);
min-width: 0;
flex: 1;
}
.model-combobox-option-icon {
display: inline-flex;
flex-shrink: 0;
}
.model-combobox-option-text {
flex: 1;
overflow: hidden;

View File

@@ -522,8 +522,12 @@ export function CustomModelDropdown({
role="option"
aria-selected={isSelected}
>
<ProviderIcon provider={model.provider} size="sm" />
<span className="model-combobox-option-text">{model.name}</span>
<span className="model-combobox-option-main">
<span className="model-combobox-option-icon">
<ProviderIcon provider={model.provider} size="sm" />
</span>
<span className="model-combobox-option-text">{model.name}</span>
</span>
<span className="model-combobox-option-id">{model.id}</span>
{onToggleModelFavorite && (
<button

View File

@@ -152,29 +152,26 @@
min-width: var(--executor-status-touch-size);
min-height: var(--executor-status-touch-size);
padding: 0;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
border: none;
background: transparent;
color: var(--text-muted);
cursor: pointer;
transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
transition: color var(--transition-fast);
}
.executor-status-bar__folder-toggle:hover {
background: var(--card-hover);
color: var(--text);
border-color: var(--text-dim);
}
.executor-status-bar__folder-toggle:focus-visible,
.executor-status-bar__project-path:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
border-radius: var(--radius-sm);
}
.executor-status-bar__folder-toggle--active {
color: var(--todo);
border-color: var(--todo);
}
.executor-status-bar__project-path {

View File

@@ -204,6 +204,7 @@ const providerConfig: Record<
zai: { component: ZaiIcon, color: "#1A6DFF" }, // blue
kimi: { component: KimiIcon, color: "#6C5CE7" }, // purple
moonshot: { component: KimiIcon, color: "#6C5CE7" }, // purple (same as kimi)
"kimi-coding": { component: KimiIcon, color: "#6C5CE7", label: "Kimi" }, // purple (Kimi alias)
};
export function ProviderIcon({ provider, size = "sm" }: ProviderIconProps) {

View File

@@ -138,6 +138,29 @@ describe("CustomModelDropdown ProviderIcon Integration", () => {
expect(kimiIcon.querySelector("path")).toHaveAttribute("fill", "#6C5CE7");
});
it("uses explicit icon/text layout hooks for favorited model rows", async () => {
const user = userEvent.setup();
render(<CustomModelDropdown {...defaultProps} favoriteModels={["openai/gpt-4o"]} />);
await user.click(screen.getByLabelText("Test Model"));
const pinnedRow = screen
.getByText("GPT-4o")
.closest(".model-combobox-option--favorite");
expect(pinnedRow).toBeInTheDocument();
const mainLayout = pinnedRow?.querySelector(".model-combobox-option-main");
expect(mainLayout).toBeInTheDocument();
const iconSlot = mainLayout?.querySelector(".model-combobox-option-icon");
expect(iconSlot).toBeInTheDocument();
expect(iconSlot?.querySelector("[data-testid='openai-icon']")).toBeInTheDocument();
const textSlot = mainLayout?.querySelector(".model-combobox-option-text");
expect(textSlot).toBeInTheDocument();
expect(textSlot).toHaveTextContent("GPT-4o");
});
it("renders Kimi brand icon for moonshot provider model in dropdown (alias)", async () => {
const user = userEvent.setup();
render(<CustomModelDropdown {...defaultProps} />);

View File

@@ -291,6 +291,26 @@ describe("ProviderIcon", () => {
expect(wrapper).toHaveAttribute("data-provider", "moonshot");
});
it("renders Kimi brand icon for kimi-coding provider (alias)", () => {
render(<ProviderIcon provider="kimi-coding" />);
expect(screen.getByTestId("kimi-icon")).toBeInTheDocument();
expect(screen.getByLabelText("Kimi")).toBeInTheDocument();
});
it("applies provider-specific color for kimi-coding (alias)", () => {
render(<ProviderIcon provider="kimi-coding" />);
const icon = screen.getByTestId("kimi-icon").parentElement;
expect(icon).toHaveStyle({ color: "#6C5CE7" });
});
it("passes correct color to SVG fill for kimi-coding (alias)", () => {
render(<ProviderIcon provider="kimi-coding" />);
const svg = screen.getByTestId("kimi-icon");
const paths = svg.querySelectorAll("path");
expect(paths.length).toBeGreaterThan(0);
expect(paths[0]).toHaveAttribute("fill", "#6C5CE7");
});
// Regression test: verify the Kimi icon is the crescent moon shape, not the old "K" placeholder
it("renders crescent moon icon geometry (not the old K placeholder)", () => {
render(<ProviderIcon provider="kimi" />);