style(webui): refine first-run model prompt

This commit is contained in:
Xubin Ren
2026-09-02 03:48:10 +08:00
parent ae7618f458
commit cd88c422d9
3 changed files with 60 additions and 1 deletions
@@ -533,6 +533,7 @@ function PresetPill({
return ( return (
<span <span
data-fallback={fallbackModelName ? "true" : undefined} data-fallback={fallbackModelName ? "true" : undefined}
data-needs-setup={needsSetup ? "true" : undefined}
data-preset-offset={offset} data-preset-offset={offset}
title={fallbackTitle || undefined} title={fallbackTitle || undefined}
className={cn( className={cn(
@@ -540,6 +541,7 @@ function PresetPill({
"w-fit", "w-fit",
"transition-[color,background-color,border-color,transform] duration-150 ease-out group-focus-visible:ring-2 group-focus-visible:ring-ring/45", "transition-[color,background-color,border-color,transform] duration-150 ease-out group-focus-visible:ring-2 group-focus-visible:ring-ring/45",
isHero ? "gap-1.5 px-2.5 text-[12px]" : "gap-2 px-3 text-[12.5px]", isHero ? "gap-1.5 px-2.5 text-[12px]" : "gap-2 px-3 text-[12.5px]",
needsSetup && "composer-model-pill-setup",
offset !== undefined && "composer-model-pill-dock", offset !== undefined && "composer-model-pill-dock",
)} )}
style={scale === undefined ? undefined : { style={scale === undefined ? undefined : {
@@ -564,7 +566,26 @@ function PresetPill({
labelOverflows && "thread-composer-model-label-fade", labelOverflows && "thread-composer-model-label-fade",
)} )}
> >
{label} {needsSetup ? <SetupPromptLabel label={label} /> : label}
</span>
</span>
);
}
function SetupPromptLabel({ label }: { label: string }) {
const separator = label.indexOf(" ");
if (separator < 0) {
return <span className="text-foreground/80">{label}</span>;
}
return (
<span data-testid="composer-model-setup-label">
<span className="text-muted-foreground/90 transition-colors duration-150 group-hover:text-muted-foreground motion-reduce:transition-none">
{label.slice(0, separator)}
</span>
{" "}
<span className="text-foreground/80 transition-colors duration-150 group-hover:text-foreground/90 motion-reduce:transition-none">
{label.slice(separator + 1)}
</span> </span>
</span> </span>
); );
+34
View File
@@ -735,6 +735,36 @@
transform: scale(0.98); transform: scale(0.98);
} }
.composer-model-pill-setup {
border-color: hsl(var(--border) / 0.72);
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.62),
0 1px 2px hsl(var(--foreground) / 0.035);
transition-property: color, background-color, border-color, box-shadow, transform;
transition-duration: 180ms;
transition-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
}
.dark .composer-model-pill-setup {
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.045),
0 1px 2px rgb(0 0 0 / 0.12);
}
.thread-composer-model-badge:hover > .composer-model-pill-setup {
border-color: hsl(var(--border));
background-color: hsl(var(--accent) / 0.7);
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.72),
0 2px 5px hsl(var(--foreground) / 0.06);
}
.dark .thread-composer-model-badge:hover > .composer-model-pill-setup {
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.07),
0 2px 5px rgb(0 0 0 / 0.16);
}
@keyframes composer-model-pill-viewport-enter { @keyframes composer-model-pill-viewport-enter {
from { from {
transform: scale(0.9074); transform: scale(0.9074);
@@ -783,6 +813,10 @@
transform: none !important; transform: none !important;
} }
.composer-model-pill-setup {
transition: none;
}
.composer-model-pill-track[data-settling="true"], .composer-model-pill-track[data-settling="true"],
.composer-model-pill-dock { .composer-model-pill-dock {
transition: none; transition: none;
+4
View File
@@ -876,6 +876,10 @@ describe("ThreadShell", () => {
const badge = await screen.findByRole("button", { name: "Choose your AI" }); const badge = await screen.findByRole("button", { name: "Choose your AI" });
expect(screen.queryByTestId("composer-model-setup-icon")).not.toBeInTheDocument(); expect(screen.queryByTestId("composer-model-setup-icon")).not.toBeInTheDocument();
expect(badge.querySelector('[data-needs-setup="true"]')).toHaveClass(
"composer-model-pill-setup",
);
expect(screen.getByTestId("composer-model-setup-label")).toHaveTextContent("Choose your AI");
expect(badge).not.toHaveClass("border-amber-500/35"); expect(badge).not.toHaveClass("border-amber-500/35");
expect(screen.queryByTestId("composer-model-logo-openai_codex")).not.toBeInTheDocument(); expect(screen.queryByTestId("composer-model-logo-openai_codex")).not.toBeInTheDocument();
fireEvent.click(badge); fireEvent.click(badge);