feat(webui): add guided setup flows
* feat(channels): add guided setup flows * test(channels): preserve setup config values * fix(channels): reflect saved setup state * refactor(channels): simplify setup state metadata * fix(channels): harden setup lifecycle * refactor(channels): centralize setup contracts * fix(channels): route setup actions through webui shim * fix(channels): adapt settings for compact screens * fix(models): preserve default preset display * feat(models): add curated Codex catalog * fix(webui): stop attached gateway on interrupt * fix(webui): simplify apps catalog * docs(webui): clarify apps and runtime features * feat(settings): add guided capability setup * fix(webui): harden setup and managed services * test: keep managed runtime checks portable * test: scope POSIX runtime coverage * fix(webui): simplify file settings * feat(files): bundle document reading * fix(webui): harden setup request boundaries * fix(webui): prevent channel setup status squeeze * fix(settings): group provider compatibility aliases * refactor(settings): remove redundant setup surfaces * fix(webui): harden guided setup lifecycle * fix(webui): preserve channel setup compatibility
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
type ActivityEvidence,
|
||||
} from "@/lib/activity-timeline";
|
||||
import { useFileEditDisplayMode } from "@/hooks/useFileEditDisplayMode";
|
||||
import { useLogoFallback } from "@/hooks/useLogoFallback";
|
||||
import { hasRenderableFileDiff } from "@/lib/file-diff";
|
||||
import type { FileEditDisplayMode } from "@/lib/local-preferences";
|
||||
import { faviconUrls, logoFallbackUrls } from "@/lib/provider-brand";
|
||||
@@ -943,10 +944,12 @@ function TraceIconMark({
|
||||
fallbackIcon: LucideIcon;
|
||||
active: boolean;
|
||||
}) {
|
||||
const [faviconIndex, setFaviconIndex] = useState(0);
|
||||
const faviconUrl = trace.host ? faviconUrls(trace.host)[faviconIndex] : undefined;
|
||||
|
||||
useEffect(() => setFaviconIndex(0), [trace.host]);
|
||||
const faviconCandidates = useMemo(() => (trace.host ? faviconUrls(trace.host) : []), [trace.host]);
|
||||
const {
|
||||
logoUrl: faviconUrl,
|
||||
onLogoError: onFaviconError,
|
||||
onLogoLoad: onFaviconLoad,
|
||||
} = useLogoFallback(faviconCandidates);
|
||||
|
||||
if (trace.url && trace.host && faviconUrl) {
|
||||
return (
|
||||
@@ -962,7 +965,10 @@ function TraceIconMark({
|
||||
src={faviconUrl}
|
||||
alt=""
|
||||
className="h-3.5 w-3.5 object-contain"
|
||||
onError={() => setFaviconIndex((index) => index + 1)}
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
onLoad={onFaviconLoad}
|
||||
onError={onFaviconError}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
@@ -1661,19 +1667,16 @@ function CliRunGroup({
|
||||
|
||||
function CliRunRow({ run, active, app }: { run: CliRunSummary; active: boolean; app?: CliAppInfo }) {
|
||||
const { t } = useTranslation();
|
||||
const [logoIndex, setLogoIndex] = useState(0);
|
||||
const args = formatCliArgs(run);
|
||||
const failed = run.status === "error";
|
||||
const rowActive = active && run.status === "running";
|
||||
const color = failed ? "#DC2626" : app?.brand_color || "#0891B2";
|
||||
const logoUrls = useMemo(() => logoFallbackUrls(app?.logo_url), [app?.logo_url]);
|
||||
const logoUrl = logoUrls[logoIndex];
|
||||
const { logoUrl, onLogoError, onLogoLoad } = useLogoFallback(logoUrls);
|
||||
const label = t(cliRunLabelKey(run, active), {
|
||||
defaultValue: cliRunLabelDefault(run, active),
|
||||
});
|
||||
|
||||
useEffect(() => setLogoIndex(0), [app?.logo_url]);
|
||||
|
||||
return (
|
||||
<ActivityStep
|
||||
as="li"
|
||||
@@ -1699,8 +1702,11 @@ function CliRunRow({ run, active, app }: { run: CliRunSummary; active: boolean;
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt=""
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
className="h-[78%] w-[78%] object-contain"
|
||||
onError={() => setLogoIndex((index) => index + 1)}
|
||||
onLoad={onLogoLoad}
|
||||
onError={onLogoError}
|
||||
/>
|
||||
) : app ? (
|
||||
cliAppInitials(app).slice(0, 2)
|
||||
@@ -1772,19 +1778,16 @@ function McpRunGroup({
|
||||
|
||||
function McpRunRow({ run, active, preset }: { run: McpRunSummary; active: boolean; preset?: McpPresetInfo }) {
|
||||
const { t } = useTranslation();
|
||||
const [logoIndex, setLogoIndex] = useState(0);
|
||||
const failed = run.status === "error";
|
||||
const rowActive = active && run.status === "running";
|
||||
const color = failed ? "#DC2626" : preset?.brand_color || "#6D5DF6";
|
||||
const logoUrls = useMemo(() => logoFallbackUrls(preset?.logo_url), [preset?.logo_url]);
|
||||
const logoUrl = logoUrls[logoIndex];
|
||||
const { logoUrl, onLogoError, onLogoLoad } = useLogoFallback(logoUrls);
|
||||
const displayName = preset?.display_name || run.displayName;
|
||||
const label = t(mcpRunLabelKey(run, active), {
|
||||
defaultValue: mcpRunLabelDefault(run, active),
|
||||
});
|
||||
|
||||
useEffect(() => setLogoIndex(0), [preset?.logo_url]);
|
||||
|
||||
return (
|
||||
<ActivityStep
|
||||
as="li"
|
||||
@@ -1810,8 +1813,11 @@ function McpRunRow({ run, active, preset }: { run: McpRunSummary; active: boolea
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt=""
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
className="h-[78%] w-[78%] object-contain"
|
||||
onError={() => setLogoIndex((index) => index + 1)}
|
||||
onLoad={onLogoLoad}
|
||||
onError={onLogoError}
|
||||
/>
|
||||
) : preset ? (
|
||||
mcpPresetInitials(preset).slice(0, 2)
|
||||
|
||||
@@ -65,6 +65,7 @@ import {
|
||||
type RestoredReadyImage,
|
||||
} from "@/hooks/useAttachedImages";
|
||||
import { useClipboardAndDrop } from "@/hooks/useClipboardAndDrop";
|
||||
import { useLogoFallback } from "@/hooks/useLogoFallback";
|
||||
import type { SendImage, SendOptions } from "@/hooks/useNanobotStream";
|
||||
import { useVoiceRecorder, type VoiceRecorderErrorKey } from "@/hooks/useVoiceRecorder";
|
||||
import type {
|
||||
@@ -2260,15 +2261,12 @@ function ComposerModelBadge({
|
||||
}) {
|
||||
const inferredProvider = needsSetup ? null : provider || inferProviderFromModelName(label);
|
||||
const brand = providerBrand(inferredProvider);
|
||||
const [logoIndex, setLogoIndex] = useState(0);
|
||||
const logoUrl = brand?.logoUrls[logoIndex];
|
||||
const { logoUrl, onLogoError, onLogoLoad } = useLogoFallback(brand?.logoUrls);
|
||||
const showLogo = !!logoUrl;
|
||||
const title = providerLabel ? `${label} · ${providerLabel}` : label;
|
||||
const interactive = Boolean(onClick);
|
||||
const Container = interactive ? "button" : "span";
|
||||
|
||||
useEffect(() => setLogoIndex(0), [inferredProvider]);
|
||||
|
||||
return (
|
||||
<Container
|
||||
title={title}
|
||||
@@ -2305,8 +2303,11 @@ function ComposerModelBadge({
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt=""
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
className={cn("object-contain", isHero ? "h-3 w-3" : "h-3.5 w-3.5")}
|
||||
onError={() => setLogoIndex((index) => index + 1)}
|
||||
onLoad={onLogoLoad}
|
||||
onError={onLogoError}
|
||||
/>
|
||||
) : brand ? (
|
||||
<span
|
||||
@@ -2502,15 +2503,12 @@ function MentionCandidateLogo({
|
||||
candidate: MentionCandidate;
|
||||
selected: boolean;
|
||||
}) {
|
||||
const [logoIndex, setLogoIndex] = useState(0);
|
||||
const color = (candidate.kind === "cli"
|
||||
? candidate.app.brand_color
|
||||
: candidate.preset.brand_color) || "hsl(var(--primary))";
|
||||
const rawLogoUrl = candidate.kind === "cli" ? candidate.app.logo_url : candidate.preset.logo_url;
|
||||
const logoUrls = useMemo(() => logoFallbackUrls(rawLogoUrl), [rawLogoUrl]);
|
||||
const logoUrl = logoUrls[logoIndex];
|
||||
|
||||
useEffect(() => setLogoIndex(0), [rawLogoUrl]);
|
||||
const { logoUrl, onLogoError, onLogoLoad } = useLogoFallback(logoUrls);
|
||||
|
||||
if (logoUrl) {
|
||||
return (
|
||||
@@ -2523,8 +2521,11 @@ function MentionCandidateLogo({
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt=""
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
className="h-5 w-5 object-contain"
|
||||
onError={() => setLogoIndex((index) => index + 1)}
|
||||
onLoad={onLogoLoad}
|
||||
onError={onLogoError}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user