Repository navigation
Commit aedab1d
committed
feat(webui): a favourites tab beside the rail's three views
The rail had three views -- projects, running, unread -- and no way to keep
a session. Pinning answers "put this at the top", which is an ordering
preference; it does not answer "I will come back to this one", which is a
different question and outlives a re-sort. Starring is that answer, and it
was already declared: `WEBUI_SESSION_OVERLAY_KEYS.stars` existed in
`LeftRail.tsx` with nothing reading or writing it.
Design compliance
-----------------
The star deliberately does NOT follow the pin's convention, and that is the
point rather than an oversight. The pin's two states are a pin and a slashed
pin because a slash means "remove" across this icon vocabulary. Nobody reads
a filled star as "un-star" -- Gmail, Mail and Notion all use outline/filled.
Reusing the pin's convention here would have made the rail internally
consistent and externally wrong, and a rail that is consistently wrong about
its most-used affordance teaches the reader to distrust every glyph in it.
The pin's button draws the ACTION (already pinned means the click unpins, so
it offers the slashed pin). The star's button draws the STATE. One row,
two conventions, each correct for its own glyph.
The one thing the two share is that both states are drawn by one component,
shared between the row button and the context menu, so they cannot drift.
Counts come from the page, not from the storage map. A star left on a session
that is archived, or that this page has not paged in, is a stale key in
localStorage rather than a row; counting the map would put a number on a tab
whose body is the empty state.
The favourites view is a filter, not a fifth list. Switching to projects and
back returns the full rail rather than a list that consumed the other view.
`starred` is a required argument of both `filterWebuiRailViewSessions` and
`selectWebuiRailViewTabs`, not an optional one. An optional argument
typechecks at every call site that forgot to pass it and reports a count of
zero -- the same shape of bug as a prop that is accepted and then ignored.
Only one of the four views reads it; that is not a reason to make it optional.
Both views reach a standing mark. The row's star button only exists while the
row is hovered, so without a mark a favourite is invisible everywhere except
the tab the reader has to go and open. Pinning already needed this for the
same reason and the same fix.
Colour: the star is the one mark allowed to shout, in the project's own yellow
token (`--text_status_banana`, flips with the theme). The pin beside it stays
grey -- a pin is an ordering preference, a favourite is a thing the reader
singled out, and they are not the same kind of claim. The filled glyph has to
survive the button's own `:hover`, which meant re-asserting the token at
(0,3,0) rather than (0,2,0); see the stylesheet comment for why the obvious
version silently greys out under the pointer.
Validation
----------
`packages/webui/test/unit/rail-star-favorites.test.ts` (29 new cases) and the
`rail-buckets` suite retargeted to the new 4-argument signature.
`packages/webui/test/unit/webui-shell.test.ts` -- "never leaves a control
operable but unbound" moves 11 -> 12 for the fourth tab, and its per-view
loop now names `stars` so a view added to the rail without being added there
would fail rather than sail past the contract. Both halves of that were
checked separately: disabling the tab trips the count, and blanking the count
so it cannot trip trips the per-view loop.
`test/webui-browser/rail-star.spec.mjs` (18 new cases) covers what static
markup cannot: that the mark is visible without a hover, that the paint is
actually the yellow token and not the meta's inherited grey -- including with
the pointer resting on the button itself -- that the two glyph states are two
different drawings rather than one drawing relabelled, that the tab actually
filters, and that a starred child row lights up when its parent is expanded,
the one row shape a node-environment `renderToStaticMarkup` cannot reach
because `childrenExpanded` is internal `useState` with no prop to inject.
Test evidence
-------------
Unit: 1335 passed, 9 failed -- all 9 pre-existing on Windows
(`webui-boundary-check` 7 path assertions, `webui-design-tokens` 1,
`webui-service` 1 shutdown timeout). Baseline, unrelated to this change.
Browser: 18/18 in this spec; 64/64 across the browser suite.
Gates: `typecheck`, `typecheck:webui`, `check:source` (4679 files) all clean.
Negative injection, because a suite that has only ever been green proves
nothing about what it guards:
* unit: 21/21 mutations caught, zero survivors.
* browser: 11/11 mutations caught, zero survivors.
Every mutation was reverted and the tree rebuilt afterwards, so the final
build is the unmutated one.
The one bug the suites did not find, and the reason the dev-app look was not
skipped: the yellow assertion hovered the row, which never triggers the
button's own `:hover`, so it passed over the top of a rule that repainted the
glyph the moment the pointer reached the control. The fix's regression test
was written, watched fail against the unfixed stylesheet, and only then added.
Known scope left out on purpose
-------------------------------
Project rows have no favourite. The ask was sessions, and a starred project
would need its own storage key, its own tab semantics and its own view
filter; half of that is worse than none of it.1 parent 478bffa commit aedab1d
11 files changed
Lines changed: 1057 additions & 46 deletions
File tree
- packages/webui
- src/client
- components
- styles
- test/unit
- release
- test
- webui-browser
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
| 34 | + | |
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
| |||
228 | 230 | | |
229 | 231 | | |
230 | 232 | | |
| 233 | + | |
231 | 234 | | |
232 | 235 | | |
233 | 236 | | |
234 | 237 | | |
235 | 238 | | |
236 | 239 | | |
237 | 240 | | |
| 241 | + | |
238 | 242 | | |
239 | 243 | | |
240 | 244 | | |
| |||
256 | 260 | | |
257 | 261 | | |
258 | 262 | | |
| 263 | + | |
| 264 | + | |
259 | 265 | | |
260 | 266 | | |
261 | 267 | | |
262 | 268 | | |
263 | 269 | | |
264 | 270 | | |
265 | 271 | | |
| 272 | + | |
266 | 273 | | |
267 | 274 | | |
268 | 275 | | |
| |||
401 | 408 | | |
402 | 409 | | |
403 | 410 | | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
404 | 419 | | |
405 | 420 | | |
406 | 421 | | |
| |||
585 | 600 | | |
586 | 601 | | |
587 | 602 | | |
588 | | - | |
589 | | - | |
| 603 | + | |
| 604 | + | |
590 | 605 | | |
591 | 606 | | |
592 | | - | |
593 | | - | |
| 607 | + | |
| 608 | + | |
594 | 609 | | |
595 | 610 | | |
596 | 611 | | |
| |||
637 | 652 | | |
638 | 653 | | |
639 | 654 | | |
| 655 | + | |
640 | 656 | | |
641 | 657 | | |
642 | 658 | | |
| |||
807 | 823 | | |
808 | 824 | | |
809 | 825 | | |
| 826 | + | |
810 | 827 | | |
811 | 828 | | |
812 | 829 | | |
| |||
829 | 846 | | |
830 | 847 | | |
831 | 848 | | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
832 | 868 | | |
833 | 869 | | |
834 | 870 | | |
| |||
884 | 920 | | |
885 | 921 | | |
886 | 922 | | |
| 923 | + | |
887 | 924 | | |
888 | 925 | | |
889 | 926 | | |
| |||
970 | 1007 | | |
971 | 1008 | | |
972 | 1009 | | |
| 1010 | + | |
973 | 1011 | | |
974 | 1012 | | |
975 | 1013 | | |
976 | 1014 | | |
977 | 1015 | | |
978 | 1016 | | |
| 1017 | + | |
| 1018 | + | |
979 | 1019 | | |
980 | | - | |
981 | | - | |
982 | | - | |
983 | | - | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
984 | 1024 | | |
985 | | - | |
| 1025 | + | |
986 | 1026 | | |
987 | 1027 | | |
988 | 1028 | | |
| |||
999 | 1039 | | |
1000 | 1040 | | |
1001 | 1041 | | |
| 1042 | + | |
| 1043 | + | |
| 1044 | + | |
| 1045 | + | |
| 1046 | + | |
| 1047 | + | |
| 1048 | + | |
| 1049 | + | |
| 1050 | + | |
| 1051 | + | |
| 1052 | + | |
1002 | 1053 | | |
1003 | 1054 | | |
1004 | 1055 | | |
| |||
1033 | 1084 | | |
1034 | 1085 | | |
1035 | 1086 | | |
| 1087 | + | |
1036 | 1088 | | |
1037 | 1089 | | |
1038 | 1090 | | |
| |||
1053 | 1105 | | |
1054 | 1106 | | |
1055 | 1107 | | |
| 1108 | + | |
| 1109 | + | |
| 1110 | + | |
| 1111 | + | |
| 1112 | + | |
| 1113 | + | |
1056 | 1114 | | |
1057 | 1115 | | |
1058 | 1116 | | |
| |||
1141 | 1199 | | |
1142 | 1200 | | |
1143 | 1201 | | |
| 1202 | + | |
1144 | 1203 | | |
1145 | 1204 | | |
1146 | 1205 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
353 | 353 | | |
354 | 354 | | |
355 | 355 | | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
356 | 359 | | |
357 | 360 | | |
358 | 361 | | |
| |||
560 | 563 | | |
561 | 564 | | |
562 | 565 | | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
563 | 569 | | |
564 | 570 | | |
565 | 571 | | |
| |||
1185 | 1191 | | |
1186 | 1192 | | |
1187 | 1193 | | |
| 1194 | + | |
1188 | 1195 | | |
1189 | 1196 | | |
1190 | 1197 | | |
1191 | 1198 | | |
1192 | 1199 | | |
1193 | 1200 | | |
1194 | 1201 | | |
| 1202 | + | |
1195 | 1203 | | |
1196 | 1204 | | |
1197 | 1205 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
247 | 247 | | |
248 | 248 | | |
249 | 249 | | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
250 | 316 | | |
251 | 317 | | |
252 | 318 | | |
| |||
0 commit comments