+
{isAgent ? (
) : null}
-
-
-
+
+
+
{displayName}
{isAgent ? (
@@ -67,7 +71,7 @@ export function NoteCard({
{profile.nip05Handle}
) : null}
-
+
{formatRelativeTime(note.createdAt)}
{!isOwnNote ? (
@@ -97,8 +101,8 @@ export function NoteCard({
) : null}
-
-
+ ("foryou");
const [agentFilter, setAgentFilter] = React.useState(null);
+ const [searchQuery, setSearchQuery] = React.useState("");
// ── Contact list & follow state ────────────────────────────────────────
const contactListQuery = useContactListQuery(currentPubkey);
@@ -274,11 +276,6 @@ export function PulseView({ currentPubkey }: PulseViewProps) {
? agentTimelineQuery
: myNotesQuery;
const isLoading = activeQuery.isLoading;
- const isRefetching = activeQuery.isRefetching;
-
- function handleRefresh() {
- void activeQuery.refetch();
- }
function handleFollow(pubkey: string) {
followMutation.mutate(pubkey);
@@ -290,6 +287,7 @@ export function PulseView({ currentPubkey }: PulseViewProps) {
// ── Render ─────────────────────────────────────────────────────────────
const emptyMessages: Record = {
+ search: "Search Pulse notes by author or text.",
foryou:
"No notes yet. Follow people and agents to build your personalized feed.",
people: "No notes yet. Follow people to see their updates here.",
@@ -346,25 +344,34 @@ export function PulseView({ currentPubkey }: PulseViewProps) {
{/* Tab bar */}
@@ -397,47 +393,77 @@ export function PulseView({ currentPubkey }: PulseViewProps) {
className="mt-0 min-h-0 flex-1 overflow-y-auto"
>
+
{!isOwnNote ? (
diff --git a/desktop/src/features/pulse/ui/PulseView.tsx b/desktop/src/features/pulse/ui/PulseView.tsx
index 313430657c..7ed34c4df0 100644
--- a/desktop/src/features/pulse/ui/PulseView.tsx
+++ b/desktop/src/features/pulse/ui/PulseView.tsx
@@ -1,4 +1,4 @@
-import { Check, Filter, RefreshCw } from "lucide-react";
+import { Check, Filter, Search } from "lucide-react";
import * as React from "react";
import { useRelayAgentsQuery } from "@/features/agents/hooks";
@@ -28,14 +28,15 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/shared/ui/dropdown-menu";
+import { Input } from "@/shared/ui/input";
import { Skeleton } from "@/shared/ui/skeleton";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/shared/ui/tabs";
import { UserAvatar } from "@/shared/ui/UserAvatar";
-type PulseTab = "foryou" | "people" | "agents" | "mine";
+type PulseTab = "search" | "foryou" | "people" | "agents" | "mine";
const tabTriggerClassName =
- "h-6 rounded-full px-3 py-0 text-[11px] font-semibold shadow-none transition-colors text-muted-foreground hover:text-foreground data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm";
+ "h-6 rounded-full px-3 py-0 text-[11px] font-semibold shadow-none transition-colors !text-muted-foreground hover:!text-foreground data-[state=active]:bg-background data-[state=active]:!text-foreground data-[state=active]:shadow-sm dark:!text-white/35 dark:hover:!text-white/70 dark:data-[state=active]:!text-white";
type PulseViewProps = {
currentPubkey?: string;
@@ -136,6 +137,7 @@ function AgentFilter({
export function PulseView({ currentPubkey }: PulseViewProps) {
const [activeTab, setActiveTab] = React.useState
-
-
- Everyone
-
-
- Following
-
-
- Agents
- {relayAgents.length > 0 ? (
-
- {relayAgents.length}
-
- ) : null}
-
-
- Mine
-
-
+
+
+
+
+
+
+ Everyone
+
+
+ Following
+
+
+ Agents
+ {relayAgents.length > 0 ? (
+
+ {relayAgents.length}
+
+ ) : null}
+
+
+ Mine
+
+
+
{activeTab === "agents" && relayAgents.length > 1 ? (
@@ -375,17 +382,6 @@ export function PulseView({ currentPubkey }: PulseViewProps) {
selectedPubkey={agentFilter}
/>
) : null}
-
- {activeTab !== "agents" ? (
-
-
diff --git a/desktop/src/shared/styles/globals.css b/desktop/src/shared/styles/globals.css
index 9a7ba1cd92..e15eeded49 100644
--- a/desktop/src/shared/styles/globals.css
+++ b/desktop/src/shared/styles/globals.css
@@ -22,6 +22,20 @@
height: 0;
}
+.pulse-composer
+ .rich-text-composer
+ .tiptap
+ p.is-editor-empty:first-child::before {
+ color: hsl(var(--muted-foreground) / 0.65);
+}
+
+.pulse-composer:focus-within
+ .rich-text-composer
+ .tiptap
+ p.is-editor-empty:first-child::before {
+ color: hsl(var(--foreground));
+}
+
.rich-text-composer .tiptap strong {
font-weight: 600;
}
-
+
) : null}
-
+
+
-
-
-
+ ) : activeTab !== "agents" ? (
+
-
- {currentDisplayName}
-
-
- {publishMutation.isError && (
-
- {publishMutation.error instanceof Error
- ? publishMutation.error.message
- : "Failed to publish note"}
+
+
+
- publishMutation.mutateAsync({
- content,
- mentionPubkeys,
- mediaTags,
- })
- }
- profiles={mentionProfiles}
- />
+
+
+ setSearchQuery(event.target.value)}
+ placeholder="What would you like to know?"
+ type="search"
+ value={searchQuery}
+ />
+
- )}
-
+ {publishMutation.isError && (
+
+
+
+ }
+ members={pulseMentionMembers}
+ placeholder="What's on your mind?"
+ isSending={publishMutation.isPending}
+ onSubmit={(content, mentionPubkeys, mediaTags) =>
+ publishMutation.mutateAsync({
+ content,
+ mentionPubkeys,
+ mediaTags,
+ })
+ }
+ profiles={mentionProfiles}
+ />
+
+ {publishMutation.error instanceof Error
+ ? publishMutation.error.message
+ : "Failed to publish note"}
+
+ )}
+ {renderTimeline()}
+ {activeTab !== "search" ? (
+ {renderTimeline()}
+ ) : null}