| ... | ... | @@ -92,6 +92,7 @@ import { |
| 92 | 92 | useState, |
| 93 | 93 | type KeyboardEvent as ReactKeyboardEvent, |
| 94 | 94 | type MouseEvent as ReactMouseEvent, |
| 95 | type PointerEvent as ReactPointerEvent, |
| 95 | 96 | type ReactNode, |
| 96 | 97 | } from "react"; |
| 97 | 98 | import { useParams, useRouter } from "@tanstack/react-router"; |
| ... | ... | @@ -188,6 +189,7 @@ import { |
| 188 | 189 | planSidebarThreadDrop, |
| 189 | 190 | reduceSidebarProjectScopeMenuState, |
| 190 | 191 | resolveAdjacentThreadId, |
| 192 | resolveSidebarSweepKeys, |
| 191 | 193 | resolveSidebarDropTarget, |
| 192 | 194 | resolveSidebarDropVerb, |
| 193 | 195 | resolveSidebarThreadSection, |
| ... | ... | @@ -202,6 +204,7 @@ import { |
| 202 | 204 | resolveWorkingStartedAt, |
| 203 | 205 | sidebarListItemId, |
| 204 | 206 | sidebarMarkerId, |
| 207 | sidebarThreadKeyAtY, |
| 205 | 208 | sortInboxThreadsByReturn, |
| 206 | 209 | sortPinnedThreadsForSidebar, |
| 207 | 210 | sortSidebarV2ProjectGroups, |
| ... | ... | @@ -706,6 +709,12 @@ function SidebarSectionPlaceholder(props: { |
| 706 | 709 | ); |
| 707 | 710 | } |
| 708 | 711 | |
| 712 | // Pointer travel before a press on a row starts a drag, or a press on its |
| 713 | // action button starts a sweep. Shorter presses stay clicks. |
| 714 | const SIDEBAR_DRAG_DISTANCE = 6; |
| 715 | |
| 716 | type SidebarSweepAction = "settle" | "unsettle" | "unsnooze"; |
| 717 | |
| 709 | 718 | // Zero-height markers reserve no label space at rest. During a drag the |
| 710 | 719 | // sorting strategy opens 24px for a 16px label with 4px clearance on each side. |
| 711 | 720 | const SIDEBAR_DRAG_LABEL_HEIGHT = 24; |
| ... | ... | @@ -1064,9 +1073,8 @@ const dropVerbBadge: Record<SidebarDropVerb, ReactNode> = { |
| 1064 | 1073 | const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1065 | 1074 | thread: SidebarThreadSummary; |
| 1066 | 1075 | variant: "card" | "slim"; |
| 1067 | | // Slim rows are either settled (action: un-settle) or merely quiet |
| 1068 | | // (seen Ready threads — action: settle). |
| 1069 | | variantAction: "settle" | "unsettle" | "unsnooze"; |
| 1076 | // Settled rows un-settle, snoozed rows wake, and cards settle. |
| 1077 | variantAction: SidebarSweepAction; |
| 1070 | 1078 | // False on environments whose server predates thread.settle/unsettle: |
| 1071 | 1079 | // the lifecycle affordances hide entirely rather than fail on click. |
| 1072 | 1080 | settlementSupported: boolean; |
| ... | ... | @@ -1090,6 +1098,8 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1090 | 1098 | // the pinned section. Any other position shows the verb badge instead, and |
| 1091 | 1099 | // the badge carries its own icon. |
| 1092 | 1100 | dragOverPinned: boolean; |
| 1101 | // The action this row will take when the sweep is released. |
| 1102 | sweepAction: SidebarSweepAction | null; |
| 1093 | 1103 | // Compact wake countdown ("2h") for rows in the snoozed shelf. |
| 1094 | 1104 | snoozeWakeLabelText: string | null; |
| 1095 | 1105 | // When a snooze ended (timer or early wake); drives the Woke pill until |
| ... | ... | @@ -1115,6 +1125,11 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1115 | 1125 | renamingTitle: string; |
| 1116 | 1126 | onContextMenu: (threadRef: ScopedThreadRef, position: { x: number; y: number }) => void; |
| 1117 | 1127 | onSettle: (threadRef: ScopedThreadRef) => void; |
| 1128 | onActionSweepStart: ( |
| 1129 | threadRef: ScopedThreadRef, |
| 1130 | action: SidebarSweepAction, |
| 1131 | event: PointerEvent, |
| 1132 | ) => void; |
| 1118 | 1133 | onUnsettle: (threadRef: ScopedThreadRef) => void; |
| 1119 | 1134 | onSnooze: (threadRef: ScopedThreadRef, preset: Pick<SnoozePreset, "snoozedUntil">) => void; |
| 1120 | 1135 | onUnsnooze: (threadRef: ScopedThreadRef) => void; |
| ... | ... | @@ -1138,6 +1153,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1138 | 1153 | onFileDropThreads, |
| 1139 | 1154 | onRenameTitleChange, |
| 1140 | 1155 | onSettle, |
| 1156 | onActionSweepStart, |
| 1141 | 1157 | onSnooze, |
| 1142 | 1158 | onStartRename, |
| 1143 | 1159 | onThreadActivate, |
| ... | ... | @@ -1433,6 +1449,15 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1433 | 1449 | }, |
| 1434 | 1450 | [onSettle, threadRef], |
| 1435 | 1451 | ); |
| 1452 | const handleActionPointerDown = useCallback( |
| 1453 | (event: ReactPointerEvent) => { |
| 1454 | if (!event.isPrimary || event.button !== 0) return; |
| 1455 | // Action buttons sweep their section rather than picking up the row. |
| 1456 | event.stopPropagation(); |
| 1457 | onActionSweepStart(threadRef, variantAction, event.nativeEvent); |
| 1458 | }, |
| 1459 | [onActionSweepStart, threadRef, variantAction], |
| 1460 | ); |
| 1436 | 1461 | const handleUnsettleClick = useCallback( |
| 1437 | 1462 | (event: ReactMouseEvent) => { |
| 1438 | 1463 | event.preventDefault(); |
| ... | ... | @@ -1510,7 +1535,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1510 | 1535 | variantAction === "unsettle" && "[&:not(:hover):not(:focus-within)_*]:text-secondary-label/70", |
| 1511 | 1536 | props.isActive |
| 1512 | 1537 | ? "bg-sidebar-row-active text-sidebar-foreground" |
| 1513 | | : isSelected |
| 1538 | : isSelected || props.sweepAction !== null |
| 1514 | 1539 | ? "bg-sidebar-row-selected text-sidebar-foreground" |
| 1515 | 1540 | : hasUnsentDraft |
| 1516 | 1541 | ? cn(draftSurfaceClassName, "text-sidebar-foreground") |
| ... | ... | @@ -1550,13 +1575,19 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1550 | 1575 | ...sortable.listeners, |
| 1551 | 1576 | } |
| 1552 | 1577 | : {}; |
| 1578 | // Sweeps reuse the corresponding row-drop action badge. |
| 1579 | const destinationVerb = sortable?.isDragging |
| 1580 | ? props.dropVerb |
| 1581 | : props.sweepAction === "unsnooze" |
| 1582 | ? "wake" |
| 1583 | : props.sweepAction; |
| 1553 | 1584 | const dragDestination = |
| 1554 | | sortable?.isDragging && props.dropVerb !== null ? ( |
| 1585 | destinationVerb !== null ? ( |
| 1555 | 1586 | <span |
| 1556 | 1587 | role="status" |
| 1557 | 1588 | className="pointer-events-none ml-auto inline-flex h-5 shrink-0 items-center gap-1 rounded-sm border border-primary/40 bg-primary/10 px-1.5 text-2xs font-medium text-primary" |
| 1558 | 1589 | > |
| 1559 | | {dropVerbBadge[props.dropVerb]} |
| 1590 | {dropVerbBadge[destinationVerb]} |
| 1560 | 1591 | </span> |
| 1561 | 1592 | ) : null; |
| 1562 | 1593 | |
| ... | ... | @@ -1701,7 +1732,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1701 | 1732 | if (variant === "slim") { |
| 1702 | 1733 | return ( |
| 1703 | 1734 | <li |
| 1704 | | data-thread-item |
| 1735 | data-thread-item={threadKey} |
| 1705 | 1736 | {...sortableRootProps} |
| 1706 | 1737 | {...(fileDropHandlers ?? {})} |
| 1707 | 1738 | className={cn( |
| ... | ... | @@ -1757,7 +1788,12 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1757 | 1788 | {sortable?.isDragging ? ( |
| 1758 | 1789 | dragDestination |
| 1759 | 1790 | ) : ( |
| 1760 | | <span className="relative ml-auto flex h-6 min-w-8 shrink-0 items-center justify-end"> |
| 1791 | <span |
| 1792 | className={cn( |
| 1793 | "relative ml-auto flex h-6 min-w-8 shrink-0 items-center justify-end", |
| 1794 | props.sweepAction !== null && "hidden", |
| 1795 | )} |
| 1796 | > |
| 1761 | 1797 | <span |
| 1762 | 1798 | className={cn( |
| 1763 | 1799 | "inline-flex justify-end tabular-nums text-secondary-label transition-opacity", |
| ... | ... | @@ -1803,6 +1839,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1803 | 1839 | type="button" |
| 1804 | 1840 | aria-label="Wake thread now" |
| 1805 | 1841 | onClick={handleUnsnoozeClick} |
| 1842 | onPointerDown={handleActionPointerDown} |
| 1806 | 1843 | className={cn( |
| 1807 | 1844 | "pointer-events-none absolute inset-y-0 right-0 -mr-1 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-1.5 text-xs text-muted-foreground opacity-0 transition-opacity hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", |
| 1808 | 1845 | isWoke && "group-hover/sidebar-row:static", |
| ... | ... | @@ -1819,6 +1856,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1819 | 1856 | type="button" |
| 1820 | 1857 | aria-label="Un-settle thread" |
| 1821 | 1858 | onClick={handleUnsettleClick} |
| 1859 | onPointerDown={handleActionPointerDown} |
| 1822 | 1860 | className={cn( |
| 1823 | 1861 | "pointer-events-none absolute inset-y-0 right-0 -mr-1 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-1.5 text-xs text-muted-foreground opacity-0 transition-opacity hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", |
| 1824 | 1862 | isWoke && "group-hover/sidebar-row:static", |
| ... | ... | @@ -1835,6 +1873,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1835 | 1873 | type="button" |
| 1836 | 1874 | aria-label="Settle thread" |
| 1837 | 1875 | onClick={handleSettleClick} |
| 1876 | onPointerDown={handleActionPointerDown} |
| 1838 | 1877 | className={cn( |
| 1839 | 1878 | "pointer-events-none absolute inset-y-0 right-0 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-2 text-xs text-muted-foreground opacity-0 transition-opacity hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 group-hover/sidebar-row:pointer-events-auto group-hover/sidebar-row:opacity-100", |
| 1840 | 1879 | isWoke && "group-hover/sidebar-row:static", |
| ... | ... | @@ -1845,6 +1884,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1845 | 1884 | )} |
| 1846 | 1885 | </span> |
| 1847 | 1886 | )} |
| 1887 | {props.sweepAction !== null ? dragDestination : null} |
| 1848 | 1888 | {props.jumpLabel ? <JumpHintBadge label={props.jumpLabel} /> : null} |
| 1849 | 1889 | </TooltipTrigger> |
| 1850 | 1890 | {detailsTooltip} |
| ... | ... | @@ -1857,7 +1897,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1857 | 1897 | |
| 1858 | 1898 | return ( |
| 1859 | 1899 | <li |
| 1860 | | data-thread-item |
| 1900 | data-thread-item={threadKey} |
| 1861 | 1901 | {...sortableRootProps} |
| 1862 | 1902 | {...(fileDropHandlers ?? {})} |
| 1863 | 1903 | className={cn( |
| ... | ... | @@ -1912,7 +1952,12 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1912 | 1952 | {sortable?.isDragging ? ( |
| 1913 | 1953 | dragDestination |
| 1914 | 1954 | ) : ( |
| 1915 | | <span className="group/sidebar-status-slot relative ml-auto flex h-5 min-w-8 shrink-0 items-stretch justify-end text-xs"> |
| 1955 | <span |
| 1956 | className={cn( |
| 1957 | "group/sidebar-status-slot relative ml-auto flex h-5 min-w-8 shrink-0 items-stretch justify-end text-xs", |
| 1958 | props.sweepAction !== null && "hidden", |
| 1959 | )} |
| 1960 | > |
| 1916 | 1961 | {/* Read-only status labels yield to the hover actions. Woke is |
| 1917 | 1962 | itself an action, so it stays pointer-enabled and visible |
| 1918 | 1963 | while the other controls appear beside it. */} |
| ... | ... | @@ -2024,6 +2069,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 2024 | 2069 | type="button" |
| 2025 | 2070 | aria-label="Settle thread" |
| 2026 | 2071 | onClick={handleSettleClick} |
| 2072 | onPointerDown={handleActionPointerDown} |
| 2027 | 2073 | className="-mr-1 inline-flex cursor-pointer items-center gap-1 rounded-md bg-transparent px-1.5 text-xs text-muted-foreground hover:text-foreground" |
| 2028 | 2074 | /> |
| 2029 | 2075 | } |
| ... | ... | @@ -2038,6 +2084,10 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 2038 | 2084 | ) : null} |
| 2039 | 2085 | </span> |
| 2040 | 2086 | )} |
| 2087 | {/* A sweep hides the slot rather than unmounting it, so the |
| 2088 | pressed action button stays connected and a cancelled sweep's |
| 2089 | release click still fires and is consumed. */} |
| 2090 | {props.sweepAction !== null ? dragDestination : null} |
| 2041 | 2091 | </div> |
| 2042 | 2092 | <div className="mt-1 flex min-w-0"> |
| 2043 | 2093 | {title} |
| ... | ... | @@ -3252,6 +3302,22 @@ export default function Sidebar() { |
| 3252 | 3302 | }, |
| 3253 | 3303 | [planForwardNavigation, settleThread], |
| 3254 | 3304 | ); |
| 3305 | // Post-settle navigation must skip threads settling in this same batch — |
| 3306 | // they are all leaving the card block together. Rows that are already |
| 3307 | // explicitly settled are skipped: nothing to do on a valid mixed selection. |
| 3308 | // Pinned rows ARE included: the decider clears the pin as part of settling, |
| 3309 | // so they park like the rest. |
| 3310 | const settleThreads = useCallback( |
| 3311 | (threadKeys: readonly string[]) => { |
| 3312 | const coSettlingKeys = new Set(threadKeys); |
| 3313 | for (const threadKey of threadKeys) { |
| 3314 | const thread = threadByKeyRef.current.get(threadKey); |
| 3315 | if (!thread || thread.settledOverride === "settled") continue; |
| 3316 | attemptSettle(scopeThreadRef(thread.environmentId, thread.id), { coSettlingKeys }); |
| 3317 | } |
| 3318 | }, |
| 3319 | [attemptSettle], |
| 3320 | ); |
| 3255 | 3321 | const attemptUnsettle = useCallback( |
| 3256 | 3322 | (threadRef: ScopedThreadRef) => { |
| 3257 | 3323 | void (async () => { |
| ... | ... | @@ -3378,7 +3444,7 @@ export default function Sidebar() { |
| 3378 | 3444 | ); |
| 3379 | 3445 | const dndSensors = useSensors( |
| 3380 | 3446 | useSensor(SidebarPointerSensor, { |
| 3381 | | distance: 6, |
| 3447 | distance: SIDEBAR_DRAG_DISTANCE, |
| 3382 | 3448 | onAttach: attachDragSensor, |
| 3383 | 3449 | onFinish: finishThreadDrag, |
| 3384 | 3450 | onMove: moveThreadContextDrag, |
| ... | ... | @@ -3399,6 +3465,77 @@ export default function Sidebar() { |
| 3399 | 3465 | add(settledThreads, "settled"); |
| 3400 | 3466 | return map; |
| 3401 | 3467 | }, [activeThreads, pinnedThreads, settledThreads, snoozedThreads, workingThreads]); |
| 3468 | const sectionByThreadKeyRef = useRef(sectionByThreadKey); |
| 3469 | sectionByThreadKeyRef.current = sectionByThreadKey; |
| 3470 | // Drag a row action to apply it to the armed rows in the same section. |
| 3471 | // Staying in one section keeps an overshoot from changing neighboring |
| 3472 | // sections, whose boundaries may be hidden outside a row drag. The sweep |
| 3473 | // runs on the row drag's sensor and ref, so unmounting the list cancels it. |
| 3474 | const [actionSweep, setActionSweep] = useState<{ |
| 3475 | action: SidebarSweepAction; |
| 3476 | keys: ReadonlySet<string>; |
| 3477 | } | null>(null); |
| 3478 | const startActionSweep = useCallback( |
| 3479 | (threadRef: ScopedThreadRef, action: SidebarSweepAction, event: PointerEvent) => { |
| 3480 | const originKey = scopedThreadKey(threadRef); |
| 3481 | const originSection = sectionByThreadKeyRef.current.get(originKey); |
| 3482 | const canApply = (key: string) => { |
| 3483 | const thread = threadByKeyRef.current.get(key); |
| 3484 | const capabilities = |
| 3485 | thread && serverConfigs.get(thread.environmentId)?.environment.capabilities; |
| 3486 | return ( |
| 3487 | thread !== undefined && |
| 3488 | sectionByThreadKeyRef.current.get(key) === originSection && |
| 3489 | (action === "unsnooze" |
| 3490 | ? capabilities?.threadSnooze === true |
| 3491 | : capabilities?.threadSettlement === true) |
| 3492 | ); |
| 3493 | }; |
| 3494 | let targetKey: string | null = null; |
| 3495 | let sweptKeys: string[] = []; |
| 3496 | const sweepTo = (key: string | null) => { |
| 3497 | if (key === null || key === targetKey) return; |
| 3498 | targetKey = key; |
| 3499 | sweptKeys = resolveSidebarSweepKeys(orderedThreadKeysRef.current, originKey, key, canApply); |
| 3500 | setActionSweep({ action, keys: new Set(sweptKeys) }); |
| 3501 | }; |
| 3502 | dragSensorRef.current = new SidebarPointerSensor({ |
| 3503 | active: originKey, |
| 3504 | event, |
| 3505 | options: { |
| 3506 | distance: SIDEBAR_DRAG_DISTANCE, |
| 3507 | onAttach: () => {}, |
| 3508 | onFinish: () => { |
| 3509 | dragSensorRef.current = null; |
| 3510 | setActionSweep(null); |
| 3511 | }, |
| 3512 | }, |
| 3513 | onPending: () => {}, |
| 3514 | onStart: () => sweepTo(originKey), |
| 3515 | onMove: ({ y }) => |
| 3516 | sweepTo(threadListRef.current && sidebarThreadKeyAtY(threadListRef.current, y)), |
| 3517 | // Also runs after a press that never moved. Nothing is swept then, |
| 3518 | // and the button's own click applies its action. Rows that changed |
| 3519 | // section mid-gesture, say pinned from another device, are skipped. |
| 3520 | onEnd: () => { |
| 3521 | const keys = sweptKeys.filter(canApply); |
| 3522 | if (action === "settle") { |
| 3523 | settleThreads(keys); |
| 3524 | return; |
| 3525 | } |
| 3526 | for (const key of keys) { |
| 3527 | const ref = parseScopedThreadKey(key); |
| 3528 | if (ref === null) continue; |
| 3529 | if (action === "unsettle") attemptUnsettle(ref); |
| 3530 | else attemptUnsnooze(ref); |
| 3531 | } |
| 3532 | }, |
| 3533 | onCancel: () => {}, |
| 3534 | onAbort: () => {}, |
| 3535 | }); |
| 3536 | }, |
| 3537 | [attemptUnsettle, attemptUnsnooze, serverConfigs, settleThreads], |
| 3538 | ); |
| 3402 | 3539 | const pinnedKeys = useMemo( |
| 3403 | 3540 | () => |
| 3404 | 3541 | pinnedThreads.map((thread) => |
| ... | ... | @@ -4155,17 +4292,7 @@ export default function Sidebar() { |
| 4155 | 4292 | return; |
| 4156 | 4293 | } |
| 4157 | 4294 | if (clicked.value === "settle") { |
| 4158 | | // Post-settle navigation must skip threads settling in this same |
| 4159 | | // batch — they are all leaving the card block together. Rows that |
| 4160 | | // are already explicitly settled are skipped: nothing to do on a |
| 4161 | | // valid mixed selection. Pinned rows ARE included: the decider |
| 4162 | | // clears the pin as part of settling, so they park like the rest. |
| 4163 | | const coSettlingKeys = new Set(threadKeys); |
| 4164 | | for (const threadKey of threadKeys) { |
| 4165 | | const thread = threadByKeyRef.current.get(threadKey); |
| 4166 | | if (!thread || thread.settledOverride === "settled") continue; |
| 4167 | | attemptSettle(scopeThreadRef(thread.environmentId, thread.id), { coSettlingKeys }); |
| 4168 | | } |
| 4295 | settleThreads(threadKeys); |
| 4169 | 4296 | clearSelection(); |
| 4170 | 4297 | return; |
| 4171 | 4298 | } |
| ... | ... | @@ -4218,7 +4345,6 @@ export default function Sidebar() { |
| 4218 | 4345 | ); |
| 4219 | 4346 | }, |
| 4220 | 4347 | [ |
| 4221 | | attemptSettle, |
| 4222 | 4348 | attemptUnpin, |
| 4223 | 4349 | clearSelection, |
| 4224 | 4350 | confirmThreadDelete, |
| ... | ... | @@ -4227,6 +4353,7 @@ export default function Sidebar() { |
| 4227 | 4353 | performSnooze, |
| 4228 | 4354 | removeFromSelection, |
| 4229 | 4355 | serverConfigs, |
| 4356 | settleThreads, |
| 4230 | 4357 | updateThreadMetadata, |
| 4231 | 4358 | timestampFormat, |
| 4232 | 4359 | ], |
| ... | ... | @@ -4953,6 +5080,10 @@ export default function Sidebar() { |
| 4953 | 5080 | className={cn( |
| 4954 | 5081 | "relative flex flex-col gap-px", |
| 4955 | 5082 | sidebarListItems.length > 0 && "flex-1", |
| 5083 | // An action sweep owns the pointer: rows it passes over |
| 5084 | // neither show hover actions nor open tooltips, even |
| 5085 | // controls that opt back in, like the Woke pill. |
| 5086 | actionSweep !== null && "**:pointer-events-none", |
| 4956 | 5087 | )} |
| 4957 | 5088 | > |
| 4958 | 5089 | {(() => { |
| ... | ... | @@ -5009,6 +5140,9 @@ export default function Sidebar() { |
| 5009 | 5140 | dragOverPinned={ |
| 5010 | 5141 | dragState?.activeKey === threadKey && dragTargetSection === "pinned" |
| 5011 | 5142 | } |
| 5143 | sweepAction={ |
| 5144 | actionSweep?.keys.has(threadKey) ? actionSweep.action : null |
| 5145 | } |
| 5012 | 5146 | snoozeWakeLabelText={ |
| 5013 | 5147 | section === "snoozed" && thread.snoozedUntil != null |
| 5014 | 5148 | ? snoozeWakeLabel(thread.snoozedUntil, { |
| ... | ... | @@ -5057,6 +5191,7 @@ export default function Sidebar() { |
| 5057 | 5191 | renamingTitle={renamingThreadKey === threadKey ? renamingTitle : ""} |
| 5058 | 5192 | onContextMenu={handleThreadContextMenu} |
| 5059 | 5193 | onSettle={attemptSettle} |
| 5194 | onActionSweepStart={startActionSweep} |
| 5060 | 5195 | onUnsettle={attemptUnsettle} |
| 5061 | 5196 | onSnooze={attemptSnooze} |
| 5062 | 5197 | onUnsnooze={attemptUnsnooze} |