Skip to content
Draft
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
17 changes: 16 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,22 @@ subscription. Navigate to the channel first (triggers subscription), then away
(so unread indicators appear), then inject.

**Animation timing:** Radix components animate in via CSS. `toBeVisible()`
resolves mid-animation — wait for completion before screenshotting:
resolves mid-animation — wait for completion before screenshotting. Use the
shared helper (mandatory before any `page.screenshot()` or
`locator.screenshot()` in specs):

```ts
import { waitForAnimations } from "../helpers/animations";

// ... after the element is visible but before capturing:
await waitForAnimations(page);
await page.screenshot({ path: "...", clip: { ... } });
```

The `just desktop-screenshot` path (`screenshot.mjs`) calls
`waitForAnimations` automatically — no manual step needed there.

For per-element waits (rare — prefer the page-level helper above):

```ts
await menuItem.evaluate((el) =>
Expand Down
1 change: 1 addition & 0 deletions desktop/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default defineConfig({
"**/workflows.spec.ts",
"**/identity-archive.spec.ts",
"**/identity-archive-hide.spec.ts",
"**/reminders-screenshots.spec.ts",
],
use: {
...devices["Desktop Chrome"],
Expand Down
444 changes: 232 additions & 212 deletions desktop/src/app/AppShell.tsx

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions desktop/src/app/navigation/useAppNavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ export function useAppNavigation() {
[commitNavigation],
);

const goReminders = React.useCallback(
(behavior?: NavigationBehavior) =>
commitNavigation(
{
to: "/reminders",
},
behavior,
),
[commitNavigation],
);

const goProject = React.useCallback(
(projectId: string, behavior?: NavigationBehavior) =>
commitNavigation(
Expand Down Expand Up @@ -239,6 +250,7 @@ export function useAppNavigation() {
goProject,
goProjects,
goPulse,
goReminders,
goWorkflow,
goWorkflows,
openSearchHit,
Expand Down
21 changes: 21 additions & 0 deletions desktop/src/app/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { Route as rootRouteImport } from "./routes/root";
import { Route as workflowsRouteImport } from "./routes/workflows";
import { Route as remindersRouteImport } from "./routes/reminders";
import { Route as pulseRouteImport } from "./routes/pulse";
import { Route as projectsRouteImport } from "./routes/projects";
import { Route as agentsRouteImport } from "./routes/agents";
Expand All @@ -20,6 +21,11 @@ const workflowsRoute = workflowsRouteImport.update({
path: "/workflows",
getParentRoute: () => rootRouteImport,
} as any);
const remindersRoute = remindersRouteImport.update({
id: "/reminders",
path: "/reminders",
getParentRoute: () => rootRouteImport,
} as any);
const pulseRoute = pulseRouteImport.update({
id: "/pulse",
path: "/pulse",
Expand Down Expand Up @@ -67,6 +73,7 @@ export interface FileRoutesByFullPath {
"/agents": typeof agentsRoute;
"/projects": typeof projectsRoute;
"/pulse": typeof pulseRoute;
"/reminders": typeof remindersRoute;
"/workflows": typeof workflowsRoute;
"/channels/$channelId": typeof channelsDotchannelIdRoute;
"/projects/$projectId": typeof projectsDotprojectIdRoute;
Expand All @@ -78,6 +85,7 @@ export interface FileRoutesByTo {
"/agents": typeof agentsRoute;
"/projects": typeof projectsRoute;
"/pulse": typeof pulseRoute;
"/reminders": typeof remindersRoute;
"/workflows": typeof workflowsRoute;
"/channels/$channelId": typeof channelsDotchannelIdRoute;
"/projects/$projectId": typeof projectsDotprojectIdRoute;
Expand All @@ -90,6 +98,7 @@ export interface FileRoutesById {
"/agents": typeof agentsRoute;
"/projects": typeof projectsRoute;
"/pulse": typeof pulseRoute;
"/reminders": typeof remindersRoute;
"/workflows": typeof workflowsRoute;
"/channels/$channelId": typeof channelsDotchannelIdRoute;
"/projects/$projectId": typeof projectsDotprojectIdRoute;
Expand All @@ -103,6 +112,7 @@ export interface FileRouteTypes {
| "/agents"
| "/projects"
| "/pulse"
| "/reminders"
| "/workflows"
| "/channels/$channelId"
| "/projects/$projectId"
Expand All @@ -114,6 +124,7 @@ export interface FileRouteTypes {
| "/agents"
| "/projects"
| "/pulse"
| "/reminders"
| "/workflows"
| "/channels/$channelId"
| "/projects/$projectId"
Expand All @@ -125,6 +136,7 @@ export interface FileRouteTypes {
| "/agents"
| "/projects"
| "/pulse"
| "/reminders"
| "/workflows"
| "/channels/$channelId"
| "/projects/$projectId"
Expand All @@ -137,6 +149,7 @@ export interface RootRouteChildren {
agentsRoute: typeof agentsRoute;
projectsRoute: typeof projectsRoute;
pulseRoute: typeof pulseRoute;
remindersRoute: typeof remindersRoute;
workflowsRoute: typeof workflowsRoute;
channelsDotchannelIdRoute: typeof channelsDotchannelIdRoute;
projectsDotprojectIdRoute: typeof projectsDotprojectIdRoute;
Expand All @@ -153,6 +166,13 @@ declare module "@tanstack/react-router" {
preLoaderRoute: typeof workflowsRouteImport;
parentRoute: typeof rootRouteImport;
};
"/reminders": {
id: "/reminders";
path: "/reminders";
fullPath: "/reminders";
preLoaderRoute: typeof remindersRouteImport;
parentRoute: typeof rootRouteImport;
};
"/pulse": {
id: "/pulse";
path: "/pulse";
Expand Down Expand Up @@ -217,6 +237,7 @@ const rootRouteChildren: RootRouteChildren = {
agentsRoute: agentsRoute,
projectsRoute: projectsRoute,
pulseRoute: pulseRoute,
remindersRoute: remindersRoute,
workflowsRoute: workflowsRoute,
channelsDotchannelIdRoute: channelsDotchannelIdRoute,
projectsDotprojectIdRoute: projectsDotprojectIdRoute,
Expand Down
1 change: 1 addition & 0 deletions desktop/src/app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const routes = rootRoute("root.tsx", [
index("index.tsx"),
route("/agents", "agents.tsx"),
route("/pulse", "pulse.tsx"),
route("/reminders", "reminders.tsx"),
route("/workflows", "workflows.tsx"),
route("/workflows/$workflowId", "workflows.$workflowId.tsx"),
route("/projects", "projects.tsx"),
Expand Down
19 changes: 19 additions & 0 deletions desktop/src/app/routes/reminders.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from "react";
import { createFileRoute } from "@tanstack/react-router";

const RemindersScreen = React.lazy(async () => {
const module = await import("@/features/reminders/ui/RemindersScreen");
return { default: module.RemindersScreen };
});

export const Route = createFileRoute("/reminders")({
component: RemindersRouteComponent,
});

function RemindersRouteComponent() {
return (
<React.Suspense fallback={null}>
<RemindersScreen />
</React.Suspense>
);
}
18 changes: 18 additions & 0 deletions desktop/src/features/messages/ui/MessageActionBar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
BellOff,
BellRing,
Clock,
Copy,
CornerUpLeft,
EllipsisVertical,
Expand Down Expand Up @@ -66,6 +67,7 @@ function MoreActionsMenu({
onFollowThread,
onMarkUnread,
onOpenChange,
onRemindLater,
onUnfollowThread,
open,
isFollowingThread,
Expand All @@ -79,6 +81,7 @@ function MoreActionsMenu({
onFollowThread?: (message: TimelineMessage) => void;
onMarkUnread?: (message: TimelineMessage) => void;
onOpenChange: (open: boolean) => void;
onRemindLater?: (message: TimelineMessage) => void;
onUnfollowThread?: (message: TimelineMessage) => void;
open: boolean;
isFollowingThread?: boolean;
Expand Down Expand Up @@ -181,6 +184,17 @@ function MoreActionsMenu({
</DropdownMenuItem>
) : null}

{onRemindLater ? (
<DropdownMenuItem
onClick={() => {
onRemindLater(message);
}}
>
<Clock className="h-4 w-4" />
Remind me later
</DropdownMenuItem>
) : null}

{hasCopyActions && channelId ? (
<DropdownMenuItem
data-testid={`copy-message-link-${message.id}`}
Expand Down Expand Up @@ -265,6 +279,7 @@ export function MessageActionBar({
onMarkUnread,
onReactionBadgeBurstRequest,
onReactionSelect,
onRemindLater,
onReply,
onUnfollowThread,
reactionErrorMessage = null,
Expand All @@ -281,6 +296,7 @@ export function MessageActionBar({
onMarkUnread?: (message: TimelineMessage) => void;
onReactionBadgeBurstRequest?: (emoji: string) => void;
onReactionSelect?: (emoji: string) => Promise<void>;
onRemindLater?: (message: TimelineMessage) => void;
onReply?: (message: TimelineMessage) => void;
onUnfollowThread?: (message: TimelineMessage) => void;
reactionErrorMessage?: string | null;
Expand All @@ -298,6 +314,7 @@ export function MessageActionBar({
Boolean(onMarkUnread) ||
Boolean(onFollowThread) ||
Boolean(onUnfollowThread) ||
Boolean(onRemindLater) ||
!message.pending;

if (!hasReplyAction && !hasReactionAction && !hasMoreMenuActions) {
Expand Down Expand Up @@ -418,6 +435,7 @@ export function MessageActionBar({
onFollowThread={onFollowThread}
onMarkUnread={onMarkUnread}
onOpenChange={setIsDropdownOpen}
onRemindLater={onRemindLater}
onUnfollowThread={onUnfollowThread}
open={isDropdownOpen}
isFollowingThread={isFollowingThread}
Expand Down
10 changes: 10 additions & 0 deletions desktop/src/features/messages/ui/MessageRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MessageReactions } from "@/features/messages/ui/MessageReactions";
import { useReactionHandler } from "@/features/messages/ui/useReactionHandler";
import type { UserProfileLookup } from "@/features/profile/lib/identity";
import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
import { useRemindLater } from "@/features/reminders/ui/RemindMeLaterProvider";
import { KIND_STREAM_MESSAGE_DIFF } from "@/shared/constants/kinds";
import { cn } from "@/shared/lib/cn";
import { normalizePubkey } from "@/shared/lib/pubkey";
Expand Down Expand Up @@ -81,6 +82,7 @@ export const MessageRow = React.memo(
errorMessage: reactionErrorMessage,
select: handleReactionSelect,
} = useReactionHandler(message, onToggleReaction);
const { openReminder } = useRemindLater();
const mentionNames = React.useMemo(
() => resolveMentionNames(message.tags, profiles),
[profiles, message.tags],
Expand Down Expand Up @@ -266,6 +268,14 @@ export const MessageRow = React.memo(
onReactionSelect={
canToggleReactions ? handleReactionSelect : undefined
}
onRemindLater={(msg) => {
openReminder({
eventId: msg.id,
channelId: channelId ?? "",
preview: msg.body.slice(0, 100),
authorPubkey: msg.pubkey ?? "",
});
}}
onReply={onReply}
onUnfollowThread={onUnfollowThread}
reactionErrorMessage={reactionErrorMessage}
Expand Down
Loading