diff --git a/packages/views/issues/components/board-card.tsx b/packages/views/issues/components/board-card.tsx
index d4b206e8d2..7a940a5a90 100644
--- a/packages/views/issues/components/board-card.tsx
+++ b/packages/views/issues/components/board-card.tsx
@@ -14,6 +14,7 @@ import { useUpdateIssue } from "@multica/core/issues/mutations";
import { useWorkspacePaths } from "@multica/core/paths";
import { useWorkspaceId } from "@multica/core/hooks";
import { projectListOptions } from "@multica/core/projects/queries";
+import { ProjectIcon } from "../../projects/components/project-icon";
import { PriorityIcon } from "./priority-icon";
import { PriorityPicker, AssigneePicker, DueDatePicker } from "./pickers";
import { PRIORITY_CONFIG } from "@multica/core/issues/config";
@@ -101,7 +102,7 @@ export const BoardCardContent = memo(function BoardCardContent({
)}
{showProject && (
- {project!.icon || "📁"}
+
{project!.title}
)}
diff --git a/packages/views/issues/components/issues-header.tsx b/packages/views/issues/components/issues-header.tsx
index ce945cdaa1..878b6e9482 100644
--- a/packages/views/issues/components/issues-header.tsx
+++ b/packages/views/issues/components/issues-header.tsx
@@ -49,6 +49,7 @@ import { useQuery } from "@tanstack/react-query";
import { useWorkspaceId } from "@multica/core/hooks";
import { memberListOptions, agentListOptions } from "@multica/core/workspace/queries";
import { projectListOptions } from "@multica/core/projects/queries";
+import { ProjectIcon } from "../../projects/components/project-icon";
import { ActorAvatar } from "../../common/actor-avatar";
import {
SORT_OPTIONS,
@@ -353,9 +354,7 @@ function ProjectSubContent({
className={FILTER_ITEM_CLASS}
>
-
- {p.icon || }
-
+
{p.title}
{count > 0 && (
diff --git a/packages/views/issues/components/list-row.tsx b/packages/views/issues/components/list-row.tsx
index 8d6f20b27f..54d63b3164 100644
--- a/packages/views/issues/components/list-row.tsx
+++ b/packages/views/issues/components/list-row.tsx
@@ -10,6 +10,7 @@ import { useWorkspacePaths } from "@multica/core/paths";
import { useWorkspaceId } from "@multica/core/hooks";
import { useViewStore } from "@multica/core/issues/stores/view-store-context";
import { projectListOptions } from "@multica/core/projects/queries";
+import { ProjectIcon } from "../../projects/components/project-icon";
import { PriorityIcon } from "./priority-icon";
import { ProgressRing } from "./progress-ring";
import { IssueActionsContextMenu } from "../actions";
@@ -90,7 +91,7 @@ export const ListRow = memo(function ListRow({
{showProject && (
- {project!.icon || "📁"}
+
{project!.title}
)}
diff --git a/packages/views/layout/app-sidebar.tsx b/packages/views/layout/app-sidebar.tsx
index 748edcaa6c..28220e8e47 100644
--- a/packages/views/layout/app-sidebar.tsx
+++ b/packages/views/layout/app-sidebar.tsx
@@ -78,6 +78,15 @@ import { issueDetailOptions } from "@multica/core/issues/queries";
import { projectDetailOptions } from "@multica/core/projects/queries";
import type { PinnedItem } from "@multica/core/types";
import { useLogout } from "../auth";
+import { ProjectIcon } from "../projects/components/project-icon";
+
+// Top-level nav items stay active when the user is on a child route
+// (e.g. "Projects" stays lit on /:slug/projects/:id). Pinned items keep
+// strict equality elsewhere — a pinned project shouldn't highlight on
+// sub-pages of itself.
+function isNavActive(pathname: string, href: string): boolean {
+ return pathname === href || pathname.startsWith(href + "/");
+}
// Stable empty arrays for query defaults. Using an inline `= []` default on
// `useQuery` creates a new array reference on every render when `data` is
@@ -269,11 +278,7 @@ function PinRow({
if (projectQuery.isPending) return ;
if (projectQuery.isError || !projectQuery.data) return null;
const project = projectQuery.data;
- const iconNode = (
-
- {project.icon || "📁"}
-
- );
+ const iconNode = ;
return (
{personalNav.map((item) => {
const href = p[item.key]();
- const isActive = pathname === href;
+ const isActive = isNavActive(pathname, href);
return (
{workspaceNav.map((item) => {
const href = p[item.key]();
- const isActive = pathname === href;
+ const isActive = isNavActive(pathname, href);
return (
{configureNav.map((item) => {
const href = p[item.key]();
- const isActive = pathname === href;
+ const isActive = isNavActive(pathname, href);
return (
- 📁
+
{fallbackLabel ?? "Project"}
@@ -55,7 +53,7 @@ export function ProjectChip({
return (
- {project.icon || "📁"}
+
{project.title}
);
diff --git a/packages/views/projects/components/project-icon.tsx b/packages/views/projects/components/project-icon.tsx
new file mode 100644
index 0000000000..a07e9ec9c2
--- /dev/null
+++ b/packages/views/projects/components/project-icon.tsx
@@ -0,0 +1,31 @@
+import type { Project } from "@multica/core/types";
+import { cn } from "@multica/ui/lib/utils";
+
+export type ProjectIconSize = "sm" | "md" | "lg";
+
+export interface ProjectIconProps {
+ project?: Pick | null;
+ size?: ProjectIconSize;
+ className?: string;
+}
+
+const SIZE_CLASS: Record = {
+ sm: "size-3.5 text-xs leading-none",
+ md: "size-4 text-sm leading-none",
+ lg: "size-6 text-2xl leading-none",
+};
+
+export function ProjectIcon({ project, size = "sm", className }: ProjectIconProps) {
+ return (
+
+ {project?.icon || "📁"}
+
+ );
+}
diff --git a/packages/views/projects/components/project-picker.tsx b/packages/views/projects/components/project-picker.tsx
index 1dff9c1761..6147c12d0e 100644
--- a/packages/views/projects/components/project-picker.tsx
+++ b/packages/views/projects/components/project-picker.tsx
@@ -12,6 +12,7 @@ import {
DropdownMenuTrigger,
DropdownMenuSeparator,
} from "@multica/ui/components/ui/dropdown-menu";
+import { ProjectIcon } from "./project-icon";
export function ProjectPicker({
projectId,
@@ -34,13 +35,17 @@ export function ProjectPicker({
className={triggerRender ? undefined : "flex items-center gap-1.5 cursor-pointer rounded px-1 -mx-1 hover:bg-accent/30 transition-colors overflow-hidden"}
render={triggerRender}
>
-
+ {current ? (
+
+ ) : (
+
+ )}
{current ? current.title : "No project"}
{projects.map((p) => (
onUpdate({ project_id: p.id })}>
- {p.icon || "📁"}
+
{p.title}
{p.id === projectId && }
diff --git a/packages/views/projects/components/projects-page.tsx b/packages/views/projects/components/projects-page.tsx
index 8f2647c445..3e5a3b9e32 100644
--- a/packages/views/projects/components/projects-page.tsx
+++ b/packages/views/projects/components/projects-page.tsx
@@ -36,6 +36,7 @@ import { Tooltip, TooltipTrigger, TooltipContent } from "@multica/ui/components/
import type { Project, ProjectStatus, ProjectPriority, UpdateProjectRequest } from "@multica/core/types";
import { PageHeader } from "../../layout/page-header";
import { PriorityIcon } from "../../issues/components/priority-icon";
+import { ProjectIcon } from "./project-icon";
function formatRelativeDate(date: string): string {
const diff = Date.now() - new Date(date).getTime();
@@ -77,7 +78,7 @@ function ProjectRow({ project }: { project: Project }) {
href={wsPaths.projectDetail(project.id)}
className="flex min-w-0 flex-1 items-center gap-2"
>
- {project.icon || "📁"}
+
{project.title}
diff --git a/packages/views/search/search-command.tsx b/packages/views/search/search-command.tsx
index 97c6cc0d56..ba63b624ec 100644
--- a/packages/views/search/search-command.tsx
+++ b/packages/views/search/search-command.tsx
@@ -36,6 +36,7 @@ import type { WorkspacePaths } from "@multica/core/paths";
import { useModalStore } from "@multica/core/modals";
import { workspaceListOptions } from "@multica/core/workspace/queries";
import { StatusIcon } from "../issues/components";
+import { ProjectIcon } from "../projects/components/project-icon";
import { STATUS_CONFIG } from "@multica/core/issues/config";
import { PROJECT_STATUS_CONFIG } from "@multica/core/projects/config";
import type { ProjectStatus } from "@multica/core/types";
@@ -573,9 +574,7 @@ export function SearchCommand() {
className="flex cursor-default select-none flex-col gap-1 rounded-lg px-3 py-2.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-accent"
>
-
- {project.icon || }
-
+