From 6af4902849abdd0788cd8eee9e6a676730430b6d Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Wed, 23 Sep 2026 22:05:58 -0400 Subject: [PATCH] Drop the selection line; Post goes last The summary printed back the cells you had just clicked in the grid -- count, total and filter, none of it news. It is a peek button now, and the peek still carries the pivot filter and the duplicate-slice warning, which are the parts that are not visible in the grid. Post moves to a footer of its own, last in the flow: pick the operation, read the ledger, name the change, post it. It sits on the same label column as every other button, and sticks to the bottom of the dock -- a fixed-height dock with its own scrollbar would otherwise put the primary action below the fold. Co-Authored-By: Claude Opus 5 --- ui/src/components/OperationPanel.jsx | 78 ++++++++++++---------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/ui/src/components/OperationPanel.jsx b/ui/src/components/OperationPanel.jsx index 45e770a..a4df1de 100644 --- a/ui/src/components/OperationPanel.jsx +++ b/ui/src/components/OperationPanel.jsx @@ -166,45 +166,7 @@ function scopeText(viewScope = []) { .join(' · ') } -// The selection, in one line you can open. What it always shows is what is not -// visible anywhere else: how many slices, what they total, and the pivot filter -// they sit inside -- which scopes every figure in the ledger and every row the -// write touches, while appearing in none of the slices themselves. -function SliceSummary({ slices, viewScope, distinctSlices, currentTotals, open, onToggle }) { - const scope = scopeText(viewScope) - const one = slices.length === 1 ? sliceLabel(slices[0]) : null - const total = currentTotals - ? (currentTotals.total?.value || 0) + (currentTotals.excluded?.value || 0) - : null - const dupes = distinctSlices != null && distinctSlices < slices.length - - return ( -
- - {one ? '1 slice' : `${slices.length} slices`} - - {one && {one}} - {total != null && ( - {fmtNum(total)} - )} - {scope && ( - - within - {scope} - - )} - {dupes && ( - - {distinctSlices} distinct - - )} - {open ? '▾ hide' : '▸ peek'} -
- ) -} - -function SelectionList({ slices, viewScope = [], currentTotals, onRemove, onClear }) { +function SelectionList({ slices, viewScope = [], distinctSlices, currentTotals, onRemove, onClear }) { const multi = slices.length > 1 const perSlice = currentTotals?.perSlice || [] const valueCol = currentTotals?.valueCol @@ -226,6 +188,15 @@ function SelectionList({ slices, viewScope = [], currentTotals, onRemove, onClea return (
+ {/* Cells that differ only by a column operations cannot filter on collapse + to the same slice — say so, rather than implying more reach than there is */} + {distinctSlices != null && distinctSlices < slices.length && ( +

+ {slices.length} cells cover {distinctSlices} distinct{' '} + {distinctSlices === 1 ? 'slice' : 'slices'} — some differ only by a column + operations cannot target. The duplicates are applied once. +

+ )} {scopeLine && (
within @@ -1450,13 +1421,10 @@ export default function OperationPanel({ {hasSlice && ( -
- setPeek(o => !o)} /> - {opBusy && writing rows…} - submitOp(activeOp)} busy={!!opBusy}> - {opBusy ? POSTING : POST} - +
+ setPeek(o => !o)}> + {peek ? '▾' : '▸'} {slices.length} {slices.length === 1 ? 'slice' : 'slices'} +
)}
@@ -1476,6 +1444,7 @@ export default function OperationPanel({ setSlices(prev => prev.filter((_, x) => x !== i))} onClear={() => setSlices([])} @@ -1506,6 +1475,23 @@ export default function OperationPanel({ {labelZone}
)} + + {/* Last, because it is last: pick the operation, read the ledger, name + the change, post it. The blank label keeps Post in the same column as + every other button in the panel rather than at an edge of its own. + Sticky, because the bottom dock is a fixed height with its own scroll + -- a primary action that has to be scrolled to is a primary action + people miss. */} + {hasSlice && ( +
+ + submitOp(activeOp)} busy={!!opBusy}> + {opBusy ? POSTING : POST} + + + {opBusy && writing rows — the pivot updates when it finishes} +
+ )}
) }