Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions desktop/src/app/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export function AppShell() {
React.useState(false);
const [searchFocusRequest, setSearchFocusRequest] = React.useState(0);
const [topbarSearchHidden, setTopbarSearchHidden] = React.useState(false);
const [topbarSearchLoading, setTopbarSearchLoading] = React.useState(false);
const [browseDialogType, setBrowseDialogType] =
React.useState<BrowseDialogType>(null);
const [isNewDmOpen, setIsNewDmOpen] = React.useState(false);
Expand Down Expand Up @@ -743,6 +744,7 @@ export function AppShell() {
isFollowingThread,
isNotifiedForThread,
setTopbarSearchHidden,
setTopbarSearchLoading,
threadActivityItems,
}}
>
Expand All @@ -762,6 +764,7 @@ export function AppShell() {
}}
onOpenResult={handleOpenSearchResult}
searchHidden={topbarSearchHidden}
searchLoading={topbarSearchLoading}
searchFocusRequest={searchFocusRequest}
/>
) : null}
Expand Down
2 changes: 2 additions & 0 deletions desktop/src/app/AppShellContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type AppShellContextValue = {
isFollowingThread: (rootId: string) => boolean;
isNotifiedForThread: (rootId: string) => boolean;
setTopbarSearchHidden: (hidden: boolean) => void;
setTopbarSearchLoading: (loading: boolean) => void;
threadActivityItems: ThreadActivityItem[];
};

Expand All @@ -38,6 +39,7 @@ const AppShellContext = React.createContext<AppShellContextValue>({
isFollowingThread: () => false,
isNotifiedForThread: () => false,
setTopbarSearchHidden: () => {},
setTopbarSearchLoading: () => {},
threadActivityItems: [],
});

