Unverified 提交 87beccf7 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

feat: gate home chat app selector behind experiment setting (#2964)

## Summary - Add `enableSelectAppFromHomeChatInput` experiment setting with toggle in Settings UI - Conditionally render the app selector ("No app selected") in HomeChatInput based on the setting - Update e2e tests to enable the flag via `setUp()` UI toggle instead of writing `user-settings.json` directly ## Test plan - [ ] Verify the app selector is hidden by default on the home chat input - [ ] Enable the toggle in Settings > Experiments and verify the app selector appears - [ ] Run e2e tests: `npx playwright test home_chat_existing_app` 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2964" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> --------- Co-authored-by: 's avatarClaude Opus 4.6 <noreply@anthropic.com> Co-authored-by: 's avatarclaude[bot] <41898282+claude[bot]@users.noreply.github.com>
上级 ca4b2da9
...@@ -89,11 +89,13 @@ export class PageObject { ...@@ -89,11 +89,13 @@ export class PageObject {
disableNativeGit = false, disableNativeGit = false,
enableAutoFixProblems = false, enableAutoFixProblems = false,
enableBasicAgent = false, enableBasicAgent = false,
enableSelectAppFromHomeChatInput = false,
}: { }: {
autoApprove?: boolean; autoApprove?: boolean;
disableNativeGit?: boolean; disableNativeGit?: boolean;
enableAutoFixProblems?: boolean; enableAutoFixProblems?: boolean;
enableBasicAgent?: boolean; enableBasicAgent?: boolean;
enableSelectAppFromHomeChatInput?: boolean;
} = {}) { } = {}) {
await this.baseSetup(); await this.baseSetup();
await this.navigation.goToSettingsTab(); await this.navigation.goToSettingsTab();
...@@ -106,6 +108,9 @@ export class PageObject { ...@@ -106,6 +108,9 @@ export class PageObject {
if (enableAutoFixProblems) { if (enableAutoFixProblems) {
await this.settings.toggleAutoFixProblems(); await this.settings.toggleAutoFixProblems();
} }
if (enableSelectAppFromHomeChatInput) {
await this.settings.toggleEnableSelectAppFromHomeChatInput();
}
await this.settings.setUpTestProvider(); await this.settings.setUpTestProvider();
await this.settings.setUpTestModel(); await this.settings.setUpTestModel();
await this.navigation.goToAppsTab(); await this.navigation.goToAppsTab();
......
...@@ -36,6 +36,14 @@ export class Settings { ...@@ -36,6 +36,14 @@ export class Settings {
.click(); .click();
} }
async toggleEnableSelectAppFromHomeChatInput() {
await this.page
.getByRole("switch", {
name: "Enable Select App from Home Chat Input",
})
.click();
}
async toggleAutoUpdate() { async toggleAutoUpdate() {
await this.page.getByRole("switch", { name: "Auto-update" }).click(); await this.page.getByRole("switch", { name: "Auto-update" }).click();
} }
......
...@@ -2,7 +2,10 @@ import { test } from "./helpers/test_helper"; ...@@ -2,7 +2,10 @@ import { test } from "./helpers/test_helper";
import { expect } from "@playwright/test"; import { expect } from "@playwright/test";
test("home chat - start new chat in existing app", async ({ po }) => { test("home chat - start new chat in existing app", async ({ po }) => {
await po.setUp({ autoApprove: true }); await po.setUp({
autoApprove: true,
enableSelectAppFromHomeChatInput: true,
});
// Create an app first // Create an app first
await po.sendPrompt("create a todo application"); await po.sendPrompt("create a todo application");
...@@ -48,7 +51,10 @@ test("home chat - start new chat in existing app", async ({ po }) => { ...@@ -48,7 +51,10 @@ test("home chat - start new chat in existing app", async ({ po }) => {
}); });
test("home chat - clear selected app", async ({ po }) => { test("home chat - clear selected app", async ({ po }) => {
await po.setUp({ autoApprove: true }); await po.setUp({
autoApprove: true,
enableSelectAppFromHomeChatInput: true,
});
// Create an app first // Create an app first
await po.sendPrompt("create a todo application"); await po.sendPrompt("create a todo application");
......
...@@ -36,7 +36,7 @@ import { AppSearchDialog } from "../AppSearchDialog"; ...@@ -36,7 +36,7 @@ import { AppSearchDialog } from "../AppSearchDialog";
import { useVoiceToText } from "@/hooks/useVoiceToText"; import { useVoiceToText } from "@/hooks/useVoiceToText";
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo"; import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
import { ipc } from "@/ipc/types"; import { ipc } from "@/ipc/types";
import { useCallback } from "react"; import { useCallback, useEffect } from "react";
import { showError } from "@/lib/toast"; import { showError } from "@/lib/toast";
export function HomeChatInput({ export function HomeChatInput({
...@@ -71,6 +71,13 @@ export function HomeChatInput({ ...@@ -71,6 +71,13 @@ export function HomeChatInput({
const [appSearchOpen, setAppSearchOpen] = useState(false); const [appSearchOpen, setAppSearchOpen] = useState(false);
const { apps } = useLoadApps(); const { apps } = useLoadApps();
// Clear selected app when the experiment flag is disabled
useEffect(() => {
if (!settings?.enableSelectAppFromHomeChatInput) {
setSelectedApp(null);
}
}, [settings?.enableSelectAppFromHomeChatInput, setSelectedApp]);
const typingText = useTypingPlaceholder([ const typingText = useTypingPlaceholder([
"an ecommerce store...", "an ecommerce store...",
"an information page...", "an information page...",
...@@ -277,46 +284,48 @@ export function HomeChatInput({ ...@@ -277,46 +284,48 @@ export function HomeChatInput({
<div className="px-2 flex items-center justify-between pb-0.5 pt-0.5"> <div className="px-2 flex items-center justify-between pb-0.5 pt-0.5">
<div className="flex items-center"> <div className="flex items-center">
<ChatInputControls showContextFilesPicker={false} /> <ChatInputControls showContextFilesPicker={false} />
<Tooltip> {settings?.enableSelectAppFromHomeChatInput && (
<TooltipTrigger <Tooltip>
render={ <TooltipTrigger
<button render={
onClick={() => setAppSearchOpen(true)} <button
className={cn( onClick={() => setAppSearchOpen(true)}
"cursor-pointer px-2 py-1 ml-1.5 text-xs font-medium rounded-lg transition-colors flex items-center gap-1", className={cn(
selectedApp "cursor-pointer px-2 py-1 ml-1.5 text-xs font-medium rounded-lg transition-colors flex items-center gap-1",
? "bg-primary/10 text-primary hover:bg-primary/15" selectedApp
: "text-foreground/80 hover:text-foreground hover:bg-muted/60", ? "bg-primary/10 text-primary hover:bg-primary/15"
)} : "text-foreground/80 hover:text-foreground hover:bg-muted/60",
data-testid="home-app-selector" )}
/> data-testid="home-app-selector"
} />
> }
<FolderOpenIcon size={14} /> >
<span className="truncate max-w-[150px]"> <FolderOpenIcon size={14} />
{selectedApp ? selectedApp.name : "No app selected"} <span className="truncate max-w-[150px]">
</span> {selectedApp ? selectedApp.name : "No app selected"}
{selectedApp && ( </span>
<button {selectedApp && (
type="button" <button
onClick={(e) => { type="button"
e.stopPropagation(); onClick={(e) => {
setSelectedApp(null); e.stopPropagation();
}} setSelectedApp(null);
className="hover:bg-primary/20 rounded-sm p-0.5 transition-colors" }}
aria-label="Deselect app" className="hover:bg-primary/20 rounded-sm p-0.5 transition-colors"
data-testid="home-app-selector-clear" aria-label="Deselect app"
> data-testid="home-app-selector-clear"
<XIcon size={12} /> >
</button> <XIcon size={12} />
)} </button>
</TooltipTrigger> )}
<TooltipContent> </TooltipTrigger>
{selectedApp <TooltipContent>
? "Change selected app" {selectedApp
: "Select an existing app"} ? "Change selected app"
</TooltipContent> : "Select an existing app"}
</Tooltip> </TooltipContent>
</Tooltip>
)}
</div> </div>
<AuxiliaryActionsMenu <AuxiliaryActionsMenu
......
...@@ -352,6 +352,7 @@ const BaseUserSettingsFields = { ...@@ -352,6 +352,7 @@ const BaseUserSettingsFields = {
hideLocalAgentNewChatToast: z.boolean().optional(), hideLocalAgentNewChatToast: z.boolean().optional(),
enableContextCompaction: z.boolean().optional(), enableContextCompaction: z.boolean().optional(),
skipNotificationBanner: z.boolean().optional(), skipNotificationBanner: z.boolean().optional(),
enableSelectAppFromHomeChatInput: z.boolean().optional(),
}; };
/** /**
......
...@@ -34,6 +34,8 @@ export const SETTING_IDS = { ...@@ -34,6 +34,8 @@ export const SETTING_IDS = {
neon: "setting-neon", neon: "setting-neon",
nativeGit: "setting-native-git", nativeGit: "setting-native-git",
enableMcpServersForBuildMode: "setting-enable-mcp-servers-for-build-mode", enableMcpServersForBuildMode: "setting-enable-mcp-servers-for-build-mode",
enableSelectAppFromHomeChatInput:
"setting-enable-select-app-from-home-chat-input",
reset: "setting-reset", reset: "setting-reset",
} as const; } as const;
...@@ -331,6 +333,15 @@ export const SETTINGS_SEARCH_INDEX: SearchableSettingItem[] = [ ...@@ -331,6 +333,15 @@ export const SETTINGS_SEARCH_INDEX: SearchableSettingItem[] = [
sectionId: SECTION_IDS.experiments, sectionId: SECTION_IDS.experiments,
sectionLabel: "Experiments", sectionLabel: "Experiments",
}, },
{
id: SETTING_IDS.enableSelectAppFromHomeChatInput,
label: "Enable Select App from Home Chat Input",
description:
"Show an app selector in the home chat input to start a chat referencing an existing app",
keywords: ["app", "select", "home", "chat", "experiment", "input"],
sectionId: SECTION_IDS.experiments,
sectionLabel: "Experiments",
},
// Danger Zone // Danger Zone
{ {
......
...@@ -219,6 +219,30 @@ export default function SettingsPage() { ...@@ -219,6 +219,30 @@ export default function SettingsPage() {
servers are always enabled in Agent mode. servers are always enabled in Agent mode.
</div> </div>
</div> </div>
<div
id={SETTING_IDS.enableSelectAppFromHomeChatInput}
className="space-y-1 mt-4"
>
<div className="flex items-center space-x-2">
<Switch
id="enable-select-app-from-home-chat-input"
aria-label="Enable Select App from Home Chat Input"
checked={!!settings?.enableSelectAppFromHomeChatInput}
onCheckedChange={(checked) => {
updateSettings({
enableSelectAppFromHomeChatInput: checked,
});
}}
/>
<Label htmlFor="enable-select-app-from-home-chat-input">
Enable Select App from Home Chat Input
</Label>
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
Show an app selector in the home chat input to start a chat
referencing an existing app.
</div>
</div>
</div> </div>
</div> </div>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论