authorgravatar for 73941998+UtkarshUsername@users.noreply.github.comUtkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com> 2026-07-22 19:50:20+05:30
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-22 16:20:20+02:00
log020179c19a0073d130c30083c7623cf1196688e7
tree0cae368c9cd3013a94dee1eeb5e95b8f8c53f72d
parent571a8b44bdd4f0f0b0464ef24240021689616e7d
signature Signed by PGP key B5690EEEBB952194

fix(web): use message-square icon for settled icon-less project threads in sidebar v2 (#4279)


2 files changed, 27 insertions(+), 5 deletions(-)

apps/web/src/components/ProjectFavicon.tsx+25-5
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1import type { EnvironmentId } from "@t3tools/contracts";1import type { EnvironmentId } from "@t3tools/contracts";
2import { isProjectFaviconFallbackUrl } from "@t3tools/shared/projectFavicon";2import { isProjectFaviconFallbackUrl } from "@t3tools/shared/projectFavicon";
3import { FolderIcon } from "lucide-react";3import { FolderIcon } from "lucide-react";
4import type { ComponentType } from "react";
4import { useState } from "react";5import { useState } from "react";
5import { useAssetUrl } from "../assets/assetUrls";6import { useAssetUrl } from "../assets/assetUrls";
67
...@@ -10,29 +11,46 @@ export function ProjectFavicon(input: {...@@ -10,29 +11,46 @@ export function ProjectFavicon(input: {
10 environmentId: EnvironmentId;11 environmentId: EnvironmentId;
11 cwd: string;12 cwd: string;
12 className?: string | undefined;13 className?: string | undefined;
14 fallbackIcon?: ComponentType<{ className?: string }>;
13}) {15}) {
14 const src = useAssetUrl(input.environmentId, {16 const src = useAssetUrl(input.environmentId, {
15 _tag: "project-favicon",17 _tag: "project-favicon",
16 cwd: input.cwd,18 cwd: input.cwd,
17 });19 });
20 const FallbackIcon = input.fallbackIcon ?? FolderIcon;
1821
19 if (!src || isProjectFaviconFallbackUrl(src)) {22 if (!src || isProjectFaviconFallbackUrl(src)) {
20 return <ProjectFaviconFallback className={input.className} />;23 return <ProjectFaviconFallback className={input.className} icon={FallbackIcon} />;
21 }24 }
2225
23 return <ProjectFaviconImage key={src} src={src} className={input.className} />;26 return (
27 <ProjectFaviconImage
28 key={src}
29 src={src}
30 className={input.className}
31 fallbackIcon={FallbackIcon}
32 />
33 );
24}34}
2535
26function ProjectFaviconFallback({ className }: { readonly className?: string | undefined }) {36function ProjectFaviconFallback({
27 return <FolderIcon className={`size-3.5 shrink-0 text-muted-foreground/50 ${className ?? ""}`} />;37 className,
38 icon: Icon,
39}: {
40 readonly className?: string | undefined;
41 readonly icon: ComponentType<{ className?: string }>;
42}) {
43 return <Icon className={`size-3.5 shrink-0 text-muted-foreground/50 ${className ?? ""}`} />;
28}44}
2945
30function ProjectFaviconImage({46function ProjectFaviconImage({
31 src,47 src,
32 className,48 className,
49 fallbackIcon: FallbackIcon,
33}: {50}: {
34 readonly src: string;51 readonly src: string;
35 readonly className?: string | undefined;52 readonly className?: string | undefined;
53 readonly fallbackIcon: ComponentType<{ className?: string }>;
36}) {54}) {
37 const [status, setStatus] = useState<"loading" | "loaded" | "error">(() =>55 const [status, setStatus] = useState<"loading" | "loaded" | "error">(() =>
38 loadedProjectFaviconSrcs.has(src) ? "loaded" : "loading",56 loadedProjectFaviconSrcs.has(src) ? "loaded" : "loading",
...@@ -40,7 +58,9 @@ function ProjectFaviconImage({...@@ -40,7 +58,9 @@ function ProjectFaviconImage({
4058
41 return (59 return (
42 <>60 <>
43 {status !== "loaded" ? <ProjectFaviconFallback className={className} /> : null}61 {status !== "loaded" ? (
62 <ProjectFaviconFallback className={className} icon={FallbackIcon} />
63 ) : null}
44 <img64 <img
45 src={src}65 src={src}
46 alt=""66 alt=""
apps/web/src/components/SidebarV2.tsx+2
...@@ -14,6 +14,7 @@ import {...@@ -14,6 +14,7 @@ import {
14 CircleDashedIcon,14 CircleDashedIcon,
15 CloudIcon,15 CloudIcon,
16 FolderPlusIcon,16 FolderPlusIcon,
17 MessageSquareIcon,
17 PlusIcon,18 PlusIcon,
18 SearchIcon,19 SearchIcon,
19 SquarePenIcon,20 SquarePenIcon,
...@@ -426,6 +427,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {...@@ -426,6 +427,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: {
426 environmentId={thread.environmentId}427 environmentId={thread.environmentId}
427 cwd={props.projectCwd ?? ""}428 cwd={props.projectCwd ?? ""}
428 className="size-3.5"429 className="size-3.5"
430 fallbackIcon={MessageSquareIcon}
429 />431 />
430 </span>432 </span>
431 {title}433 {title}