feat(webui): add first-run AI setup flow
This commit is contained in:
@@ -70,6 +70,10 @@ import {
|
||||
ModelPresetBadge,
|
||||
type ModelPresetOption,
|
||||
} from "@/components/thread/ModelPresetBadge";
|
||||
import {
|
||||
ModelSetupDialog,
|
||||
type ModelSetupAvailability,
|
||||
} from "@/components/thread/ModelSetupDialog";
|
||||
import {
|
||||
ACCEPT_ATTR,
|
||||
MAX_ATTACHMENTS_PER_MESSAGE,
|
||||
@@ -298,6 +302,7 @@ interface ThreadComposerProps {
|
||||
modelProvider?: string | null;
|
||||
modelProviderLabel?: string | null;
|
||||
modelNeedsSetup?: boolean;
|
||||
modelSetupAvailability?: ModelSetupAvailability;
|
||||
fallbackModelName?: string | null;
|
||||
onModelBadgeClick?: () => void;
|
||||
onManageModels?: () => void;
|
||||
@@ -997,6 +1002,7 @@ export function ThreadComposer({
|
||||
modelProvider = null,
|
||||
modelProviderLabel = null,
|
||||
modelNeedsSetup = false,
|
||||
modelSetupAvailability = { account: false, apiKey: false, local: false },
|
||||
fallbackModelName = null,
|
||||
onModelBadgeClick,
|
||||
onManageModels,
|
||||
@@ -1036,6 +1042,7 @@ export function ThreadComposer({
|
||||
} | null>(null);
|
||||
const [inlineError, setInlineError] = useState<string | null>(null);
|
||||
const [sendPending, setSendPending] = useState(false);
|
||||
const [modelSetupOpen, setModelSetupOpen] = useState(false);
|
||||
const interactionDisabled = !!disabled || sendPending;
|
||||
const [voiceErrorFading, setVoiceErrorFading] = useState(false);
|
||||
const [slashMenuDismissed, setSlashMenuDismissed] = useState(false);
|
||||
@@ -2008,7 +2015,7 @@ export function ThreadComposer({
|
||||
|
||||
const submit = useCallback(() => {
|
||||
if (modelNeedsSetup) {
|
||||
onModelBadgeClick?.();
|
||||
setModelSetupOpen(true);
|
||||
return;
|
||||
}
|
||||
if (!canSend) return;
|
||||
@@ -2116,7 +2123,6 @@ export function ThreadComposer({
|
||||
isStreaming,
|
||||
maxTextBytes,
|
||||
modelNeedsSetup,
|
||||
onModelBadgeClick,
|
||||
onSend,
|
||||
onStop,
|
||||
onQuotedContextChange,
|
||||
@@ -2127,6 +2133,15 @@ export function ThreadComposer({
|
||||
value,
|
||||
]);
|
||||
|
||||
const openModelSetup = useCallback(() => {
|
||||
setModelSetupOpen(true);
|
||||
}, []);
|
||||
|
||||
const continueModelSetup = useCallback(() => {
|
||||
setModelSetupOpen(false);
|
||||
onModelBadgeClick?.();
|
||||
}, [onModelBadgeClick]);
|
||||
|
||||
const onKeyDown = (e: ReactKeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (showCliAppMenu) {
|
||||
if (e.key === "ArrowDown") {
|
||||
@@ -2548,7 +2563,7 @@ export function ThreadComposer({
|
||||
needsSetup={modelNeedsSetup}
|
||||
fallbackModelName={fallbackModelName}
|
||||
isHero={isHero}
|
||||
onClick={modelNeedsSetup ? onModelBadgeClick : undefined}
|
||||
onClick={modelNeedsSetup ? openModelSetup : undefined}
|
||||
/>
|
||||
) : null}
|
||||
{!voiceRecorder.isRecording ? <ComposerContextBadge usage={contextUsage} /> : null}
|
||||
@@ -2607,10 +2622,10 @@ export function ThreadComposer({
|
||||
showStopButton
|
||||
? t("thread.composer.stop")
|
||||
: modelNeedsSetup
|
||||
? t("thread.composer.configureModel", { defaultValue: "Configure model" })
|
||||
? t("thread.composer.openModelSetup", { defaultValue: "Open AI setup" })
|
||||
: t("thread.composer.send")
|
||||
}
|
||||
onClick={showStopButton ? handleStop : modelNeedsSetup ? onModelBadgeClick : undefined}
|
||||
onClick={showStopButton ? handleStop : modelNeedsSetup ? openModelSetup : undefined}
|
||||
className={cn(
|
||||
"thread-composer-action touch-target rounded-full transition-transform",
|
||||
showStopButton
|
||||
@@ -2656,6 +2671,13 @@ export function ThreadComposer({
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<ModelSetupDialog
|
||||
availability={modelSetupAvailability}
|
||||
open={modelSetupOpen}
|
||||
onOpenChange={setModelSetupOpen}
|
||||
onReturnFocus={() => textareaRef.current?.focus()}
|
||||
onSelect={continueModelSetup}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user