@crewlethq/ui 0.83.0 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +34 -0
  2. package/dist/ActionCard/ActionCard.d.ts +7 -7
  3. package/dist/ActionCard/index.js +1 -1
  4. package/dist/ActionCard-YZKL3CKR.css +130 -0
  5. package/dist/AppShell/AppShell.d.ts +14 -11
  6. package/dist/AppShell/index.js +1 -1
  7. package/dist/{AppShell-U7D2MUUF.css → AppShell-NX5HCD4O.css} +84 -44
  8. package/dist/ArtifactBar/index.js +1 -1
  9. package/dist/ArtifactCard/index.js +2 -2
  10. package/dist/ArtifactKindIllustration/index.js +1 -1
  11. package/dist/{ArtifactKinds-3EH4KR3F.css → ArtifactKinds-27TE3KJL.css} +7 -7
  12. package/dist/ArtifactMakeRow/index.js +1 -1
  13. package/dist/ArtifactNameCell/index.js +1 -1
  14. package/dist/BriefCard/index.js +2 -1
  15. package/dist/CatalogTile/CatalogTile.d.ts +23 -5
  16. package/dist/CatalogTile/index.d.ts +1 -1
  17. package/dist/CatalogTile/index.js +2 -1
  18. package/dist/{CatalogTile-6VF36JWA.css → CatalogTile-TMOSGSV3.css} +4 -3
  19. package/dist/CompactNav/CompactNav.d.ts +10 -6
  20. package/dist/CompactNav/index.js +1 -1
  21. package/dist/DataView/index.js +1 -1
  22. package/dist/DirectoryList/index.js +1 -1
  23. package/dist/{DirectoryList-XZOWYOA3.css → DirectoryList-IXQIMNFS.css} +8 -1
  24. package/dist/InboxList/InboxList.d.ts +28 -2
  25. package/dist/InboxList/index.d.ts +1 -1
  26. package/dist/InboxList/index.js +6 -1
  27. package/dist/{InboxList-OFGSX5IH.css → InboxList-X5Q5WWAI.css} +63 -2
  28. package/dist/ListDetail/ListDetail.d.ts +6 -0
  29. package/dist/ListDetail/index.js +1 -1
  30. package/dist/{ListDetail-N3L7PB46.css → ListDetail-JGESKTR7.css} +38 -0
  31. package/dist/ReadingPane/ReadingPane.d.ts +17 -1
  32. package/dist/ReadingPane/index.d.ts +1 -1
  33. package/dist/ReadingPane/index.js +2 -1
  34. package/dist/{ReadingPane-VLYOWMCM.css → ReadingPane-VFTRDPC5.css} +19 -0
  35. package/dist/RichEditor/index.js +1 -1
  36. package/dist/SectionFrame/index.js +2 -2
  37. package/dist/ShareDialog/index.js +1 -1
  38. package/dist/SidebarNav/SidebarNav.d.ts +18 -2
  39. package/dist/SidebarNav/index.d.ts +1 -1
  40. package/dist/SidebarNav/index.js +1 -1
  41. package/dist/{SidebarNav-E7PZGGA6.css → SidebarNav-IKBKGXW4.css} +20 -0
  42. package/dist/{chunk-AUSFKGKL.js → chunk-3AMWXJQX.js} +1 -1
  43. package/dist/{chunk-Q7N2ATMC.js → chunk-64JNKLLP.js} +57 -9
  44. package/dist/chunk-6C7GQOBV.js +10 -0
  45. package/dist/{chunk-YRNNUCQP.js → chunk-7BDBO2NP.js} +1 -1
  46. package/dist/{chunk-25JQNHTC.js → chunk-BRK3HXGZ.js} +8 -2
  47. package/dist/{chunk-QM3432PH.js → chunk-C4JTX25K.js} +2 -2
  48. package/dist/{chunk-QE4OIE3Q.js → chunk-DJIWCW5Z.js} +6 -4
  49. package/dist/{chunk-QPHD4UUG.js → chunk-F6ZUM66S.js} +160 -25
  50. package/dist/{chunk-MVMVMHYI.js → chunk-HDEKHP65.js} +1 -1
  51. package/dist/{chunk-BXGBGYKH.js → chunk-HKUZ2GYX.js} +1 -1
  52. package/dist/{chunk-ZB3E2Z4Y.js → chunk-J5T5TDM2.js} +1 -1
  53. package/dist/{chunk-C46DDVTK.js → chunk-LTD7KLOU.js} +1 -1
  54. package/dist/{chunk-YBC2CYPD.js → chunk-O4VTQMEF.js} +1 -1
  55. package/dist/{chunk-HTBTAIH6.js → chunk-OM2LABPS.js} +2 -2
  56. package/dist/{chunk-PGBKZSSQ.js → chunk-ORONAXSF.js} +10 -7
  57. package/dist/{chunk-ICE3D63E.js → chunk-QWRAWKB2.js} +50 -9
  58. package/dist/{chunk-SRDNGGZE.js → chunk-RKIJAXQA.js} +1 -1
  59. package/dist/{chunk-CF4M6N6A.js → chunk-XYSSFI5C.js} +1 -7
  60. package/dist/generated/index.js +31 -30
  61. package/dist/index.js +31 -30
  62. package/dist/styles.css +294 -88
  63. package/dist/utils/developing.d.ts +10 -0
  64. package/package.json +3 -3
  65. package/dist/ActionCard-47WSZ2O5.css +0 -110
  66. package/dist/{Editor-5D5Z3LBQ.js → Editor-VGNB35NJ.js} +1 -1
