diff --git a/desktop/src/features/projects/ui/ProjectsOverviewPanel.tsx b/desktop/src/features/projects/ui/ProjectsOverviewPanel.tsx
index 9f950b87a84..81d1e954a86 100644
--- a/desktop/src/features/projects/ui/ProjectsOverviewPanel.tsx
+++ b/desktop/src/features/projects/ui/ProjectsOverviewPanel.tsx
@@ -139,10 +139,10 @@ export function ProjectsActivityIntro() {
className="text-xl font-semibold tracking-tight text-foreground"
data-testid="projects-page-header"
>
- Welcome to Activity
+ Projects Activity
- Keep up with commits, reviews, and tasks across your projects.
+ Keeping up with the community has never been easier—or mattered more.
);
diff --git a/desktop/src/features/projects/ui/ProjectsSelectionCountMenu.tsx b/desktop/src/features/projects/ui/ProjectsSelectionCountMenu.tsx
index 821419896d2..b03d5e243a3 100644
--- a/desktop/src/features/projects/ui/ProjectsSelectionCountMenu.tsx
+++ b/desktop/src/features/projects/ui/ProjectsSelectionCountMenu.tsx
@@ -1,14 +1,7 @@
import { Bot, GitPullRequest, Link2, X } from "lucide-react";
import * as React from "react";
-import { useAppNavigation } from "@/app/navigation/useAppNavigation";
import {
- loadDraftEntry,
- saveDraftEntry,
-} from "@/features/messages/lib/useDrafts";
-import {
- mergeSelectionDiscussDraft,
- projectSelectionDiscussContent,
projectSelectionShareLinks,
type ProjectSelectionAction,
type ProjectSelectionItem,
@@ -18,6 +11,7 @@ import { useProjectSelection } from "@/features/projects/lib/useProjectSelection
import { copyTextToClipboard } from "@/shared/lib/clipboard";
import { Button } from "@/shared/ui/button";
import { ProjectSelectionDiscussAction } from "./ProjectSelectionDiscussAction";
+import { useProjectDiscussInChannel } from "./useProjectDiscussInChannel";
function selectionActionIcon(id: ProjectSelectionAction["id"]) {
if (id === "chat-agent") return Bot;
@@ -37,33 +31,15 @@ export function ProjectsSelectionCountMenu({
presentation: ProjectSelectionPresentation;
selectionItems: ProjectSelectionItem[];
}) {
- const { goChannel } = useAppNavigation();
const selection = useProjectSelection();
+ const openChannelWithDraft = useProjectDiscussInChannel(selectionItems);
const discussInChannel = React.useCallback(
(channelId: string) => {
- const now = new Date().toISOString();
- const existing = loadDraftEntry(channelId);
- const content = mergeSelectionDiscussDraft(
- existing?.content,
- projectSelectionDiscussContent(selectionItems),
- );
- saveDraftEntry(channelId, {
- channelId,
- content,
- createdAt: existing?.createdAt ?? now,
- mentionRefs: existing?.mentionRefs ?? [],
- pendingImeta: existing?.pendingImeta ?? [],
- selectionEnd: content.length,
- selectionStart: content.length,
- spoileredAttachmentUrls: existing?.spoileredAttachmentUrls ?? [],
- status: "active",
- updatedAt: now,
- });
- void goChannel(channelId);
+ openChannelWithDraft(channelId);
selection?.clear();
},
- [goChannel, selection, selectionItems],
+ [openChannelWithDraft, selection],
);
const handleAction = React.useCallback(
diff --git a/desktop/src/features/projects/ui/ProjectsView.tsx b/desktop/src/features/projects/ui/ProjectsView.tsx
index 08f0e29b194..ac4a8dde234 100644
--- a/desktop/src/features/projects/ui/ProjectsView.tsx
+++ b/desktop/src/features/projects/ui/ProjectsView.tsx
@@ -19,11 +19,7 @@ import { useRepositoryActivitySummariesQuery } from "@/features/projects/reposit
import { useCreateProjectMutation } from "@/features/projects/useCreateProject";
import { selectProjectRepository } from "@/features/projects/projectModels";
import { useProjectsRepoSnapshotsQuery } from "@/features/projects/useProjectsRepoSnapshots";
-import {
- buildProjectSelectionAgentContext,
- buildProjectsOverviewAgentContext,
- type ProjectDetailAgentContext,
-} from "@/features/projects/lib/projectDetailAgentContext";
+import { buildProjectSelectionAgentContext } from "@/features/projects/lib/projectDetailAgentContext";
import type { ProjectSelectionItem } from "@/features/projects/lib/projectSelection";
import {
useMemberChannelIds,
@@ -115,6 +111,7 @@ import { normalizePubkey } from "@/shared/lib/pubkey";
import { useRelayOrigin } from "@/shared/lib/useRelayOrigin";
import { Button } from "@/shared/ui/button";
import { useOptionalSidebar } from "@/shared/ui/sidebar";
+import { useProjectsOverviewAgentContext } from "./useProjectsOverviewAgentContext";
const MANY_PROJECTS_THRESHOLD = 12;
const PROJECTS_CONTEXT_POD_MIN_VIEWPORT_PX = 1024;
@@ -139,8 +136,6 @@ export function ProjectsView() {
: storedFilter;
});
const [overviewPanelOpen, setOverviewPanelOpen] = React.useState(true);
- const [selectionAgentContext, setSelectionAgentContext] =
- React.useState
(null);
// Narrow layouts present the same context as a dismissible sheet instead of
// the docked rail; the sheet starts closed so resizing never pops a modal.
const [narrowContextOpen, setNarrowContextOpen] = React.useState(false);
@@ -244,22 +239,6 @@ export function ProjectsView() {
[],
);
- const handleFilterChange = React.useCallback(
- (nextFilter: ProjectsFilter) => {
- if (
- nextFilter === "projects" &&
- (repositoryScope === "buzz" || repositoryScope === "linked")
- ) {
- setRepositoryScope("all");
- writeStoredRepositoryScope("all");
- }
- setSelectionAgentContext(null);
- setFilter(nextFilter);
- writeStoredFilter(nextFilter);
- },
- [repositoryScope],
- );
-
const handleRepositoryScopeChange = React.useCallback(
(scope: ProjectsRepositoryScope) => {
setRepositoryScope(scope);
@@ -487,6 +466,36 @@ export function ProjectsView() {
return right.issue.updatedAt - left.issue.updatedAt;
});
}, [currentPubkey, issueScope, projectsWorkItemsQuery.data, sort]);
+ const {
+ agentContext: selectionAgentContext,
+ overviewContext: overviewAgentContext,
+ setAgentContext: setSelectionAgentContext,
+ } = useProjectsOverviewAgentContext({
+ filter,
+ issues: projectsWorkItemsQuery.data?.issues.items,
+ projects,
+ pullRequests: projectsWorkItemsQuery.data?.pullRequests.items,
+ snapshots: repoSnapshotsQuery.data?.snapshots,
+ visibleIssues,
+ visibleProjects,
+ visiblePullRequests,
+ visibleRepositories,
+ });
+ const handleFilterChange = React.useCallback(
+ (nextFilter: ProjectsFilter) => {
+ if (
+ nextFilter === "projects" &&
+ (repositoryScope === "buzz" || repositoryScope === "linked")
+ ) {
+ setRepositoryScope("all");
+ writeStoredRepositoryScope("all");
+ }
+ setSelectionAgentContext(null);
+ setFilter(nextFilter);
+ writeStoredFilter(nextFilter);
+ },
+ [repositoryScope, setSelectionAgentContext],
+ );
// Route by the canonical `owner:dtag` project ID — a bare dtag is
// ambiguous across owners (forks can share the same dtag).
@@ -837,11 +846,7 @@ export function ProjectsView() {
active={selectionAgentContext !== null}
onToggle={() =>
setSelectionAgentContext((context) =>
- context
- ? null
- : buildProjectsOverviewAgentContext(
- projectsSectionTitle(filter),
- ),
+ context ? null : overviewAgentContext,
)
}
sectionTitle={projectsSectionTitle(filter)}
@@ -935,7 +940,6 @@ export function ProjectsView() {
onClose={() => setSelectionAgentContext(null)}
onResetWidth={overviewAgentPanelWidth.onResetWidth}
onResizeStart={overviewAgentPanelWidth.onResizeStart}
- sharedHeaderBackdrop
widthPx={overviewAgentPanelWidth.widthPx}
/>
) : null}
diff --git a/desktop/src/features/projects/ui/RepositoryCards.tsx b/desktop/src/features/projects/ui/RepositoryCards.tsx
index 4116763bbee..75d87843126 100644
--- a/desktop/src/features/projects/ui/RepositoryCards.tsx
+++ b/desktop/src/features/projects/ui/RepositoryCards.tsx
@@ -20,7 +20,6 @@ import {
} from "@/features/projects/lib/projectSelection";
import {
formatExactTimestamp,
- listRowDescription,
relativeTime,
} from "@/features/projects/lib/projectsViewHelpers";
import { cn } from "@/shared/lib/cn";
@@ -302,12 +301,13 @@ export function RepositoryListRow(props: RepositoryItemProps) {
});
return (
+
+
+ }
dateSeconds={updatedAt}
dateTestId="repositories-row-date"
- description={listRowDescription(repository.description, repository.name)}
- descriptionTestId="repositories-row-description"
icon={
}
onClick={() => onOpen(project, repository)}
people={repositoryPeople(repository, summary)}
@@ -321,6 +321,8 @@ export function RepositoryListRow(props: RepositoryItemProps) {
testId={`repository-row-${repository.dtag}`}
title={repository.name}
titleAttr={repository.name}
+ titleSecondary={repository.description || undefined}
+ titleSecondaryTestId="repositories-row-description"
trailing={