mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
fix(web): restore chat composer disabled-state messaging and clean up code (#2526)
This commit is contained in:
@@ -14,6 +14,7 @@ import { Link } from "@tanstack/react-router"
|
||||
import * as React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { postLauncherDashboardLogout } from "@/api/launcher-auth"
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -40,7 +41,6 @@ import {
|
||||
} from "@/components/ui/tooltip"
|
||||
import { useGateway } from "@/hooks/use-gateway.ts"
|
||||
import { useTheme } from "@/hooks/use-theme.ts"
|
||||
import { postLauncherDashboardLogout } from "@/api/launcher-auth"
|
||||
|
||||
export function AppHeader() {
|
||||
const { i18n, t } = useTranslation()
|
||||
@@ -198,27 +198,42 @@ export function AppHeader() {
|
||||
<IconPower className="h-4 w-4 opacity-80" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{gwError ?? t("header.gateway.action.stop")}</TooltipContent>
|
||||
<TooltipContent>
|
||||
{gwError ?? t("header.gateway.action.stop")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip delayDuration={(gwError || (!canStart && startReason)) ? 0 : 700}>
|
||||
<Tooltip
|
||||
delayDuration={gwError || (!canStart && startReason) ? 0 : 700}
|
||||
>
|
||||
<TooltipTrigger asChild>
|
||||
{/* Wrap in span so the tooltip still fires when the button is disabled */}
|
||||
<span
|
||||
className={!canStart && startReason ? "cursor-not-allowed" : undefined}
|
||||
className={
|
||||
!canStart && startReason ? "cursor-not-allowed" : undefined
|
||||
}
|
||||
tabIndex={!canStart && startReason ? 0 : undefined}
|
||||
>
|
||||
<Button
|
||||
variant={
|
||||
isStarting || isRestarting || isStopping ? "secondary" : "default"
|
||||
isStarting || isRestarting || isStopping
|
||||
? "secondary"
|
||||
: "default"
|
||||
}
|
||||
size="sm"
|
||||
data-tour="gateway-button"
|
||||
className={`h-8 gap-2 px-3 ${isStopped ? "bg-green-500 text-white hover:bg-green-600" : ""
|
||||
} ${!canStart ? "pointer-events-none" : ""}`}
|
||||
className={`h-8 gap-2 px-3 ${
|
||||
isStopped
|
||||
? "bg-green-500 text-white hover:bg-green-600"
|
||||
: ""
|
||||
} ${!canStart ? "pointer-events-none" : ""}`}
|
||||
onClick={handleGatewayToggle}
|
||||
disabled={
|
||||
gwLoading || isStarting || isRestarting || isStopping || !canStart
|
||||
gwLoading ||
|
||||
isStarting ||
|
||||
isRestarting ||
|
||||
isStopping ||
|
||||
!canStart
|
||||
}
|
||||
>
|
||||
{gwLoading || isStarting || isRestarting || isStopping ? (
|
||||
@@ -238,7 +253,7 @@ export function AppHeader() {
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
{(gwError || (!canStart && startReason)) ? (
|
||||
{gwError || (!canStart && startReason) ? (
|
||||
<TooltipContent>{gwError ?? startReason}</TooltipContent>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
|
||||
@@ -42,15 +42,11 @@ export function ChatComposer({
|
||||
}: ChatComposerProps) {
|
||||
const { t } = useTranslation()
|
||||
const canInput = inputDisabledReason === null
|
||||
const placeholder = canInput
|
||||
? t("chat.placeholder")
|
||||
: t(`chat.disabledPlaceholder.${inputDisabledReason}`)
|
||||
|
||||
const inputDisabledReason = (() => {
|
||||
if (!isConnected) return t("chat.inputDisabled.notConnected")
|
||||
if (!hasDefaultModel) return t("chat.inputDisabled.noModel")
|
||||
return null
|
||||
})()
|
||||
const disabledMessage =
|
||||
inputDisabledReason === null
|
||||
? null
|
||||
: t(`chat.disabledPlaceholder.${inputDisabledReason}`)
|
||||
const placeholder = disabledMessage ?? t("chat.placeholder")
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.nativeEvent.isComposing) return
|
||||
@@ -95,7 +91,7 @@ export function ChatComposer({
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={placeholder}
|
||||
disabled={!canInput}
|
||||
title={inputDisabledReason || undefined}
|
||||
title={disabledMessage || undefined}
|
||||
className={cn(
|
||||
"placeholder:text-muted-foreground/50 max-h-[200px] min-h-[60px] resize-none border-0 bg-transparent px-2 py-1 text-[15px] shadow-none transition-colors focus-visible:ring-0 focus-visible:outline-none dark:bg-transparent",
|
||||
!canInput && "cursor-not-allowed",
|
||||
@@ -103,9 +99,9 @@ export function ChatComposer({
|
||||
minRows={1}
|
||||
maxRows={8}
|
||||
/>
|
||||
{!canInput && inputDisabledReason && (
|
||||
<div className="px-3 py-1 text-xs text-muted-foreground">
|
||||
{inputDisabledReason}
|
||||
{!canInput && disabledMessage && (
|
||||
<div className="text-muted-foreground px-3 py-1 text-xs">
|
||||
{disabledMessage}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import { toast } from "sonner"
|
||||
|
||||
import { AssistantMessage } from "@/components/chat/assistant-message"
|
||||
import {
|
||||
type ChatInputDisabledReason,
|
||||
ChatComposer,
|
||||
type ChatInputDisabledReason,
|
||||
} from "@/components/chat/chat-composer"
|
||||
import { ChatEmptyState } from "@/components/chat/chat-empty-state"
|
||||
import { ModelSelector } from "@/components/chat/model-selector"
|
||||
|
||||
Reference in New Issue
Block a user