@@ -79,9 +79,10 @@
79
79
  overflow-wrap: anywhere;
80
80
  }
81
81
 
82
- /* A state in words (a StatusDot's label) at a Tag's caption step, so a row
83
- reads the same whichever one a tile carries. It inherited the page's size,
84
- and on a 16px page "Connected" stood taller than the name before it. */
82
+ /* THE STATE, the tone's dot and its word, at the caption step the fact
83
+ beside it takes, so a compact tile's line reads name, state, fact at one
84
+ height. It inherited the page's size, and on a 16px page "Connected" stood
85
+ taller than the name before it. */
85
86
  .crewlet-catalog-tile__status {
86
87
  display: inline-flex;
87
88
  font-size: var(--font-size-xs);
@@ -24,7 +24,7 @@
24
24
  */
25
25
  import { type MouseEventHandler, type ReactNode } from 'react';
26
26
  import { type PlacementRect } from '../Layer/index.js';
27
- import type { NavFigure, NavLinkProps } from '../SidebarNav/index.js';
27
+ import type { NavBadge, NavFigure, NavLinkProps } from '../SidebarNav/index.js';
28
28
  export interface CompactNavProps {
29
29
  /** Names the navigation region, as `SidebarNav`'s label does. */
30
30
  label: string;
@@ -45,9 +45,11 @@ interface CompactPlace {
45
45
  current?: boolean | undefined;
46
46
  /**
47
47
  * How many things here are waiting on the reader: the accent figure on the
48
- * tile's corner, as `SidebarNav.Item`'s badge.
48
+ * tile's corner, as `SidebarNav.Item`'s badge. `tone: 'quiet'` draws it as
49
+ * the quiet figure, as the full rail's quiet badge, and keeps it out of a
50
+ * group's sum.
49
51
  */
50
- badge?: NavFigure | undefined;
52
+ badge?: NavBadge | undefined;
51
53
  /** How many of something the place holds: the quiet figure, as `SidebarNav.Item`'s count. */
52
54
  count?: NavFigure | undefined;
53
55
  /**
@@ -123,7 +125,8 @@ export interface CompactNavGroupProps {
123
125
  * badge's own value with a `+`, since there is more than it says.
124
126
  *
125
127
  * The quiet counts are not carried: a count says how much a place holds, and
126
- * a sum of how much six places hold is a figure about nothing.
128
+ * a sum of how much six places hold is a figure about nothing. Nor is a quiet
129
+ * badge: the group's figure is the accent, and only what calls is added to it.
127
130
  */
128
131
  export declare function groupBadge(links: readonly Pick<CompactNavLink, 'badge'>[]): (number | string) | undefined;
129
132
  /**
@@ -157,8 +160,9 @@ lift?: number): {
157
160
  * whatever came next in the document.
158
161
  *
159
162
  * EVERY PLACE IS A REAL LINK, so a modified press opens a tab as it does in
160
- * the full rail; choosing one closes the flyout and, where the keyboard chose
161
- * it, hands focus back to the tile.
163
+ * the full rail; choosing one closes the flyout, and focus follows the
164
+ * navigation: where the page it opened took focus it stays there, and where
165
+ * it is still in the flyout it goes back to the tile, by a press as by a key.
162
166
  *
163
167
  * WHAT OPENED IT DECIDES WHAT CLOSES IT. Opened by the pointer resting on the
164
168
  * tile, it closes when the pointer leaves both; opened by a press or the keys,
@@ -1,4 +1,4 @@
1
- export { CompactNav, CompactNavGroup, CompactNavItem, CompactNavSeparator, groupBadge, placeFlyout } from '../chunk-ICE3D63E.js';
1
+ export { CompactNav, CompactNavGroup, CompactNavItem, CompactNavSeparator, groupBadge, placeFlyout } from '../chunk-QWRAWKB2.js';
2
2
  import '../chunk-TEV5REGB.js';
3
3
  import '../chunk-HTS5RJ7S.js';
4
4
  import '../chunk-OMV5W53A.js';
@@ -4,9 +4,9 @@ import '../chunk-K4LZXSKZ.js';
4
4
  import '../chunk-7K6LVM5I.js';
5
5
  import '../chunk-WANKINXG.js';
6
6
  import '../chunk-ZXXZESCD.js';
7
+ import '../chunk-YGRTX67J.js';
7
8
  import '../chunk-STJ7QBC3.js';
8
9
  import '../chunk-DTIW4CHZ.js';
9
- import '../chunk-YGRTX67J.js';
10
10
  import '../chunk-EE3DJMSQ.js';
11
11
  import '../chunk-NXFTCPF6.js';
12
12
  import '../chunk-EC6EUIAD.js';
@@ -1,3 +1,3 @@
1
- export { DirectoryList } from '../chunk-SRDNGGZE.js';
1
+ export { DirectoryList } from '../chunk-RKIJAXQA.js';
2
2
  import '../chunk-PQADV6P6.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -17,7 +17,12 @@
17
17
  font-family: var(--font-family-sans);
18
18
  }
19
19
 
20
- /* The groups take the room and scroll on their own, so the foot stays put. */
20
+ /* The groups take the room and scroll on their own, so the foot stays put.
21
+
22
+ THEIR INSET IS INSIDE THE SCROLLER. The gutter is reserved whether or not
23
+ they scroll, and the lines end a step before it: a line's state pill stood
24
+ against the scrollbar, and moved by its width when the list grew long
25
+ enough to scroll. */
21
26
  .crewlet-directory__groups {
22
27
  display: flex;
23
28
  flex: 1 1 auto;
@@ -26,7 +31,9 @@
26
31
  under it rather than a band between two runs of them. */
27
32
  gap: var(--spacing-3);
28
33
  min-height: 0;
34
+ padding-right: var(--spacing-2);
29
35
  overflow-y: auto;
36
+ scrollbar-gutter: stable;
30
37
  }
31
38
 
32
39
  .crewlet-directory__group {
@@ -48,13 +48,27 @@ export interface InboxGroupProps extends Omit<HTMLAttributes<HTMLElement>, 'titl
48
48
  /** Whether a collapsible group starts open, when the group holds it. */
49
49
  defaultOpen?: boolean | undefined;
50
50
  onOpenChange?: ((open: boolean) => void) | undefined;
51
+ /**
52
+ * The latest row to arrive in the group, as a token (the row's key): each
53
+ * new value plays a brief wash of the accent over the group's label and
54
+ * count, so a row that lands in a closed group (Answered) is seen to land.
55
+ * Pass it only for a row that has just arrived, never on a first draw.
56
+ */
57
+ arrived?: string | number | undefined;
51
58
  }
59
+ /**
60
+ * The motion a leaving row waits for: its height closing, a transition of the
61
+ * row's own `grid-template-rows` (InboxList.css). A suite fakes exactly this.
62
+ */
63
+ export declare const INBOX_ROW_LEAVE: {
64
+ readonly transitions: readonly ["grid-template-rows"];
65
+ };
52
66
  /**
53
67
  * A labelled run of rows. The label is a heading set in the micro-label
54
68
  * register, so a reader navigating by heading lands on each group and a
55
69
  * reader scanning sees where one ends.
56
70
  */
57
- export declare function InboxGroup({ label, hideLabel, headingLevel, count, collapsible, open: controlledOpen, defaultOpen, onOpenChange, className, children, ...rest }: InboxGroupProps): import("react").JSX.Element;
71
+ export declare function InboxGroup({ label, hideLabel, headingLevel, count, collapsible, open: controlledOpen, defaultOpen, onOpenChange, arrived, className, children, ...rest }: InboxGroupProps): import("react").JSX.Element;
58
72
  export type InboxRowTone = 'default' | 'danger';
59
73
  export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'onClick' | 'title'> {
60
74
  /** Who the row is from: a seat's or a person's name. */
@@ -114,6 +128,18 @@ export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'on
114
128
  * open the link in a tab.
115
129
  */
116
130
  onClick?: ((event: MouseEvent<HTMLElement>) => void) | undefined;
131
+ /**
132
+ * On its way out: the row stays drawn, inert, while its height closes, so
133
+ * the rows under it slide up rather than jump, and is drawn no more once it
134
+ * has closed. An `InboxGroup` does this itself for a keyed row that leaves
135
+ * its children, and a wrapper the host draws its rows through forwards
136
+ * this and `onLeft` to its `InboxRow`; pass it for a list of your own.
137
+ */
138
+ leaving?: boolean | undefined;
139
+ /** Runs once a leaving row has closed and is drawn no more, once for each leave. */
140
+ onLeft?: (() => void) | undefined;
141
+ /** Just arrived: a brief wash of the accent that fades back to the row's own ground. */
142
+ arrived?: boolean | undefined;
117
143
  }
118
144
  /**
119
145
  * One row: WHO, then WHAT in up to three lines, then the task it is about.
@@ -137,7 +163,7 @@ export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'on
137
163
  * the words take up to three lines before they stop, so a row says what it is
138
164
  * rather than its first few words.
139
165
  */
140
- export declare function InboxRow({ title, qualifier, qualifierTone, meta, leading, excerpt, aside, selected, unread, unreadLabel, actionable, done, doneLabel, status, outcome, outcomeIcon, outcomeTone, href, onClick, className, children, ...rest }: InboxRowProps): import("react").JSX.Element;
166
+ export declare function InboxRow({ title, qualifier, qualifierTone, meta, leading, excerpt, aside, selected, unread, unreadLabel, actionable, done, doneLabel, status, outcome, outcomeIcon, outcomeTone, href, onClick, leaving, onLeft, arrived, className, children, ...rest }: InboxRowProps): import("react").JSX.Element | null;
141
167
  export type InboxMoreProps = HTMLAttributes<HTMLDivElement>;
142
168
  /**
143
169
  * The end of the list: the quiet toggles that open what is not in it by
@@ -1,3 +1,3 @@
1
1
  import './InboxList.css';
2
- export { InboxList, InboxGroup, InboxRow, InboxMore, InboxToggle } from './InboxList.js';
2
+ export { InboxList, InboxGroup, InboxRow, InboxMore, InboxToggle, INBOX_ROW_LEAVE } from './InboxList.js';
3
3
  export type { InboxListProps, InboxGroupProps, InboxRowProps, InboxRowTone, InboxMoreProps, InboxToggleProps, } from './InboxList.js';
@@ -1,5 +1,10 @@
1
- export { InboxGroup, InboxList, InboxMore, InboxRow, InboxToggle } from '../chunk-QPHD4UUG.js';
1
+ export { INBOX_ROW_LEAVE, InboxGroup, InboxList, InboxMore, InboxRow, InboxToggle } from '../chunk-F6ZUM66S.js';
2
2
  import '../chunk-RK4XPHVG.js';
3
+ import '../chunk-6C7GQOBV.js';
4
+ import '../chunk-PCAT2EFB.js';
5
+ import '../chunk-YJQXESOJ.js';
6
+ import '../chunk-HTS5RJ7S.js';
7
+ import '../chunk-4ITETIGZ.js';
3
8
  import '../chunk-PQADV6P6.js';
4
9
  import '../chunk-UT6CW3C7.js';
5
10
  import '../chunk-ODEQTPFM.js';
@@ -26,6 +26,9 @@
26
26
 
27
27
  /* The heading's own line box is the label's, so the label sits where it is drawn. */
28
28
  .crewlet-inbox-list__label {
29
+ /* The arrival's wash stands behind the label's words, inside it. */
30
+ position: relative;
31
+ isolation: isolate;
29
32
  margin: 0;
30
33
  padding: var(--spacing-3) var(--spacing-2) var(--spacing-1);
31
34
  font: inherit;
@@ -33,6 +36,32 @@
33
36
  line-height: var(--font-line-height-snug);
34
37
  }
35
38
 
39
+ /*
40
+ * A ROW ARRIVED: a wash of the accent that fades back to the ground, over the
41
+ * row itself, or over a group's label and count where the row landed in a
42
+ * closed group. The row's keyframes name no last step, so the wash returns
43
+ * to whatever the row draws of its own (hover, selected).
44
+ */
45
+ .crewlet-inbox-list__arrival {
46
+ position: absolute;
47
+ inset: 0;
48
+ z-index: -1;
49
+ border-radius: var(--radius-xs);
50
+ pointer-events: none;
51
+ animation: crewlet-inbox-arrive calc(var(--motion-duration-moderate) * 8) var(--motion-easing-out) both;
52
+ }
53
+
54
+ .crewlet-inbox-row--arrived {
55
+ animation: crewlet-inbox-arrive calc(var(--motion-duration-moderate) * 8) var(--motion-easing-out);
56
+ }
57
+
58
+ @keyframes crewlet-inbox-arrive {
59
+ 0%,
60
+ 40% {
61
+ background: var(--color-brand-accent-soft-strong);
62
+ }
63
+ }
64
+
36
65
  /* The count after a label, in the quiet ink and the label's own register. */
37
66
  .crewlet-inbox-list__count {
38
67
  margin-inline-start: var(--spacing-1);
@@ -107,12 +136,38 @@
107
136
  * a selected row takes no hover on top of it: both are a background on the
108
137
  * same element, and a composite of the two is a colour nothing has measured.
109
138
  */
139
+ /* ONE TRACK, so a leaving row can close its height (below). */
110
140
  .crewlet-inbox-row {
141
+ display: grid;
142
+ grid-template-rows: 1fr;
111
143
  min-width: 0;
112
144
  border-radius: var(--radius-md);
113
145
  transition: background var(--motion-duration-fast) var(--motion-easing-default);
114
146
  }
115
147
 
148
+ .crewlet-inbox-row__body {
149
+ min-width: 0;
150
+ min-height: 0;
151
+ }
152
+
153
+ /*
154
+ * A ROW LEAVING closes its one track to nothing and fades, on the slow step
155
+ * with the emphasized curve, the motion a column opens and shuts by, so the
156
+ * rows under it slide up into its place. The shell's presence waits on the
157
+ * track's transition (`INBOX_ROW_LEAVE`), and the row is inert all the while.
158
+ */
159
+ .crewlet-inbox-row--leaving {
160
+ grid-template-rows: 0fr;
161
+ opacity: 0;
162
+ transition:
163
+ grid-template-rows var(--motion-duration-slow) var(--motion-easing-emphasized),
164
+ opacity var(--motion-duration-moderate) var(--motion-easing-out);
165
+ }
166
+
167
+ .crewlet-inbox-row--leaving .crewlet-inbox-row__body {
168
+ overflow: hidden;
169
+ }
170
+
116
171
  .crewlet-inbox-row:hover {
117
172
  background: var(--color-surface-hover);
118
173
  }
@@ -349,7 +404,7 @@
349
404
  }
350
405
 
351
406
  /* A row with no leading slot: the words start at the control's own pad, and so does the task. */
352
- .crewlet-inbox-row:not(:has(.crewlet-inbox-row__leading)) > .crewlet-inbox-row__aside {
407
+ .crewlet-inbox-row:not(:has(.crewlet-inbox-row__leading)) .crewlet-inbox-row__aside {
353
408
  padding-inline-start: var(--spacing-2);
354
409
  }
355
410
 
@@ -385,10 +440,16 @@
385
440
  }
386
441
 
387
442
  @media (prefers-reduced-motion: reduce) {
388
- .crewlet-inbox-row {
443
+ .crewlet-inbox-row,
444
+ .crewlet-inbox-row--leaving {
389
445
  transition: none;
390
446
  }
391
447
 
448
+ .crewlet-inbox-row--arrived,
449
+ .crewlet-inbox-list__arrival {
450
+ animation: none;
451
+ }
452
+
392
453
  .crewlet-inbox-list__toggle,
393
454
  .crewlet-inbox-list__chevron {
394
455
  transition: none;
@@ -21,6 +21,12 @@ export interface ListDetailProps extends Omit<HTMLAttributes<HTMLDivElement>, 'c
21
21
  * column. The list column is `--crewlet-list-detail-list` wide (17rem by
22
22
  * default), with a hairline between the two.
23
23
  *
24
+ * THE DETAIL'S SCROLLBAR IS ON THE SHEET'S EDGE. Placed directly in a fill
25
+ * screen's column, it reaches past the column's end pad, and the detail
26
+ * keeps the screen's side inset inside itself, with its scrollbar's gutter
27
+ * always reserved: its content ends under the bar's last control, and a
28
+ * trailing control never touches the scrollbar.
29
+ *
24
30
  * BELOW 640px OF ITS OWN WIDTH IT IS ONE PANE, the one `showing` names: the
25
31
  * list and a reading detail do not both fit, whether the window is a phone or
26
32
  * an aside beside the screen has taken the room. The application gives its
@@ -1,2 +1,2 @@
1
- export { ListDetail } from '../chunk-YBC2CYPD.js';
1
+ export { ListDetail } from '../chunk-O4VTQMEF.js';
2
2
  import '../chunk-ODEQTPFM.js';
@@ -10,6 +10,17 @@
10
10
  * drew the detail 130px wide beside a 17rem list. The phone rule below is the
11
11
  * one decision that stays the window's: on a phone the page scrolls as a
12
12
  * whole rather than each pane on its own.
13
+ *
14
+ * ON A SCREEN THAT FILLS THE WINDOW, THE PANE THAT SCROLLS IS ITS OUTERMOST
15
+ * BOX, and its inset is padding inside it. Standing directly in the fill
16
+ * column, the ListDetail reaches past the column's end pad to the sheet's
17
+ * edge (below), so the scrollbar is the sheet's last pixel; the detail then
18
+ * reserves its scrollbar's gutter whether or not it scrolls, and keeps the
19
+ * screen's side inset between its content and that gutter, so a trailing
20
+ * control (a header's Disconnect, a tile's Open) never touches the scrollbar,
21
+ * does not move when the detail grows long enough to scroll, and ends under
22
+ * the bar's last control. Anywhere else (a scrolling or a flush screen, or
23
+ * outside a shell) it is as wide as its box, with nothing reserved.
13
24
  */
14
25
 
15
26
  /* THE HAIRLINE STANDS MIDWAY between the two panes: the list's inset before it
@@ -17,6 +28,9 @@
17
28
  the list than to the detail it divides it from; the two still add to 64. */
18
29
  .crewlet-list-detail {
19
30
  --crewlet-list-detail-gutter: var(--spacing-7);
31
+ /* The inset at a scrolling pane's end: the screen's own side inset in a
32
+ shell, so the page's two edges match. */
33
+ --crewlet-list-detail-end: var(--crewlet-app-shell-inset, var(--spacing-5));
20
34
 
21
35
  container: crewlet-list-detail / inline-size;
22
36
  display: flex;
@@ -27,6 +41,21 @@
27
41
  min-height: 0;
28
42
  }
29
43
 
44
+ /* STANDING IN A FILL SCREEN'S COLUMN, IT TAKES THE COLUMN'S END PAD BACK: the
45
+ inset and the scrollbar's width, which the shell publishes for exactly
46
+ this (`--crewlet-app-shell-bleed-end`, AppShell.css). The detail keeps the
47
+ same inset inside itself, and reserves its gutter, there and only there. */
48
+ :where(.crewlet-app-shell--fill:not(.crewlet-app-shell--fill-flush) .crewlet-app-shell__content) > .crewlet-list-detail {
49
+ margin-right: calc(var(--crewlet-app-shell-bleed-end, 0px) * -1);
50
+ }
51
+
52
+ :where(.crewlet-app-shell--fill:not(.crewlet-app-shell--fill-flush) .crewlet-app-shell__content)
53
+ > .crewlet-list-detail
54
+ .crewlet-list-detail__detail {
55
+ padding-right: var(--crewlet-list-detail-end);
56
+ scrollbar-gutter: stable;
57
+ }
58
+
30
59
  .crewlet-list-detail__panes {
31
60
  display: grid;
32
61
  flex: 1 1 auto;
@@ -68,6 +97,15 @@
68
97
  overflow-y: auto;
69
98
  }
70
99
 
100
+ /* The list is the pane that scrolls now, and takes the detail's end where
101
+ the ListDetail reaches the sheet. */
102
+ :where(.crewlet-app-shell--fill:not(.crewlet-app-shell--fill-flush) .crewlet-app-shell__content)
103
+ > .crewlet-list-detail
104
+ .crewlet-list-detail__list {
105
+ padding-right: var(--crewlet-list-detail-end);
106
+ scrollbar-gutter: stable;
107
+ }
108
+
71
109
  .crewlet-list-detail--showing-list .crewlet-list-detail__detail,
72
110
  .crewlet-list-detail--showing-detail .crewlet-list-detail__list {
73
111
  display: none;
@@ -3,7 +3,23 @@ import { type HeadingLevel } from '../utils/headingLevel.js';
3
3
  export interface ReadingPaneProps extends HTMLAttributes<HTMLElement> {
4
4
  /** Overrides the level the title is rendered at. */
5
5
  headingLevel?: HeadingLevel | undefined;
6
+ /**
7
+ * What the pane shows, as a key: the item's id. A new value draws the new
8
+ * item afresh and plays it in, a short fade and rise on the moderate step
9
+ * (`READING_PANE_ENTER` on the column; nothing moves under reduced motion),
10
+ * and moves focus to its title, without scrolling the page, where the reader
11
+ * was last in the pane (focus came into it, or a press landed in it): the
12
+ * control they answered with went with the old item. A reader in the list
13
+ * beside the pane, or nowhere (a live update), keeps their place.
14
+ */
15
+ contentKey?: string | number | undefined;
16
+ /** Where focus goes after a change of `contentKey`, in place of the title. */
17
+ focusTarget?: (() => HTMLElement | null | undefined) | undefined;
6
18
  }
19
+ /** The keyframes a new item plays in by, on the column: the one motion a suite fakes. */
20
+ export declare const READING_PANE_ENTER: {
21
+ readonly animations: readonly ["crewlet-reading-pane-enter"];
22
+ };
7
23
  /**
8
24
  * One item read in full, top to bottom, in one column: the line saying who is
9
25
  * asking and about what, the question in large type, its context, the
@@ -19,7 +35,7 @@ export interface ReadingPaneProps extends HTMLAttributes<HTMLElement> {
19
35
  * The article is named by its title, so a reader moving by landmark hears the
20
36
  * question rather than "article".
21
37
  */
22
- export declare function ReadingPane({ headingLevel, className, children, ...rest }: ReadingPaneProps): import("react").JSX.Element;
38
+ export declare function ReadingPane({ headingLevel, contentKey, focusTarget, className, children, ...rest }: ReadingPaneProps): import("react").JSX.Element;
23
39
  export declare namespace ReadingPane {
24
40
  var Byline: typeof ReadingPaneByline;
25
41
  var Title: typeof ReadingPaneTitle;
@@ -1,3 +1,3 @@
1
1
  import './ReadingPane.css';
2
- export { ReadingPane, ReadingPaneByline, ReadingPaneTitle, ReadingPaneLead, ReadingPaneQuote, ReadingPaneReasoning, ReadingPaneOutcome, ReadingPaneSection, } from './ReadingPane.js';
2
+ export { ReadingPane, ReadingPaneByline, ReadingPaneTitle, ReadingPaneLead, ReadingPaneQuote, ReadingPaneReasoning, ReadingPaneOutcome, ReadingPaneSection, READING_PANE_ENTER, } from './ReadingPane.js';
3
3
  export type { ReadingPaneProps, ReadingPaneBylineProps, ReadingPaneTitleProps, ReadingPaneLeadProps, ReadingPaneQuoteProps, ReadingPaneReasoningProps, ReadingPaneOutcomeProps, ReadingPaneSectionProps, } from './ReadingPane.js';
@@ -1,4 +1,5 @@
1
- export { ReadingPane, ReadingPaneByline, ReadingPaneLead, ReadingPaneOutcome, ReadingPaneQuote, ReadingPaneReasoning, ReadingPaneSection, ReadingPaneTitle } from '../chunk-Q7N2ATMC.js';
1
+ export { READING_PANE_ENTER, ReadingPane, ReadingPaneByline, ReadingPaneLead, ReadingPaneOutcome, ReadingPaneQuote, ReadingPaneReasoning, ReadingPaneSection, ReadingPaneTitle } from '../chunk-64JNKLLP.js';
2
2
  import '../chunk-RK4XPHVG.js';
3
+ import '../chunk-4ITETIGZ.js';
3
4
  import '../chunk-PQADV6P6.js';
4
5
  import '../chunk-ODEQTPFM.js';
@@ -32,6 +32,25 @@
32
32
  }
33
33
  }
34
34
 
35
+ /* A NEW ITEM PLAYS IN (`contentKey`): a short fade and rise, so the reader
36
+ sees the pane move on rather than its words change under them. */
37
+ .crewlet-reading-pane__column--entering {
38
+ animation: crewlet-reading-pane-enter var(--motion-duration-moderate) var(--motion-easing-out);
39
+ }
40
+
41
+ @keyframes crewlet-reading-pane-enter {
42
+ from {
43
+ opacity: 0;
44
+ transform: translateY(var(--spacing-2));
45
+ }
46
+ }
47
+
48
+ @media (prefers-reduced-motion: reduce) {
49
+ .crewlet-reading-pane__column--entering {
50
+ animation: none;
51
+ }
52
+ }
53
+
35
54
  /* ---- The byline -------------------------------------------------------- */
36
55
 
37
56
  .crewlet-reading-pane__byline {
@@ -1,4 +1,4 @@
1
- export { RichEditor, preloadRichEditor } from '../chunk-AUSFKGKL.js';
1
+ export { RichEditor, preloadRichEditor } from '../chunk-3AMWXJQX.js';
2
2
  export { RICH_EDITOR_LABELS, richEditorLabels } from '../chunk-2DMTW2Y3.js';
3
3
  import '../chunk-KOJPDCGQ.js';
4
4
  import '../chunk-KBZO4B33.js';
@@ -1,5 +1,5 @@
1
- export { SectionFrame, SectionFrameBody, SectionFrameNav } from '../chunk-YRNNUCQP.js';
2
- import '../chunk-25JQNHTC.js';
1
+ export { SectionFrame, SectionFrameBody, SectionFrameNav } from '../chunk-7BDBO2NP.js';
2
+ import '../chunk-BRK3HXGZ.js';
3
3
  import '../chunk-TEV5REGB.js';
4
4
  import '../chunk-HBDA76EZ.js';
5
5
  import '../chunk-2YTOOH3A.js';
@@ -1,10 +1,10 @@
1
1
  export { ARTIFACT_ROLES, SHAREABLE_ROLES, ShareDialog, roleAtLeast } from '../chunk-AQNP3KQU.js';
2
2
  import '../chunk-3IWZVEXA.js';
3
3
  import '../chunk-ZXXZESCD.js';
4
+ import '../chunk-YGRTX67J.js';
4
5
  import '../chunk-T5GBTXNC.js';
5
6
  import '../chunk-STJ7QBC3.js';
6
7
  import '../chunk-DTIW4CHZ.js';
7
- import '../chunk-YGRTX67J.js';
8
8
  import '../chunk-BPCY7ZID.js';
9
9
  import '../chunk-HF5L56YQ.js';
10
10
  import '../chunk-IRONPJB4.js';
@@ -36,6 +36,7 @@
36
36
  * own prop types, so one list of places feeds both shapes.
37
37
  */
38
38
  import { type MouseEventHandler, type ReactNode } from 'react';
39
+ import type { CountTone } from '../Count/index.js';
39
40
  export interface SidebarNavProps {
40
41
  /**
41
42
  * Names the navigation region. A page with two of them ("Sections" and
@@ -148,6 +149,20 @@ export interface NavFigure {
148
149
  */
149
150
  label: string;
150
151
  }
152
+ /**
153
+ * A figure of what waits on the reader, and how loudly it asks.
154
+ *
155
+ * `attention` (the default) is the accent's filled pill, the one hue in the
156
+ * chrome: what the reader is asked to act on, the unread in an inbox.
157
+ * `quiet` keeps the pill's round shape and its place at the row's end, in
158
+ * the quiet count's grey: work that is the reader's own and is waiting, but
159
+ * not calling (the open tasks assigned to them). The vocabulary is `Count`'s
160
+ * own (`CountTone`), so a figure is quiet or calling in the same words
161
+ * wherever it stands.
162
+ */
163
+ export interface NavBadge extends NavFigure {
164
+ tone?: CountTone | undefined;
165
+ }
151
166
  /** A quiet figure: how many of something the destination holds. */
152
167
  export interface NavCount extends NavFigure {
153
168
  /**
@@ -180,9 +195,10 @@ export interface NavItemProps {
180
195
  * of what is waiting on the reader is the one thing in the rail that asks
181
196
  * them to act, which is what the accent means; it is a FILL carrying a
182
197
  * figure rather than a figure in a hue, so it still reads to somebody who
183
- * cannot separate the hue. Anything else is a `count`.
198
+ * cannot separate the hue. Anything else is a `count`. `tone: 'quiet'`
199
+ * keeps the pill and draws it in the quiet count's grey (`NavBadge`).
184
200
  */
185
- badge?: NavFigure | undefined;
201
+ badge?: NavBadge | undefined;
186
202
  href?: string | undefined;
187
203
  /** Whether this row is the screen the reader is on. */
188
204
  current?: boolean | undefined;
@@ -1,4 +1,4 @@
1
1
  import '../HitArea/HitArea.css';
2
2
  import './SidebarNav.css';
3
3
  export { NavGroup, NavItem, SidebarNav } from './SidebarNav.js';
4
- export type { NavCount, NavFigure, NavGroupAction, NavGroupProps, NavItemProps, NavLinkProps, SidebarNavProps, } from './SidebarNav.js';
4
+ export type { NavBadge, NavCount, NavFigure, NavGroupAction, NavGroupProps, NavItemProps, NavLinkProps, SidebarNavProps, } from './SidebarNav.js';
@@ -1,4 +1,4 @@
1
- export { NavGroup, NavItem, SidebarNav } from '../chunk-25JQNHTC.js';
1
+ export { NavGroup, NavItem, SidebarNav } from '../chunk-BRK3HXGZ.js';
2
2
  import '../chunk-TEV5REGB.js';
3
3
  import '../chunk-HBDA76EZ.js';
4
4
  import '../chunk-2YTOOH3A.js';
@@ -415,6 +415,26 @@
415
415
  line-height: 1;
416
416
  }
417
417
 
418
+ /*
419
+ * A QUIET BADGE keeps the pill, its size and its place at the row's end, and
420
+ * takes the quiet count's grey: a neutral wash (`--color-fill-soft`, the
421
+ * ground of a quiet label at rest) carrying the secondary ink, for work that
422
+ * is the reader's own and waiting, but not calling. The secondary ink rather
423
+ * than the count's tertiary, because on a wash over a hovered row the
424
+ * tertiary measured 4.41:1 in light; it is the ink the compact rail's quiet
425
+ * figure takes, so the two shapes draw the same grey. On the reader's own row
426
+ * its ink lifts to the row's, as the count's does; the suite measures the
427
+ * pair on every ground a row can have.
428
+ */
429
+ .crewlet-nav-item__badge--quiet {
430
+ background: var(--color-fill-soft);
431
+ color: var(--color-text-secondary);
432
+ }
433
+
434
+ .crewlet-nav-item__row[aria-current='page'] .crewlet-nav-item__badge--quiet {
435
+ color: inherit;
436
+ }
437
+
418
438
  /* Beside a count the badge is the second figure, not a second push to the
419
439
  end: two auto margins would split the room between them and float the
420
440
  count into the middle of the row. */
@@ -9,7 +9,7 @@ import { jsx } from 'react/jsx-runtime';
9
9
  var engine = null;
10
10
  var loading = null;
11
11
  function loadEngine() {
12
- loading ??= import('./Editor-5D5Z3LBQ.js').then(
12
+ loading ??= import('./Editor-VGNB35NJ.js').then(
13
13
  (module) => {
14
14
  engine = module.Editor;
15
15
  return engine;