diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx
index e2fd573fe8ce..6af2a007accf 100644
--- a/apps/web/src/components/ChatView.browser.tsx
+++ b/apps/web/src/components/ChatView.browser.tsx
@@ -814,7 +814,7 @@ describe("ChatView timeline estimator parity (full app)", () => {
try {
const initialModeButton = await waitForInteractionModeButton("Chat");
- expect(initialModeButton.title).toContain("enter plan mode");
+ expect(initialModeButton.getAttribute("title")).toBeNull();
window.dispatchEvent(
new KeyboardEvent("keydown", {
@@ -826,7 +826,7 @@ describe("ChatView timeline estimator parity (full app)", () => {
);
await waitForLayout();
- expect((await waitForInteractionModeButton("Chat")).title).toContain("enter plan mode");
+ expect(await waitForInteractionModeButton("Chat")).toBeTruthy();
const composerEditor = await waitForComposerEditor();
composerEditor.focus();
@@ -841,9 +841,8 @@ describe("ChatView timeline estimator parity (full app)", () => {
await vi.waitFor(
async () => {
- expect((await waitForInteractionModeButton("Plan")).title).toContain(
- "return to normal chat mode",
- );
+ const planButton = await waitForInteractionModeButton("Plan");
+ expect(planButton.getAttribute("title")).toBeNull();
},
{ timeout: 8_000, interval: 16 },
);
@@ -859,7 +858,8 @@ describe("ChatView timeline estimator parity (full app)", () => {
await vi.waitFor(
async () => {
- expect((await waitForInteractionModeButton("Chat")).title).toContain("enter plan mode");
+ const chatButton = await waitForInteractionModeButton("Chat");
+ expect(chatButton.getAttribute("title")).toBeNull();
},
{ timeout: 8_000, interval: 16 },
);
diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
index 51b300ba8ec2..c907ba6ac3cc 100644
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -31,6 +31,8 @@ import {
} from "@t3tools/shared/model";
import {
memo,
+ type ComponentProps,
+ type ReactNode,
useCallback,
useEffect,
useLayoutEffect,
@@ -251,6 +253,7 @@ function formatWorkingTimer(startIso: string, endIso: string): string | null {
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
}
+
const LAST_EDITOR_KEY = "t3code:last-editor";
const LAST_INVOKED_SCRIPT_BY_PROJECT_KEY = "t3code:last-invoked-script-by-project";
const MAX_VISIBLE_WORK_LOG_ENTRIES = 6;
@@ -3720,49 +3723,85 @@ export default function ChatView({ threadId }: ChatViewProps) {
{/* Interaction mode toggle */}
-
+
+
+
+
+ {interactionMode === "plan" ? "Plan" : "Chat"}
+
+
+ }
+ />
+
+
+
+ {interactionMode === "plan"
+ ? "Plan mode — click to return to normal chat mode"
+ : "Default mode — click to enter plan mode"}
+
+
+ ⇧
+ Tab
+
+
+
+
{/* Divider */}
{/* Runtime mode toggle */}
-
+ }
+ />
+
+ {runtimeMode === "full-access"
? "Full access — click to require approvals"
- : "Approval required — click for full access"
- }
- >
- {runtimeMode === "full-access" ? : }
-
- {runtimeMode === "full-access" ? "Full access" : "Supervised"}
-
-
+ : "Approval required — click for full access"}
+
+
{/* Plan sidebar toggle */}
{(activePlan || activeProposedPlan || planSidebarOpen) ? (
diff --git a/apps/web/src/components/ui/tooltip.tsx b/apps/web/src/components/ui/tooltip.tsx
index f41c4520075c..3bd799bf6916 100644
--- a/apps/web/src/components/ui/tooltip.tsx
+++ b/apps/web/src/components/ui/tooltip.tsx
@@ -9,7 +9,16 @@ const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root;
function TooltipTrigger(props: TooltipPrimitive.Trigger.Props) {
- return ;
+ const { delay = 120, closeDelay = 80, ...restProps } = props;
+
+ return (
+
+ );
}
function TooltipPopup({
@@ -38,7 +47,7 @@ function TooltipPopup({
>