mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-02 17:22:06 +03:00
fix(webui): nudge model setup on blocked submit
This commit is contained in:
@@ -90,6 +90,7 @@ interface ModelPresetBadgeProps {
|
||||
provider?: string | null;
|
||||
providerLabel?: string | null;
|
||||
needsSetup?: boolean;
|
||||
attentionRequest?: number;
|
||||
fallbackModelName?: string | null;
|
||||
isHero: boolean;
|
||||
onClick?: () => void;
|
||||
@@ -106,6 +107,7 @@ export function ModelPresetBadge({
|
||||
provider,
|
||||
providerLabel,
|
||||
needsSetup = false,
|
||||
attentionRequest = 0,
|
||||
fallbackModelName,
|
||||
isHero,
|
||||
onClick,
|
||||
@@ -272,11 +274,13 @@ export function ModelPresetBadge({
|
||||
|
||||
const pill = (
|
||||
<PresetPill
|
||||
key={needsSetup ? attentionRequest : undefined}
|
||||
label={displayLabel}
|
||||
modelDetail={displayModelDetail}
|
||||
provider={displayProvider}
|
||||
providerLabel={fallbackModelName ? null : providerLabel}
|
||||
needsSetup={needsSetup}
|
||||
needsAttention={needsSetup && attentionRequest > 0}
|
||||
fallbackModelName={fallbackModelName}
|
||||
fallbackFromLabel={fallbackModelName ? label : null}
|
||||
isHero={isHero}
|
||||
@@ -493,6 +497,7 @@ function PresetPill({
|
||||
provider,
|
||||
providerLabel,
|
||||
needsSetup = false,
|
||||
needsAttention = false,
|
||||
fallbackModelName,
|
||||
fallbackFromLabel,
|
||||
isHero,
|
||||
@@ -504,6 +509,7 @@ function PresetPill({
|
||||
provider?: string | null;
|
||||
providerLabel?: string | null;
|
||||
needsSetup?: boolean;
|
||||
needsAttention?: boolean;
|
||||
fallbackModelName?: string | null;
|
||||
fallbackFromLabel?: string | null;
|
||||
isHero: boolean;
|
||||
@@ -542,6 +548,7 @@ function PresetPill({
|
||||
"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]",
|
||||
needsSetup && "composer-model-pill-setup",
|
||||
needsAttention && "composer-model-pill-setup-attention",
|
||||
offset !== undefined && "composer-model-pill-dock",
|
||||
)}
|
||||
style={scale === undefined ? undefined : {
|
||||
|
||||
@@ -1036,6 +1036,7 @@ export function ThreadComposer({
|
||||
} | null>(null);
|
||||
const [inlineError, setInlineError] = useState<string | null>(null);
|
||||
const [sendPending, setSendPending] = useState(false);
|
||||
const [modelSetupAttentionRequest, setModelSetupAttentionRequest] = useState(0);
|
||||
const interactionDisabled = !!disabled || sendPending;
|
||||
const [voiceErrorFading, setVoiceErrorFading] = useState(false);
|
||||
const [slashMenuDismissed, setSlashMenuDismissed] = useState(false);
|
||||
@@ -2008,7 +2009,9 @@ export function ThreadComposer({
|
||||
|
||||
const submit = useCallback(() => {
|
||||
if (modelNeedsSetup) {
|
||||
onModelBadgeClick?.();
|
||||
if (hasComposerContent) {
|
||||
setModelSetupAttentionRequest((request) => request + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!canSend) return;
|
||||
@@ -2112,11 +2115,11 @@ export function ThreadComposer({
|
||||
clear,
|
||||
clearComposerText,
|
||||
hasTouchPrimaryPointer,
|
||||
hasComposerContent,
|
||||
handleStop,
|
||||
isStreaming,
|
||||
maxTextBytes,
|
||||
modelNeedsSetup,
|
||||
onModelBadgeClick,
|
||||
onSend,
|
||||
onStop,
|
||||
onQuotedContextChange,
|
||||
@@ -2546,6 +2549,7 @@ export function ThreadComposer({
|
||||
provider={modelProvider}
|
||||
providerLabel={modelProviderLabel}
|
||||
needsSetup={modelNeedsSetup}
|
||||
attentionRequest={modelSetupAttentionRequest}
|
||||
fallbackModelName={fallbackModelName}
|
||||
isHero={isHero}
|
||||
onClick={modelNeedsSetup ? onModelBadgeClick : undefined}
|
||||
|
||||
@@ -765,6 +765,33 @@
|
||||
0 2px 5px rgb(0 0 0 / 0.16);
|
||||
}
|
||||
|
||||
@keyframes composer-model-pill-setup-attention {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
22% {
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
46% {
|
||||
transform: translateX(3px);
|
||||
}
|
||||
|
||||
68% {
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
|
||||
86% {
|
||||
transform: translateX(1px);
|
||||
}
|
||||
}
|
||||
|
||||
.composer-model-pill-setup-attention {
|
||||
animation: composer-model-pill-setup-attention 280ms cubic-bezier(0.22, 0.8, 0.32, 1);
|
||||
}
|
||||
|
||||
@keyframes composer-model-pill-viewport-enter {
|
||||
from {
|
||||
transform: scale(0.9074);
|
||||
@@ -817,6 +844,10 @@
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.composer-model-pill-setup-attention {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.composer-model-pill-track[data-settling="true"],
|
||||
.composer-model-pill-dock {
|
||||
transition: none;
|
||||
|
||||
@@ -895,11 +895,18 @@ describe("ThreadShell", () => {
|
||||
expect(client.sendMessage).not.toHaveBeenCalled();
|
||||
|
||||
onOpenModelSettings.mockClear();
|
||||
const firstSetupPill = badge.querySelector('[data-needs-setup="true"]');
|
||||
fireEvent.keyDown(input, { key: "Enter", code: "Enter" });
|
||||
|
||||
expect(onOpenModelSettings).toHaveBeenCalledTimes(1);
|
||||
const secondSetupPill = badge.querySelector('[data-needs-setup="true"]');
|
||||
expect(onOpenModelSettings).not.toHaveBeenCalled();
|
||||
expect(secondSetupPill).not.toBe(firstSetupPill);
|
||||
expect(secondSetupPill).toHaveClass("composer-model-pill-setup-attention");
|
||||
expect(input).toHaveValue("hello");
|
||||
expect(client.sendMessage).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.keyDown(input, { key: "Enter", code: "Enter" });
|
||||
expect(badge.querySelector('[data-needs-setup="true"]')).not.toBe(secondSetupPill);
|
||||
});
|
||||
|
||||
it("keeps image generation controls out of the composer", async () => {
|
||||
|
||||
Reference in New Issue
Block a user