0 ? "justify-start" : "justify-end",
+ "transition-colors",
+ swipingAction === "delete"
+ ? swipeCommitted
+ ? "bg-destructive/20 text-destructive"
+ : "bg-destructive/10 text-destructive/70"
+ : swipeCommitted
+ ? "bg-accent text-accent-foreground"
+ : "bg-accent/50 text-accent-foreground/70",
+ )}
+ >
+
+ {swipingAction === "delete" ? (
+
+ ) : isArchived ? (
+ // Archiving toggles, so on an archived row the gesture restores —
+ // mirror the kebab's Unarchive glyph rather than promising a re-archive.
+
+ ) : (
+
+ )}
+
+
+ )}
+ {/* Moving surface: the WHOLE row — link, session-state badge, and the
+ pin/kebab controls — moves together, so the trailing icons travel with
+ the text instead of the text sliding out from under them.
+ It INSETS from the swiped edge rather than translating: a translate
+ would push the title past the panel boundary and cut it mid-word (the
+ hint needs ~48px of gap, but the title only has ~18px of slack). An
+ inset re-truncates the title with its existing ellipsis instead, and
+ keeps every row control inside the panel.
+ `bg-sidebar` only while mid-swipe: an unconditional background would
+ plate every row and cover the sidebar canvas. */}
+
+ {/* Right-click anywhere on the row opens the same actions as the kebab.
Suppressed in selection mode (bulk-select owns the row), where the
bare link is rendered instead. ContextMenuTrigger preventDefaults the
native contextmenu event, so right-click never navigates; asChild
@@ -3304,10 +3588,38 @@ function ConversationRow({
hovering surfaces the project flyout — the trigger sits innermost so
both the context menu and the hover card keep their handlers/refs on
the Link. */}
- {selectionMode ? (
- projectFlyoutName ? (
+ {selectionMode ? (
+ projectFlyoutName ? (
+
+ {rowLink}
+
+
+ ) : isMobile ? (
+ rowLink
+ ) : (
+
+ {rowLink}
+
+
+ )
+ ) : projectFlyoutName ? (
- {rowLink}
+
+
+ {rowLink}
+
+
+ {}}
+ {...menuItemProps}
+ />
+
+
) : isMobile ? (
- rowLink
- ) : (
-
- {rowLink}
-
-
- )
- ) : projectFlyoutName ? (
-
-
-
- {rowLink}
-
-
- {}}
- {...menuItemProps}
- />
-
-
-
-
- ) : isMobile ? (
-
- {rowLink}
-
- {}}
- {...menuItemProps}
- />
-
-
- ) : (
-
-
-
-
- {rowLink}
-
-
+
+ {rowLink}
-
-
- )}
- {selectionMode ? (
-
- {isSelected ? (
-
- ) : (
-
- )}
-
- ) : sessionState !== null ? (
-
-
-
- ) : null}
- {/* Trailing controls (pin + kebab) share one absolutely-positioned flex
- row, so their spacing is defined once (gap-0.5) and stays aligned
- with the project-folder header actions, which use the same pattern.
- The kebab is the rightmost child (pinned to right-1); the pin sits a
- gap to its left. Hidden entirely while selecting (bulk mode owns the
- row controls). */}
- {!selectionMode && (
-
- {/* Archived rows omit the pin entirely: pinning is meaningless there
- (archive outranks pin), so there's no pin action even on hover. */}
- {!isArchived && (
-
- )}
-
-
+ ) : (
+
+
+
+
+ {rowLink}
+
+
+
+ {}}
+ {...menuItemProps}
+ />
+
+
+
+
+ )}
+ {selectionMode ? (
+
+ {isSelected ? (
+
+ ) : (
+
+ )}
+
+ ) : sessionState !== null ? (
+
+
+
+ ) : null}
+ {/* Trailing controls (pin + kebab) share one absolutely-positioned flex
+ row, so their spacing is defined once (gap-0.5) and stays aligned
+ with the project-folder header actions, which use the same pattern.
+ The kebab is the rightmost child (pinned to right-1); the pin sits a
+ gap to its left. Hidden entirely while selecting (bulk mode owns the
+ row controls). */}
+ {!selectionMode && (
+
+ {/* Archived rows omit the pin entirely: pinning is meaningless there
+ (archive outranks pin), so there's no pin action even on hover. */}
+ {!isArchived && (
-
-
-
-
-
-
- )}
+ )}
+
+
+
+
+
+
+
+
+
+ )}
+