Repository navigation
Commit ad18da4
authored
feat(desktop): pick session rows with shift and cmd (#4455)
* refactor(desktop): move task deletion out of the Session rail
Deleting a task is the one row action that cannot be undone, and the rail
is where a mis-click is likeliest: rows are dense, the ⋯ menu is one hover
away, and the row under the cursor moves as the catalog refreshes. The rail
now stops at 归档 — the row menu, the selection bar's second button, and the
Delete/Backspace binding are all gone.
Deleting still exists, in Settings › 已归档任务, which can only reach a task
that was archived first. That archive step is what makes the intent
deliberate, and it is the same route the bulk purge already took.
Two behaviours change for the user. Deleting a task that was never archived
is now two steps instead of one: archive it in the rail, then delete it in
Settings. Delete and Backspace no longer act on the focused row.
With the rail's bulk delete gone, `sweepSessions` had one caller left and
one live value for its `requireArchivedFor` parameter, so it collapses back
into `purgeSessions`.
Generated-by: Claude Opus 5 via Claude Code
* feat(desktop): pick session rows with shift and cmd
The Session rail's multi-select was a MODE. A row's ⋯ offered 选择任务,
which entered it; entering swapped a checkbox column in beside every row,
put a bar with a master checkbox above the list, and changed what an
ordinary click meant. Leaving it was a 取消 button or Escape. The mode
lived in one state, the marks in another, and the bar and the rows read
two different halves of it — while the ⋯ menu went on offering to act on
the single row under the cursor with twelve rows visibly marked.
Replace it with the gesture every file list already uses. A plain click
opens a task and picks exactly it; ⌘/Ctrl-click adds or removes one row
without moving the main pane; Shift-click picks the run from the anchor
to the row clicked. There is nothing to enter and nothing to leave, so
Escape simply drops the picks.
Picked rows and the open row share one ground — the same
`var(--color-neutral)` Astryx's own `isSelected` compiles to, so the two
are one paint rather than a near-match kept by hand. `isSelected` itself
stays on the open row alone: it renders `aria-current="page"`, and a set
of picked rows is not a set of current pages. What a screen reader hears
instead rides in the row's existing description.
⋯ and right-click are one menu, not two lists to keep in agreement:
right-click opens the row's own ⋯. Its verbs count the set — 置顶 3 项,
归档 3 项 — and a set already pinned throughout offers 取消置顶 instead,
which is what a set-wide toggle has to do to be unambiguous. A menu
opened on a row outside the set replaces the set with that row, the way
a file list answers a right-click on an unselected file; a row already
in the set leaves it alone. 重命名 is absent from a set menu: renaming
names one task.
The picked set reaches rows as PROPS. A plain click now changes the
selection, so a context the rows subscribed to would redraw all ~1,000
fibers for a switch that moved two of them (#4109); the rendered order a
Shift range needs is read off the DOM at the moment of the click, which
costs nothing per render and is the true order rather than a
reconstruction of it.
Archiving a set does not confirm, matching the single row — it is
reversible, and Settings › 已归档任务 is where it can be undone or made
permanent.
Generated-by: Claude Opus 5 via Claude Code
* fix(desktop): only let a gesture pick rows the user can see and act on
A Shift range read the rail's rendered order straight off the DOM, and
rendered is not the same as reachable. `SideNavItem` collapses a project
by grid track rather than by unmounting, so a range dragged past a
collapsed group swept up rows that were nowhere on screen — twenty tasks
archived from a gesture that highlighted three. A session shared from
someone else's Host is handed no row actions for the same reason it has
no ⋯, but nothing stopped it joining a set and being archived with it;
and right-clicking one claimed the press, discarded the set, and opened
nothing, because the ⋯ it went looking for was never rendered.
One question was being answered in two selectors that only knew how to
find a row, so `isPickableRow` answers it once and the list reads it
everywhere: a row is pickable when it is ours to act on and not inside
an inert subtree. A row with no actions is now a plain navigation item
all the way down — a modifier on it navigates rather than dying, and a
right-click on it goes to the native menu.
Escape had the same shape of bug one layer up: the guard asked whether
focus was in a text field, when the question is whether anything above
the rail owns the press. Astryx's layer stack listens on `document`,
below this handler in the bubble, and stands down on a press that is
already defaultPrevented — so Escape in a rename dialog cleared the set
AND left the dialog with no way to close.
Also drops the sweep's post-completion unmark. "The selection follows
the catalog" already has an owner in the prune effect, and an archive
refreshes the catalog before it resolves; saying it twice was redundant
for archive and wrong for pin, which leaves its rows listed and had the
set empty itself out from under the next action.
Generated-by: Claude Opus 5 via Claude Code
* fix(desktop): sweep only the picked rows the user can still see
A pick can stop being visible without leaving the catalog: collapsing its
project keeps the row mounted and merely `inert`, and a session that becomes a
shared projection loses its actions in place. Pruning against the catalog still
lists both, so a menu opened from a visible row could count — and archive —
rows that are not on screen.
Narrow the set at the menu, which is the single entrance to the sweeps, so the
menu means the set it names: opening one fixes the set at the rows the user can
see and act on, and a pick that was folded away is out of it from then on,
dismissed menu included.
Not narrowed when the project actually collapses, and not derived during
render, because collapse is uncontrolled state inside Astryx's `SideNavItem`
that the rail can only read back off the DOM — the menu's entrance is the one
moment that holds both the set and the DOM.
* fix(desktop): drop the picks when a task opens from outside the rail
Command palette, Module Hub and 新建任务 move the open task without going
through a row, so the rail hears about it only as a new active id. Picked rows
and the open row share one ground, so a set built around the task the user
walked away from goes on reading as a set around the one they are now looking
at — and the next ⋯ sweeps it.
Cleared rather than replaced onto the new row: the rail paints the open row
whether or not it is picked, so an empty selection already reads as "just this
row", and a set of one that happens to equal it would be a second way to say
the same thing. A click inside the rail arrives here too, but its `replace` has
already put the new row in the set, so it is left alone.1 parent c86da40 commit ad18da4
28 files changed
Lines changed: 1777 additions & 1329 deletions
File tree
- apps/desktop
- e2e
- src
- main/__tests__
- renderer
- locales
- styles
- stories
- docs
- packages/ui
- src
- __tests__
- stories
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
359 | 359 | | |
360 | 360 | | |
361 | 361 | | |
362 | | - | |
| 362 | + | |
363 | 363 | | |
364 | 364 | | |
365 | 365 | | |
366 | 366 | | |
367 | 367 | | |
368 | 368 | | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
369 | 374 | | |
370 | 375 | | |
371 | 376 | | |
| |||
389 | 394 | | |
390 | 395 | | |
391 | 396 | | |
392 | | - | |
393 | | - | |
394 | | - | |
| 397 | + | |
395 | 398 | | |
396 | 399 | | |
397 | 400 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
27 | 34 | | |
28 | 35 | | |
29 | 36 | | |
| |||
38 | 45 | | |
39 | 46 | | |
40 | 47 | | |
41 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
42 | 65 | | |
43 | 66 | | |
44 | 67 | | |
| |||
49 | 72 | | |
50 | 73 | | |
51 | 74 | | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | 75 | | |
61 | 76 | | |
62 | 77 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | | - | |
25 | | - | |
| 25 | + | |
26 | 26 | | |
27 | | - | |
28 | | - | |
29 | 27 | | |
30 | 28 | | |
31 | 29 | | |
32 | | - | |
| 30 | + | |
33 | 31 | | |
34 | 32 | | |
35 | 33 | | |
36 | 34 | | |
37 | 35 | | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
44 | 49 | | |
45 | 50 | | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
51 | 62 | | |
| 63 | + | |
52 | 64 | | |
53 | | - | |
54 | | - | |
55 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
56 | 69 | | |
57 | 70 | | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
65 | 74 | | |
66 | 75 | | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
74 | 83 | | |
75 | 84 | | |
76 | 85 | | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
85 | 99 | | |
86 | 100 | | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
94 | 106 | | |
95 | 107 | | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
| 108 | + | |
| 109 | + | |
100 | 110 | | |
101 | 111 | | |
102 | | - | |
103 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
104 | 130 | | |
105 | 131 | | |
106 | 132 | | |
107 | 133 | | |
108 | 134 | | |
109 | | - | |
110 | | - | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
111 | 145 | | |
112 | 146 | | |
113 | 147 | | |
114 | 148 | | |
115 | 149 | | |
116 | | - | |
| 150 | + | |
117 | 151 | | |
118 | 152 | | |
119 | 153 | | |
0 commit comments