diff --git a/apps/web/src/components/ChatView.logic.test.ts b/apps/web/src/components/ChatView.logic.test.ts index 80c842d91f..0ee6cb71e8 100644 --- a/apps/web/src/components/ChatView.logic.test.ts +++ b/apps/web/src/components/ChatView.logic.test.ts @@ -3,6 +3,8 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { useStore } from "../store"; import { + buildDefaultComposerPlaceholder, + buildDisconnectedComposerPlaceholder, buildExpiredTerminalContextToastCopy, createLocalDispatchSnapshot, deriveComposerSendState, @@ -75,6 +77,34 @@ describe("buildExpiredTerminalContextToastCopy", () => { }); }); +describe("buildDefaultComposerPlaceholder", () => { + it("mentions skills for Codex threads", () => { + expect(buildDefaultComposerPlaceholder("codex")).toBe( + "Ask anything, @tag files/folders, type $ to mention skills, or use / to show available commands", + ); + }); + + it("keeps non-Codex placeholders focused on supported composer commands", () => { + expect(buildDefaultComposerPlaceholder("claudeAgent")).toBe( + "Ask anything, @tag files/folders, or use / to show available commands", + ); + }); +}); + +describe("buildDisconnectedComposerPlaceholder", () => { + it("mentions skills for disconnected Codex threads", () => { + expect(buildDisconnectedComposerPlaceholder("codex")).toBe( + "Ask for follow-up changes, type $ to mention skills, or attach images", + ); + }); + + it("keeps disconnected non-Codex placeholders unchanged", () => { + expect(buildDisconnectedComposerPlaceholder("claudeAgent")).toBe( + "Ask for follow-up changes or attach images", + ); + }); +}); + const makeThread = (input?: { id?: ThreadId; latestTurn?: { diff --git a/apps/web/src/components/ChatView.logic.ts b/apps/web/src/components/ChatView.logic.ts index 1821c65ed9..6ba7097180 100644 --- a/apps/web/src/components/ChatView.logic.ts +++ b/apps/web/src/components/ChatView.logic.ts @@ -1,4 +1,10 @@ -import { ProjectId, type ModelSelection, type ThreadId, type TurnId } from "@t3tools/contracts"; +import { + ProjectId, + type ModelSelection, + type ProviderKind, + type ThreadId, + type TurnId, +} from "@t3tools/contracts"; import { type ChatMessage, type SessionPhase, type Thread, type ThreadSession } from "../types"; import { randomUUID } from "~/lib/utils"; import { type ComposerImageAttachment, type DraftThreadState } from "../composerDraftStore"; @@ -160,6 +166,20 @@ export function buildExpiredTerminalContextToastCopy( }; } +export function buildDefaultComposerPlaceholder(provider: ProviderKind): string { + if (provider === "codex") { + return "Ask anything, @tag files/folders, type $ to mention skills, or use / to show available commands"; + } + return "Ask anything, @tag files/folders, or use / to show available commands"; +} + +export function buildDisconnectedComposerPlaceholder(provider: ProviderKind): string { + if (provider === "codex") { + return "Ask for follow-up changes, type $ to mention skills, or attach images"; + } + return "Ask for follow-up changes or attach images"; +} + export function threadHasStarted(thread: Thread | null | undefined): boolean { return Boolean( thread && (thread.latestTurn !== null || thread.messages.length > 0 || thread.session !== null), diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 7562f845e2..53c62f3df9 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -171,6 +171,8 @@ import { import { ProviderStatusBanner } from "./chat/ProviderStatusBanner"; import { ThreadErrorBanner } from "./chat/ThreadErrorBanner"; import { + buildDefaultComposerPlaceholder, + buildDisconnectedComposerPlaceholder, buildExpiredTerminalContextToastCopy, buildLocalDraftThread, buildTemporaryWorktreeBranchName, @@ -3951,8 +3953,8 @@ export default function ChatView({ threadId }: ChatViewProps) { : showPlanFollowUpPrompt && activeProposedPlan ? "Add feedback to refine the plan, or leave this blank to implement it" : phase === "disconnected" - ? "Ask for follow-up changes or attach images" - : "Ask anything, @tag files/folders, or use / to show available commands" + ? buildDisconnectedComposerPlaceholder(selectedProvider) + : buildDefaultComposerPlaceholder(selectedProvider) } disabled={isConnecting || isComposerApprovalState} />