diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx
index efe053473a8..88630271e33 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx
@@ -46,8 +46,10 @@ export function DashboardSidebarWorkspaceItem({
handleDeleted,
handleOpenInFinder,
handleRemoveFromSidebar,
+ handleToggleUnread,
isActive,
isDeleteDialogOpen,
+ isUnread,
isRenaming,
moveWorkspaceToSection,
renameValue,
@@ -110,6 +112,7 @@ export function DashboardSidebarWorkspaceItem({
setIsDeleteDialogOpen(true)}
+ onToggleUnread={handleToggleUnread}
>
{content}
@@ -176,6 +180,7 @@ export function DashboardSidebarWorkspaceItem({
setIsDeleteDialogOpen(true)}
+ onToggleUnread={handleToggleUnread}
>
{expandedContent}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceContextMenu/DashboardSidebarWorkspaceContextMenu.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceContextMenu/DashboardSidebarWorkspaceContextMenu.tsx
index c4b799efa0c..e1b4dcad459 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceContextMenu/DashboardSidebarWorkspaceContextMenu.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceContextMenu/DashboardSidebarWorkspaceContextMenu.tsx
@@ -20,6 +20,8 @@ import {
LuArrowRightLeft,
LuArrowUp,
LuCopy,
+ LuEye,
+ LuEyeOff,
LuFolderOpen,
LuFolderPlus,
LuGitBranch,
@@ -34,6 +36,7 @@ interface DashboardSidebarWorkspaceContextMenuProps {
projectId: string;
isInSection?: boolean;
isLocalWorkspace: boolean;
+ isUnread: boolean;
onHoverCardOpen?: () => void;
onCreateSection: () => void;
onMoveToSection: (sectionId: string | null) => void;
@@ -43,6 +46,7 @@ interface DashboardSidebarWorkspaceContextMenuProps {
onRemoveFromSidebar: () => void;
onRename: () => void;
onDelete: () => void;
+ onToggleUnread: () => void;
children: React.ReactNode;
}
@@ -50,6 +54,7 @@ export function DashboardSidebarWorkspaceContextMenu({
projectId,
isInSection,
isLocalWorkspace,
+ isUnread,
onHoverCardOpen,
hoverCardContent,
onCreateSection,
@@ -60,6 +65,7 @@ export function DashboardSidebarWorkspaceContextMenu({
onRemoveFromSidebar,
onRename,
onDelete,
+ onToggleUnread,
children,
}: DashboardSidebarWorkspaceContextMenuProps) {
const collections = useCollections();
@@ -105,6 +111,20 @@ export function DashboardSidebarWorkspaceContextMenu({
Copy Branch Name
+
+ {isUnread ? (
+ <>
+
+ Mark as Read
+ >
+ ) : (
+ <>
+
+ Mark as Unread
+ >
+ )}
+
+
New group from workspace
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts
index 459287e130a..fb1d61cb578 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts
@@ -9,7 +9,10 @@ import { useNavigateAwayFromWorkspace } from "renderer/routes/_authenticated/_da
import { useDashboardSidebarState } from "renderer/routes/_authenticated/hooks/useDashboardSidebarState";
import { useOptimisticCollectionActions } from "renderer/routes/_authenticated/hooks/useOptimisticCollectionActions";
import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider";
-import { useV2NotificationStore } from "renderer/stores/v2-notifications";
+import {
+ useV2NotificationStore,
+ useV2WorkspaceIsUnread,
+} from "renderer/stores/v2-notifications";
interface UseDashboardSidebarWorkspaceItemActionsOptions {
workspaceId: string;
@@ -34,6 +37,8 @@ export function useDashboardSidebarWorkspaceItemActions({
const clearWorkspaceAttention = useV2NotificationStore(
(s) => s.clearWorkspaceAttention,
);
+ const setManualUnread = useV2NotificationStore((s) => s.setManualUnread);
+ const isUnread = useV2WorkspaceIsUnread(workspaceId);
const { createSection, moveWorkspaceToSection, removeWorkspaceFromSidebar } =
useDashboardSidebarState();
@@ -128,6 +133,14 @@ export function useDashboardSidebarWorkspaceItemActions({
}
};
+ const handleToggleUnread = () => {
+ if (isUnread) {
+ clearWorkspaceAttention(workspaceId);
+ } else {
+ setManualUnread(workspaceId);
+ }
+ };
+
const handleCopyBranchName = async () => {
if (!branch) {
toast.error("Branch name is not available");
@@ -152,9 +165,11 @@ export function useDashboardSidebarWorkspaceItemActions({
handleDeleted,
handleOpenInFinder,
handleRemoveFromSidebar,
+ handleToggleUnread,
isActive,
isDeleteDialogOpen,
isRenaming,
+ isUnread,
moveWorkspaceToSection,
renameValue,
setIsDeleteDialogOpen,
diff --git a/apps/desktop/src/renderer/stores/v2-notifications/index.ts b/apps/desktop/src/renderer/stores/v2-notifications/index.ts
index bc268226195..5917869b71c 100644
--- a/apps/desktop/src/renderer/stores/v2-notifications/index.ts
+++ b/apps/desktop/src/renderer/stores/v2-notifications/index.ts
@@ -1,5 +1,6 @@
export {
getV2ChatNotificationSource,
+ getV2ManualNotificationSource,
getV2NotificationSourceKey,
getV2NotificationSourcesForPane,
getV2NotificationSourcesForTab,
@@ -9,6 +10,7 @@ export {
selectV2SourcesNotificationStatus,
selectV2TabNotificationStatus,
selectV2TerminalNotificationStatus,
+ selectV2WorkspaceIsUnread,
selectV2WorkspaceNotificationStatus,
useV2ChatNotificationStatus,
useV2NotificationStore,
@@ -16,6 +18,7 @@ export {
useV2SourcesNotificationStatus,
useV2TabNotificationStatus,
useV2TerminalNotificationStatus,
+ useV2WorkspaceIsUnread,
useV2WorkspaceNotificationStatus,
type V2NotificationPaneLike,
type V2NotificationSource,
diff --git a/apps/desktop/src/renderer/stores/v2-notifications/store.ts b/apps/desktop/src/renderer/stores/v2-notifications/store.ts
index 288e3e80d26..26617b97e97 100644
--- a/apps/desktop/src/renderer/stores/v2-notifications/store.ts
+++ b/apps/desktop/src/renderer/stores/v2-notifications/store.ts
@@ -10,7 +10,8 @@ export type V2NotificationTabLike = Pick, "panes">;
export type V2NotificationSource =
| { type: "terminal"; id: string }
- | { type: "chat"; id: string };
+ | { type: "chat"; id: string }
+ | { type: "manual"; id: string };
export type V2NotificationSourceType = V2NotificationSource["type"];
export type V2NotificationSourceKey = `${V2NotificationSourceType}:${string}`;
@@ -46,6 +47,7 @@ export interface V2NotificationState {
status: ActivePaneStatus,
occurredAt?: number,
) => void;
+ setManualUnread: (workspaceId: string) => void;
clearSourceStatus: (
source: V2NotificationSourceInput,
workspaceId?: string,
@@ -99,6 +101,15 @@ export const useV2NotificationStore = create()((set) => ({
occurredAt,
);
},
+ setManualUnread: (workspaceId) => {
+ useV2NotificationStore
+ .getState()
+ .setSourceStatus(
+ getV2ManualNotificationSource(workspaceId),
+ workspaceId,
+ "review",
+ );
+ },
clearSourceStatus: (source, workspaceId) => {
const sourceKey = getV2NotificationSourceKey(source);
set((state) => {
@@ -194,6 +205,12 @@ export function getV2ChatNotificationSource(
return { type: "chat", id: chatId };
}
+export function getV2ManualNotificationSource(
+ workspaceId: string,
+): V2NotificationSource {
+ return { type: "manual", id: workspaceId };
+}
+
export function getV2NotificationSourcesForPane(
pane: V2NotificationPaneLike | null | undefined,
): V2NotificationSource[] {
@@ -285,6 +302,21 @@ export function useV2WorkspaceNotificationStatus(workspaceId: string) {
);
}
+export function selectV2WorkspaceIsUnread(workspaceId: string) {
+ return (state: V2NotificationState) => {
+ for (const entry of Object.values(state.sources)) {
+ if (entry.workspaceId === workspaceId && entry.status === "review") {
+ return true;
+ }
+ }
+ return false;
+ };
+}
+
+export function useV2WorkspaceIsUnread(workspaceId: string) {
+ return useV2NotificationStore(selectV2WorkspaceIsUnread(workspaceId));
+}
+
export function useV2TabNotificationStatus(
workspaceId: string,
tab: V2NotificationTabLike | null | undefined,