# Files changed · PR \#23894 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23894/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[fix\(web\): honor the scratch project name and icon](https://git.cafe/pingdotgg/t3code/pull/23894.md)

Version: 6

Head commit: d99a50058fffc09b82df0f80779c30c60afd49bd

Base commit: 200dad3b39aa9c2562b4bae54106ab8acdcc4163

Comparison base: 200dad3b39aa9c2562b4bae54106ab8acdcc4163

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/web/src/components/CommandPalette.tsx

Status: modified; +13; −5

```diff
@@ -107,6 +107,7 @@
 import { useScratchProject } from "../hooks/useScratchProject";
 import { useNewProject } from "../hooks/useNewProject";
 import { isScratchProject } from "@t3tools/client-runtime/state/projects";
+import { ProjectFavicon } from "./ProjectFavicon";
 import {
   useEnvironmentProviders,
   useEnvironments,
@@ -161,7 +162,6 @@
   findHighlightedCommandPaletteItem,
   type CommandPaletteActionItem,
   type CommandPaletteOpenIntent,
-  type CommandPaletteProject,
   type CommandPaletteReturnContext,
   type CommandPaletteSubmenuItem,
   type CommandPaletteView,
@@ -853,7 +853,7 @@
   });
 
   const projectThreadItems = (() => {
+    const isScratch = (project: (typeof projects)[number]) =>
-    const isScratch = (project: CommandPaletteProject) =>
       isScratchProject(project, scratchWorkspaceRootFor(project.environmentId));
     const projectItems = enumerateCommandPaletteItems(
       buildNewThreadInProjectItems({
@@ -879,15 +879,23 @@
     );
     if (scratchTargetEnvironmentId === null) return projectItems;
 
+    const scratchProject = projects.find(
+      (project) => project.environmentId === scratchTargetEnvironmentId && isScratch(project),
+    );
     const noProjectIndex = pickerProjects[0] !== undefined && isScratch(pickerProjects[0]) ? 0 : 1;
+    const noProjectTitle = scratchProject?.title ?? "No project";
     return [
       ...projectItems.slice(0, noProjectIndex),
       {
         kind: "action" as const,
         value: "new-thread-in:no-project",
-        searchTerms: ["no project", "without project", "none"],
+        searchTerms: [noProjectTitle, "no project", "without project", "none"],
+        title: noProjectTitle,
+        icon: scratchProject ? (
+          <ProjectFavicon project={scratchProject} className={ITEM_ICON_CLASS} />
+        ) : (
-        title: "No project",
-        icon: <MessageSquareDashedIcon className={ITEM_ICON_CLASS} />,
+          <MessageSquareDashedIcon className={ITEM_ICON_CLASS} />
+        ),
         shortcutCommand: "chat.newWithoutProject" as const,
         run: () => startScratchThread(scratchTargetEnvironmentId),
       },
```

## apps/web/src/components/chat/ChatHeader.tsx

Status: modified; +1; −1

```diff
@@ -78,7 +78,7 @@
   onNewThreadInProject,
   onOpenProjectSettings,
 }: ChatHeaderProps) {
-  const activeProjectName = activeProject?.isScratch ? "No project" : activeProject?.title;
+  const activeProjectName = activeProject?.title;
   const activeProjectCwd = activeProject?.workspaceRoot ?? null;
   const interfaceFont = useClientSettings((settings) => settings.fontFamilySans);
   const breadcrumbContainerRef = useRef<HTMLDivElement>(null);
```

## apps/web/src/components/chat/DraftHeroHeadline.tsx

Status: modified; +1; −4

```diff
@@ -259,10 +259,7 @@
             />
           }
         >
-          <DraftProjectName
+          <DraftProjectName value={activeProjectDisplayName ?? "Choose a project"} singleLine />
-            value={isScratchDraft ? "No project" : (activeProjectDisplayName ?? "Choose a project")}
-            singleLine
-          />
         </TooltipTrigger>
         {activeProjectDisplayName && !isScratchDraft ? (
           <TooltipPopup side="top">{activeProjectDisplayName}</TooltipPopup>
```

## packages/client-runtime/src/state/projectGrouping.test.ts

Status: modified; +29; −0

```diff
@@ -120,6 +120,35 @@
 }
 
 describe("buildProjectGroups", () => {
+  it("keeps the preferred scratch home's saved identity when grouping environments", () => {
+    const remoteEnvironmentId = EnvironmentId.make("remote");
+    const local = makeProject("scratch-local", "/local/scratch", {
+      title: "Scratchpad",
+      isScratch: true,
+      repositoryIdentity: null,
+      projectIcon: { kind: "lucide", name: "info", color: "gray" },
+    });
+    const remote = makeProject("scratch-remote", "/remote/scratch", {
+      title: "Notes",
+      environmentId: remoteEnvironmentId,
+      isScratch: true,
+      repositoryIdentity: null,
+    });
+
+    for (const preferred of [local, remote]) {
+      const groups = buildProjectGroups({
+        projects: [local, remote],
+        settings: settings("repository"),
+        groupScratchProjects: true,
+        preferredEnvironmentId: preferred.environmentId,
+      });
+      expect(groups).toHaveLength(1);
+      expect(groups[0]?.label).toBe(preferred.title);
+      expect(groups[0]?.representative.projectIcon).toEqual(preferred.projectIcon);
+      expect(groups[0]?.memberProjectRefs).toHaveLength(2);
+    }
+  });
+
   it.each(["repository", "repository_path", "separate"] as const)(
     "keeps scratch project ownership separate by default in %s mode",
     (mode) => {
```

## packages/client-runtime/src/state/projectGrouping.ts

Status: modified; +1; −1

```diff
@@ -353,7 +353,7 @@
       key,
       label:
         input.groupScratchProjects && representative.isScratch
-          ? "No project"
+          ? representative.title
           : members.length > 1
             ? deriveProjectGroupLabel({
                 representative,
```
