@peasant-labs/fairtrade 0.0.20 → 0.0.22

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.
@@ -34,6 +34,7 @@
34
34
  --bp-lg: 1024px;
35
35
  --bp-xl: 1440px;
36
36
  --dialog-w: 420px;
37
+ --dialog-w-wide: 720px;
37
38
  --fs-label: 14px;
38
39
  --fs-sm: 14px;
39
40
  --fs-body: 16px;
@@ -590,6 +590,10 @@
590
590
  "$type": "other",
591
591
  "$value": "420px"
592
592
  },
593
+ "dialog-w-wide": {
594
+ "$type": "other",
595
+ "$value": "720px"
596
+ },
593
597
  "menu-min-w": {
594
598
  "$type": "other",
595
599
  "$value": "256px"
@@ -79,4 +79,30 @@ export function TeachingEmptyState({ icon: Icon, title, body, command, privacy,
79
79
  as?: keyof JSX.IntrinsicElements;
80
80
  className?: string;
81
81
  }): import("react").JSX.Element;
82
+ /**
83
+ * LocalOfflineBanner — the page-level notice for "the peasant app on this computer stopped". It
84
+ * says what is wrong in plain words (the local program, not the internet), gives the command that
85
+ * starts it again in the existing copy-able command chip, and offers `try again`. There is no
86
+ * wifi glyph: the icon is the same unplug the lost-connection panel uses.
87
+ *
88
+ * The message is the `role="status"` live region. The command, the button and the `last checked`
89
+ * time sit outside it, so a retry that only moves the time never re-announces the whole banner.
90
+ * The command is code: mono, never lowercased. Everything else is lowercase chrome.
91
+ *
92
+ * @param {object} props
93
+ * @param {() => void} [props.onRetry] - `try again`; omit it and no button renders.
94
+ * @param {boolean} [props.retrying=false] - a retry is in flight: the button reports busy and
95
+ * ignores further presses until the host clears it.
96
+ * @param {Date | string | number} [props.checkedAt] - when the host last checked; shown as
97
+ * hh:mm:ss with tabular numbers. omitted, no time renders.
98
+ * @param {string} [props.command='peasant web start'] - the command that starts the local app.
99
+ * @param {string} [props.className]
100
+ */
101
+ export function LocalOfflineBanner({ onRetry, retrying, checkedAt, command, className, ...rest }: {
102
+ onRetry?: () => void;
103
+ retrying?: boolean;
104
+ checkedAt?: Date | string | number;
105
+ command?: string;
106
+ className?: string;
107
+ }): import("react").JSX.Element;
82
108
  export default ConnectionPill;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @param {object} props
3
+ * @param {string} props.value - the exact text written to the clipboard.
4
+ * @param {string} props.label - the accessible name, a verb and an object ("copy link").
5
+ * @param {() => void} [props.onCopied] - called after a successful write.
6
+ * @param {'sm' | 'md'} [props.size='sm'] - `sm` uses the compact control height.
7
+ * @param {string} [props.className]
8
+ */
9
+ export default function CopyIconButton({ value, label, onCopied, size, className, ...rest }: {
10
+ value: string;
11
+ label: string;
12
+ onCopied?: () => void;
13
+ size?: "sm" | "md";
14
+ className?: string;
15
+ }): import("react").JSX.Element;
@@ -1,4 +1,4 @@
1
- export default function Dialog({ open, onClose, title, labelId, children, footer, returnFocusRef }: {
1
+ export default function Dialog({ open, onClose, title, labelId, children, footer, returnFocusRef, size, className, dismissible }: {
2
2
  open: any;
3
3
  onClose: any;
4
4
  title: any;
@@ -6,4 +6,7 @@ export default function Dialog({ open, onClose, title, labelId, children, footer
6
6
  children: any;
7
7
  footer: any;
8
8
  returnFocusRef: any;
9
+ size?: string;
10
+ className?: string;
11
+ dismissible?: boolean;
9
12
  }): import("react").JSX.Element;
@@ -2,7 +2,7 @@
2
2
  * A grouped tri-state multi-select tree. See GroupedMultiSelectProps.
3
3
  * @param {GroupedMultiSelectProps} props
4
4
  */
5
- export default function GroupedMultiSelect({ groups, value, defaultValue, onChange, tokenLabel, defaultOpen, ariaLabel, }: GroupedMultiSelectProps): import("react").JSX.Element;
5
+ export default function GroupedMultiSelect({ groups, value, defaultValue, onChange, tokenLabel, defaultOpen, ariaLabel, searchable, searchLabel, }: GroupedMultiSelectProps): import("react").JSX.Element;
6
6
  export type GMSItem = {
7
7
  /**
8
8
  * unique selection id (what lands in the value Set).
@@ -20,6 +20,10 @@ export type GMSItem = {
20
20
  * token count for the running tally; defaults to 0.
21
21
  */
22
22
  tokens?: number;
23
+ /**
24
+ * the text a search matches; defaults to a string `label`.
25
+ */
26
+ searchText?: string;
23
27
  };
24
28
  export type GMSGroup = {
25
29
  /**
@@ -64,4 +68,14 @@ export type GroupedMultiSelectProps = {
64
68
  * accessible name for the tree container.
65
69
  */
66
70
  ariaLabel?: string;
71
+ /**
72
+ * adds a search field and a clear button: the search
73
+ * filters items (by `searchText`, else a string `label`), hides groups left empty,
74
+ * opens the groups that match, and select all / clear act on the visible items only.
75
+ */
76
+ searchable?: boolean;
77
+ /**
78
+ * the search field's label.
79
+ */
80
+ searchLabel?: string;
67
81
  };
@@ -13,18 +13,24 @@
13
13
  * Menu - a dropdown menu controlled by a single trigger button.
14
14
  *
15
15
  * @param {Object} props
16
- * @param {React.ReactNode} props.label trigger label (lowercase chrome by default)
16
+ * @param {React.ReactNode} [props.label] trigger label (lowercase chrome by default); omit it with `icon`
17
17
  * @param {MenuItem[]} [props.items=[]] menu rows
18
18
  * @param {'start'|'end'} [props.align='start'] which edge of the trigger the popout aligns to
19
19
  * @param {React.ReactNode} [props.caption] optional .menu-cap caption rendered inside the popout before the list
20
20
  * @param {boolean} [props.defaultOpen=false] render with the popout already open (specimen / screenshot state)
21
+ * @param {React.ComponentType<any>} [props.icon] icon-only trigger: the glyph replaces the label and the caret
22
+ * @param {string} [props.ariaLabel] the trigger's accessible name; required with `icon`, and the menu's name
23
+ * @param {'sm'|'md'} [props.size='md'] `sm` uses the compact control height (header rows)
21
24
  */
22
- export default function Menu({ label, items, align, caption, defaultOpen }: {
23
- label: React.ReactNode;
25
+ export default function Menu({ label, items, align, caption, defaultOpen, icon: TriggerIcon, ariaLabel, size }: {
26
+ label?: React.ReactNode;
24
27
  items?: MenuItem[];
25
28
  align?: "start" | "end";
26
29
  caption?: React.ReactNode;
27
30
  defaultOpen?: boolean;
31
+ icon?: React.ComponentType<any>;
32
+ ariaLabel?: string;
33
+ size?: "sm" | "md";
28
34
  }): import("react").JSX.Element;
29
35
  export type MenuItem = {
30
36
  /**
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @param {object} props
3
+ * @param {import('react').ReactNode[]} props.items - the host's item nodes, in order.
4
+ * @param {number} [props.limit=2] - how many items show before the `+N` button.
5
+ * @param {string} props.label - the list's accessible name ("pull requests").
6
+ * @param {[string, string]} [props.noun=['item', 'items']] - singular and plural for the button's name.
7
+ * @param {import('react').ReactNode} [props.empty=null] - rendered when there are no items.
8
+ * @param {string} [props.className]
9
+ */
10
+ export default function OverflowList({ items, limit, label, noun, empty, className, ...rest }: {
11
+ items: import("react").ReactNode[];
12
+ limit?: number;
13
+ label: string;
14
+ noun?: [string, string];
15
+ empty?: import("react").ReactNode;
16
+ className?: string;
17
+ }): string | number | bigint | boolean | import("react").JSX.Element | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode>>;
@@ -14,12 +14,26 @@
14
14
  * wire data). when given, every prompt row's avatar renders the writer's GitHub profile
15
15
  * photo via the existing `Avatar` family; when omitted, prompt rows keep today's generic
16
16
  * glyph.
17
+ * @param {'stacked' | 'split'} [props.layout='stacked'] - `stacked` renders the one chain with its
18
+ * session boundaries as separators; `split` lists the sessions beside a reading pane that
19
+ * shows the selected session's prompts.
20
+ * @param {number} [props.selected] - split layout only: the selected entry's position in the list
21
+ * (controlled). The list has one entry per session boundary, in chain order, plus a leading
22
+ * entry at 0 when items precede the first boundary. It is a position, not a transcript id,
23
+ * because the same transcript can open more than one entry. Keep the index `onSelect`
24
+ * reports. Omit it and the component keeps its own selection, starting at the first.
25
+ * @param {(index: number, session: PromptDigestItemPayload | null) => void} [props.onSelect] - split
26
+ * layout only: called with the next index and its session boundary item (null for the
27
+ * leading entry of items that precede every boundary).
17
28
  * @param {string} [props.className] - extra classes appended after `.pd`
18
29
  */
19
- export default function PromptDigest({ digest, itemHref, author, className, ...rest }: {
30
+ export default function PromptDigest({ digest, itemHref, author, layout, selected, onSelect, className, ...rest }: {
20
31
  digest: PromptDigestPayload;
21
32
  itemHref?: (item: PromptDigestItemPayload) => string;
22
33
  author?: PromptAuthor;
34
+ layout?: "stacked" | "split";
35
+ selected?: number;
36
+ onSelect?: (index: number, session: PromptDigestItemPayload | null) => void;
23
37
  className?: string;
24
38
  }): import("react").JSX.Element;
25
39
  export type PromptDigestPayload = import("@peasant-labs/schema").PromptDigest;
@@ -0,0 +1,211 @@
1
+ /**
2
+ * PublishStateLabel — the transcript's publish state: an icon and its words.
3
+ *
4
+ * @param {object} props
5
+ * @param {PublishState} props.state
6
+ * @param {number} [props.collectives] - how many collectives can read it (published, new-turns, publishing); left out of the words when omitted.
7
+ * @param {number} [props.newTurns] - turns recorded since the last publish (new-turns).
8
+ * @param {string} [props.collective] - the collective(s) the project's auto-publish rule targets (auto-publish).
9
+ * @param {string} [props.className]
10
+ */
11
+ export function PublishStateLabel({ state, collectives, newTurns, collective, className, ...rest }: {
12
+ state: PublishState;
13
+ collectives?: number;
14
+ newTurns?: number;
15
+ collective?: string;
16
+ className?: string;
17
+ }): import("react").JSX.Element;
18
+ /**
19
+ * PublishBar — the status and the one action for it, for a transcript header row. The action is
20
+ * publish (not published, outside the saved lists), update (new turns) or manage (published,
21
+ * auto-publish on); while publishing it stays visible and busy.
22
+ *
23
+ * @param {object} props
24
+ * @param {PublishState} props.state - a PublishStateLabel state.
25
+ * @param {number} [props.collectives]
26
+ * @param {number} [props.newTurns]
27
+ * @param {string} [props.collective]
28
+ * @param {() => void} [props.onAction] - the one button. omit it and no button renders.
29
+ * @param {string} [props.actionLabel] - replaces the state's default label.
30
+ * @param {import('./Menu.jsx').MenuItem[]} [props.moreItems] - an optional overflow menu after the button.
31
+ * @param {string} [props.className]
32
+ */
33
+ export function PublishBar({ state, collectives, newTurns, collective, onAction, actionLabel, moreItems, className, ...rest }: {
34
+ state: PublishState;
35
+ collectives?: number;
36
+ newTurns?: number;
37
+ collective?: string;
38
+ onAction?: () => void;
39
+ actionLabel?: string;
40
+ moreItems?: import("./Menu.jsx").MenuItem[];
41
+ className?: string;
42
+ }): import("react").JSX.Element;
43
+ /**
44
+ * AccessList — who can read the transcript: one row per collective with a named remove button.
45
+ * A row pending removal says so and offers `keep` instead.
46
+ *
47
+ * @param {object} props
48
+ * @param {AccessItem[]} props.items
49
+ * @param {(id: string) => void} [props.onRemove] - the row's x; omit it for a read-only list.
50
+ * @param {(id: string) => void} [props.onRestore] - `keep` on a row pending removal.
51
+ * @param {import('react').ReactNode} [props.empty] - shown when no collective can read it.
52
+ * @param {string} [props.label='who can read it']
53
+ * @param {string} [props.className]
54
+ */
55
+ export function AccessList({ items, onRemove, onRestore, empty, label, className, ...rest }: {
56
+ items: AccessItem[];
57
+ onRemove?: (id: string) => void;
58
+ onRestore?: (id: string) => void;
59
+ empty?: import("react").ReactNode;
60
+ label?: string;
61
+ className?: string;
62
+ }): import("react").JSX.Element;
63
+ /**
64
+ * CollectivePicker — search and suggestions for adding a collective. The host searches (village
65
+ * knows the collectives) and passes the results as `suggestions`; the picker renders them and
66
+ * reports the query and the choice.
67
+ *
68
+ * @param {object} props
69
+ * @param {{ id: string, name: string, members?: number, note?: string }[]} props.suggestions
70
+ * @param {(id: string) => void} props.onAdd
71
+ * @param {string} [props.query] - controlled query; omit it to let the picker keep its own.
72
+ * @param {(query: string) => void} [props.onQueryChange]
73
+ * @param {string} [props.label='add a collective'] - the field's label.
74
+ * @param {string} [props.placeholder='collective or github org']
75
+ * @param {import('react').ReactNode} [props.empty] - shown for a query with no suggestions.
76
+ * @param {string} [props.className]
77
+ */
78
+ export function CollectivePicker({ suggestions, onAdd, query: queryProp, onQueryChange, label, placeholder, empty, className, ...rest }: {
79
+ suggestions: {
80
+ id: string;
81
+ name: string;
82
+ members?: number;
83
+ note?: string;
84
+ }[];
85
+ onAdd: (id: string) => void;
86
+ query?: string;
87
+ onQueryChange?: (query: string) => void;
88
+ label?: string;
89
+ placeholder?: string;
90
+ empty?: import("react").ReactNode;
91
+ className?: string;
92
+ }): import("react").JSX.Element;
93
+ /**
94
+ * PublishDialog — the one publish popup, for a first publish (`mode="publish"`) and an update
95
+ * (`mode="update"`). It shows what leaves the machine, who can read it, and the auto-publish
96
+ * checkbox, then the done state with the village link. The host drives `state`; the popup never
97
+ * advances on its own.
98
+ *
99
+ * States: `connect` (this computer is not signed in to village), `waiting-github` (after continue
100
+ * with github), `checking` (the redaction check is running), `scan-failed` (publish is off,
101
+ * re-scan stays on), `no-collective` (signed in, in no collective), `ready`, `publishing`,
102
+ * `stopped` (a named step failed; village keeps what it had), `done`, `waits-approval` (done, and
103
+ * a curated collective shows it once its owner approves).
104
+ *
105
+ * @param {object} props
106
+ * @param {boolean} props.open
107
+ * @param {() => void} props.onClose - cancel, the close button, Escape and the scrim.
108
+ * @param {string} props.title - the session title, kept in its case.
109
+ * @param {'publish'|'update'} [props.mode='publish']
110
+ * @param {PublishDialogState} props.state - one of PUBLISH_DIALOG_STATES.
111
+ * @param {{ matches?: object[], total?: number, failure?: string }} [props.scan] - the host's scan result. Until it carries a
112
+ * `matches` list, the popup says the transcript is not scanned and keeps publish off. A match with `kept: true` leaves un-redacted, and the
113
+ * popup says so and opens the matches.
114
+ * @param {() => void} [props.onRescan]
115
+ * @param {{ summary: string }} [props.changes] - update mode: what changed since the last publish.
116
+ * @param {AccessItem[]} [props.access] - who can read it after this publish.
117
+ * @param {(id: string) => void} [props.onRemove]
118
+ * @param {(id: string) => void} [props.onRestore]
119
+ * @param {object} [props.picker] - CollectivePicker props (suggestions, onAdd, query, onQueryChange).
120
+ * @param {{ checked: boolean, onChange: (checked: boolean) => void, hint?: string }} [props.autoPublish]
121
+ * @param {string} [props.accessSummary] - update mode: the access change in words ("adds ML Reading Group · removes nothing").
122
+ * @param {string} [props.primaryLabel] - replaces the derived primary label.
123
+ * @param {() => void} [props.onPublish]
124
+ * @param {() => void} [props.onConnect] - `continue with github`.
125
+ * @param {string} [props.joinHref] - `create or join one on village`.
126
+ * @param {string} [props.stoppedAt] - the step that failed, in words ("setting who can read it").
127
+ * @param {() => void} [props.onRetry]
128
+ * @param {{ url: string, collectives: string[], pending?: string[], pullRequest?: { number: number, branch: string, href?: string } }} [props.done]
129
+ */
130
+ export function PublishDialog({ open, onClose, title, mode, state, scan, onRescan, changes, access, onRemove, onRestore, picker, autoPublish, accessSummary, primaryLabel, onPublish, onConnect, joinHref, stoppedAt, onRetry, done, }: {
131
+ open: boolean;
132
+ onClose: () => void;
133
+ title: string;
134
+ mode?: "publish" | "update";
135
+ state: PublishDialogState;
136
+ scan?: {
137
+ matches?: object[];
138
+ total?: number;
139
+ failure?: string;
140
+ };
141
+ onRescan?: () => void;
142
+ changes?: {
143
+ summary: string;
144
+ };
145
+ access?: AccessItem[];
146
+ onRemove?: (id: string) => void;
147
+ onRestore?: (id: string) => void;
148
+ picker?: object;
149
+ autoPublish?: {
150
+ checked: boolean;
151
+ onChange: (checked: boolean) => void;
152
+ hint?: string;
153
+ };
154
+ accessSummary?: string;
155
+ primaryLabel?: string;
156
+ onPublish?: () => void;
157
+ onConnect?: () => void;
158
+ joinHref?: string;
159
+ stoppedAt?: string;
160
+ onRetry?: () => void;
161
+ done?: {
162
+ url: string;
163
+ collectives: string[];
164
+ pending?: string[];
165
+ pullRequest?: {
166
+ number: number;
167
+ branch: string;
168
+ href?: string;
169
+ };
170
+ };
171
+ }): import("react").JSX.Element;
172
+ /**
173
+ * A transcript's publish state. `auto-publish` states the rule for the project, not how this
174
+ * transcript was published.
175
+ * @typedef {'not-published'|'publishing'|'published'|'new-turns'|'auto-publish'|'outside-lists'} PublishState
176
+ */
177
+ /** Every PublishState, in order. @type {ReadonlyArray<PublishState>} */
178
+ export const PUBLISH_STATES: ReadonlyArray<PublishState>;
179
+ /**
180
+ * A state of the publish popup.
181
+ * @typedef {'connect'|'waiting-github'|'checking'|'scan-failed'|'no-collective'|'ready'|'publishing'|'stopped'|'done'|'waits-approval'} PublishDialogState
182
+ */
183
+ /** Every state the popup reaches, in the order a first publish meets them. @type {ReadonlyArray<PublishDialogState>} */
184
+ export const PUBLISH_DIALOG_STATES: ReadonlyArray<PublishDialogState>;
185
+ /**
186
+ * A transcript's publish state. `auto-publish` states the rule for the project, not how this
187
+ * transcript was published.
188
+ */
189
+ export type PublishState = "not-published" | "publishing" | "published" | "new-turns" | "auto-publish" | "outside-lists";
190
+ export type AccessItem = {
191
+ id: string;
192
+ /**
193
+ * - the collective's name, kept in its case.
194
+ */
195
+ name: string;
196
+ members?: number;
197
+ /**
198
+ * - why it is here ("suggested · repo acme/ingest-api is linked").
199
+ */
200
+ note?: string;
201
+ /**
202
+ * - a change the next publish or update makes:
203
+ * `adding` (joins on publish), `approval` (joins once its owner approves), `removal`
204
+ * (loses access on update). stated in words on the row.
205
+ */
206
+ pending?: "adding" | "approval" | "removal";
207
+ };
208
+ /**
209
+ * A state of the publish popup.
210
+ */
211
+ export type PublishDialogState = "connect" | "waiting-github" | "checking" | "scan-failed" | "no-collective" | "ready" | "publishing" | "stopped" | "done" | "waits-approval";
@@ -16,9 +16,11 @@
16
16
  * @param {number} [props.scanned] - sessions/files scanned so far (defaults to `total`: done).
17
17
  * @param {number} [props.total=0] - total to scan.
18
18
  * @param {string|boolean} [props.failure] - honest-failure banner text; truthy shows the banner.
19
+ * @param {boolean} [props.readOnly=false] - show each match's decision without the keep/revert
20
+ * button; nothing in the review changes state. `onToggle` is ignored.
19
21
  * @param {string} [props.className]
20
22
  */
21
- export function RedactionReview({ level, availableLevels, onLevel, matches, onToggle, scanned, total, failure, className, ...rest }: {
23
+ export function RedactionReview({ level, availableLevels, onLevel, matches, onToggle, scanned, total, failure, readOnly, className, ...rest }: {
22
24
  level?: "minimal" | "standard" | "maximum";
23
25
  availableLevels?: readonly ("minimal" | "standard" | "maximum")[];
24
26
  onLevel?: (level: "minimal" | "standard" | "maximum") => void;
@@ -35,6 +37,7 @@ export function RedactionReview({ level, availableLevels, onLevel, matches, onTo
35
37
  scanned?: number;
36
38
  total?: number;
37
39
  failure?: string | boolean;
40
+ readOnly?: boolean;
38
41
  className?: string;
39
42
  }): import("react").JSX.Element;
40
43
  /**
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @param {object} props
3
+ * @param {boolean} props.open
4
+ * @param {() => void} props.onClose
5
+ * @param {RepoPickerOwner[]} props.owners
6
+ * @param {string[]} [props.initialSelected] - the repositories linked when the popup opens.
7
+ * @param {(diff: { add: string[], remove: string[] }) => void} props.onSave
8
+ * @param {string} [props.title='link repositories']
9
+ * @param {import('react').ReactNode} [props.description] - one line under the title.
10
+ * @param {boolean} [props.saving=false] - the host's write is in flight.
11
+ * @param {string} [props.error] - the host's write failed; shown in a role="alert" line.
12
+ */
13
+ export default function RepoPicker({ open, onClose, owners, initialSelected, onSave, title, description, saving, error }: {
14
+ open: boolean;
15
+ onClose: () => void;
16
+ owners: RepoPickerOwner[];
17
+ initialSelected?: string[];
18
+ onSave: (diff: {
19
+ add: string[];
20
+ remove: string[];
21
+ }) => void;
22
+ title?: string;
23
+ description?: import("react").ReactNode;
24
+ saving?: boolean;
25
+ error?: string;
26
+ }): import("react").JSX.Element;
27
+ export type RepoPickerOwner = {
28
+ id: string;
29
+ /**
30
+ * - the owner's GitHub login.
31
+ */
32
+ login: string;
33
+ kind: "org" | "user";
34
+ /**
35
+ * - a user's photo; an organisation shows the organisation icon.
36
+ */
37
+ avatarUrl?: string;
38
+ repos: {
39
+ id: string;
40
+ name: string;
41
+ private?: boolean;
42
+ note?: string;
43
+ }[];
44
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * SessionGroupDisclosure - the one collapsed count control for a session list.
3
+ *
4
+ * The label names the group's membership and its count ("orphan sessions 2"),
5
+ * a chevron leads the control, and a trailing `show`/`hide` states what the
6
+ * press will do. The control is a real `<button>` with `aria-expanded` and
7
+ * `aria-controls`, so the row it reveals is named, not inferred.
8
+ *
9
+ * Expansion state stays with the caller: a group is an aside, and a viewer who
10
+ * opened it once has not asked for it to be open on every future visit. The
11
+ * revealed content renders only while expanded, so a folded row cannot be
12
+ * mistaken for a visible one and holds no page height.
13
+ *
14
+ * @param {object} props
15
+ * @param {import('react').ReactNode} props.label - the control's text while OPEN
16
+ * @param {import('react').ReactNode} props.collapsedLabel - the control's text
17
+ * while CLOSED; the caller writes it, because the sibling groups do not agree
18
+ * on a leading mark
19
+ * @param {boolean} props.expanded
20
+ * @param {() => void} props.onToggle
21
+ * @param {string} props.rowsID - the `id` of the element this control reveals;
22
+ * the caller owns that element
23
+ * @param {boolean} [props.bare] - drop the outer border when the group sits
24
+ * inside an already-bordered panel
25
+ * @param {boolean} [props.indent] - indent one step, so a group nested inside a
26
+ * list reads as belonging to it rather than as another list row
27
+ * @param {string} [props.testID] - base for the wrapper and control test ids:
28
+ * `<base>`, `<base>-toggle`, and `<base>-label`
29
+ * @param {import('react').ReactNode} [props.children] - revealed only while
30
+ * expanded
31
+ */
32
+ export default function SessionGroupDisclosure({ label, collapsedLabel, expanded, onToggle, rowsID, bare, indent, testID, children, }: {
33
+ label: import("react").ReactNode;
34
+ collapsedLabel: import("react").ReactNode;
35
+ expanded: boolean;
36
+ onToggle: () => void;
37
+ rowsID: string;
38
+ bare?: boolean;
39
+ indent?: boolean;
40
+ testID?: string;
41
+ children?: import("react").ReactNode;
42
+ }): import("react").JSX.Element;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * SettingRow — one setting: a label, optional help, and one control that saves on its own.
3
+ *
4
+ * @param {object} props
5
+ * @param {import('react').ReactNode} props.label - the setting's name (lowercase chrome). a text
6
+ * control needs a string, since its edit button is named after it.
7
+ * @param {import('react').ReactNode} [props.help] - one line of help under the label.
8
+ * @param {'switch'|'select'|'checkbox'|'text'} props.control
9
+ * @param {boolean|string} props.value - the saved value.
10
+ * @param {{ value: string, label: string, disabled?: boolean }[]} [props.options] - select only; a disabled option shows a value that cannot be picked ("not set").
11
+ * @param {(next: boolean|string) => Promise<void>|void} props.onCommit - the one write; reject to fail.
12
+ * @param {string} [props.tag] - a note on the row, shown on a Chip ("not in peasant config").
13
+ * @param {boolean} [props.disabled]
14
+ * @param {string} [props.className]
15
+ */
16
+ export function SettingRow({ label, help, control, value, options, onCommit, tag, disabled, className, ...rest }: {
17
+ label: import("react").ReactNode;
18
+ help?: import("react").ReactNode;
19
+ control: "switch" | "select" | "checkbox" | "text";
20
+ value: boolean | string;
21
+ options?: {
22
+ value: string;
23
+ label: string;
24
+ disabled?: boolean;
25
+ }[];
26
+ onCommit: (next: boolean | string) => Promise<void> | void;
27
+ tag?: string;
28
+ disabled?: boolean;
29
+ className?: string;
30
+ }): import("react").JSX.Element;
31
+ /**
32
+ * SettingGroup — a collapsible group of setting rows: a native <details> whose summary names the
33
+ * group and counts its rows. The host sets which groups start open.
34
+ *
35
+ * @param {object} props
36
+ * @param {string} props.label - the group's name (lowercase chrome).
37
+ * @param {boolean} [props.defaultOpen=false]
38
+ * @param {number|null} [props.count] - the count shown in the summary; defaults to the children.
39
+ * null shows no count (a group of actions rather than settings).
40
+ * @param {[string, string]} [props.noun=['setting', 'settings']] - what the count counts.
41
+ * @param {import('react').ReactNode} [props.description] - one line under the summary when open.
42
+ * @param {import('react').ReactNode} props.children - the rows.
43
+ * @param {string} [props.className]
44
+ */
45
+ export function SettingGroup({ label, defaultOpen, count, noun, description, children, className, ...rest }: {
46
+ label: string;
47
+ defaultOpen?: boolean;
48
+ count?: number | null;
49
+ noun?: [string, string];
50
+ description?: import("react").ReactNode;
51
+ children: import("react").ReactNode;
52
+ className?: string;
53
+ }): import("react").JSX.Element;
54
+ export const SETTING_ROW_STATES: readonly string[];
@@ -6,7 +6,7 @@
6
6
  * primary or any menu row calls onSignIn(providerId).
7
7
  *
8
8
  * @param {object} props
9
- * @param {Array<{id:string,label:string}>} [props.providers] ordered; [0] is primary. defaults to PROVIDERS.
9
+ * @param {Array<{id:string,label:string}>} [props.providers] ordered; [0] is primary. defaults to GitHub only; pass PROVIDERS for all five.
10
10
  * @param {(id:string)=>void} [props.onSignIn] called with the chosen provider id.
11
11
  * @param {string} [props.className]
12
12
  * @returns {JSX.Element|null}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @typedef {object} StatsStripItem
3
+ * @property {string} label - lowercase chrome naming the value ("sessions", "longest streak").
4
+ * @property {string | number} value - the host-formatted value; a number is grouped (1284 -> 1,284).
5
+ * @property {'value-first' | 'label-first'} [order='value-first'] - reading order of the pair.
6
+ */
7
+ /**
8
+ * @param {object} props
9
+ * @param {StatsStripItem[]} props.items - the pairs, in reading order.
10
+ * @param {string} [props.label='summary'] - the list's accessible name.
11
+ * @param {string} [props.className]
12
+ */
13
+ export default function StatsStrip({ items, label, className, ...rest }: {
14
+ items: StatsStripItem[];
15
+ label?: string;
16
+ className?: string;
17
+ }): import("react").JSX.Element;
18
+ export type StatsStripItem = {
19
+ /**
20
+ * - lowercase chrome naming the value ("sessions", "longest streak").
21
+ */
22
+ label: string;
23
+ /**
24
+ * - the host-formatted value; a number is grouped (1284 -> 1,284).
25
+ */
26
+ value: string | number;
27
+ /**
28
+ * - reading order of the pair.
29
+ */
30
+ order?: "value-first" | "label-first";
31
+ };