Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2456,7 +2456,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
? parseStandaloneComposerSlashCommand(trimmed)
: null;
if (standaloneSlashCommand) {
await handleInteractionModeChange(standaloneSlashCommand);
handleInteractionModeChange(standaloneSlashCommand);
promptRef.current = "";
clearComposerDraftContent(activeThread.id);
setComposerHighlightedItemId(null);
Expand Down Expand Up @@ -2512,7 +2512,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
const outgoingMessageText = formatOutgoingPrompt({
provider: selectedProvider,
effort: selectedPromptEffort,
text: trimmed || IMAGE_ONLY_BOOTSTRAP_PROMPT,
text: messageTextForSend || IMAGE_ONLY_BOOTSTRAP_PROMPT,
});
const turnAttachmentsPromise = Promise.all(
composerImagesSnapshot.map(async (image) => ({
Expand Down Expand Up @@ -3670,8 +3670,6 @@ export default function ChatView({ threadId }: ChatViewProps) {
/>
</div>
) : null}

{/* Textarea area */}
<div
className={cn(
"relative px-3 pb-2 sm:px-4",
Expand Down Expand Up @@ -4068,7 +4066,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
disabled={isSendBusy || isConnecting}
onClick={() => void onImplementPlanInNewThread()}
>
Implement in new thread
Implement in a new thread
</MenuItem>
</MenuPopup>
</Menu>
Expand All @@ -4079,9 +4077,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
type="submit"
className="flex h-9 w-9 items-center justify-center rounded-full bg-primary/90 text-primary-foreground transition-all duration-150 hover:bg-primary hover:scale-105 disabled:opacity-30 disabled:hover:scale-100 sm:h-8 sm:w-8"
disabled={
isSendBusy ||
isConnecting ||
(!prompt.trim() && composerImages.length === 0)
isSendBusy || isConnecting || !composerSendState.hasSendableContent
}
aria-label={
isConnecting
Expand Down
40 changes: 21 additions & 19 deletions apps/web/src/components/chat/ClaudeTraitsPicker.browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,29 @@ async function mountPicker(props?: {
fastModeEnabled?: boolean;
}) {
const threadId = ThreadId.makeUnsafe("thread-claude-traits");
useComposerDraftStore.setState({
draftsByThreadId: {
[threadId]: {
prompt: props?.prompt ?? "",
images: [],
nonPersistedImageIds: [],
persistedAttachments: [],
terminalContexts: [],
provider: "claudeAgent",
model: props?.model ?? "claude-opus-4-6",
modelOptions: {
claudeAgent: {
...(props?.effort ? { effort: props.effort } : {}),
...(props?.thinkingEnabled === false ? { thinking: false } : {}),
...(props?.fastModeEnabled ? { fastMode: true } : {}),
},
},
runtimeMode: null,
interactionMode: null,
const draftsByThreadId = {} as ReturnType<
typeof useComposerDraftStore.getState
>["draftsByThreadId"];
draftsByThreadId[threadId] = {
prompt: props?.prompt ?? "",
images: [],
nonPersistedImageIds: [],
persistedAttachments: [],
terminalContexts: [],
provider: "claudeAgent",
model: props?.model ?? "claude-opus-4-6",
modelOptions: {
claudeAgent: {
...(props?.effort ? { effort: props.effort } : {}),
...(props?.thinkingEnabled === false ? { thinking: false } : {}),
...(props?.fastModeEnabled ? { fastMode: true } : {}),
},
},
runtimeMode: null,
interactionMode: null,
};
useComposerDraftStore.setState({
draftsByThreadId,
draftThreadsByThreadId: {},
projectDraftThreadIdByProjectId: {},
});
Expand Down
38 changes: 20 additions & 18 deletions apps/web/src/components/chat/CodexTraitsPicker.browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,28 @@ async function mountPicker(props: {
fastModeEnabled: boolean;
}) {
const threadId = ThreadId.makeUnsafe("thread-codex-traits");
useComposerDraftStore.setState({
draftsByThreadId: {
[threadId]: {
prompt: "",
images: [],
nonPersistedImageIds: [],
persistedAttachments: [],
terminalContexts: [],
provider: "codex",
model: null,
modelOptions: {
codex: {
...(props.reasoningEffort ? { reasoningEffort: props.reasoningEffort } : {}),
...(props.fastModeEnabled ? { fastMode: true } : {}),
},
},
runtimeMode: null,
interactionMode: null,
const draftsByThreadId = {} as ReturnType<
typeof useComposerDraftStore.getState
>["draftsByThreadId"];
draftsByThreadId[threadId] = {
prompt: "",
images: [],
nonPersistedImageIds: [],
persistedAttachments: [],
terminalContexts: [],
provider: "codex",
model: null,
modelOptions: {
codex: {
...(props.reasoningEffort ? { reasoningEffort: props.reasoningEffort } : {}),
...(props.fastModeEnabled ? { fastMode: true } : {}),
},
},
runtimeMode: null,
interactionMode: null,
};
useComposerDraftStore.setState({
draftsByThreadId,
draftThreadsByThreadId: {},
projectDraftThreadIdByProjectId: {
[ProjectId.makeUnsafe("project-codex-traits")]: threadId,
Expand Down
18 changes: 10 additions & 8 deletions apps/web/src/components/chat/CodexTraitsPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
type CodexModelOptions,
type CodexReasoningEffort,
type ProviderModelOptions,
type ThreadId,
import type {
CodexModelOptions,
CodexReasoningEffort,
ProviderModelOptions,
ThreadId,
} from "@t3tools/contracts";
import {
getDefaultReasoningEffort,
Expand Down Expand Up @@ -132,12 +132,14 @@ export const CodexTraitsPicker = memo(function CodexTraitsPicker(props: { thread
<Button
size="sm"
variant="ghost"
className="shrink-0 whitespace-nowrap px-2 text-muted-foreground/70 hover:text-foreground/80 sm:px-3"
className="min-w-0 max-w-40 shrink justify-start overflow-hidden whitespace-nowrap px-2 text-muted-foreground/70 hover:text-foreground/80 sm:max-w-48 sm:px-3 [&_svg]:mx-0"
/>
}
>
<span>{triggerLabel}</span>
<ChevronDownIcon aria-hidden="true" className="size-3 opacity-60" />
<span className="flex min-w-0 w-full items-center gap-2 overflow-hidden">
{triggerLabel}
<ChevronDownIcon aria-hidden="true" className="size-3 shrink-0 opacity-60" />
</span>
</MenuTrigger>
<MenuPopup align="start">
<CodexTraitsMenuContent threadId={props.threadId} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ThreadId } from "@t3tools/contracts";
import { type ProviderModelOptions, ThreadId } from "@t3tools/contracts";
import "../../index.css";

import { page } from "vitest/browser";
Expand All @@ -14,47 +14,27 @@ async function mountMenu(props?: {
model?: string;
prompt?: string;
provider?: "codex" | "claudeAgent";
codexEffort?: "low" | "medium" | "high" | "xhigh";
codexFastMode?: boolean;
claudeEffort?: "low" | "medium" | "high" | "max" | "ultrathink";
claudeThinking?: boolean;
claudeFastMode?: boolean;
modelOptions?: ProviderModelOptions | null;
}) {
const threadId = ThreadId.makeUnsafe("thread-compact-menu");
const provider = props?.provider ?? "claudeAgent";
const draftsByThreadId = {} as ReturnType<
typeof useComposerDraftStore.getState
>["draftsByThreadId"];
draftsByThreadId[threadId] = {
prompt: props?.prompt ?? "",
images: [],
nonPersistedImageIds: [],
persistedAttachments: [],
terminalContexts: [],
provider,
model: props?.model ?? "claude-opus-4-6",
modelOptions: props?.modelOptions ?? null,
runtimeMode: null,
interactionMode: null,
};
useComposerDraftStore.setState({
draftsByThreadId: {
[threadId]: {
prompt: props?.prompt ?? "",
images: [],
nonPersistedImageIds: [],
persistedAttachments: [],
terminalContexts: [],
provider,
model: props?.model ?? "claude-opus-4-6",
modelOptions: {
...(provider === "codex"
? {
codex: {
...(props?.codexEffort ? { reasoningEffort: props.codexEffort } : {}),
...(props?.codexFastMode ? { fastMode: true } : {}),
},
}
: {}),
...(provider === "claudeAgent"
? {
claudeAgent: {
...(props?.claudeEffort ? { effort: props.claudeEffort } : {}),
...(props?.claudeThinking === false ? { thinking: false } : {}),
...(props?.claudeFastMode ? { fastMode: true } : {}),
},
}
: {}),
},
runtimeMode: null,
interactionMode: null,
},
},
draftsByThreadId,
draftThreadsByThreadId: {},
projectDraftThreadIdByProjectId: {},
});
Expand Down Expand Up @@ -158,7 +138,11 @@ describe("CompactComposerControlsMenu", () => {
it("shows a Claude thinking on/off section for Haiku", async () => {
const mounted = await mountMenu({
model: "claude-haiku-4-5",
claudeThinking: true,
modelOptions: {
claudeAgent: {
thinking: true,
},
},
});

try {
Expand All @@ -179,7 +163,11 @@ describe("CompactComposerControlsMenu", () => {
const mounted = await mountMenu({
model: "claude-opus-4-6",
prompt: "Ultrathink:\nInvestigate this",
claudeEffort: "high",
modelOptions: {
claudeAgent: {
effort: "high",
},
},
});

try {
Expand Down
13 changes: 8 additions & 5 deletions apps/web/src/components/chat/ProviderModelPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,18 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
size="sm"
variant="ghost"
className={cn(
"min-w-0 shrink-0 whitespace-nowrap px-2 text-muted-foreground/70 hover:text-foreground/80",
props.compact ? "max-w-42" : "sm:px-3",
"min-w-0 justify-start overflow-hidden whitespace-nowrap px-2 text-muted-foreground/70 hover:text-foreground/80 [&_svg]:mx-0",
props.compact ? "max-w-42 shrink-0" : "max-w-48 shrink sm:max-w-56 sm:px-3",
)}
disabled={props.disabled}
/>
}
>
<span
className={cn("flex min-w-0 items-center gap-2", props.compact ? "max-w-36" : undefined)}
className={cn(
"flex min-w-0 w-full items-center gap-2 overflow-hidden",
props.compact ? "max-w-36" : undefined,
)}
>
<ProviderIcon
aria-hidden="true"
Expand All @@ -156,13 +159,13 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
: undefined,
)}
/>
<span className="truncate">{selectedModelLabel}</span>
<span className="min-w-0 flex-1 truncate">{selectedModelLabel}</span>
{props.bedrockActive && activeProvider === "claudeAgent" && (
<span className="shrink-0 rounded bg-amber-500/15 px-1 py-px text-[10px] font-medium text-amber-600 dark:text-amber-400">
Bedrock
</span>
)}
<ChevronDownIcon aria-hidden="true" className="size-3 opacity-60" />
<ChevronDownIcon aria-hidden="true" className="size-3 shrink-0 opacity-60" />
</span>
</MenuTrigger>
<MenuPopup align="start">
Expand Down
Loading
Loading