Expand Down
71 changes: 62 additions & 9 deletions desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import type {
} from "@/shared/api/types";
import { Badge } from "@/shared/ui/badge";
import { Button } from "@/shared/ui/button";
import { Card } from "@/shared/ui/card";
import {
DropdownMenu,
DropdownMenuContent,
Expand Down Expand Up @@ -329,7 +328,7 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
size="sm"
variant="ghost"
>
<Trash2 className="mr-1.5 h-3.5 w-3.5" />
<Trash2 className="mr-1.5 h-4 w-4" />
Remove stopped
</Button>
</div>
Expand Down Expand Up @@ -471,18 +470,72 @@ function SectionHeader({

function LoadingSkeleton() {
return (
<Card className="overflow-hidden">
{["a", "b", "c"].map((k) => (
<div className="space-y-3">
{["a", "b", "c"].map((k, index) => (
<div
className="flex items-center gap-4 border-b border-border/60 px-4 py-3 last:border-b-0"
className="overflow-hidden rounded-xl border border-border/70 bg-card/40"
key={k}
>
<Skeleton className="h-8 w-8 rounded-lg" />
<Skeleton className="h-4 w-28" />
<Skeleton className="h-5 w-16 rounded-full" />
<div className="flex items-center gap-2 px-3 py-2">
<div className="flex min-w-0 flex-1 items-center gap-2 py-1">
<Skeleton className="h-4 w-4 shrink-0 rounded-sm" />
<Skeleton className="h-8 w-8 shrink-0 rounded-lg" />
<div className="flex min-w-0 items-center gap-2">
<Skeleton className={index === 2 ? "h-4 w-36" : "h-4 w-32"} />
<Skeleton className="h-5 w-14 rounded-full" />
</div>
<Skeleton className="ml-1 h-3 w-20 shrink-0" />
</div>
{index === 1 ? (
<Skeleton className="h-5 w-16 rounded-full" />
) : null}
<Skeleton className="h-8 w-8 shrink-0 rounded-lg" />
</div>
<div className="divide-y divide-border/50 border-t border-border/50">
<div className="flex items-start gap-3 px-4 py-3">
<div className="min-w-0 flex-1">
<div className="grid gap-3 lg:grid-cols-[minmax(0,1.8fr)_minmax(120px,0.8fr)_minmax(0,1.1fr)] lg:gap-4">
<div className="min-w-0">
<div className="flex items-start gap-3">
<Skeleton className="mt-0.5 h-4 w-4 shrink-0 rounded-sm" />
<Skeleton className="mt-1 h-2 w-2 shrink-0 rounded-full" />
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
<Skeleton className="h-4 w-36" />
<Skeleton className="h-5 w-16 rounded-full" />
</div>
<div className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1">
<Skeleton className="h-3 w-20" />
<Skeleton className="h-3 w-24" />
</div>
{index === 0 ? (
<div className="mt-1.5 flex flex-wrap items-center gap-1.5">
<Skeleton className="h-5 w-20 rounded-full" />
<Skeleton className="h-5 w-24 rounded-full" />
</div>
) : null}
</div>
</div>
</div>
<div className="space-y-1.5">
<Skeleton className="h-5 w-20 rounded-full" />
<Skeleton className="h-3 w-24" />
</div>
<div className="space-y-1.5">
<Skeleton className="h-4 w-28" />
<Skeleton className="h-3 w-20" />
</div>
</div>
</div>
<div className="flex shrink-0 items-start gap-2 lg:pt-0.5">
<Skeleton className="h-7 w-24 rounded-md" />
<Skeleton className="h-7 w-7 rounded-md" />
</div>
</div>
</div>
</div>
))}
</Card>
</div>
);
}

Expand Down
83 changes: 56 additions & 27 deletions desktop/src/features/channels/ui/ChannelPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { Bot, Hash, LogIn, Plus, Sparkles, UserPlus } from "lucide-react";
import { useMediaUpload } from "@/features/messages/lib/useMediaUpload";
import { MessageComposer } from "@/features/messages/ui/MessageComposer";
import { DropZoneOverlay } from "@/features/messages/ui/ComposerAttachments";
import { MessageThreadPanel } from "@/features/messages/ui/MessageThreadPanel";
import {
MessageThreadPanel,
MessageThreadPanelSkeleton,
} from "@/features/messages/ui/MessageThreadPanel";
import { MessageTimeline } from "@/features/messages/ui/MessageTimeline";
import type { ImetaMedia } from "@/features/messages/lib/imetaMediaMarkdown";
import { useComposerHeightPadding } from "@/features/messages/ui/useComposerHeightPadding";
Expand Down Expand Up @@ -714,7 +717,7 @@ export const ChannelPane = React.memo(function ChannelPane({
size="sm"
variant="default"
>
<LogIn className="mr-1.5 h-3.5 w-3.5" />
<LogIn className="mr-1.5 h-4 w-4" />
{isJoining ? "Joining..." : "Join to participate"}
</Button>
</div>
Expand Down Expand Up @@ -854,27 +857,15 @@ export const ChannelPane = React.memo(function ChannelPane({
panel
);
})()
: activeChannel && selectedAgent
: openThreadHeadId && activeChannel
? (() => {
const panel = (
<AgentSessionThreadPanel
agent={selectedAgent}
canInterruptTurn={selectedAgent.canInterruptTurn}
channel={activeChannel}
isWorking={botTypingEntries.some(
(entry) =>
entry.pubkey.toLowerCase() ===
selectedAgent.pubkey.toLowerCase(),
)}
<MessageThreadPanelSkeleton
isSinglePanelView={
useSplitAuxiliaryPane ? false : isSinglePanelView
}
layout={useSplitAuxiliaryPane ? "split" : "standalone"}
profiles={profiles}
onBackToProfile={() =>
onOpenProfilePanel(selectedAgent.pubkey)
}
onClose={onCloseAgentSession}
onClose={onCloseThread}
widthPx={threadPanelWidthPx}
/>
);
Expand All @@ -883,7 +874,7 @@ export const ChannelPane = React.memo(function ChannelPane({
canResetWidth={canResetThreadPanelWidth}
onResetWidth={onResetThreadPanelWidth}
onResizeStart={onThreadPanelResizeStart}
testId="agent-session-thread-panel"
testId="message-thread-panel"
widthPx={threadPanelWidthPx}
>
{panel}
Expand All @@ -892,19 +883,27 @@ export const ChannelPane = React.memo(function ChannelPane({
panel
);
})()
: profilePanelPubkey
: activeChannel && selectedAgent
? (() => {
const panel = (
<UserProfilePanel
currentPubkey={currentPubkey}
<AgentSessionThreadPanel
agent={selectedAgent}
canInterruptTurn={selectedAgent.canInterruptTurn}
channel={activeChannel}
isWorking={botTypingEntries.some(
(entry) =>
entry.pubkey.toLowerCase() ===
selectedAgent.pubkey.toLowerCase(),
)}
isSinglePanelView={
useSplitAuxiliaryPane ? false : isSinglePanelView
}
layout={useSplitAuxiliaryPane ? "split" : "standalone"}
onClose={onCloseProfilePanel}
onOpenDm={onOpenDm}
pubkey={profilePanelPubkey}
splitPaneClamp
profiles={profiles}
onBackToProfile={() =>
onOpenProfilePanel(selectedAgent.pubkey)
}
onClose={onCloseAgentSession}
widthPx={threadPanelWidthPx}
/>
);
Expand All @@ -913,7 +912,7 @@ export const ChannelPane = React.memo(function ChannelPane({
canResetWidth={canResetThreadPanelWidth}
onResetWidth={onResetThreadPanelWidth}
onResizeStart={onThreadPanelResizeStart}
testId="user-profile-panel"
testId="agent-session-thread-panel"
widthPx={threadPanelWidthPx}
>
{panel}
Expand All @@ -922,7 +921,37 @@ export const ChannelPane = React.memo(function ChannelPane({
panel
);
})()
: null}
: profilePanelPubkey
? (() => {
const panel = (
<UserProfilePanel
currentPubkey={currentPubkey}
isSinglePanelView={
useSplitAuxiliaryPane ? false : isSinglePanelView
}
layout={useSplitAuxiliaryPane ? "split" : "standalone"}
onClose={onCloseProfilePanel}
onOpenDm={onOpenDm}
pubkey={profilePanelPubkey}
splitPaneClamp
widthPx={threadPanelWidthPx}
/>
);
return useSplitAuxiliaryPane ? (
<RightAuxiliaryPane
canResetWidth={canResetThreadPanelWidth}
onResetWidth={onResetThreadPanelWidth}
onResizeStart={onThreadPanelResizeStart}
testId="user-profile-panel"
widthPx={threadPanelWidthPx}
>
{panel}
</RightAuxiliaryPane>
) : (
panel
);
})()
: null}
</div>
);
});
14 changes: 10 additions & 4 deletions desktop/src/features/channels/ui/ChannelScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -429,11 +429,13 @@ export function ChannelScreen({
setThreadReplyTargetId,
setThreadScrollTargetId,
});
const hasTimelineData = messagesQuery.data !== undefined;
const isTimelineLoading =
activeChannel !== null &&
activeChannel.channelType !== "forum" &&
(messagesQuery.isPending ||
(messagesQuery.isFetching && resolvedMessages.length === 0));
!hasTimelineData &&
messagesQuery.isPending;
const shouldShowInitialChannelLoading = isTimelineLoading;
const resetComposerTargets = React.useCallback(
(_channelId: string | null) => {
setOpenThreadHeadId(null);
Expand Down Expand Up @@ -549,7 +551,9 @@ export function ChannelScreen({
ref={channelContentRef}
>
{activeChannel ? (
activeChannel.channelType === "forum" ? (
shouldShowInitialChannelLoading ? (
<ViewLoadingFallback includeHeader kind="channel" />
Comment thread
klopez4212 marked this conversation as resolved.
) : activeChannel.channelType === "forum" ? (
<>
{channelHeader}
<React.Suspense fallback={<ViewLoadingFallback kind="forum" />}>
Expand All @@ -564,7 +568,9 @@ export function ChannelScreen({
</React.Suspense>
</>
) : (
<React.Suspense fallback={<ViewLoadingFallback kind="channel" />}>
<React.Suspense
fallback={<ViewLoadingFallback includeHeader kind="channel" />}
>
<ChannelPane
activeChannel={activeChannel}
agentPubkeys={agentPubkeys}
Expand Down
Loading