From 0770674327269ac0f0a076c740bc50053e8873ad Mon Sep 17 00:00:00 2001 From: KaviiSuri Date: Sat, 14 Mar 2026 14:32:04 +0530 Subject: [PATCH] feat(web): add docsUrl for providers to install from --- .../components/chat/ProviderHealthBanner.tsx | 19 +++++++++++++++- apps/web/src/lib/utils.ts | 8 +++++++ apps/web/src/session-logic.ts | 22 ++++++++++++++++--- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/chat/ProviderHealthBanner.tsx b/apps/web/src/components/chat/ProviderHealthBanner.tsx index 12c7f6054..38a6a5e4a 100644 --- a/apps/web/src/components/chat/ProviderHealthBanner.tsx +++ b/apps/web/src/components/chat/ProviderHealthBanner.tsx @@ -2,6 +2,8 @@ import { type ServerProviderStatus } from "@t3tools/contracts"; import { memo } from "react"; import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; import { CircleAlertIcon } from "lucide-react"; +import { PROVIDER_OPTIONS } from "~/session-logic"; +import { ensureSentenceEnds } from "~/lib/utils"; export const ProviderHealthBanner = memo(function ProviderHealthBanner({ status, @@ -17,6 +19,8 @@ export const ProviderHealthBanner = memo(function ProviderHealthBanner({ ? `${status.provider} provider is unavailable.` : `${status.provider} provider has limited availability.`; + const opts = PROVIDER_OPTIONS.find((opt) => opt.value === status.provider); + return (
@@ -25,7 +29,20 @@ export const ProviderHealthBanner = memo(function ProviderHealthBanner({ {status.provider === "codex" ? "Codex provider status" : `${status.provider} status`} - {status.message ?? defaultMessage} + {ensureSentenceEnds(status.message ?? defaultMessage)} + {opts?.docsUrl ? ( + <> + {" "} + + Installation Guide + + + ) : null}
diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts index 08a5de91e..13c781059 100644 --- a/apps/web/src/lib/utils.ts +++ b/apps/web/src/lib/utils.ts @@ -30,3 +30,11 @@ export const newProjectId = (): ProjectId => ProjectId.makeUnsafe(randomUUID()); export const newThreadId = (): ThreadId => ThreadId.makeUnsafe(randomUUID()); export const newMessageId = (): MessageId => MessageId.makeUnsafe(randomUUID()); + +export const ensureSentenceEnds = (value: string): string => { + const trimmed = value.trim(); + if ([".", "!", "?"].includes(trimmed.at(-1) ?? "")) { + return trimmed; + } + return `${trimmed}.`; +}; diff --git a/apps/web/src/session-logic.ts b/apps/web/src/session-logic.ts index e389f10e2..31555cecf 100644 --- a/apps/web/src/session-logic.ts +++ b/apps/web/src/session-logic.ts @@ -24,10 +24,26 @@ export const PROVIDER_OPTIONS: Array<{ value: ProviderPickerKind; label: string; available: boolean; + docsUrl: string | null; }> = [ - { value: "codex", label: "Codex", available: true }, - { value: "claudeCode", label: "Claude Code", available: false }, - { value: "cursor", label: "Cursor", available: false }, + { + value: "codex", + label: "Codex", + available: true, + docsUrl: "https://developers.openai.com/codex/cli/#cli-setup", + }, + { + value: "claudeCode", + label: "Claude Code", + available: false, + docsUrl: "https://code.claude.com/docs/en/quickstart#step-1-install-claude-code", + }, + { + value: "cursor", + label: "Cursor", + available: false, + docsUrl: "https://cursor.com/docs/cli/installation#installation", + }, ]; export interface WorkLogEntry {