diff --git a/apps/desktop/src/app/chat/sidebar/chrome.tsx b/apps/desktop/src/app/chat/sidebar/chrome.tsx index a08bfa608c..ef83cc6d94 100644 --- a/apps/desktop/src/app/chat/sidebar/chrome.tsx +++ b/apps/desktop/src/app/chat/sidebar/chrome.tsx @@ -23,9 +23,19 @@ export function SidebarSectionMeta({ children }: { children: React.ReactNode }) // Height lives ONLY on SidebarRowShell (min-h-[1.625rem]). Inset children // stretch to fill the cell and center content internally — never items-center // on the shell grid, or short clusters (projects) float 1–2px off sessions. +// +// `rowPadX` is the BODY's padding: the lead's inset, plus the gap the label +// keeps from the actions column, both inside the row's click target. +// `rowPadTrail` is the row's own trailing inset and belongs to the SHELL — the +// only box containing both the actions column AND the card's in-body cluster, +// so one class insets every trailing thing a row can render. Owned anywhere +// else, the age / chips / kebab sit flush on the border box, which is exactly +// where a working row paints its arc (`.arc-row` has zero standoff) — the ring +// ran through the text. const rowMinH = 'min-h-[1.625rem]' -const rowPadX = 'pl-2 pr-1' +const rowPadX = 'pl-2 pr-2' +const rowPadTrail = 'pr-2' const rowGap = 'gap-1.5' const rowLead = 'grid size-3.5 shrink-0 place-items-center' const rowInset = cn(rowPadX, rowGap, 'flex h-full min-w-0 items-center self-stretch py-0.5') @@ -71,9 +81,11 @@ export function SidebarDateDivider({ ) } -/** Outer grid — sole owner of row height. The trailing `actions` slot is - * marked `data-row-actions` so a row-wide drag gesture can exclude it with - * one selector: it holds real controls, never grab surface. */ +/** Outer grid — sole owner of row height and of the trailing inset. The + * `actions` slot is marked `data-row-actions` so a row-wide drag gesture can + * exclude it with one selector: it holds real controls, never grab surface. + * It stretches so that exclusion covers the column, not just its tallest + * control. */ export function SidebarRowShell({ actions, actionsClassName, @@ -82,10 +94,13 @@ export function SidebarRowShell({ ...props }: React.ComponentProps<'div'> & { actions?: React.ReactNode; actionsClassName?: string }) { return ( -
+
{children} {actions ? ( -
+
{actions}
) : null} diff --git a/apps/desktop/src/app/chat/sidebar/cron-jobs-section.tsx b/apps/desktop/src/app/chat/sidebar/cron-jobs-section.tsx index 9d4d54b9af..0ab0583447 100644 --- a/apps/desktop/src/app/chat/sidebar/cron-jobs-section.tsx +++ b/apps/desktop/src/app/chat/sidebar/cron-jobs-section.tsx @@ -23,6 +23,7 @@ import type { CronJob } from '@/types/hermes' import { jobState, jobTitle, STATE_DOT } from '../../cron/job-state' import { SidebarPanelLabel } from '../../shell/sidebar-label' +import { SidebarRowBody, SidebarRowLabel, SidebarRowLead, SidebarRowShell } from './chrome' import { SidebarLoadMoreRow } from './load-more-row' const INACTIVE_STATES = new Set(['completed', 'disabled', 'error', 'paused']) @@ -304,21 +305,57 @@ function CronJobSidebarRow({ return (
+ {/* The shared row chrome, not a copy of it: a cron job and a session sit + in the same list, so they line up only if one place owns the geometry. */} -
- {/* Lead with the dot in the same w-3.5 cell + pl-2 the session rows use - so the cron dots line up with the sessions above; the caret sits next - to the label (matching the other sidebar disclosures) and the whole - label area toggles the run peek. */} + + + {meta} + +
+ + + + + + +
+
+ } + className="group/cron relative hover:bg-(--chrome-action-hover)" + > + {/* The caret sits next to the label (matching the other sidebar + disclosures) and the whole label area toggles the run peek. */} - + - {/* Trailing cluster: countdown by default, quick actions on hover. */} -
- - {meta} - -
- - - - - - -
-
-
+ {expanded && }
diff --git a/apps/desktop/src/app/chat/sidebar/load-more-row.tsx b/apps/desktop/src/app/chat/sidebar/load-more-row.tsx index 617bad9172..a11e6d27c7 100644 --- a/apps/desktop/src/app/chat/sidebar/load-more-row.tsx +++ b/apps/desktop/src/app/chat/sidebar/load-more-row.tsx @@ -11,7 +11,9 @@ interface SidebarLoadMoreRowProps { // Compact "load more" affordance shared by recents, messaging, and cron. Kept // intentionally identical to workspace "show more" controls (ellipsis button) -// so pagination reads as one interaction everywhere. +// so pagination reads as one interaction everywhere. It hangs off the list +// instead of sitting in a row, so it repeats the row's trailing inset +// (SidebarRowShell's `pr-2`) to stay on the edge the rows stop at. export function SidebarLoadMoreRow({ step, onClick, loading = false }: SidebarLoadMoreRowProps) { const { t } = useI18n() const label = loading ? t.sidebar.loading : step > 0 ? t.sidebar.loadCount(step) : t.sidebar.loadMore @@ -20,7 +22,7 @@ export function SidebarLoadMoreRow({ step, onClick, loading = false }: SidebarLo