@flytedan/flytebot-design-system 0.1.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.
- package/dist/index.cjs +9762 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2727 -0
- package/dist/index.d.ts +2727 -0
- package/dist/index.js +9580 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
- package/styles/components.css +5557 -0
- package/styles/foundations.css +230 -0
- package/styles/tokens.css +290 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,2727 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The one action primitive. Primary is a gradient; everything else is flat.
|
|
5
|
+
* Never more than one primary per view.
|
|
6
|
+
*/
|
|
7
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
/** primary = the one action. brandSoft = marketing CTA at >=19px/700 only (AA large-text). */
|
|
10
|
+
variant?: "primary" | "brandSoft" | "secondary" | "ghost" | "danger" | "link";
|
|
11
|
+
size?: "sm" | "md" | "lg";
|
|
12
|
+
/** Phosphor icon name, leading, e.g. "plus" */
|
|
13
|
+
icon?: string;
|
|
14
|
+
/** Phosphor icon name, trailing */
|
|
15
|
+
trailingIcon?: string;
|
|
16
|
+
/** The site's signature inset-circle arrow. Use on hero CTAs only. */
|
|
17
|
+
arrow?: boolean;
|
|
18
|
+
/** Label stays put; a spinner replaces the leading icon. */
|
|
19
|
+
loading?: boolean;
|
|
20
|
+
block?: boolean;
|
|
21
|
+
as?: "button" | "a";
|
|
22
|
+
}
|
|
23
|
+
declare function Button({ children, variant, size, icon, trailingIcon, arrow, loading, disabled, block, as, className, ...rest }: ButtonProps): React.JSX.Element;
|
|
24
|
+
|
|
25
|
+
/** A square action carrying only a Phosphor glyph. Always needs a label for a11y. */
|
|
26
|
+
interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
27
|
+
/** Phosphor icon name, e.g. "dots-three-vertical" */
|
|
28
|
+
icon: string;
|
|
29
|
+
/** Accessible name, also the title tooltip. Required. */
|
|
30
|
+
label: string;
|
|
31
|
+
size?: "sm" | "md" | "lg";
|
|
32
|
+
variant?: "ghost" | "outline";
|
|
33
|
+
round?: boolean;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
declare function IconButton({ icon, label, size, variant, round, disabled, className, ...rest }: IconButtonProps): React.JSX.Element;
|
|
38
|
+
|
|
39
|
+
type Placement = "bottom-start" | "bottom-end" | "bottom-center" | "top-start" | "top-end" | "top-center";
|
|
40
|
+
interface PopoverProps {
|
|
41
|
+
open?: boolean;
|
|
42
|
+
anchorRef: React.RefObject<HTMLElement>;
|
|
43
|
+
onClose?: () => void;
|
|
44
|
+
placement?: Placement;
|
|
45
|
+
offset?: number;
|
|
46
|
+
matchWidth?: boolean;
|
|
47
|
+
width?: number;
|
|
48
|
+
minWidth?: number;
|
|
49
|
+
maxHeight?: number;
|
|
50
|
+
padded?: boolean;
|
|
51
|
+
role?: string;
|
|
52
|
+
label?: string;
|
|
53
|
+
closeOnOutside?: boolean;
|
|
54
|
+
closeOnEscape?: boolean;
|
|
55
|
+
returnFocus?: boolean;
|
|
56
|
+
className?: string;
|
|
57
|
+
style?: React.CSSProperties;
|
|
58
|
+
children?: React.ReactNode;
|
|
59
|
+
}
|
|
60
|
+
interface PopoverPosition {
|
|
61
|
+
left: number;
|
|
62
|
+
width: number | undefined;
|
|
63
|
+
minWidth: number | undefined;
|
|
64
|
+
top: number | null;
|
|
65
|
+
bottom: number | null;
|
|
66
|
+
maxH: number;
|
|
67
|
+
side: "top" | "bottom";
|
|
68
|
+
anchorWidth: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Fixed-position layer geometry for an anchored surface. Flips side when the
|
|
72
|
+
* preferred one doesn't fit, clamps to the viewport, and re-measures on scroll
|
|
73
|
+
* and resize (capture phase, so it tracks inside scrolling panels too).
|
|
74
|
+
*/
|
|
75
|
+
declare function usePopoverPosition(open: boolean, anchorRef: React.RefObject<HTMLElement>, opts?: Partial<PopoverProps> & {
|
|
76
|
+
estHeight?: number;
|
|
77
|
+
estWidth?: number;
|
|
78
|
+
}): PopoverPosition | null;
|
|
79
|
+
/**
|
|
80
|
+
* An anchored floating surface: menus, pickers, disclosure panels, meters.
|
|
81
|
+
* Owns nothing but placement, dismissal and focus return — the content is yours.
|
|
82
|
+
*/
|
|
83
|
+
declare function Popover({ open, anchorRef, onClose, placement, offset, matchWidth, width, minWidth, maxHeight, padded, role, label, closeOnOutside, closeOnEscape, returnFocus, className, style, children, }: PopoverProps): React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
|
|
84
|
+
interface MenuItem {
|
|
85
|
+
id?: string;
|
|
86
|
+
label?: React.ReactNode;
|
|
87
|
+
icon?: string;
|
|
88
|
+
description?: React.ReactNode;
|
|
89
|
+
meta?: React.ReactNode;
|
|
90
|
+
/** A key HINT. Renders a cap, binds nothing. */
|
|
91
|
+
shortcut?: React.ReactNode;
|
|
92
|
+
/** Present (true/false) makes the row a menuitemradio with a check column. */
|
|
93
|
+
checked?: boolean;
|
|
94
|
+
disabled?: boolean;
|
|
95
|
+
submenu?: MenuItem[];
|
|
96
|
+
/** A secondary action pinned to the row (e.g. delete). Rendered as a sibling button, so it is keyboard-reachable and does not nest inside the row button. */
|
|
97
|
+
trailingAction?: {
|
|
98
|
+
icon?: string;
|
|
99
|
+
label: string;
|
|
100
|
+
danger?: boolean;
|
|
101
|
+
closeOnSelect?: boolean;
|
|
102
|
+
onSelect?: (item: MenuItem) => void;
|
|
103
|
+
};
|
|
104
|
+
onSelect?: (item: MenuItem) => void;
|
|
105
|
+
kind?: "separator" | "section" | "custom";
|
|
106
|
+
render?: () => React.ReactNode;
|
|
107
|
+
}
|
|
108
|
+
interface MenuProps {
|
|
109
|
+
items: MenuItem[];
|
|
110
|
+
onSelect?: (item: MenuItem) => void;
|
|
111
|
+
onClose?: () => void;
|
|
112
|
+
autoFocus?: boolean;
|
|
113
|
+
header?: React.ReactNode;
|
|
114
|
+
footer?: React.ReactNode;
|
|
115
|
+
className?: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A keyboard-complete menu list. Items may be:
|
|
119
|
+
* {id,label,icon,description,meta,shortcut,checked,disabled,submenu,onSelect}
|
|
120
|
+
* {kind:"separator"} · {kind:"section",label} · {kind:"custom",render}
|
|
121
|
+
* `shortcut` is a HINT — it renders a cap and binds nothing.
|
|
122
|
+
*/
|
|
123
|
+
declare function Menu({ items, onSelect, onClose, autoFocus, className, footer, header }: MenuProps): React.JSX.Element;
|
|
124
|
+
interface MenuButtonProps extends Omit<MenuProps, "onClose" | "autoFocus"> {
|
|
125
|
+
label?: React.ReactNode;
|
|
126
|
+
icon?: string;
|
|
127
|
+
trailingIcon?: string | null;
|
|
128
|
+
placement?: Placement;
|
|
129
|
+
variant?: "chip" | "ghost";
|
|
130
|
+
size?: "sm" | "md";
|
|
131
|
+
disabled?: boolean;
|
|
132
|
+
title?: string;
|
|
133
|
+
ariaLabel?: string;
|
|
134
|
+
width?: number;
|
|
135
|
+
minWidth?: number;
|
|
136
|
+
maxHeight?: number;
|
|
137
|
+
children?: React.ReactNode;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Trigger + menu in one: a compact chip that opens an anchored Menu.
|
|
141
|
+
* Used for model pickers, row overflow actions, view switchers.
|
|
142
|
+
*/
|
|
143
|
+
declare function MenuButton({ label, icon, trailingIcon, items, onSelect, placement, variant, size, disabled, title, ariaLabel, width, minWidth, maxHeight, className, children, header, footer, }: MenuButtonProps): React.JSX.Element;
|
|
144
|
+
|
|
145
|
+
/** Two to four mutually exclusive views — density, date range, light/dark. */
|
|
146
|
+
interface SegmentedControlProps {
|
|
147
|
+
options: Array<string | {
|
|
148
|
+
value: string;
|
|
149
|
+
label: string;
|
|
150
|
+
}>;
|
|
151
|
+
value?: string;
|
|
152
|
+
onChange?: (value: string) => void;
|
|
153
|
+
className?: string;
|
|
154
|
+
}
|
|
155
|
+
declare function SegmentedControl({ options, value, onChange, className, ...rest }: SegmentedControlProps): React.JSX.Element;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The container. White, 12px radius, hairline border, low soft shadow, and a
|
|
159
|
+
* divider under the header — that divider is the signature detail on flytedesk.com.
|
|
160
|
+
*/
|
|
161
|
+
interface CardProps {
|
|
162
|
+
title?: React.ReactNode;
|
|
163
|
+
/** Right-aligned control in the header — usually an IconButton or a link Button. */
|
|
164
|
+
action?: React.ReactNode;
|
|
165
|
+
footer?: React.ReactNode;
|
|
166
|
+
children?: React.ReactNode;
|
|
167
|
+
elevation?: "flat" | "rest" | "raised";
|
|
168
|
+
hoverable?: boolean;
|
|
169
|
+
/** Set false to let children run edge to edge (tables, images). */
|
|
170
|
+
padded?: boolean;
|
|
171
|
+
className?: string;
|
|
172
|
+
}
|
|
173
|
+
declare function Card({ title, action, footer, children, elevation, hoverable, padded, className, ...rest }: CardProps): React.JSX.Element;
|
|
174
|
+
interface CardRowProps {
|
|
175
|
+
label: React.ReactNode;
|
|
176
|
+
children?: React.ReactNode;
|
|
177
|
+
className?: string;
|
|
178
|
+
}
|
|
179
|
+
declare function CardRow({ label, children, className }: CardRowProps): React.JSX.Element;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The system's main density valve: a large, calm trigger row that opens a panel.
|
|
183
|
+
* Prefer this over tabs or a second page when a screen has more than it can show.
|
|
184
|
+
*/
|
|
185
|
+
interface CollapsibleProps {
|
|
186
|
+
title: React.ReactNode;
|
|
187
|
+
subtitle?: React.ReactNode;
|
|
188
|
+
/** Right-aligned status — a Badge or CsiBadge. */
|
|
189
|
+
badge?: React.ReactNode;
|
|
190
|
+
/** Phosphor icon name shown in a soft brand chip on the left. */
|
|
191
|
+
icon?: string;
|
|
192
|
+
defaultOpen?: boolean;
|
|
193
|
+
/** Pass with onToggle for controlled use. */
|
|
194
|
+
open?: boolean;
|
|
195
|
+
onToggle?: (open: boolean) => void;
|
|
196
|
+
children?: React.ReactNode;
|
|
197
|
+
className?: string;
|
|
198
|
+
}
|
|
199
|
+
declare function Collapsible({ title, subtitle, badge, icon, defaultOpen, open, onToggle, children, className, ...rest }: CollapsibleProps): React.JSX.Element;
|
|
200
|
+
|
|
201
|
+
/** A right-side panel for detail and editing without losing the list behind it. */
|
|
202
|
+
interface DrawerProps {
|
|
203
|
+
open?: boolean;
|
|
204
|
+
title?: React.ReactNode;
|
|
205
|
+
onClose?: () => void;
|
|
206
|
+
/** Plain content, or a render function receiving `close` — call it to trigger the closing animation before onClose fires. */
|
|
207
|
+
footer?: React.ReactNode | ((close: () => void) => React.ReactNode);
|
|
208
|
+
children?: React.ReactNode;
|
|
209
|
+
className?: string;
|
|
210
|
+
}
|
|
211
|
+
declare function Drawer({ open, title, onClose, footer, children, className, ...rest }: DrawerProps): React.ReactPortal | null;
|
|
212
|
+
|
|
213
|
+
/** A focused decision on a scrim. Max 560px, 760 with `wide` for data. Escape closes. */
|
|
214
|
+
interface ModalProps {
|
|
215
|
+
open?: boolean;
|
|
216
|
+
title?: React.ReactNode;
|
|
217
|
+
description?: React.ReactNode;
|
|
218
|
+
onClose?: () => void;
|
|
219
|
+
/** Plain content, or a render function receiving `close` — call it to trigger the closing animation before onClose fires. */
|
|
220
|
+
footer?: React.ReactNode | ((close: () => void) => React.ReactNode);
|
|
221
|
+
/** 760px — for tables and side-by-side comparisons. */
|
|
222
|
+
wide?: boolean;
|
|
223
|
+
children?: React.ReactNode;
|
|
224
|
+
className?: string;
|
|
225
|
+
}
|
|
226
|
+
declare function Modal({ open, title, description, onClose, footer, wide, children, className, ...rest }: ModalProps): React.ReactPortal | null;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The async contract for one region of a screen. Four states, one component:
|
|
230
|
+
* loading (skeleton), refreshing (previous data stays visible, dimmed and inert),
|
|
231
|
+
* ready, empty/error.
|
|
232
|
+
*/
|
|
233
|
+
interface LoadingRegionProps {
|
|
234
|
+
state?: "loading" | "refreshing" | "ready" | "empty" | "error";
|
|
235
|
+
/** What to show before first data. Mirror the ready layout. */
|
|
236
|
+
skeleton?: React.ReactNode;
|
|
237
|
+
children?: React.ReactNode;
|
|
238
|
+
/** Announced to screen readers while busy. */
|
|
239
|
+
label?: string;
|
|
240
|
+
error?: string;
|
|
241
|
+
onRetry?: () => void;
|
|
242
|
+
className?: string;
|
|
243
|
+
}
|
|
244
|
+
declare function LoadingRegion({ state, skeleton, children, label, error, onRetry, className, ...rest }: LoadingRegionProps): React.JSX.Element;
|
|
245
|
+
|
|
246
|
+
/** Determinate or indeterminate bar for a known long job — uploads, exports, plan builds. */
|
|
247
|
+
interface ProgressBarProps {
|
|
248
|
+
/** 0-100. Omit for indeterminate. */
|
|
249
|
+
value?: number;
|
|
250
|
+
label?: string;
|
|
251
|
+
showValue?: boolean;
|
|
252
|
+
className?: string;
|
|
253
|
+
}
|
|
254
|
+
declare function ProgressBar({ value, label, showValue, className, ...rest }: ProgressBarProps): React.JSX.Element;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The shape data will occupy, shown before the first payload lands.
|
|
258
|
+
* Skeletons mirror the real layout so nothing shifts on arrival.
|
|
259
|
+
*/
|
|
260
|
+
interface SkeletonProps {
|
|
261
|
+
w?: number | string;
|
|
262
|
+
h?: number | string;
|
|
263
|
+
shape?: "block" | "text" | "circle" | "card";
|
|
264
|
+
radius?: string;
|
|
265
|
+
style?: React.CSSProperties;
|
|
266
|
+
className?: string;
|
|
267
|
+
}
|
|
268
|
+
declare function Skeleton({ w, h, shape, radius, style, className, ...rest }: SkeletonProps): React.JSX.Element;
|
|
269
|
+
interface SkeletonTextProps {
|
|
270
|
+
lines?: number;
|
|
271
|
+
/** Per-line widths, cycled. Ragged widths read as text; equal widths read as a bug. */
|
|
272
|
+
width?: string[];
|
|
273
|
+
size?: number;
|
|
274
|
+
gap?: number;
|
|
275
|
+
className?: string;
|
|
276
|
+
}
|
|
277
|
+
declare function SkeletonText({ lines, width, size, gap, className }: SkeletonTextProps): React.JSX.Element;
|
|
278
|
+
|
|
279
|
+
/** A small indeterminate indicator. For inline waits only — never over a whole page. */
|
|
280
|
+
interface SpinnerProps {
|
|
281
|
+
size?: "sm" | "md" | "lg";
|
|
282
|
+
label?: string;
|
|
283
|
+
className?: string;
|
|
284
|
+
style?: React.CSSProperties;
|
|
285
|
+
}
|
|
286
|
+
declare function Spinner({ size, label, className, style, ...rest }: SpinnerProps): React.JSX.Element;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The app's 264px left rail. Grouped items under overline headers; the active item
|
|
290
|
+
* takes a soft blue fill and deep blue label. Collapses off-canvas below 1024px.
|
|
291
|
+
*/
|
|
292
|
+
interface SidebarNavItem {
|
|
293
|
+
value: string;
|
|
294
|
+
label: string;
|
|
295
|
+
/** Phosphor icon name */
|
|
296
|
+
icon: string;
|
|
297
|
+
count?: number;
|
|
298
|
+
/** Count still loading — shows a small skeleton instead of a number. */
|
|
299
|
+
loading?: boolean;
|
|
300
|
+
/** Member lacks the permission — shows a lock marker. Still navigable; the screen explains. */
|
|
301
|
+
locked?: boolean;
|
|
302
|
+
/** Designed but the backend is not wired — shows a construction marker. Still navigable;
|
|
303
|
+
* the screen renders inert behind a coming-soon banner. */
|
|
304
|
+
preview?: boolean;
|
|
305
|
+
}
|
|
306
|
+
interface SidebarNavProps {
|
|
307
|
+
brand?: React.ReactNode;
|
|
308
|
+
groups: Array<{
|
|
309
|
+
label?: string;
|
|
310
|
+
items: SidebarNavItem[];
|
|
311
|
+
}>;
|
|
312
|
+
current?: string;
|
|
313
|
+
onNavigate?: (value: string) => void;
|
|
314
|
+
footer?: React.ReactNode;
|
|
315
|
+
/** Mobile: slides the off-canvas rail in. */
|
|
316
|
+
open?: boolean;
|
|
317
|
+
className?: string;
|
|
318
|
+
}
|
|
319
|
+
declare function SidebarNav({ brand, groups, current, onNavigate, footer, open, className, ...rest }: SidebarNavProps): React.JSX.Element;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Places within one screen. Underline only — no pill tabs in the product.
|
|
323
|
+
*/
|
|
324
|
+
interface TabsProps {
|
|
325
|
+
tabs: Array<string | {
|
|
326
|
+
value: string;
|
|
327
|
+
label: string;
|
|
328
|
+
count?: number;
|
|
329
|
+
}>;
|
|
330
|
+
value?: string;
|
|
331
|
+
onChange?: (value: string) => void;
|
|
332
|
+
className?: string;
|
|
333
|
+
}
|
|
334
|
+
declare function Tabs({ tabs, value, onChange, className, ...rest }: TabsProps): React.JSX.Element;
|
|
335
|
+
|
|
336
|
+
/** 60px app header: breadcrumb left, actions right, and a 2px route loader along its top edge. */
|
|
337
|
+
interface TopbarProps {
|
|
338
|
+
crumbs?: Array<{
|
|
339
|
+
label: string;
|
|
340
|
+
href?: string;
|
|
341
|
+
}>;
|
|
342
|
+
actions?: React.ReactNode;
|
|
343
|
+
/** Shows the indeterminate route loader on the top edge. */
|
|
344
|
+
loading?: boolean;
|
|
345
|
+
/** Mobile menu handler — renders the hamburger. */
|
|
346
|
+
onMenu?: () => void;
|
|
347
|
+
className?: string;
|
|
348
|
+
}
|
|
349
|
+
declare function Topbar({ crumbs, actions, loading, onMenu, className, ...rest }: TopbarProps): React.JSX.Element;
|
|
350
|
+
|
|
351
|
+
/** A person or organisation mark. Initials on a soft brand tint when no image exists. */
|
|
352
|
+
interface AvatarProps {
|
|
353
|
+
name?: string;
|
|
354
|
+
src?: string;
|
|
355
|
+
size?: "sm" | "md" | "lg";
|
|
356
|
+
className?: string;
|
|
357
|
+
}
|
|
358
|
+
declare function Avatar({ name, src, size, className, ...rest }: AvatarProps): React.JSX.Element;
|
|
359
|
+
interface AvatarStackProps {
|
|
360
|
+
people: Array<string | {
|
|
361
|
+
name: string;
|
|
362
|
+
src?: string;
|
|
363
|
+
}>;
|
|
364
|
+
size?: "sm" | "md" | "lg";
|
|
365
|
+
max?: number;
|
|
366
|
+
className?: string;
|
|
367
|
+
}
|
|
368
|
+
declare function AvatarStack({ people, size, max, className }: AvatarStackProps): React.JSX.Element;
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* A pill of state. Badges never carry actions — if it is clickable it is a Tag.
|
|
372
|
+
*/
|
|
373
|
+
interface BadgeProps {
|
|
374
|
+
children?: React.ReactNode;
|
|
375
|
+
/** live = the site's blue gradient "Active". approved = its mint pill. count = the blue number chip. */
|
|
376
|
+
tone?: "neutral" | "info" | "success" | "warning" | "danger" | "live" | "approved" | "count";
|
|
377
|
+
dot?: boolean;
|
|
378
|
+
/** Phosphor icon name */
|
|
379
|
+
icon?: string;
|
|
380
|
+
className?: string;
|
|
381
|
+
}
|
|
382
|
+
declare function Badge({ children, tone, dot, icon, className, ...rest }: BadgeProps): React.JSX.Element;
|
|
383
|
+
|
|
384
|
+
/** Says what will appear here, and offers exactly one button. */
|
|
385
|
+
interface EmptyStateProps {
|
|
386
|
+
/** Phosphor icon name */
|
|
387
|
+
icon?: string;
|
|
388
|
+
title: React.ReactNode;
|
|
389
|
+
children?: React.ReactNode;
|
|
390
|
+
action?: React.ReactNode;
|
|
391
|
+
className?: string;
|
|
392
|
+
}
|
|
393
|
+
declare function EmptyState({ icon, title, children, action, className, ...rest }: EmptyStateProps): React.JSX.Element;
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* A callout with a fixed structure: statement, then cost, then two actions.
|
|
397
|
+
* A flag without a number and a choice is not a flag — use plain body copy.
|
|
398
|
+
*/
|
|
399
|
+
interface FlagProps {
|
|
400
|
+
/** What is true. "Ohio State is Weak." */
|
|
401
|
+
statement: React.ReactNode;
|
|
402
|
+
/** What it costs to fix. "+$4,200 reaches Adequate." Rendered bold. */
|
|
403
|
+
cost?: React.ReactNode;
|
|
404
|
+
/** Two Buttons — the fix and the dismissal. */
|
|
405
|
+
actions?: React.ReactNode;
|
|
406
|
+
tone?: "info" | "success" | "warning" | "danger";
|
|
407
|
+
className?: string;
|
|
408
|
+
}
|
|
409
|
+
declare function Flag$1({ statement, cost, actions, tone, className, ...rest }: FlagProps): React.JSX.Element;
|
|
410
|
+
|
|
411
|
+
/** A key hint that binds NOTHING — register the real shortcut separately. */
|
|
412
|
+
interface KeyHintProps {
|
|
413
|
+
keys: string;
|
|
414
|
+
size?: "sm" | "md";
|
|
415
|
+
tone?: "quiet" | "solid";
|
|
416
|
+
className?: string;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* A key hint. Renders a label and binds NOTHING — register the real shortcut
|
|
420
|
+
* separately, or the cap and the binding drift apart.
|
|
421
|
+
*/
|
|
422
|
+
declare function KeyHint({ keys, size, tone, className, ...rest }: KeyHintProps): React.JSX.Element;
|
|
423
|
+
/** The platform modifier as text, for prose: "⌘" or "Ctrl". */
|
|
424
|
+
declare function modifierLabel(): string;
|
|
425
|
+
/** True when the event carries the platform's primary modifier. */
|
|
426
|
+
declare function hasModifier(e: KeyboardEvent | React.KeyboardEvent): boolean;
|
|
427
|
+
|
|
428
|
+
/** A clickable chip — filters, selected campuses, applied channels. */
|
|
429
|
+
interface TagProps {
|
|
430
|
+
children?: React.ReactNode;
|
|
431
|
+
icon?: string;
|
|
432
|
+
selected?: boolean;
|
|
433
|
+
onRemove?: () => void;
|
|
434
|
+
onClick?: () => void;
|
|
435
|
+
className?: string;
|
|
436
|
+
}
|
|
437
|
+
declare function Tag({ children, icon, selected, onRemove, onClick, className, ...rest }: TagProps): React.JSX.Element;
|
|
438
|
+
|
|
439
|
+
/** Confirmation of something that already happened. Bottom-right, 5s, undo where reversible. */
|
|
440
|
+
interface ToastProps {
|
|
441
|
+
title: React.ReactNode;
|
|
442
|
+
children?: React.ReactNode;
|
|
443
|
+
tone?: "success" | "error" | "info" | "loading";
|
|
444
|
+
onUndo?: () => void;
|
|
445
|
+
onDismiss?: () => void;
|
|
446
|
+
className?: string;
|
|
447
|
+
}
|
|
448
|
+
declare function Toast({ title, children, tone, onUndo, onDismiss, className, ...rest }: ToastProps): React.JSX.Element;
|
|
449
|
+
|
|
450
|
+
/** A short clarification on hover or focus. Never the only place information lives. */
|
|
451
|
+
interface TooltipProps {
|
|
452
|
+
label: React.ReactNode;
|
|
453
|
+
placement?: "top" | "bottom";
|
|
454
|
+
children?: React.ReactNode;
|
|
455
|
+
className?: string;
|
|
456
|
+
}
|
|
457
|
+
declare function Tooltip({ label, placement, children, className }: TooltipProps): React.JSX.Element;
|
|
458
|
+
|
|
459
|
+
interface ClampProps {
|
|
460
|
+
children?: React.ReactNode;
|
|
461
|
+
/** With maxChars, pass the raw string instead of children. */
|
|
462
|
+
text?: string;
|
|
463
|
+
maxChars?: number;
|
|
464
|
+
maxHeight?: number;
|
|
465
|
+
moreLabel?: string;
|
|
466
|
+
lessLabel?: string;
|
|
467
|
+
onToggle?: (open: boolean) => void;
|
|
468
|
+
className?: string;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Height- or character-clamped content with a Show more control.
|
|
472
|
+
* The control is a real button, always in the DOM when the content overflows.
|
|
473
|
+
*/
|
|
474
|
+
declare function Clamp({ children, text, maxChars, maxHeight, moreLabel, lessLabel, onToggle, className, }: ClampProps): React.JSX.Element;
|
|
475
|
+
|
|
476
|
+
interface CsiMeterProps {
|
|
477
|
+
band: "weak" | "adequate" | "strong" | "dominant";
|
|
478
|
+
size?: number;
|
|
479
|
+
width?: number;
|
|
480
|
+
gap?: number;
|
|
481
|
+
}
|
|
482
|
+
declare function CsiMeter({ band, size, width, gap }: CsiMeterProps): React.JSX.Element;
|
|
483
|
+
/**
|
|
484
|
+
* Campus Saturation Index — the product's signature encoding. Four ordered bands
|
|
485
|
+
* in a reserved teal ramp that appears nowhere else: blue is interaction, teal is
|
|
486
|
+
* saturation. Never color-only — the band name and a 4-segment meter always ship
|
|
487
|
+
* with the fill.
|
|
488
|
+
*/
|
|
489
|
+
interface CsiBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
490
|
+
band: "weak" | "adequate" | "strong" | "dominant";
|
|
491
|
+
/** Cost per reachable person. Internal surfaces only — never client-facing. */
|
|
492
|
+
crp?: number;
|
|
493
|
+
size?: "compact" | "medium";
|
|
494
|
+
/** The "will become" state while a reallocation is being dragged: 70% opacity, dashed edge. */
|
|
495
|
+
preview?: boolean;
|
|
496
|
+
/** Amber dot marking a Weak campus that can be fixed. */
|
|
497
|
+
alert?: boolean;
|
|
498
|
+
}
|
|
499
|
+
declare function CsiBadge({ band, crp, size, preview, alert, className, ...rest }: CsiBadgeProps): React.JSX.Element;
|
|
500
|
+
interface CsiHeroProps {
|
|
501
|
+
band: "weak" | "adequate" | "strong" | "dominant";
|
|
502
|
+
crp?: number;
|
|
503
|
+
label?: string;
|
|
504
|
+
className?: string;
|
|
505
|
+
}
|
|
506
|
+
declare function CsiHero({ band, crp, label, className }: CsiHeroProps): React.JSX.Element;
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* The product's dense read surface. Sticky overline header, hairline rows, no zebra,
|
|
510
|
+
* numbers right-aligned and tabular. Renders its own skeleton rows so the table
|
|
511
|
+
* never collapses while data is in flight.
|
|
512
|
+
*/
|
|
513
|
+
interface DataTableColumn {
|
|
514
|
+
key: string;
|
|
515
|
+
label: string;
|
|
516
|
+
/** Right-aligned tabular figures. Never center a number. */
|
|
517
|
+
numeric?: boolean;
|
|
518
|
+
/** Set widths explicitly; auto layout shifts as data arrives. */
|
|
519
|
+
width?: string;
|
|
520
|
+
sortable?: boolean;
|
|
521
|
+
render?: (row: any) => React.ReactNode;
|
|
522
|
+
}
|
|
523
|
+
interface DataTableProps {
|
|
524
|
+
columns: DataTableColumn[];
|
|
525
|
+
rows: any[];
|
|
526
|
+
compact?: boolean;
|
|
527
|
+
selectedId?: string | number;
|
|
528
|
+
onRowClick?: (row: any) => void;
|
|
529
|
+
rowKey?: string;
|
|
530
|
+
/** First load: nothing on screen yet, so skeleton rows are drawn. */
|
|
531
|
+
loading?: boolean;
|
|
532
|
+
/** A re-query (sort, filter, page) while the PREVIOUS rows are still rendered. Dims them,
|
|
533
|
+
* makes them inert and shows an "Updating…" pill, so nobody acts on superseded data. */
|
|
534
|
+
refreshing?: boolean;
|
|
535
|
+
skeletonRows?: number;
|
|
536
|
+
sort?: {
|
|
537
|
+
key: string;
|
|
538
|
+
dir: "asc" | "desc";
|
|
539
|
+
};
|
|
540
|
+
onSort?: (key: string) => void;
|
|
541
|
+
className?: string;
|
|
542
|
+
}
|
|
543
|
+
declare function DataTable({ columns, rows, compact, selectedId, onRowClick, rowKey, loading, refreshing, skeletonRows, sort, onSort, className, ...rest }: DataTableProps): React.JSX.Element;
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* A flat figure tile — no shadow, no background color, never more than four in a row.
|
|
547
|
+
*/
|
|
548
|
+
interface StatTileProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
549
|
+
label: string;
|
|
550
|
+
value?: React.ReactNode;
|
|
551
|
+
sub?: React.ReactNode;
|
|
552
|
+
/** "+12.4%" or "-3.1%" — colour and arrow follow the sign. */
|
|
553
|
+
delta?: string;
|
|
554
|
+
loading?: boolean;
|
|
555
|
+
/** Thin one-line variant for stat rows above tables. */
|
|
556
|
+
compact?: boolean;
|
|
557
|
+
className?: string;
|
|
558
|
+
}
|
|
559
|
+
declare function StatTile({ label, value, sub, delta, loading, compact, className, ...rest }: StatTileProps): React.JSX.Element;
|
|
560
|
+
|
|
561
|
+
declare function languageLabel(lang?: string): string;
|
|
562
|
+
/** Tokenise source into [{t, c}] where c is a token class or null for plain text. */
|
|
563
|
+
declare function tokenize(src: string, lang?: string): Array<{
|
|
564
|
+
t: string;
|
|
565
|
+
c: string | null;
|
|
566
|
+
}>;
|
|
567
|
+
interface CodeBlockProps {
|
|
568
|
+
code: string;
|
|
569
|
+
language?: string;
|
|
570
|
+
filename?: string;
|
|
571
|
+
lineNumbers?: boolean;
|
|
572
|
+
wrap?: boolean;
|
|
573
|
+
/** Fold past this many lines. 0 disables. Default 26. */
|
|
574
|
+
collapseAfter?: number;
|
|
575
|
+
copyable?: boolean;
|
|
576
|
+
actions?: React.ReactNode;
|
|
577
|
+
className?: string;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* A fenced code block: language label, copy, soft-wrap toggle, optional line
|
|
581
|
+
* numbers, and a fold for long listings.
|
|
582
|
+
*/
|
|
583
|
+
declare function CodeBlock({ code, language, filename, lineNumbers, wrap: wrapDefault, collapseAfter, copyable, actions, className, }: CodeBlockProps): React.JSX.Element;
|
|
584
|
+
interface DiffBlockProps {
|
|
585
|
+
diff: string;
|
|
586
|
+
filename?: string;
|
|
587
|
+
language?: string;
|
|
588
|
+
collapseAfter?: number;
|
|
589
|
+
className?: string;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* A unified diff. Renders +/- lines with their own gutters and a stat line —
|
|
593
|
+
* the shape a code change should always take in a transcript.
|
|
594
|
+
*/
|
|
595
|
+
declare function DiffBlock({ diff, filename, collapseAfter, className }: DiffBlockProps): React.JSX.Element;
|
|
596
|
+
|
|
597
|
+
interface MarkdownProps {
|
|
598
|
+
source: string;
|
|
599
|
+
className?: string;
|
|
600
|
+
codeProps?: Partial<CodeBlockProps>;
|
|
601
|
+
allowImages?: boolean;
|
|
602
|
+
headingOffset?: number;
|
|
603
|
+
/** Renderer for `[^n]` markers. */
|
|
604
|
+
renderCitation?: (marker: string) => React.ReactNode;
|
|
605
|
+
onLinkClick?: (e: React.MouseEvent) => void;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Read-only markdown → React nodes. Headings, emphasis, links, lists (incl. task
|
|
609
|
+
* lists), quotes, tables, images, fenced code (handed to CodeBlock, `diff` to
|
|
610
|
+
* DiffBlock) and `[^n]` citation markers.
|
|
611
|
+
*
|
|
612
|
+
* Deliberately read-only: an editor in readonly mode carries a contenteditable,
|
|
613
|
+
* a context menu and popovers it will never use — per message.
|
|
614
|
+
*/
|
|
615
|
+
declare function Markdown({ source, className, codeProps, allowImages, headingOffset, renderCitation, onLinkClick, }: MarkdownProps): React.JSX.Element;
|
|
616
|
+
/** Inline-only markdown, for labels and single-line contexts. */
|
|
617
|
+
declare function MarkdownInline({ source, className, onLinkClick }: {
|
|
618
|
+
source: string;
|
|
619
|
+
className?: string;
|
|
620
|
+
onLinkClick?: (e: React.MouseEvent) => void;
|
|
621
|
+
}): React.JSX.Element;
|
|
622
|
+
/** Strip markdown to plain text — for copy actions, titles and previews. */
|
|
623
|
+
declare function markdownToText(src: string): string;
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* A segmented bar — budget split by channel, delivery against a flight.
|
|
627
|
+
* Unallocated space is hatched, so it looks unfinished rather than empty.
|
|
628
|
+
*/
|
|
629
|
+
interface MeterSegment {
|
|
630
|
+
label: string;
|
|
631
|
+
value: number;
|
|
632
|
+
/** Defaults to the categorical chart ramp in order. */
|
|
633
|
+
color?: string;
|
|
634
|
+
}
|
|
635
|
+
interface MeterProps {
|
|
636
|
+
segments: MeterSegment[];
|
|
637
|
+
unallocated?: number;
|
|
638
|
+
height?: number;
|
|
639
|
+
label?: string;
|
|
640
|
+
legend?: boolean;
|
|
641
|
+
className?: string;
|
|
642
|
+
}
|
|
643
|
+
declare function Meter({ segments, unallocated, height, label, legend, className }: MeterProps): React.JSX.Element;
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* The standard footer for a server-driven table: range readout, rows-per-page,
|
|
647
|
+
* and windowed page navigation. Presentational only — it holds no state.
|
|
648
|
+
*/
|
|
649
|
+
interface PaginationProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
650
|
+
/** 1-indexed current page. */
|
|
651
|
+
page?: number;
|
|
652
|
+
pageSize?: number;
|
|
653
|
+
/** Total matching rows on the server, not the length of the current page. */
|
|
654
|
+
total?: number;
|
|
655
|
+
pageSizes?: number[];
|
|
656
|
+
onPageChange?: (page: number) => void;
|
|
657
|
+
/** Omit to hide the rows-per-page control. */
|
|
658
|
+
onPageSizeChange?: (pageSize: number) => void;
|
|
659
|
+
loading?: boolean;
|
|
660
|
+
/** Noun for the readout — "rows", "campuses", "placements". */
|
|
661
|
+
unit?: string;
|
|
662
|
+
className?: string;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Pagination — the standard footer for every server-driven table.
|
|
666
|
+
* Purely presentational: it reports intent through onPageChange / onPageSizeChange
|
|
667
|
+
* and never derives its own state, so it stays honest against a real endpoint.
|
|
668
|
+
*/
|
|
669
|
+
declare function Pagination({ page, pageSize, total, pageSizes, onPageChange, onPageSizeChange, loading, unit, className, ...rest }: PaginationProps): React.JSX.Element;
|
|
670
|
+
|
|
671
|
+
/** "just now" · "4 min ago" · "in 4 min" · "Wed 5:00 PM" · "12 Mar" */
|
|
672
|
+
declare function formatRelative(value: string | Date, now?: number): string;
|
|
673
|
+
/** Absolute, for the title attribute. */
|
|
674
|
+
declare function formatAbsolute(value: string | Date): string;
|
|
675
|
+
/** Clock time only — "3:42 PM". */
|
|
676
|
+
declare function formatClock(value: string | Date): string;
|
|
677
|
+
/** ms → "1m 34s" · "12.4s" · "840ms" */
|
|
678
|
+
declare function formatDuration(ms: number): string;
|
|
679
|
+
interface RelativeTimeProps {
|
|
680
|
+
value: string | Date;
|
|
681
|
+
prefix?: string;
|
|
682
|
+
refresh?: boolean;
|
|
683
|
+
className?: string;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* A self-refreshing relative timestamp. Ticks no faster than the unit it shows,
|
|
687
|
+
* and stops entirely once the value is days old.
|
|
688
|
+
*/
|
|
689
|
+
declare function RelativeTime({ value, prefix, className, refresh, ...rest }: RelativeTimeProps): React.JSX.Element | null;
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* File intake helpers — generic, no chat vocabulary.
|
|
693
|
+
* Nothing here touches the DOM event lifecycle: extractors REPORT what they found,
|
|
694
|
+
* the host decides whether to consume the event.
|
|
695
|
+
*
|
|
696
|
+
* Shared by the forms group and the chat kit — promoted to a top-level shared kit
|
|
697
|
+
* since multiple component groups depend on it.
|
|
698
|
+
*/
|
|
699
|
+
/** The host library's file shape. Use it for every file everywhere. */
|
|
700
|
+
interface Attachment {
|
|
701
|
+
id: string;
|
|
702
|
+
name: string;
|
|
703
|
+
size: number;
|
|
704
|
+
mime: string;
|
|
705
|
+
url: string;
|
|
706
|
+
/** Local preview while uploading. */
|
|
707
|
+
blobUrl?: string;
|
|
708
|
+
/** Non-null and < 100 means uploading. */
|
|
709
|
+
progress?: number | null;
|
|
710
|
+
thumb?: {
|
|
711
|
+
url: string;
|
|
712
|
+
};
|
|
713
|
+
/** Failed upload. Takes priority over progress. */
|
|
714
|
+
error?: string;
|
|
715
|
+
/** Free-text replacement for the size line, e.g. "6 lines · pasted". */
|
|
716
|
+
meta?: string;
|
|
717
|
+
/** Present only before upload; strip it before sending. */
|
|
718
|
+
file?: File;
|
|
719
|
+
}
|
|
720
|
+
interface FileRejection {
|
|
721
|
+
file: File;
|
|
722
|
+
reason: "type" | "size";
|
|
723
|
+
message: string;
|
|
724
|
+
}
|
|
725
|
+
declare function formatBytes(n: number): string;
|
|
726
|
+
declare function extensionOf(name: string): string;
|
|
727
|
+
/** Phosphor icon name for a file: type first, extension second. */
|
|
728
|
+
declare function iconForMime(mime?: string, name?: string): string;
|
|
729
|
+
declare function isImage(mime?: string, name?: string): boolean;
|
|
730
|
+
/** accept-attribute semantics: ".png,image/*,text/plain" */
|
|
731
|
+
declare function acceptMatches(file: File, accept?: string): boolean;
|
|
732
|
+
/** Split a FileList against accept + size. Rejections carry a reason string, never a silent drop. */
|
|
733
|
+
declare function filterFiles(files: FileList | File[], opts?: {
|
|
734
|
+
accept?: string;
|
|
735
|
+
maxFileSize?: number;
|
|
736
|
+
}): {
|
|
737
|
+
accepted: File[];
|
|
738
|
+
rejected: FileRejection[];
|
|
739
|
+
};
|
|
740
|
+
/** Human name for a synthesised paste file: "Pasted text (380 lines).txt" */
|
|
741
|
+
declare function pastedTextName(text: string): string;
|
|
742
|
+
/**
|
|
743
|
+
* What a paste is carrying, in one pass. NEVER calls preventDefault — it returns
|
|
744
|
+
* `source: null` when nothing was produced, so the host can let the event fall
|
|
745
|
+
* through. That is what makes it reusable in any input.
|
|
746
|
+
*/
|
|
747
|
+
declare function extractClipboardFiles(event: ClipboardEvent, opts?: {
|
|
748
|
+
largePasteThreshold?: number;
|
|
749
|
+
accept?: string;
|
|
750
|
+
maxFileSize?: number;
|
|
751
|
+
name?: string;
|
|
752
|
+
}): {
|
|
753
|
+
files: File[];
|
|
754
|
+
source: "files" | "text" | null;
|
|
755
|
+
rejected: FileRejection[];
|
|
756
|
+
text?: string;
|
|
757
|
+
};
|
|
758
|
+
/**
|
|
759
|
+
* Local, immediately-renderable stand-in for a file being uploaded.
|
|
760
|
+
* Shape is the host library's file type — id/name/size/mime/url/progress/thumb/error.
|
|
761
|
+
*/
|
|
762
|
+
declare function toAttachment(file: File, extra?: Partial<Attachment>): Attachment;
|
|
763
|
+
declare function revokeAttachment(att: Attachment): void;
|
|
764
|
+
/**
|
|
765
|
+
* Namespace export — `const { formatBytes } = window.<Namespace>.FileKit;`
|
|
766
|
+
*/
|
|
767
|
+
declare const FileKit: {
|
|
768
|
+
formatBytes: typeof formatBytes;
|
|
769
|
+
extensionOf: typeof extensionOf;
|
|
770
|
+
iconForMime: typeof iconForMime;
|
|
771
|
+
isImage: typeof isImage;
|
|
772
|
+
acceptMatches: typeof acceptMatches;
|
|
773
|
+
filterFiles: typeof filterFiles;
|
|
774
|
+
pastedTextName: typeof pastedTextName;
|
|
775
|
+
extractClipboardFiles: typeof extractClipboardFiles;
|
|
776
|
+
toAttachment: typeof toAttachment;
|
|
777
|
+
revokeAttachment: typeof revokeAttachment;
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
declare const meterFormats: {
|
|
781
|
+
count: (n: number) => string;
|
|
782
|
+
compact: (n: number) => string;
|
|
783
|
+
bytes: typeof formatBytes;
|
|
784
|
+
percentOf: (total: number) => (n: number) => string;
|
|
785
|
+
};
|
|
786
|
+
interface MeterSegmentInput {
|
|
787
|
+
id?: string;
|
|
788
|
+
label: string;
|
|
789
|
+
value: number;
|
|
790
|
+
variant?: "brand" | "neutral" | "quiet" | "ok" | "warn" | "danger" | "info";
|
|
791
|
+
/** Any CSS colour; overrides variant. Defaults to the chart ramp in order. */
|
|
792
|
+
color?: string;
|
|
793
|
+
}
|
|
794
|
+
interface SegmentedMeterProps {
|
|
795
|
+
total: number;
|
|
796
|
+
segments: MeterSegmentInput[];
|
|
797
|
+
label?: string;
|
|
798
|
+
/** Value formatter. meterFormats.compact | .count | .bytes */
|
|
799
|
+
format?: (n: number) => string;
|
|
800
|
+
height?: number;
|
|
801
|
+
legend?: boolean;
|
|
802
|
+
showTotal?: boolean;
|
|
803
|
+
showPercent?: boolean;
|
|
804
|
+
remainderLabel?: string;
|
|
805
|
+
onClick?: () => void;
|
|
806
|
+
className?: string;
|
|
807
|
+
/** Base id for the meter's aria-labelledby target; falls back to React.useId(). */
|
|
808
|
+
id?: string;
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* A capacity bar split into categories — a context window, a disk quota, a budget
|
|
812
|
+
* burn-down, a rate limit. Deliberately free of any AI vocabulary: it takes a total
|
|
813
|
+
* and labelled segments, and reports the remainder as free space.
|
|
814
|
+
*
|
|
815
|
+
* A zero or missing total reports 0%, never NaN%.
|
|
816
|
+
*/
|
|
817
|
+
declare function SegmentedMeter({ total, segments, label, format, height, legend, showTotal, showPercent, remainderLabel, onClick, className, id, }: SegmentedMeterProps): React.JSX.Element;
|
|
818
|
+
interface QuotaRowProps {
|
|
819
|
+
label: React.ReactNode;
|
|
820
|
+
percent: number;
|
|
821
|
+
note?: React.ReactNode;
|
|
822
|
+
tone?: "brand" | "ok" | "warn" | "danger";
|
|
823
|
+
className?: string;
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* One quota line: name, an optional reset note, a percentage and a bar.
|
|
827
|
+
* Rate limits, plan allowances, storage tiers.
|
|
828
|
+
*/
|
|
829
|
+
declare function QuotaRow({ label, percent, note, tone, className }: QuotaRowProps): React.JSX.Element;
|
|
830
|
+
|
|
831
|
+
interface SortMenuField {
|
|
832
|
+
/** Sort key sent to the endpoint. */
|
|
833
|
+
key: string;
|
|
834
|
+
/** Human label shown in the menu and on the collapsed trigger. */
|
|
835
|
+
label: string;
|
|
836
|
+
/** Numeric fields read "lowest/highest first" instead of "A to Z". */
|
|
837
|
+
numeric?: boolean;
|
|
838
|
+
}
|
|
839
|
+
interface SortMenuProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
840
|
+
fields: SortMenuField[];
|
|
841
|
+
/** Current sort, straight from the endpoint's response envelope. */
|
|
842
|
+
sort?: {
|
|
843
|
+
key: string;
|
|
844
|
+
dir: "asc" | "desc";
|
|
845
|
+
} | null;
|
|
846
|
+
onSort?: (key: string, dir: "asc" | "desc") => void;
|
|
847
|
+
/** Which edge the popover aligns to. Default "right". */
|
|
848
|
+
align?: "left" | "right";
|
|
849
|
+
className?: string;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* SortMenu — the sort control for every server-driven table.
|
|
853
|
+
*
|
|
854
|
+
* A raw "sort=crp&dir=desc" readout tells a user what the machine is doing, not what
|
|
855
|
+
* they asked for. This states the field in words and offers both directions as explicit
|
|
856
|
+
* targets, so choosing "highest first" is one click rather than a toggle you have to
|
|
857
|
+
* click twice and then verify.
|
|
858
|
+
*
|
|
859
|
+
* Direction wording follows the field type — numeric fields read "lowest/highest first",
|
|
860
|
+
* text fields "A to Z" — while both use the same arrow pair, because varying the icon by
|
|
861
|
+
* type encodes a distinction nobody asked for and reads as inconsistency.
|
|
862
|
+
*
|
|
863
|
+
* Purely presentational: reports intent through onSort(key, dir) and never holds sort
|
|
864
|
+
* state, so it stays honest against a real endpoint.
|
|
865
|
+
*/
|
|
866
|
+
declare function SortMenu({ fields, sort, onSort, align, className, ...rest }: SortMenuProps): React.JSX.Element;
|
|
867
|
+
|
|
868
|
+
interface Step {
|
|
869
|
+
id: string;
|
|
870
|
+
/** Short verb + target: "Queried calls table". */
|
|
871
|
+
label: string;
|
|
872
|
+
kind?: "tool" | "search" | "read" | "write" | "reasoning" | "run" | "fetch";
|
|
873
|
+
/** Raw input/output or reasoning text, behind a disclosure. */
|
|
874
|
+
detail?: string;
|
|
875
|
+
durationMs?: number;
|
|
876
|
+
status?: "running" | "ok" | "error";
|
|
877
|
+
}
|
|
878
|
+
/** Flat collapsible rows — never one card per tool call. */
|
|
879
|
+
interface StepListProps {
|
|
880
|
+
steps: Step[];
|
|
881
|
+
label?: string;
|
|
882
|
+
defaultOpen?: boolean;
|
|
883
|
+
open?: boolean;
|
|
884
|
+
onToggle?: (open: boolean) => void;
|
|
885
|
+
showDurations?: boolean;
|
|
886
|
+
dense?: boolean;
|
|
887
|
+
className?: string;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* A flat, collapsible list of work steps — one row per step, never one card per
|
|
891
|
+
* call. A long run stays readable: it is a list, not a stack.
|
|
892
|
+
*/
|
|
893
|
+
declare function StepList({ steps, label, defaultOpen, open: openProp, onToggle, showDurations, dense, className, }: StepListProps): React.JSX.Element | null;
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Multi-select control with a 44px comfortable hit area.
|
|
897
|
+
*/
|
|
898
|
+
interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
899
|
+
label: React.ReactNode;
|
|
900
|
+
description?: React.ReactNode;
|
|
901
|
+
/** Renders as a bordered selectable card — for the big, calm choices. */
|
|
902
|
+
card?: boolean;
|
|
903
|
+
indeterminate?: boolean;
|
|
904
|
+
}
|
|
905
|
+
declare function Checkbox({ label, description, card, indeterminate, className, ...rest }: CheckboxProps): React.JSX.Element;
|
|
906
|
+
|
|
907
|
+
/** One-of-many control. Share a name across the group. */
|
|
908
|
+
interface RadioProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
909
|
+
label: React.ReactNode;
|
|
910
|
+
description?: React.ReactNode;
|
|
911
|
+
card?: boolean;
|
|
912
|
+
}
|
|
913
|
+
declare function Radio({ label, description, card, className, ...rest }: RadioProps): React.JSX.Element;
|
|
914
|
+
|
|
915
|
+
/** An immediate on/off setting — no Save button. Use Checkbox inside forms instead. */
|
|
916
|
+
interface SwitchProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
917
|
+
label?: React.ReactNode;
|
|
918
|
+
description?: React.ReactNode;
|
|
919
|
+
}
|
|
920
|
+
declare function Switch({ label, description, className, ...rest }: SwitchProps): React.JSX.Element;
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Labelled text field. 44px tall, 8px radius, hairline border, blue focus ring.
|
|
924
|
+
*/
|
|
925
|
+
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size" | "prefix"> {
|
|
926
|
+
label?: string;
|
|
927
|
+
help?: string;
|
|
928
|
+
/** Present = error state. Replaces help text. */
|
|
929
|
+
error?: string;
|
|
930
|
+
/** Static text inside the field, left — e.g. "$" */
|
|
931
|
+
prefix?: React.ReactNode;
|
|
932
|
+
/** Static text inside the field, right — e.g. "CPM" */
|
|
933
|
+
suffix?: React.ReactNode;
|
|
934
|
+
/** Phosphor icon name shown left of the input */
|
|
935
|
+
icon?: string;
|
|
936
|
+
/** Right-aligned tabular figures. Use for every money or count field. */
|
|
937
|
+
numeric?: boolean;
|
|
938
|
+
required?: boolean;
|
|
939
|
+
size?: "md" | "lg";
|
|
940
|
+
/** Inline spinner on the right — for async validation or a value still arriving. */
|
|
941
|
+
loading?: boolean;
|
|
942
|
+
/** Applied to the outer .fd-field element — this is where width belongs. */
|
|
943
|
+
style?: React.CSSProperties;
|
|
944
|
+
/** Applied to the inner input element. Rarely needed. */
|
|
945
|
+
inputStyle?: React.CSSProperties;
|
|
946
|
+
}
|
|
947
|
+
declare function Input({ label, help, error, prefix, suffix, icon, numeric, required, size, loading, disabled, id, className, style, inputStyle, ...rest }: InputProps): React.JSX.Element;
|
|
948
|
+
|
|
949
|
+
/** Multi-line field. Same chrome as Input; vertical resize only. */
|
|
950
|
+
interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
951
|
+
label?: string;
|
|
952
|
+
help?: string;
|
|
953
|
+
error?: string;
|
|
954
|
+
required?: boolean;
|
|
955
|
+
rows?: number;
|
|
956
|
+
/** Applied to the field wrapper. */
|
|
957
|
+
style?: React.CSSProperties;
|
|
958
|
+
}
|
|
959
|
+
declare function Textarea({ label, help, error, required, rows, disabled, id, className, style, ...rest }: TextareaProps): React.JSX.Element;
|
|
960
|
+
|
|
961
|
+
/** Numeric field with thousands formatting on blur, +/- steppers with hold-to-repeat acceleration, arrow keys (shift = bigStep), and min/max clamping. */
|
|
962
|
+
interface NumberInputProps {
|
|
963
|
+
label?: string;
|
|
964
|
+
help?: string;
|
|
965
|
+
error?: string;
|
|
966
|
+
prefix?: string;
|
|
967
|
+
suffix?: string;
|
|
968
|
+
required?: boolean;
|
|
969
|
+
disabled?: boolean;
|
|
970
|
+
min?: number;
|
|
971
|
+
max?: number;
|
|
972
|
+
step?: number;
|
|
973
|
+
/** Larger step used while holding the steppers or shift+arrows. */
|
|
974
|
+
bigStep?: number;
|
|
975
|
+
/** Thousands-format the display when not editing. Default true. */
|
|
976
|
+
format?: boolean;
|
|
977
|
+
value?: number | string;
|
|
978
|
+
/** Called with {target:{value}} where value is a number. */
|
|
979
|
+
onChange?: (e: any) => void;
|
|
980
|
+
placeholder?: string;
|
|
981
|
+
style?: any;
|
|
982
|
+
className?: string;
|
|
983
|
+
}
|
|
984
|
+
declare function NumberInput({ label, help, error, prefix, suffix, required, disabled, min, max, step, bigStep, value, onChange, format, className, style, placeholder }: NumberInputProps): React.JSX.Element;
|
|
985
|
+
|
|
986
|
+
/** Rich popover select: animated listbox, keyboard nav + type-ahead, search over 8 options, option icons, descriptions, meta and groups. API-compatible with the old native select. */
|
|
987
|
+
interface SelectOption {
|
|
988
|
+
value: string;
|
|
989
|
+
label: string;
|
|
990
|
+
description?: string;
|
|
991
|
+
icon?: string;
|
|
992
|
+
meta?: any;
|
|
993
|
+
group?: string;
|
|
994
|
+
disabled?: boolean;
|
|
995
|
+
}
|
|
996
|
+
interface SelectProps {
|
|
997
|
+
label?: string;
|
|
998
|
+
help?: string;
|
|
999
|
+
error?: string;
|
|
1000
|
+
options: Array<string | SelectOption>;
|
|
1001
|
+
placeholder?: string;
|
|
1002
|
+
required?: boolean;
|
|
1003
|
+
disabled?: boolean;
|
|
1004
|
+
/** Options still arriving — the control locks and shows a spinner. */
|
|
1005
|
+
loading?: boolean;
|
|
1006
|
+
/** Force the search box on/off; default: on when over 8 options. */
|
|
1007
|
+
searchable?: boolean;
|
|
1008
|
+
/** Show an x that clears back to placeholder. */
|
|
1009
|
+
clearable?: boolean;
|
|
1010
|
+
/** Pick several: `value` is an array, the popover stays open, options get
|
|
1011
|
+
* checkboxes, and a footer offers Select all / Clear. */
|
|
1012
|
+
multiple?: boolean;
|
|
1013
|
+
/** With `multiple`, override the collapsed box text for 2+ selections. */
|
|
1014
|
+
summary?: (values: string[]) => string;
|
|
1015
|
+
value?: string | string[];
|
|
1016
|
+
/** Called with {target:{value}} like a native select — an array when `multiple`. */
|
|
1017
|
+
onChange?: (e: any) => void;
|
|
1018
|
+
name?: string;
|
|
1019
|
+
/** Applied to the outer .fd-field element — this is where width belongs. */
|
|
1020
|
+
style?: any;
|
|
1021
|
+
className?: string;
|
|
1022
|
+
/** Element id — forwarded to the trigger button and the label's htmlFor. Not present on the original .d.ts but required by the source markup. */
|
|
1023
|
+
id?: string;
|
|
1024
|
+
}
|
|
1025
|
+
declare function Select({ label, help, error, options, placeholder, required, disabled, loading, value, onChange, searchable, clearable, multiple, summary, id, className, style, ...rest }: SelectProps): React.JSX.Element;
|
|
1026
|
+
|
|
1027
|
+
/** Calendar popover date field. Click the title to drill month -> year; range mode picks start then end with live hover preview. Values are ISO strings ("2026-03-01"). */
|
|
1028
|
+
interface DatePickerProps {
|
|
1029
|
+
label?: string;
|
|
1030
|
+
help?: string;
|
|
1031
|
+
error?: string;
|
|
1032
|
+
required?: boolean;
|
|
1033
|
+
disabled?: boolean;
|
|
1034
|
+
/** false: value is "YYYY-MM-DD". true: value is {start, end}. */
|
|
1035
|
+
range?: boolean;
|
|
1036
|
+
value?: any;
|
|
1037
|
+
/** Called with {target:{value}} — string, or {start,end} in range mode. */
|
|
1038
|
+
onChange?: (e: any) => void;
|
|
1039
|
+
placeholder?: string;
|
|
1040
|
+
style?: any;
|
|
1041
|
+
className?: string;
|
|
1042
|
+
}
|
|
1043
|
+
/** The bare calendar, for embedding in custom surfaces. months=2 renders two side-by-side months (used by range mode). */
|
|
1044
|
+
interface CalendarProps {
|
|
1045
|
+
value?: any;
|
|
1046
|
+
range?: boolean;
|
|
1047
|
+
onPick: (v: any) => void;
|
|
1048
|
+
initialMonth?: string;
|
|
1049
|
+
months?: number;
|
|
1050
|
+
}
|
|
1051
|
+
declare function Calendar({ value, range, onPick, initialMonth, months }: CalendarProps): React.JSX.Element;
|
|
1052
|
+
declare function DatePicker({ label, help, error, required, disabled, range, value, onChange, placeholder, className, style, ...rest }: DatePickerProps): React.JSX.Element;
|
|
1053
|
+
|
|
1054
|
+
/** Analog clock-face time field: tap or drag the hand to set the hour, auto-advances to minutes; AM/PM chips; "Now" shortcut. Value is 24h "HH:MM". */
|
|
1055
|
+
interface TimePickerProps {
|
|
1056
|
+
label?: string;
|
|
1057
|
+
help?: string;
|
|
1058
|
+
error?: string;
|
|
1059
|
+
required?: boolean;
|
|
1060
|
+
disabled?: boolean;
|
|
1061
|
+
/** 24h "HH:MM", e.g. "14:30". */
|
|
1062
|
+
value?: string;
|
|
1063
|
+
/** Called with {target:{value}}. */
|
|
1064
|
+
onChange?: (e: any) => void;
|
|
1065
|
+
placeholder?: string;
|
|
1066
|
+
style?: any;
|
|
1067
|
+
className?: string;
|
|
1068
|
+
}
|
|
1069
|
+
/** The bare clock face, for embedding (e.g. below a Calendar). */
|
|
1070
|
+
interface ClockFaceProps {
|
|
1071
|
+
value?: string;
|
|
1072
|
+
onChange: (v: string) => void;
|
|
1073
|
+
}
|
|
1074
|
+
declare function ClockFace({ value, onChange }: ClockFaceProps): React.JSX.Element;
|
|
1075
|
+
declare function TimePicker({ label, help, error, required, disabled, value, onChange, placeholder, className, style }: TimePickerProps): React.JSX.Element;
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* Continuous value control. In the media planner this is the reallocation slider:
|
|
1079
|
+
* a live chip follows the handle while dragging and the commit is explicit.
|
|
1080
|
+
*/
|
|
1081
|
+
interface SliderProps {
|
|
1082
|
+
label?: string;
|
|
1083
|
+
min?: number;
|
|
1084
|
+
max?: number;
|
|
1085
|
+
step?: number;
|
|
1086
|
+
value?: number;
|
|
1087
|
+
onChange?: (value: number) => void;
|
|
1088
|
+
/** Renders the value in the chip and header, e.g. v => "$" + v.toLocaleString() */
|
|
1089
|
+
format?: (value: number) => string;
|
|
1090
|
+
showChip?: boolean;
|
|
1091
|
+
help?: string;
|
|
1092
|
+
className?: string;
|
|
1093
|
+
}
|
|
1094
|
+
declare function Slider({ label, min, max, step, value, onChange, format, showChip, help, className, ...rest }: SliderProps): React.JSX.Element;
|
|
1095
|
+
|
|
1096
|
+
/** Dual-thumb range slider: drag either thumb or the rail, value bubbles while dragging or focused, tick marks with labels, optional magnetic snap, keyboard (arrows, shift for 10%, Home/End). */
|
|
1097
|
+
interface RangeSliderProps {
|
|
1098
|
+
label?: string;
|
|
1099
|
+
help?: string;
|
|
1100
|
+
min?: number;
|
|
1101
|
+
max?: number;
|
|
1102
|
+
step?: number;
|
|
1103
|
+
/** [low, high] */
|
|
1104
|
+
value: [number, number];
|
|
1105
|
+
onChange: (v: [number, number]) => void;
|
|
1106
|
+
/** Minimum distance the thumbs keep between them. */
|
|
1107
|
+
minGap?: number;
|
|
1108
|
+
/** Tick stops; label is optional. */
|
|
1109
|
+
marks?: Array<{
|
|
1110
|
+
value: number;
|
|
1111
|
+
label?: string;
|
|
1112
|
+
}>;
|
|
1113
|
+
/** Ordinal mode: unevenly spaced legal values (numbers, or {value,label}); the
|
|
1114
|
+
* slider then travels in even steps between them and reports real values.
|
|
1115
|
+
* Infinity is allowed for an open upper bound — give it a label like "No limit". */
|
|
1116
|
+
stops?: Array<number | {
|
|
1117
|
+
value: number;
|
|
1118
|
+
label?: string;
|
|
1119
|
+
}>;
|
|
1120
|
+
/** Magnetically snap to nearby marks while dragging. */
|
|
1121
|
+
snap?: boolean;
|
|
1122
|
+
format?: (v: number) => string;
|
|
1123
|
+
className?: string;
|
|
1124
|
+
}
|
|
1125
|
+
declare function RangeSlider({ label, min, max, step, value, onChange, minGap, marks, snap, stops, format, help, className, ...rest }: RangeSliderProps & Record<string, any>): JSX.Element;
|
|
1126
|
+
|
|
1127
|
+
interface DropzoneProps {
|
|
1128
|
+
onFiles?: (files: File[]) => void;
|
|
1129
|
+
onReject?: (rejections: FileRejection[]) => void;
|
|
1130
|
+
accept?: string;
|
|
1131
|
+
maxFileSize?: number;
|
|
1132
|
+
multiple?: boolean;
|
|
1133
|
+
disabled?: boolean;
|
|
1134
|
+
label?: string;
|
|
1135
|
+
hint?: string;
|
|
1136
|
+
children?: React.ReactNode;
|
|
1137
|
+
className?: string;
|
|
1138
|
+
style?: React.CSSProperties;
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* A drop target that wraps any content. Reports accepted files and rejections;
|
|
1142
|
+
* it never uploads anything itself.
|
|
1143
|
+
*/
|
|
1144
|
+
declare function Dropzone({ onFiles, onReject, accept, maxFileSize, multiple, disabled, label, hint, children, className, style, }: DropzoneProps): React.JSX.Element;
|
|
1145
|
+
interface FilePickButtonProps extends Omit<DropzoneProps, "children" | "label" | "hint" | "style"> {
|
|
1146
|
+
label?: string;
|
|
1147
|
+
icon?: string;
|
|
1148
|
+
children?: React.ReactNode;
|
|
1149
|
+
}
|
|
1150
|
+
/** A file input behind any trigger. Resets between picks so re-picking the same file fires. */
|
|
1151
|
+
declare function FilePickButton({ onFiles, onReject, accept, maxFileSize, multiple, disabled, label, icon, className, children, }: FilePickButtonProps): React.JSX.Element;
|
|
1152
|
+
type FileUploadHandler = (file: File, onProgress: (pct: number) => void, signal?: AbortSignal) => Promise<{
|
|
1153
|
+
url: string;
|
|
1154
|
+
thumb?: {
|
|
1155
|
+
url: string;
|
|
1156
|
+
};
|
|
1157
|
+
} | void>;
|
|
1158
|
+
/**
|
|
1159
|
+
* Upload orchestration for a staged set of files: progress per file, per-file
|
|
1160
|
+
* errors, retry, and cancel — the same behaviour an upload field has.
|
|
1161
|
+
*
|
|
1162
|
+
* `upload(file, onProgress, signal)` must resolve to `{url, thumb?}` or throw.
|
|
1163
|
+
* Nothing here knows what a chat is; it is the app's upload handler that does.
|
|
1164
|
+
*/
|
|
1165
|
+
declare function useStagedFiles(upload?: FileUploadHandler, opts?: {
|
|
1166
|
+
onUploaded?: (a: Attachment) => void;
|
|
1167
|
+
onError?: (e: unknown, a: Attachment) => void;
|
|
1168
|
+
}): {
|
|
1169
|
+
items: Attachment[];
|
|
1170
|
+
add: (files: File[] | FileList) => Attachment[];
|
|
1171
|
+
remove: (a: Attachment) => void;
|
|
1172
|
+
retry: (a: Attachment) => void;
|
|
1173
|
+
clear: () => void;
|
|
1174
|
+
busy: boolean;
|
|
1175
|
+
setItems: (items: Attachment[]) => void;
|
|
1176
|
+
};
|
|
1177
|
+
/** Namespace export — useStagedFiles is a hook, so it cannot be capital-initial itself. */
|
|
1178
|
+
declare const DropzoneKit: {
|
|
1179
|
+
useStagedFiles: typeof useStagedFiles;
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
interface FileChipProps {
|
|
1183
|
+
file: Attachment;
|
|
1184
|
+
onOpen?: (f: Attachment) => void;
|
|
1185
|
+
onRemove?: (f: Attachment) => void;
|
|
1186
|
+
onRetry?: (f: Attachment) => void;
|
|
1187
|
+
compact?: boolean;
|
|
1188
|
+
className?: string;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* A compact file row: icon or thumbnail, name, size, live upload progress,
|
|
1192
|
+
* error state, and an optional remove control. One file component everywhere —
|
|
1193
|
+
* images get real thumbnails, video gets a play badge, uploads get a bar.
|
|
1194
|
+
*/
|
|
1195
|
+
declare function FileChip({ file, onOpen, onRemove, onRetry, compact, className }: FileChipProps): React.JSX.Element | null;
|
|
1196
|
+
interface FileTileProps {
|
|
1197
|
+
file: Attachment;
|
|
1198
|
+
onOpen?: (f: Attachment) => void;
|
|
1199
|
+
onRemove?: (f: Attachment) => void;
|
|
1200
|
+
maxHeight?: number;
|
|
1201
|
+
className?: string;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* An image-forward tile. Pasted screenshots belong here, not in a chip —
|
|
1205
|
+
* the picture is the content.
|
|
1206
|
+
*/
|
|
1207
|
+
declare function FileTile({ file, onOpen, onRemove, maxHeight, className }: FileTileProps): React.JSX.Element | null;
|
|
1208
|
+
/**
|
|
1209
|
+
* One row of uniform tiles — the staging tray. Same size whatever the type:
|
|
1210
|
+
* images crop to fill, documents show a truncated name and their size.
|
|
1211
|
+
* Overflow scrolls sideways instead of growing the tray.
|
|
1212
|
+
*/
|
|
1213
|
+
interface FileStripProps {
|
|
1214
|
+
files: Attachment[];
|
|
1215
|
+
size?: number;
|
|
1216
|
+
onOpen?: (f: Attachment) => void;
|
|
1217
|
+
onRemove?: (f: Attachment) => void;
|
|
1218
|
+
onRetry?: (f: Attachment) => void;
|
|
1219
|
+
className?: string;
|
|
1220
|
+
}
|
|
1221
|
+
declare function FileStrip({ files, size, onOpen, onRemove, onRetry, className }: FileStripProps): React.JSX.Element | null;
|
|
1222
|
+
/** Images as tiles, everything else as chips. */
|
|
1223
|
+
interface FileGridProps {
|
|
1224
|
+
files: Attachment[];
|
|
1225
|
+
onOpen?: (f: Attachment) => void;
|
|
1226
|
+
onRemove?: (f: Attachment) => void;
|
|
1227
|
+
onRetry?: (f: Attachment) => void;
|
|
1228
|
+
tiles?: boolean;
|
|
1229
|
+
maxHeight?: number;
|
|
1230
|
+
compact?: boolean;
|
|
1231
|
+
className?: string;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* A set of files: images as tiles, everything else as chips.
|
|
1235
|
+
* `tiles={false}` forces the compact all-chips form for tight columns.
|
|
1236
|
+
*/
|
|
1237
|
+
declare function FileGrid({ files, onOpen, onRemove, onRetry, tiles, maxHeight, compact, className }: FileGridProps): React.JSX.Element | null;
|
|
1238
|
+
|
|
1239
|
+
interface MarkdownEditorHandle {
|
|
1240
|
+
focus(opts?: FocusOptions): void;
|
|
1241
|
+
blur(): void;
|
|
1242
|
+
readonly element: HTMLElement | null;
|
|
1243
|
+
getSelection(): {
|
|
1244
|
+
start: number;
|
|
1245
|
+
end: number;
|
|
1246
|
+
};
|
|
1247
|
+
replaceRange(start: number, end: number, text: string, caretAt?: number): void;
|
|
1248
|
+
insert(text: string): void;
|
|
1249
|
+
wrapSelection(before: string, after?: string, placeholder?: string): void;
|
|
1250
|
+
prefixLines(prefix: string): void;
|
|
1251
|
+
}
|
|
1252
|
+
interface EditorTrigger {
|
|
1253
|
+
type: "slash" | "mention";
|
|
1254
|
+
query: string;
|
|
1255
|
+
from: number;
|
|
1256
|
+
to: number;
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Rich markdown composer. A contenteditable whose text content IS the markdown
|
|
1260
|
+
* source, one element per line — so headings can be bigger and bold can be bold
|
|
1261
|
+
* without a mirror to keep in metric lockstep.
|
|
1262
|
+
*
|
|
1263
|
+
* Keydown ordering contract: the host sees the event FIRST (onKeyDown), may call
|
|
1264
|
+
* preventDefault(), and the editor then checks defaultPrevented and skips its own
|
|
1265
|
+
* handling. onPaste follows the same contract.
|
|
1266
|
+
*/
|
|
1267
|
+
interface MarkdownEditorProps {
|
|
1268
|
+
value: string;
|
|
1269
|
+
onChange?: (next: string) => void;
|
|
1270
|
+
onSubmit?: (value: string) => void;
|
|
1271
|
+
onKeyDown?: (e: React.KeyboardEvent) => void;
|
|
1272
|
+
onPaste?: (e: React.ClipboardEvent) => void;
|
|
1273
|
+
onTrigger?: (t: EditorTrigger | null) => void;
|
|
1274
|
+
placeholder?: string;
|
|
1275
|
+
disabled?: boolean;
|
|
1276
|
+
readOnly?: boolean;
|
|
1277
|
+
maxLength?: number;
|
|
1278
|
+
minRows?: number;
|
|
1279
|
+
maxRows?: number;
|
|
1280
|
+
autoFocus?: boolean;
|
|
1281
|
+
/** Enter sends, Shift+Enter inserts a newline. Default true. */
|
|
1282
|
+
submitOnEnter?: boolean;
|
|
1283
|
+
ariaLabel?: string;
|
|
1284
|
+
className?: string;
|
|
1285
|
+
id?: string;
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* The composer field.
|
|
1289
|
+
*
|
|
1290
|
+
* Keydown ordering contract — the host sees the event FIRST:
|
|
1291
|
+
* editor emits onKeyDown → host may call preventDefault()
|
|
1292
|
+
* → editor checks event.defaultPrevented and skips its own handling
|
|
1293
|
+
* Emitting after the internal handler would make the host's preventDefault
|
|
1294
|
+
* arrive too late. onPaste follows the same contract.
|
|
1295
|
+
*/
|
|
1296
|
+
declare const MarkdownEditor: React.ForwardRefExoticComponent<MarkdownEditorProps & React.RefAttributes<MarkdownEditorHandle>>;
|
|
1297
|
+
|
|
1298
|
+
interface AccountMenuLink {
|
|
1299
|
+
id: string;
|
|
1300
|
+
label: string;
|
|
1301
|
+
icon: string;
|
|
1302
|
+
href?: string;
|
|
1303
|
+
/** Hidden unless the signed-in user holds this permission. */
|
|
1304
|
+
perm?: string;
|
|
1305
|
+
badge?: string;
|
|
1306
|
+
onSelect?: () => void;
|
|
1307
|
+
}
|
|
1308
|
+
interface AccountMenuProps {
|
|
1309
|
+
links?: AccountMenuLink[];
|
|
1310
|
+
/** Administration section. Each entry is filtered by its own `perm`. */
|
|
1311
|
+
adminLinks?: AccountMenuLink[];
|
|
1312
|
+
/** Preferred over href navigation — lets an SPA route without a page load. */
|
|
1313
|
+
onNavigate?: (href: string | undefined, item: AccountMenuLink) => void;
|
|
1314
|
+
onSignOut?: () => void;
|
|
1315
|
+
showRoles?: boolean;
|
|
1316
|
+
/** Demo affordance: impersonate another member to see their UI. Default true. */
|
|
1317
|
+
allowUserSwitch?: boolean;
|
|
1318
|
+
className?: string;
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* The avatar button + popover every flytedesk app puts in its top right.
|
|
1322
|
+
* Reads the signed-in user from SessionKit; shows Administration only to those who can use it.
|
|
1323
|
+
*/
|
|
1324
|
+
declare function AccountMenu({ links, adminLinks, onNavigate, onSignOut, showRoles, allowUserSwitch, className, ...rest }: AccountMenuProps): React.JSX.Element;
|
|
1325
|
+
|
|
1326
|
+
interface EndpointSpec {
|
|
1327
|
+
id: string;
|
|
1328
|
+
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
1329
|
+
path: string;
|
|
1330
|
+
title: string;
|
|
1331
|
+
purpose: string;
|
|
1332
|
+
usedBy?: string[];
|
|
1333
|
+
/** [lo, hi] ms. Doubles as the latency SLO the UI's loading states are designed around. */
|
|
1334
|
+
latency: [number, number];
|
|
1335
|
+
request?: unknown;
|
|
1336
|
+
query?: unknown;
|
|
1337
|
+
response: unknown;
|
|
1338
|
+
notes?: string;
|
|
1339
|
+
/** Marks a paged list returning the { rows, pager, sort, filter } envelope. */
|
|
1340
|
+
isList?: boolean;
|
|
1341
|
+
}
|
|
1342
|
+
interface SpecModule {
|
|
1343
|
+
id: string;
|
|
1344
|
+
label: string;
|
|
1345
|
+
icon?: string;
|
|
1346
|
+
endpoints: string[];
|
|
1347
|
+
}
|
|
1348
|
+
interface ApiSpecBrowserProps {
|
|
1349
|
+
spec?: EndpointSpec[];
|
|
1350
|
+
/** The app's simulated transport: (id, params) => Promise<any>. Powers "Try it". */
|
|
1351
|
+
onRequest: (id: string, params: unknown) => Promise<unknown>;
|
|
1352
|
+
/** Screen → endpoints map. Rendered as the primary filter. */
|
|
1353
|
+
modules?: SpecModule[];
|
|
1354
|
+
/** Lifecycle grouping: [[label, endpointIds]] */
|
|
1355
|
+
groups?: Array<[string, string[]]>;
|
|
1356
|
+
title?: string;
|
|
1357
|
+
kicker?: string;
|
|
1358
|
+
lede?: string;
|
|
1359
|
+
/** Where the spec lives and how to swap in the real transport. */
|
|
1360
|
+
sourceNote?: string;
|
|
1361
|
+
conventions?: Array<[string, string]>;
|
|
1362
|
+
openQuestions?: Array<[string, string, string]>;
|
|
1363
|
+
className?: string;
|
|
1364
|
+
}
|
|
1365
|
+
declare function Json({ obj }: {
|
|
1366
|
+
obj: unknown;
|
|
1367
|
+
}): React.JSX.Element;
|
|
1368
|
+
/** Drop-in browser for a simulated API contract. Takes the spec as data. */
|
|
1369
|
+
declare function ApiSpecBrowser({ spec, onRequest, modules, groups, title, kicker, lede, sourceNote, conventions, openQuestions, className, ...rest }: ApiSpecBrowserProps): React.JSX.Element;
|
|
1370
|
+
|
|
1371
|
+
interface PermissionItem {
|
|
1372
|
+
key: string;
|
|
1373
|
+
label: string;
|
|
1374
|
+
detail: string;
|
|
1375
|
+
}
|
|
1376
|
+
interface PermissionGroup {
|
|
1377
|
+
group: string;
|
|
1378
|
+
items: PermissionItem[];
|
|
1379
|
+
}
|
|
1380
|
+
interface Role {
|
|
1381
|
+
id: string;
|
|
1382
|
+
name: string;
|
|
1383
|
+
system: boolean;
|
|
1384
|
+
description: string;
|
|
1385
|
+
permissions: string[];
|
|
1386
|
+
}
|
|
1387
|
+
interface Flag {
|
|
1388
|
+
key: string;
|
|
1389
|
+
project: string;
|
|
1390
|
+
label: string;
|
|
1391
|
+
description: string;
|
|
1392
|
+
/** Kill switch. False means off for everyone, whatever the targeting says. */
|
|
1393
|
+
enabled: boolean;
|
|
1394
|
+
/** 0-100. Bucketed on a stable hash of the user id so answers never flicker. */
|
|
1395
|
+
rollout: number;
|
|
1396
|
+
/** Role ids the flag is forced on for. */
|
|
1397
|
+
roles: string[];
|
|
1398
|
+
/** Per-user overrides, highest precedence: { userId: true | false } */
|
|
1399
|
+
users: Record<string, boolean>;
|
|
1400
|
+
owner?: string;
|
|
1401
|
+
updated?: string;
|
|
1402
|
+
/** Which screen this flag gates, if any. */
|
|
1403
|
+
screen?: string;
|
|
1404
|
+
/** Target ship date for an unshipped feature. */
|
|
1405
|
+
eta?: string | null;
|
|
1406
|
+
/** Date this shipped — set instead of `eta` once it's done. */
|
|
1407
|
+
shippedAt?: string;
|
|
1408
|
+
/** Build phase index into PHASES. */
|
|
1409
|
+
phase?: number;
|
|
1410
|
+
/** Human estimate, e.g. "4 days". */
|
|
1411
|
+
effort?: string;
|
|
1412
|
+
/** What has to be built for this to be real. */
|
|
1413
|
+
backend?: string;
|
|
1414
|
+
/** Other flag keys this depends on. */
|
|
1415
|
+
dependsOn?: string[];
|
|
1416
|
+
}
|
|
1417
|
+
interface User {
|
|
1418
|
+
id: string;
|
|
1419
|
+
name: string;
|
|
1420
|
+
email: string;
|
|
1421
|
+
title?: string;
|
|
1422
|
+
team?: string;
|
|
1423
|
+
roles: string[];
|
|
1424
|
+
status: "active" | "invited" | "suspended";
|
|
1425
|
+
lastActive?: string;
|
|
1426
|
+
joined?: string;
|
|
1427
|
+
sso?: boolean;
|
|
1428
|
+
timezone?: string;
|
|
1429
|
+
phone?: string;
|
|
1430
|
+
bio?: string;
|
|
1431
|
+
notify?: Record<string, boolean>;
|
|
1432
|
+
}
|
|
1433
|
+
interface Session {
|
|
1434
|
+
user: User;
|
|
1435
|
+
roles: Role[];
|
|
1436
|
+
permissions: string[];
|
|
1437
|
+
/** Resolved flag key → on/off for THIS user. */
|
|
1438
|
+
flags: Record<string, boolean>;
|
|
1439
|
+
allRoles: Role[];
|
|
1440
|
+
allFlags: Flag[];
|
|
1441
|
+
allUsers: User[];
|
|
1442
|
+
}
|
|
1443
|
+
interface FlagExplanation {
|
|
1444
|
+
on: boolean;
|
|
1445
|
+
reason: string;
|
|
1446
|
+
detail: string;
|
|
1447
|
+
}
|
|
1448
|
+
/** roadmap()'s per-flag result row. Not declared in session.d.ts (roadmap() itself is one of
|
|
1449
|
+
* the six SessionKit members missing from that file) — inferred from what roadmap() actually
|
|
1450
|
+
* builds and returns for each flag. */
|
|
1451
|
+
interface RoadmapItem {
|
|
1452
|
+
key: string;
|
|
1453
|
+
label: string;
|
|
1454
|
+
project: string;
|
|
1455
|
+
description: string;
|
|
1456
|
+
backend?: string;
|
|
1457
|
+
effort?: string;
|
|
1458
|
+
owner?: string;
|
|
1459
|
+
dependsOn: string[];
|
|
1460
|
+
screen: string | null;
|
|
1461
|
+
enabled: boolean;
|
|
1462
|
+
/** Which build phase this sits in, and why that phase comes where it does. */
|
|
1463
|
+
phase?: number;
|
|
1464
|
+
phaseName: string;
|
|
1465
|
+
phaseWhy: string | null;
|
|
1466
|
+
phaseStart: string | null;
|
|
1467
|
+
/** Derived, not stored: whether every endpoint the feature needs is wired. */
|
|
1468
|
+
implemented: boolean;
|
|
1469
|
+
date: string | null;
|
|
1470
|
+
/** True when the only date on record is when the UI was designed, not when it ships. */
|
|
1471
|
+
dateIsDesign: boolean;
|
|
1472
|
+
status: "shipped" | "next" | "planned";
|
|
1473
|
+
}
|
|
1474
|
+
/** roadmap()'s return shape. */
|
|
1475
|
+
interface RoadmapResult {
|
|
1476
|
+
items: RoadmapItem[];
|
|
1477
|
+
/** Index of the first unshipped item — everything above it is history. */
|
|
1478
|
+
nextIndex: number;
|
|
1479
|
+
shipped: number;
|
|
1480
|
+
remaining: number;
|
|
1481
|
+
}
|
|
1482
|
+
/** Stable 0–99 bucket from a string, so rollout answers never flicker between reloads. */
|
|
1483
|
+
declare function bucket(seed: string): number;
|
|
1484
|
+
/** Build the session document a real GET /me would return. */
|
|
1485
|
+
declare function buildSession(userId: string, overrides?: {
|
|
1486
|
+
users?: User[];
|
|
1487
|
+
roles?: Role[];
|
|
1488
|
+
flags?: Flag[];
|
|
1489
|
+
}): Session;
|
|
1490
|
+
/** The precedence rule, in one place so the admin UI and the app can never disagree. */
|
|
1491
|
+
declare function evaluateFlag(flag: Flag, user: User): boolean;
|
|
1492
|
+
/** Why a flag resolved the way it did — shown in the admin UI, invaluable in support. */
|
|
1493
|
+
declare function explainFlag(flag: Flag, user: User): FlagExplanation;
|
|
1494
|
+
declare global {
|
|
1495
|
+
interface Window {
|
|
1496
|
+
/** Admin-app-injected overrides for the demo dataset, read by loadSession(). */
|
|
1497
|
+
__FD_ADMIN_OVERRIDES?: {
|
|
1498
|
+
users?: User[];
|
|
1499
|
+
roles?: Role[];
|
|
1500
|
+
flags?: Flag[];
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
/** Replace this with `fetch(API + "/me")` to go live. */
|
|
1505
|
+
declare function loadSession(userId?: string): Session;
|
|
1506
|
+
declare function getSession(): Session;
|
|
1507
|
+
declare function setActiveUser(userId: string): Session;
|
|
1508
|
+
/** Local profile edits persist so the demo keeps them across reloads. */
|
|
1509
|
+
declare function saveProfile(patch: Partial<User>): User;
|
|
1510
|
+
/** Recompute after the admin app mutates users/roles/flags. */
|
|
1511
|
+
declare function refresh(): Session;
|
|
1512
|
+
declare function subscribe(fn: (s: Session) => void): () => void;
|
|
1513
|
+
declare function can(permission: string | string[]): boolean;
|
|
1514
|
+
declare function canAny(permissions: string[]): boolean;
|
|
1515
|
+
declare function hasRole(roleId: string): boolean;
|
|
1516
|
+
declare function flagOn(key: string): boolean;
|
|
1517
|
+
/** The flag registry entry, whatever the session shape.
|
|
1518
|
+
* Not declared in session.d.ts — see the module doc comment on SessionKit below. */
|
|
1519
|
+
declare function findFlag(key: string): Flag | null;
|
|
1520
|
+
declare function flagState(key: string): "on" | "off";
|
|
1521
|
+
declare function isEnabled(key: string): boolean;
|
|
1522
|
+
declare function flagVisible(key: string): boolean;
|
|
1523
|
+
declare function flagLive(key: string): boolean;
|
|
1524
|
+
declare function roadmap(overrides?: {
|
|
1525
|
+
flags?: Flag[];
|
|
1526
|
+
isComplete?: (key: string) => boolean;
|
|
1527
|
+
}): RoadmapResult;
|
|
1528
|
+
/** React binding. Re-renders on user switch, profile save, or admin mutation. */
|
|
1529
|
+
declare function useSession(): Session;
|
|
1530
|
+
/**
|
|
1531
|
+
* Session, RBAC and feature flags for every flytedesk app.
|
|
1532
|
+
*
|
|
1533
|
+
* Not a security boundary: permissions gate the UI so people aren't shown affordances they
|
|
1534
|
+
* cannot use. The server must re-check all of it.
|
|
1535
|
+
*
|
|
1536
|
+
* To go live, replace loadSession() with a fetch of GET /me → { user, roles, permissions, flags }.
|
|
1537
|
+
*/
|
|
1538
|
+
declare const SessionKit: {
|
|
1539
|
+
PERMISSION_CATALOG: PermissionGroup[];
|
|
1540
|
+
ALL_PERMISSIONS: string[];
|
|
1541
|
+
ROLES: Role[];
|
|
1542
|
+
FLAGS: Flag[];
|
|
1543
|
+
USERS: User[];
|
|
1544
|
+
loadSession: typeof loadSession;
|
|
1545
|
+
getSession: typeof getSession;
|
|
1546
|
+
setActiveUser: typeof setActiveUser;
|
|
1547
|
+
saveProfile: typeof saveProfile;
|
|
1548
|
+
refresh: typeof refresh;
|
|
1549
|
+
subscribe: typeof subscribe;
|
|
1550
|
+
can: typeof can;
|
|
1551
|
+
canAny: typeof canAny;
|
|
1552
|
+
hasRole: typeof hasRole;
|
|
1553
|
+
flagOn: typeof flagOn;
|
|
1554
|
+
useSession: typeof useSession;
|
|
1555
|
+
findFlag: typeof findFlag;
|
|
1556
|
+
flagState: typeof flagState;
|
|
1557
|
+
isEnabled: typeof isEnabled;
|
|
1558
|
+
flagVisible: typeof flagVisible;
|
|
1559
|
+
flagLive: typeof flagLive;
|
|
1560
|
+
roadmap: typeof roadmap;
|
|
1561
|
+
evaluateFlag: typeof evaluateFlag;
|
|
1562
|
+
explainFlag: typeof explainFlag;
|
|
1563
|
+
bucket: typeof bucket;
|
|
1564
|
+
buildSession: typeof buildSession;
|
|
1565
|
+
};
|
|
1566
|
+
|
|
1567
|
+
interface DeviceSession {
|
|
1568
|
+
id: string;
|
|
1569
|
+
device: string;
|
|
1570
|
+
where: string;
|
|
1571
|
+
when: string;
|
|
1572
|
+
current?: boolean;
|
|
1573
|
+
}
|
|
1574
|
+
interface ProfilePageProps {
|
|
1575
|
+
/** Defaults to the signed-in user from SessionKit. */
|
|
1576
|
+
user?: User;
|
|
1577
|
+
/** Persist through your own endpoint. Defaults to SessionKit.saveProfile. */
|
|
1578
|
+
onSave?: (draft: Partial<User>) => void | Promise<void>;
|
|
1579
|
+
onNavigate?: (href: string) => void;
|
|
1580
|
+
sessions?: DeviceSession[];
|
|
1581
|
+
showSessions?: boolean;
|
|
1582
|
+
className?: string;
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* The shared profile page: identity, working preferences, notifications, devices.
|
|
1586
|
+
* Roles are shown but never editable — self-promotion is the reason RBAC exists.
|
|
1587
|
+
*/
|
|
1588
|
+
declare function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSessions, className, ...rest }: ProfilePageProps): React.JSX.Element;
|
|
1589
|
+
|
|
1590
|
+
interface RoadmapTimelineProps {
|
|
1591
|
+
/** Called with a flag key when the reader clicks one — route it to your flag admin. */
|
|
1592
|
+
onOpenFlag?: (flagKey: string) => void;
|
|
1593
|
+
/** Heading. Default "Roadmap". */
|
|
1594
|
+
title?: string;
|
|
1595
|
+
/** Replaces the default explanation of what the timeline covers. */
|
|
1596
|
+
lede?: string;
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* A vertical, chronological map of what is wired to the real backend and what is next.
|
|
1600
|
+
*
|
|
1601
|
+
* Derived entirely from the feature-flag registry (`SessionKit.roadmap()`) — status is each
|
|
1602
|
+
* flag's `implemented` field, ordering comes from its date, and blocking relationships come
|
|
1603
|
+
* from its `dependsOn`. Nothing here is maintained separately, so it cannot drift from what
|
|
1604
|
+
* the apps actually do.
|
|
1605
|
+
*
|
|
1606
|
+
* Shipped work sits above a "you are here" marker and upcoming work below it; the scroll
|
|
1607
|
+
* container opens at that marker, so the first thing in view is what comes next.
|
|
1608
|
+
*/
|
|
1609
|
+
declare function RoadmapTimeline({ onOpenFlag, title, lede }: RoadmapTimelineProps): React.JSX.Element;
|
|
1610
|
+
|
|
1611
|
+
interface PermissionDeniedProps {
|
|
1612
|
+
permission?: string | string[];
|
|
1613
|
+
title?: string;
|
|
1614
|
+
detail?: string;
|
|
1615
|
+
compact?: boolean;
|
|
1616
|
+
className?: string;
|
|
1617
|
+
}
|
|
1618
|
+
/** Explains a block rather than hiding it — silence reads as a bug and files a ticket. */
|
|
1619
|
+
declare function PermissionDenied({ permission, title, detail, compact, className, ...rest }: PermissionDeniedProps): React.JSX.Element;
|
|
1620
|
+
interface GateProps {
|
|
1621
|
+
/** All of these must be held. */
|
|
1622
|
+
perm?: string | string[];
|
|
1623
|
+
/** At least one of these must be held. */
|
|
1624
|
+
anyOf?: string[];
|
|
1625
|
+
role?: string | string[];
|
|
1626
|
+
/** Render nothing instead of an explanation — for chrome like toolbar buttons. */
|
|
1627
|
+
silent?: boolean;
|
|
1628
|
+
fallback?: React.ReactNode;
|
|
1629
|
+
compact?: boolean;
|
|
1630
|
+
children?: React.ReactNode;
|
|
1631
|
+
}
|
|
1632
|
+
/** Permission gate. Denied by default shows PermissionDenied. */
|
|
1633
|
+
declare function Gate({ perm, anyOf, role, silent, fallback, compact, children }: GateProps): React.ReactNode;
|
|
1634
|
+
interface FeatureGateProps {
|
|
1635
|
+
flag: string;
|
|
1636
|
+
fallback?: React.ReactNode;
|
|
1637
|
+
children?: React.ReactNode;
|
|
1638
|
+
/** Renders a "See the roadmap" link inside the ComingSoon banner when supplied. */
|
|
1639
|
+
onRoadmap?: () => void;
|
|
1640
|
+
/** Blur radius in px for the underlying, inert UI. Passed through to ComingSoon. */
|
|
1641
|
+
blur?: number;
|
|
1642
|
+
minHeight?: number | string;
|
|
1643
|
+
/** Overrides the ComingSoon banner headline. Defaults to the flag's own label (or a generic one). */
|
|
1644
|
+
label?: string;
|
|
1645
|
+
/** "block" wraps a panel; "inline" wraps a single control (button, chip, menu item). Default "block". */
|
|
1646
|
+
variant?: "block" | "inline";
|
|
1647
|
+
/** When false, an incomplete-in-live-mode feature renders `fallback` instead of the ComingSoon banner. Default true. */
|
|
1648
|
+
preview?: boolean;
|
|
1649
|
+
/** Permission that lets the viewer bypass the ComingSoon veil and use the feature early. Default "flags.manage". */
|
|
1650
|
+
bypassPerm?: string;
|
|
1651
|
+
}
|
|
1652
|
+
/** Feature gate. ALWAYS silent when off — an unreleased feature should leave no trace. */
|
|
1653
|
+
declare function FeatureGate({ flag, fallback, children, onRoadmap, blur, minHeight, label, variant, preview, bypassPerm }: FeatureGateProps): React.ReactNode;
|
|
1654
|
+
/** Inline "why is this disabled" affordance for buttons that must stay visible. */
|
|
1655
|
+
declare function PermissionHint({ perm, children }: {
|
|
1656
|
+
perm: string | string[];
|
|
1657
|
+
children?: React.ReactNode;
|
|
1658
|
+
}): React.ReactNode;
|
|
1659
|
+
|
|
1660
|
+
interface ComingSoonProps {
|
|
1661
|
+
/** Banner headline. Default "Coming soon". */
|
|
1662
|
+
label?: string;
|
|
1663
|
+
/** Target date, already formatted for display. */
|
|
1664
|
+
eta?: string | null;
|
|
1665
|
+
/** Rough size of the remaining work, e.g. "3 weeks". */
|
|
1666
|
+
effort?: string;
|
|
1667
|
+
/** What has to be built on the server for this to go live. */
|
|
1668
|
+
backend?: string | null;
|
|
1669
|
+
/** One line describing the feature itself. */
|
|
1670
|
+
detail?: string;
|
|
1671
|
+
/** Blur radius in px. Default 3 — enough to stop reading, not enough to hide the shape. */
|
|
1672
|
+
blur?: number;
|
|
1673
|
+
minHeight?: number | string;
|
|
1674
|
+
/** "block" wraps a panel; "inline" wraps a single control (button, chip, menu item). */
|
|
1675
|
+
variant?: "block" | "inline";
|
|
1676
|
+
/** Renders a "See the roadmap" link when supplied. */
|
|
1677
|
+
onRoadmap?: () => void;
|
|
1678
|
+
/**
|
|
1679
|
+
* Flag key identifying this feature. Supply it together with `canBypass` to offer a
|
|
1680
|
+
* "View and use it anyway" escape — the choice is remembered for the session (sessionStorage,
|
|
1681
|
+
* not localStorage: a bypass that survives days becomes the silent default).
|
|
1682
|
+
*/
|
|
1683
|
+
bypassKey?: string;
|
|
1684
|
+
/**
|
|
1685
|
+
* Whether this viewer may unblur. Gate on a permission the people building the feature hold
|
|
1686
|
+
* (`flags.manage`). While bypassed the feature is fully interactive, with a persistent,
|
|
1687
|
+
* non-dismissible bar stating that writes go to the simulated backend.
|
|
1688
|
+
*/
|
|
1689
|
+
canBypass?: boolean;
|
|
1690
|
+
children?: React.ReactNode;
|
|
1691
|
+
className?: string;
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* A feature that is designed and working but not yet wired to the real backend: the real UI,
|
|
1695
|
+
* blurred, behind a construction banner, and fully inert (`pointer-events`, the `inert`
|
|
1696
|
+
* attribute, and `aria-hidden`). Lets people see what is coming without acting on it.
|
|
1697
|
+
*
|
|
1698
|
+
* Usually reached through `FeatureGate`, which resolves the flag state for you. Use this
|
|
1699
|
+
* directly only when the gating decision is already made elsewhere.
|
|
1700
|
+
*/
|
|
1701
|
+
declare function ComingSoon({ label, eta, effort, backend, detail, blur, minHeight,
|
|
1702
|
+
/** "block" wraps a panel; "inline" wraps a single control (button, chip, menu item). */
|
|
1703
|
+
variant, onRoadmap,
|
|
1704
|
+
/** Flag key. Supplying it AND `canBypass` lets the viewer unblur and use the feature. */
|
|
1705
|
+
bypassKey, canBypass, children, className, ...rest }: ComingSoonProps): JSX.Element;
|
|
1706
|
+
declare function formatEta(iso?: string | null): string | null;
|
|
1707
|
+
declare const FormatEta: typeof formatEta;
|
|
1708
|
+
|
|
1709
|
+
interface ModeSwitchProps {
|
|
1710
|
+
/** Render nothing unless true. Pass SessionKit.can("flags.manage"). */
|
|
1711
|
+
canToggle?: boolean;
|
|
1712
|
+
/** Simulated requests this session — shown as a count badge in test mode. */
|
|
1713
|
+
requestCount?: number;
|
|
1714
|
+
onClearLog?: () => void;
|
|
1715
|
+
/** Request-log body, rendered inside the popover in test mode only. */
|
|
1716
|
+
renderLog?: () => React.ReactNode;
|
|
1717
|
+
onOpenSpec?: () => void;
|
|
1718
|
+
summary?: {
|
|
1719
|
+
total: number;
|
|
1720
|
+
complete: number;
|
|
1721
|
+
incomplete: number;
|
|
1722
|
+
endpointsWired: number;
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
/** Header control: lightning in live mode, flask + request count in test mode. */
|
|
1726
|
+
declare function ModeSwitch({ canToggle, requestCount, onClearLog, renderLog, onOpenSpec, summary }: ModeSwitchProps): React.ReactNode;
|
|
1727
|
+
interface TestModeBarProps {
|
|
1728
|
+
onExit?: () => void;
|
|
1729
|
+
}
|
|
1730
|
+
/** Persistent strip shown only in test mode. Render at the top of the app frame. */
|
|
1731
|
+
declare function TestModeBar({ onExit }: TestModeBarProps): React.ReactNode;
|
|
1732
|
+
|
|
1733
|
+
/**
|
|
1734
|
+
* The canonical channel taxonomy. One icon and one hue per medium, used
|
|
1735
|
+
* everywhere a channel is named — table cells, charts, legends, filters, the
|
|
1736
|
+
* client view. The colour is part of the label, not decoration.
|
|
1737
|
+
*
|
|
1738
|
+
* Hue families: blues = large format, violets = moving, ambers = paper,
|
|
1739
|
+
* roses = direct, slate = digital.
|
|
1740
|
+
*
|
|
1741
|
+
* `physical` is a descriptive family property, NOT a weight. The model spec
|
|
1742
|
+
* (v2026-07-21 §4) assigns each channel its own weight from four factor scores —
|
|
1743
|
+
* 23 to 50, with persistence separating the tiers. Read weights from
|
|
1744
|
+
* window.CHANNEL_WEIGHT / MODEL.weights, never from this flag.
|
|
1745
|
+
*/
|
|
1746
|
+
declare const CHANNEL_WEIGHTS: Record<string, number>;
|
|
1747
|
+
/** Spec §4 weight for a channel name, resolving §9 aliases. 0 = unmodeled (contributes no CRP). */
|
|
1748
|
+
declare function channelWeightOf(channel: string): number;
|
|
1749
|
+
declare const ChannelWeightOf: typeof channelWeightOf;
|
|
1750
|
+
interface ChannelMetaRecord {
|
|
1751
|
+
label: string;
|
|
1752
|
+
/** Phosphor icon name, without the `ph-` prefix. */
|
|
1753
|
+
icon: string;
|
|
1754
|
+
/** CSS custom-property reference, e.g. "var(--ch-ooh)". */
|
|
1755
|
+
color: string;
|
|
1756
|
+
/** Hue family: Large format | Moving | Paper | Direct | Digital | Other. */
|
|
1757
|
+
family: string;
|
|
1758
|
+
/** Descriptive family property, not a weight. Channel weights live in MODEL.weights (spec §4). */
|
|
1759
|
+
physical: boolean;
|
|
1760
|
+
name?: string;
|
|
1761
|
+
}
|
|
1762
|
+
/**
|
|
1763
|
+
* The canonical channel taxonomy — one icon and one hue per medium. Every screen
|
|
1764
|
+
* that names a channel reads from this, so a colour always means the same thing.
|
|
1765
|
+
*/
|
|
1766
|
+
declare const CHANNELS: Record<string, ChannelMetaRecord>;
|
|
1767
|
+
/** Resolve any channel spelling (exact, lowercase key, partial) to its record. */
|
|
1768
|
+
declare function ChannelMeta(name?: string | null): ChannelMetaRecord;
|
|
1769
|
+
interface ChannelTagProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
1770
|
+
/** Channel name — "OOH", "Website (CPM)", "transit"… */
|
|
1771
|
+
channel?: string | null;
|
|
1772
|
+
size?: "sm" | "md";
|
|
1773
|
+
/** Hide the text and show icon only (dense tables). */
|
|
1774
|
+
showLabel?: boolean;
|
|
1775
|
+
/** Render as a bare colour swatch, for chart legends. */
|
|
1776
|
+
dot?: boolean;
|
|
1777
|
+
/** Append the channel's spec §4 weight after the label. */
|
|
1778
|
+
weight?: boolean;
|
|
1779
|
+
className?: string;
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* The standard way to render a channel name. Icon + hue + label, in a tinted
|
|
1783
|
+
* chip. Use `dot` for a bare colour swatch in dense legends.
|
|
1784
|
+
*/
|
|
1785
|
+
declare function ChannelTag({ channel, size, showLabel, dot, weight, className, ...rest }: ChannelTagProps): React.JSX.Element;
|
|
1786
|
+
|
|
1787
|
+
/**
|
|
1788
|
+
* The plan's shape: how funded campuses spread across the four CSI bands.
|
|
1789
|
+
* Each campus is one labelled block stacked in its band's column, so the
|
|
1790
|
+
* distribution reads as a silhouette before any number is parsed. Columns are
|
|
1791
|
+
* clickable to filter the school mix by band.
|
|
1792
|
+
*/
|
|
1793
|
+
interface SaturationDistributionCampus {
|
|
1794
|
+
name: string;
|
|
1795
|
+
/** Short label shown inside the block, e.g. "Michigan State" */
|
|
1796
|
+
short?: string;
|
|
1797
|
+
band: "weak" | "adequate" | "strong" | "dominant";
|
|
1798
|
+
crp?: number;
|
|
1799
|
+
}
|
|
1800
|
+
interface SaturationDistributionProps {
|
|
1801
|
+
campuses: SaturationDistributionCampus[];
|
|
1802
|
+
/** Band of the plan-level floor score. Renders the floor explainer strip. */
|
|
1803
|
+
floorBand?: "weak" | "adequate" | "strong" | "dominant";
|
|
1804
|
+
/** The plan floor figure, e.g. 157.2 */
|
|
1805
|
+
floorScore?: number | string;
|
|
1806
|
+
onSelectBand?: (band: string | null) => void;
|
|
1807
|
+
selectedBand?: string | null;
|
|
1808
|
+
loading?: boolean;
|
|
1809
|
+
className?: string;
|
|
1810
|
+
}
|
|
1811
|
+
declare function SaturationDistribution({ campuses, floorBand, floorScore, onSelectBand, selectedBand, loading, className }: SaturationDistributionProps): React.JSX.Element;
|
|
1812
|
+
|
|
1813
|
+
/**
|
|
1814
|
+
* Realized media mix against the strategic target, one row per channel.
|
|
1815
|
+
* Realized is a solid brand bar, target a neutral tick on the same row, and the
|
|
1816
|
+
* gap is labelled directly on the bar — the gap is the story, so it never hides
|
|
1817
|
+
* in a legend.
|
|
1818
|
+
*/
|
|
1819
|
+
interface MixGapRow {
|
|
1820
|
+
channel: string;
|
|
1821
|
+
/** Target share, percent */
|
|
1822
|
+
target: number;
|
|
1823
|
+
/** Realized share, percent */
|
|
1824
|
+
realized: number;
|
|
1825
|
+
/** Why the gap exists, e.g. "Limited inventory at target campuses" — shown in the hover tooltip. */
|
|
1826
|
+
note?: string;
|
|
1827
|
+
}
|
|
1828
|
+
interface MixGapProps {
|
|
1829
|
+
rows: MixGapRow[];
|
|
1830
|
+
loading?: boolean;
|
|
1831
|
+
className?: string;
|
|
1832
|
+
}
|
|
1833
|
+
declare function MixGap({ rows, loading, className }: MixGapProps): React.JSX.Element;
|
|
1834
|
+
|
|
1835
|
+
/**
|
|
1836
|
+
* How one campus's CRP was built, channel by channel, with the surround-sound
|
|
1837
|
+
* bonus as its own hatched segment. This is the most valuable visualization in
|
|
1838
|
+
* the planner: it answers why a campus scores what it scores and where the
|
|
1839
|
+
* headroom is.
|
|
1840
|
+
*/
|
|
1841
|
+
interface ChannelContributionChannel {
|
|
1842
|
+
name: string;
|
|
1843
|
+
/** CRP contributed by this channel */
|
|
1844
|
+
crp: number;
|
|
1845
|
+
/** Formatted spend, e.g. "$12,400" */
|
|
1846
|
+
spend?: string;
|
|
1847
|
+
/** Physical formats (OOH, transit, posters) carry roughly 2x the weight of feed-based digital. */
|
|
1848
|
+
physical?: boolean;
|
|
1849
|
+
color?: string;
|
|
1850
|
+
}
|
|
1851
|
+
interface ChannelContributionProps {
|
|
1852
|
+
channels: ChannelContributionChannel[];
|
|
1853
|
+
/** Surround-sound bonus actually earned, as a percent of the channel base. */
|
|
1854
|
+
bonusPct?: number;
|
|
1855
|
+
/** The model's ceiling. Defaults to 40. */
|
|
1856
|
+
bonusMax?: number;
|
|
1857
|
+
/** Override the computed total when the engine's figure differs from base + bonus. */
|
|
1858
|
+
total?: number;
|
|
1859
|
+
loading?: boolean;
|
|
1860
|
+
showTable?: boolean;
|
|
1861
|
+
className?: string;
|
|
1862
|
+
}
|
|
1863
|
+
declare function ChannelContribution({ channels, bonusPct, bonusMax, total, loading, showTable, className }: ChannelContributionProps): React.JSX.Element;
|
|
1864
|
+
|
|
1865
|
+
/**
|
|
1866
|
+
* The planner's signature interaction: move budget at one campus and watch its
|
|
1867
|
+
* saturation band re-score live. The previewed band renders at 70% opacity with
|
|
1868
|
+
* a dashed edge until the change is committed; Escape reverts.
|
|
1869
|
+
*/
|
|
1870
|
+
interface BudgetReallocatorProps {
|
|
1871
|
+
campus: string;
|
|
1872
|
+
/** Committed spend. */
|
|
1873
|
+
spend: number;
|
|
1874
|
+
/** Committed CRP. */
|
|
1875
|
+
crp: number;
|
|
1876
|
+
min?: number;
|
|
1877
|
+
max: number;
|
|
1878
|
+
/** Defaults to 100 — the same increment as an arrow-key step. */
|
|
1879
|
+
step?: number;
|
|
1880
|
+
/** The engine's re-score for a candidate spend. Required for a live preview. */
|
|
1881
|
+
scoreFor?: (spend: number) => number;
|
|
1882
|
+
onPreview?: (spend: number, crp: number) => void;
|
|
1883
|
+
onCommit?: (spend: number, crp: number) => void;
|
|
1884
|
+
onCancel?: () => void;
|
|
1885
|
+
className?: string;
|
|
1886
|
+
}
|
|
1887
|
+
declare function BudgetReallocator({ campus, spend, crp, min, max, step, scoreFor, onPreview, onCommit, onCancel, className }: BudgetReallocatorProps): React.JSX.Element;
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
* Channel-diversity breadth at one campus and the bonus it earned or forgave.
|
|
1891
|
+
* The engine rewards reaching a campus through multiple reinforcing categories
|
|
1892
|
+
* with up to +40% on its score; thin-breadth campuses forgo most of it.
|
|
1893
|
+
*/
|
|
1894
|
+
interface SurroundSoundProps {
|
|
1895
|
+
/** Category keys present: "ooh" | "transit" | "posters" | "print" | "email" | "web" */
|
|
1896
|
+
present: string[];
|
|
1897
|
+
/** Bonus actually earned, in points of percent. */
|
|
1898
|
+
bonusPct?: number;
|
|
1899
|
+
/** Model ceiling. Defaults to 40. */
|
|
1900
|
+
bonusMax?: number;
|
|
1901
|
+
/** One sentence naming what the missed breadth costs, e.g. "+$3,000 into OOH earns +12%." */
|
|
1902
|
+
missedCost?: string;
|
|
1903
|
+
className?: string;
|
|
1904
|
+
}
|
|
1905
|
+
declare function SurroundSound({ present, bonusPct, bonusMax, missedCost, className }: SurroundSoundProps): React.JSX.Element;
|
|
1906
|
+
|
|
1907
|
+
/**
|
|
1908
|
+
* Chat state machine. Framework-shaped as a React hook, but the rules it encodes
|
|
1909
|
+
* are the portable part: thread resolution, serial sending, escalation polling,
|
|
1910
|
+
* recovery, abort semantics and retry truncation.
|
|
1911
|
+
*
|
|
1912
|
+
* It ships no HTTP client. Without an adapter it resolves nothing and renders
|
|
1913
|
+
* nothing — every app's proxy differs in auth, routing and host.
|
|
1914
|
+
*/
|
|
1915
|
+
/** Tri-state `valid`: only an explicit false blocks Apply. */
|
|
1916
|
+
interface Citation {
|
|
1917
|
+
id?: string;
|
|
1918
|
+
marker?: string | number;
|
|
1919
|
+
title?: string;
|
|
1920
|
+
url?: string;
|
|
1921
|
+
detail?: string;
|
|
1922
|
+
}
|
|
1923
|
+
/** Tri-state `valid`: only an explicit false blocks Apply. */
|
|
1924
|
+
interface Packet {
|
|
1925
|
+
type: string;
|
|
1926
|
+
payload: unknown;
|
|
1927
|
+
valid?: boolean;
|
|
1928
|
+
error?: string;
|
|
1929
|
+
repaired?: boolean;
|
|
1930
|
+
}
|
|
1931
|
+
interface PacketSchema {
|
|
1932
|
+
heading?: string;
|
|
1933
|
+
icon?: string;
|
|
1934
|
+
applyLabel?: string;
|
|
1935
|
+
}
|
|
1936
|
+
interface JobStatus {
|
|
1937
|
+
id?: string;
|
|
1938
|
+
/** queued | running keep polling · completed | recovered are success · anything else fails loudly. */
|
|
1939
|
+
status: string;
|
|
1940
|
+
detail?: string;
|
|
1941
|
+
error?: string;
|
|
1942
|
+
}
|
|
1943
|
+
interface ChatMessage {
|
|
1944
|
+
id: string;
|
|
1945
|
+
role: "user" | "assistant" | "system";
|
|
1946
|
+
text?: string | null;
|
|
1947
|
+
packet?: Packet | null;
|
|
1948
|
+
/** Escalation in flight. */
|
|
1949
|
+
working?: boolean;
|
|
1950
|
+
/** Tokens arriving — renders a live caret. */
|
|
1951
|
+
streaming?: boolean;
|
|
1952
|
+
/** Optimistic, not yet confirmed. */
|
|
1953
|
+
pending?: boolean;
|
|
1954
|
+
/** The turn was stopped by the user. */
|
|
1955
|
+
stopped?: boolean;
|
|
1956
|
+
jobId?: string;
|
|
1957
|
+
job?: JobStatus;
|
|
1958
|
+
error?: string;
|
|
1959
|
+
retryable?: boolean;
|
|
1960
|
+
timestamp?: string;
|
|
1961
|
+
author?: string;
|
|
1962
|
+
steps?: Step[];
|
|
1963
|
+
citations?: Citation[];
|
|
1964
|
+
attachments?: Attachment[];
|
|
1965
|
+
thinking?: string;
|
|
1966
|
+
thinkingMs?: number;
|
|
1967
|
+
feedback?: "up" | "down" | null;
|
|
1968
|
+
model?: string;
|
|
1969
|
+
usage?: unknown;
|
|
1970
|
+
resumed?: boolean;
|
|
1971
|
+
metadata?: {
|
|
1972
|
+
type?: string;
|
|
1973
|
+
[k: string]: unknown;
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
interface SendMessageResult {
|
|
1977
|
+
/** true = escalated to a background job. */
|
|
1978
|
+
dispatched?: boolean;
|
|
1979
|
+
job_id?: string;
|
|
1980
|
+
reply?: string | null;
|
|
1981
|
+
packet?: Packet;
|
|
1982
|
+
steps?: Step[];
|
|
1983
|
+
citations?: Citation[];
|
|
1984
|
+
usage?: unknown;
|
|
1985
|
+
model?: string;
|
|
1986
|
+
}
|
|
1987
|
+
interface StreamHandlers {
|
|
1988
|
+
/** Chunks APPEND, never replace. */
|
|
1989
|
+
onToken(chunk: string): void;
|
|
1990
|
+
onStep?(step: Step): void;
|
|
1991
|
+
onThinking?(chunk: string): void;
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* The app-supplied backend. There is no default: every proxy differs in auth,
|
|
1995
|
+
* routing and host. Reject with the backend's stable error code as the message —
|
|
1996
|
+
* the panel matches codes by exact equality, never substring sniffing.
|
|
1997
|
+
*/
|
|
1998
|
+
interface ChatAdapter {
|
|
1999
|
+
resolveThread(ctx: {
|
|
2000
|
+
contextType: string;
|
|
2001
|
+
contextId: string;
|
|
2002
|
+
}): Promise<{
|
|
2003
|
+
thread_id: string | null;
|
|
2004
|
+
}>;
|
|
2005
|
+
getThread(threadId: string): Promise<{
|
|
2006
|
+
messages: ChatMessage[];
|
|
2007
|
+
}>;
|
|
2008
|
+
sendMessage(threadId: string, text: string, signal?: AbortSignal, attachments?: Attachment[]): Promise<SendMessageResult>;
|
|
2009
|
+
getJob(jobId: string): Promise<JobStatus>;
|
|
2010
|
+
/** Preferred when present. */
|
|
2011
|
+
streamMessage?(threadId: string, text: string, handlers: StreamHandlers, signal: AbortSignal, attachments?: Attachment[]): Promise<SendMessageResult>;
|
|
2012
|
+
/** Cancels upstream only. It must not gate whether Stop appears. */
|
|
2013
|
+
cancelJob?(jobId: string): Promise<void>;
|
|
2014
|
+
}
|
|
2015
|
+
interface ChatEngineOptions {
|
|
2016
|
+
contextType: string;
|
|
2017
|
+
contextId: string;
|
|
2018
|
+
apiAdapter?: ChatAdapter | null;
|
|
2019
|
+
initialMessage?: string | null;
|
|
2020
|
+
threadId?: string | null;
|
|
2021
|
+
pollIntervalMs?: number;
|
|
2022
|
+
pollMaxAttempts?: number;
|
|
2023
|
+
store?: {
|
|
2024
|
+
get(k: string): string | null;
|
|
2025
|
+
set(k: string, v: string): void;
|
|
2026
|
+
del(k: string): void;
|
|
2027
|
+
};
|
|
2028
|
+
onPacket?: (p: Packet) => void;
|
|
2029
|
+
onThreadReady?: (id: string) => void;
|
|
2030
|
+
onError?: (e: unknown) => void;
|
|
2031
|
+
onSend?: (text: string) => void;
|
|
2032
|
+
onClear?: () => void;
|
|
2033
|
+
onFeedback?: (x: {
|
|
2034
|
+
message?: ChatMessage;
|
|
2035
|
+
feedback?: string | null;
|
|
2036
|
+
}) => void;
|
|
2037
|
+
}
|
|
2038
|
+
interface QueuedTurn {
|
|
2039
|
+
id: string;
|
|
2040
|
+
text: string;
|
|
2041
|
+
attachments?: Attachment[];
|
|
2042
|
+
}
|
|
2043
|
+
interface ChatEngine {
|
|
2044
|
+
status: "idle" | "resolving" | "loading" | "ready" | "disconnected";
|
|
2045
|
+
threadId: string | null;
|
|
2046
|
+
messages: ChatMessage[];
|
|
2047
|
+
visible: ChatMessage[];
|
|
2048
|
+
queue: QueuedTurn[];
|
|
2049
|
+
busy: boolean;
|
|
2050
|
+
fatal: string | null;
|
|
2051
|
+
turnStartedAt: number | null;
|
|
2052
|
+
send(text: string, attachments?: Attachment[]): void;
|
|
2053
|
+
stop(): void;
|
|
2054
|
+
retry(): void;
|
|
2055
|
+
clear(): void;
|
|
2056
|
+
removeQueued(id: string): void;
|
|
2057
|
+
setFeedback(id: string, value: "up" | "down"): void;
|
|
2058
|
+
reload(): Promise<void>;
|
|
2059
|
+
canSend: boolean;
|
|
2060
|
+
}
|
|
2061
|
+
declare const CHAT_UNAVAILABLE = "chat_unavailable";
|
|
2062
|
+
declare const isSystemMessage: (m: ChatMessage | null | undefined) => boolean;
|
|
2063
|
+
declare const visibleMessages: (list: ChatMessage[] | null | undefined) => ChatMessage[];
|
|
2064
|
+
declare function jobBucket(status?: string): "pending" | "success" | "failure";
|
|
2065
|
+
declare function useChatEngine(opts: ChatEngineOptions): ChatEngine;
|
|
2066
|
+
/**
|
|
2067
|
+
* Namespace export. The bundle exposes only capital-initial names, so this is how a
|
|
2068
|
+
* consuming project reaches the state machine to build its own shell:
|
|
2069
|
+
*
|
|
2070
|
+
* const { useChatEngine } = window.<Namespace>.ChatKit;
|
|
2071
|
+
* const chat = useChatEngine({ contextType, contextId, apiAdapter });
|
|
2072
|
+
*/
|
|
2073
|
+
declare const ChatKit: {
|
|
2074
|
+
useChatEngine: typeof useChatEngine;
|
|
2075
|
+
visibleMessages: (list: ChatMessage[] | null | undefined) => ChatMessage[];
|
|
2076
|
+
isSystemMessage: (m: ChatMessage | null | undefined) => boolean;
|
|
2077
|
+
jobBucket: typeof jobBucket;
|
|
2078
|
+
CHAT_UNAVAILABLE: string;
|
|
2079
|
+
};
|
|
2080
|
+
|
|
2081
|
+
interface Suggestion {
|
|
2082
|
+
id?: string;
|
|
2083
|
+
label: string;
|
|
2084
|
+
text?: string;
|
|
2085
|
+
description?: string;
|
|
2086
|
+
icon?: string;
|
|
2087
|
+
}
|
|
2088
|
+
interface SlashCommand {
|
|
2089
|
+
id: string;
|
|
2090
|
+
label: string;
|
|
2091
|
+
description?: string;
|
|
2092
|
+
icon?: string;
|
|
2093
|
+
shortcut?: string; /** Send immediately on pick. */
|
|
2094
|
+
immediate?: boolean;
|
|
2095
|
+
run?: () => void;
|
|
2096
|
+
}
|
|
2097
|
+
interface MentionSource {
|
|
2098
|
+
id?: string;
|
|
2099
|
+
label: string;
|
|
2100
|
+
value?: string;
|
|
2101
|
+
description?: string;
|
|
2102
|
+
icon?: string;
|
|
2103
|
+
meta?: React.ReactNode;
|
|
2104
|
+
}
|
|
2105
|
+
interface Model {
|
|
2106
|
+
id: string;
|
|
2107
|
+
label: string;
|
|
2108
|
+
description?: string; /** Key HINT only. */
|
|
2109
|
+
shortcut?: string;
|
|
2110
|
+
group?: string;
|
|
2111
|
+
meta?: React.ReactNode;
|
|
2112
|
+
disabled?: boolean;
|
|
2113
|
+
}
|
|
2114
|
+
interface ContextUsage {
|
|
2115
|
+
total: number;
|
|
2116
|
+
segments: MeterSegmentInput[];
|
|
2117
|
+
label?: string;
|
|
2118
|
+
format?: "count" | "compact" | "bytes";
|
|
2119
|
+
}
|
|
2120
|
+
interface UsageLimit {
|
|
2121
|
+
id: string;
|
|
2122
|
+
label: string;
|
|
2123
|
+
percent: number;
|
|
2124
|
+
resetsAt?: string;
|
|
2125
|
+
note?: string;
|
|
2126
|
+
}
|
|
2127
|
+
interface SessionStats {
|
|
2128
|
+
elapsedMs?: number;
|
|
2129
|
+
tokens?: number;
|
|
2130
|
+
runningTasks?: number;
|
|
2131
|
+
costUsd?: number;
|
|
2132
|
+
turns?: number;
|
|
2133
|
+
}
|
|
2134
|
+
interface ThreadSummary {
|
|
2135
|
+
id: string;
|
|
2136
|
+
title?: string;
|
|
2137
|
+
description?: string;
|
|
2138
|
+
updatedAt?: string;
|
|
2139
|
+
}
|
|
2140
|
+
type VoiceHandler = (controls: {
|
|
2141
|
+
onText: (text: string, meta?: {
|
|
2142
|
+
replace?: boolean;
|
|
2143
|
+
}) => void;
|
|
2144
|
+
onEnd?: () => void;
|
|
2145
|
+
}) => (() => void) | void;
|
|
2146
|
+
interface AgentChatPanelProps {
|
|
2147
|
+
contextType: string;
|
|
2148
|
+
contextId: string;
|
|
2149
|
+
apiAdapter: ChatAdapter | null;
|
|
2150
|
+
/** Show this exact thread instead of resolving one from context. Set it from
|
|
2151
|
+
* onNewThread / onSelectThread to make history switching work. */
|
|
2152
|
+
threadId?: string | null;
|
|
2153
|
+
title?: string;
|
|
2154
|
+
subtitle?: React.ReactNode;
|
|
2155
|
+
showHeader?: boolean;
|
|
2156
|
+
headerActions?: React.ReactNode;
|
|
2157
|
+
onClose?: () => void;
|
|
2158
|
+
assistantName?: string;
|
|
2159
|
+
userName?: string;
|
|
2160
|
+
assistantAvatar?: string;
|
|
2161
|
+
userAvatar?: string;
|
|
2162
|
+
/** Phosphor name for the assistant mark. Default "sparkle". */
|
|
2163
|
+
assistantIcon?: string;
|
|
2164
|
+
showAvatars?: boolean;
|
|
2165
|
+
markdown?: boolean;
|
|
2166
|
+
/** Clamp threshold before Show more. Default 600; 0 disables. */
|
|
2167
|
+
maxVisibleChars?: number;
|
|
2168
|
+
messageActions?: boolean;
|
|
2169
|
+
showSteps?: boolean;
|
|
2170
|
+
showThinking?: boolean;
|
|
2171
|
+
emptyTitle?: string;
|
|
2172
|
+
emptyDescription?: string;
|
|
2173
|
+
emptyIcon?: string;
|
|
2174
|
+
suggestions?: Array<Suggestion | string>;
|
|
2175
|
+
renderEmpty?: () => React.ReactNode;
|
|
2176
|
+
packetSchemas?: Record<string, PacketSchema>;
|
|
2177
|
+
packetRenderers?: Record<string, (p: Packet) => React.ReactNode>;
|
|
2178
|
+
onApplyPacket?: (p: Packet) => void;
|
|
2179
|
+
onOpenAttachment?: (a: Attachment) => void;
|
|
2180
|
+
/** Map a backend error code to human copy. */
|
|
2181
|
+
errorCopy?: (code: string) => React.ReactNode;
|
|
2182
|
+
placeholder?: string;
|
|
2183
|
+
maxLength?: number;
|
|
2184
|
+
hint?: React.ReactNode;
|
|
2185
|
+
/** Omit and there is no attach affordance — a pasted image falls through. */
|
|
2186
|
+
fileUploadHandler?: FileUploadHandler;
|
|
2187
|
+
acceptFiles?: string;
|
|
2188
|
+
maxFileSize?: number;
|
|
2189
|
+
/** Chars above which a paste becomes a .txt attachment. Default 4000. */
|
|
2190
|
+
largePasteThreshold?: number;
|
|
2191
|
+
slashCommands?: SlashCommand[];
|
|
2192
|
+
mentionSources?: MentionSource[] | ((query: string) => Promise<MentionSource[]>);
|
|
2193
|
+
voiceHandler?: VoiceHandler;
|
|
2194
|
+
initialMessage?: string | null;
|
|
2195
|
+
contextUsage?: ContextUsage;
|
|
2196
|
+
sessionStats?: SessionStats;
|
|
2197
|
+
usageLimits?: UsageLimit[];
|
|
2198
|
+
models?: Model[];
|
|
2199
|
+
moreModels?: Model[];
|
|
2200
|
+
model?: string;
|
|
2201
|
+
onModelChange?: (id: string) => void;
|
|
2202
|
+
effortLevels?: string[];
|
|
2203
|
+
effort?: string;
|
|
2204
|
+
onEffortChange?: (level: string) => void;
|
|
2205
|
+
fastMode?: boolean;
|
|
2206
|
+
onFastModeChange?: (on: boolean) => void;
|
|
2207
|
+
threads?: ThreadSummary[];
|
|
2208
|
+
onSelectThread?: (t: ThreadSummary) => void;
|
|
2209
|
+
onNewThread?: () => void;
|
|
2210
|
+
/** Supply to put a delete affordance on each history row. Omit and none renders. */
|
|
2211
|
+
onDeleteThread?: (t: ThreadSummary) => void;
|
|
2212
|
+
surface?: "sidebar" | "inline" | "page" | "modal" | "sheet";
|
|
2213
|
+
width?: number;
|
|
2214
|
+
minWidth?: number;
|
|
2215
|
+
maxWidth?: number;
|
|
2216
|
+
resizable?: boolean;
|
|
2217
|
+
height?: number | string;
|
|
2218
|
+
className?: string;
|
|
2219
|
+
style?: React.CSSProperties;
|
|
2220
|
+
onPacket?: (p: Packet) => void;
|
|
2221
|
+
onThreadReady?: (id: string) => void;
|
|
2222
|
+
onError?: (e: unknown) => void;
|
|
2223
|
+
onSend?: (text: string) => void;
|
|
2224
|
+
onClear?: () => void;
|
|
2225
|
+
onFeedback?: (x: {
|
|
2226
|
+
message?: ChatMessage;
|
|
2227
|
+
feedback?: string | null;
|
|
2228
|
+
}) => void;
|
|
2229
|
+
onEditMessage?: (m: ChatMessage) => void;
|
|
2230
|
+
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Agent chat panel.
|
|
2233
|
+
*
|
|
2234
|
+
* Ships no backend: without `apiAdapter` it loads nothing and says so. Every
|
|
2235
|
+
* optional surface — usage meters, model picker, attachments, avatars, steps —
|
|
2236
|
+
* renders only when its input is supplied. A panel with no telemetry shows no
|
|
2237
|
+
* telemetry, not zeroes.
|
|
2238
|
+
*/
|
|
2239
|
+
declare function AgentChatPanel({ contextType, contextId, apiAdapter, threadId, title, subtitle, showHeader, headerActions, onClose, assistantName, userName, assistantAvatar, userAvatar, assistantIcon, showAvatars, markdown, maxVisibleChars, messageActions, showSteps, showThinking, emptyTitle, emptyDescription, emptyIcon, suggestions, renderEmpty, packetSchemas, packetRenderers, onApplyPacket, onOpenAttachment, errorCopy, placeholder, maxLength, hint, fileUploadHandler, acceptFiles, maxFileSize, largePasteThreshold, slashCommands, mentionSources, voiceHandler, initialMessage, contextUsage, sessionStats, usageLimits, models, model, onModelChange, effortLevels, effort, onEffortChange, fastMode, onFastModeChange, moreModels, threads, onSelectThread, onNewThread, onDeleteThread, surface, width, minWidth, maxWidth, resizable, height, className, style, onPacket, onThreadReady, onError, onSend, onClear, onFeedback, onEditMessage, }: AgentChatPanelProps): React.JSX.Element;
|
|
2240
|
+
|
|
2241
|
+
/**
|
|
2242
|
+
* Staged files, the queue strip, the markdown editor, slash/mention triggers, send + stop.
|
|
2243
|
+
* Enter sends, Shift+Enter continues the list or quote you are in, IME composition never
|
|
2244
|
+
* sends. Omit `fileUploadHandler` and there is no attach affordance at all — a pasted
|
|
2245
|
+
* image then falls through to the editor rather than stranding on a message that cannot
|
|
2246
|
+
* carry it.
|
|
2247
|
+
*/
|
|
2248
|
+
interface ChatComposerProps {
|
|
2249
|
+
onSubmit: (text: string, attachments?: Attachment[]) => void;
|
|
2250
|
+
/** Offered whenever a turn is in flight, even with no upstream cancel. */
|
|
2251
|
+
onStop?: () => void;
|
|
2252
|
+
busy?: boolean;
|
|
2253
|
+
disabled?: boolean;
|
|
2254
|
+
placeholder?: string;
|
|
2255
|
+
maxLength?: number;
|
|
2256
|
+
fileUploadHandler?: FileUploadHandler;
|
|
2257
|
+
acceptFiles?: string;
|
|
2258
|
+
maxFileSize?: number;
|
|
2259
|
+
/** Chars above which a paste becomes a .txt attachment. Default 4000. */
|
|
2260
|
+
largePasteThreshold?: number;
|
|
2261
|
+
slashCommands?: SlashCommand[];
|
|
2262
|
+
mentionSources?: MentionSource[] | ((query: string) => Promise<MentionSource[]>);
|
|
2263
|
+
voiceHandler?: VoiceHandler;
|
|
2264
|
+
/** Messages waiting behind the turn in flight. Each row is removable. */
|
|
2265
|
+
queue?: Array<{
|
|
2266
|
+
id: string;
|
|
2267
|
+
text: string;
|
|
2268
|
+
attachments?: Attachment[];
|
|
2269
|
+
}>;
|
|
2270
|
+
onRemoveQueued?: (id: string) => void;
|
|
2271
|
+
/** Rendered above the field — where AgentChatPanel puts the session bar. */
|
|
2272
|
+
sessionBar?: React.ReactNode;
|
|
2273
|
+
toolbarExtras?: React.ReactNode;
|
|
2274
|
+
/** Controlled draft, so a host can prefill (edit-a-message does this). */
|
|
2275
|
+
draft?: string;
|
|
2276
|
+
onDraftChange?: (text: string) => void;
|
|
2277
|
+
hint?: React.ReactNode;
|
|
2278
|
+
narrow?: boolean;
|
|
2279
|
+
autoFocus?: boolean;
|
|
2280
|
+
onReject?: (rejections: FileRejection[]) => void;
|
|
2281
|
+
onOpenAttachment?: (a: Attachment) => void;
|
|
2282
|
+
}
|
|
2283
|
+
/**
|
|
2284
|
+
* The composer: staged attachments, the visible send queue, the editor, and the
|
|
2285
|
+
* send/stop control. Every optional affordance appears only when its input is
|
|
2286
|
+
* supplied — no upload handler means no attach button at all.
|
|
2287
|
+
*/
|
|
2288
|
+
declare function ChatComposer({ onSubmit, onStop, busy, disabled, placeholder, maxLength, fileUploadHandler, acceptFiles, maxFileSize, largePasteThreshold, slashCommands, mentionSources, voiceHandler, queue, onRemoveQueued, sessionBar, toolbarExtras, draft, onDraftChange, hint, narrow, autoFocus, onReject, onOpenAttachment, }: ChatComposerProps): React.JSX.Element;
|
|
2289
|
+
|
|
2290
|
+
/**
|
|
2291
|
+
* Elapsed time, token spend, context window and quota rows. Renders `null` unless at
|
|
2292
|
+
* least one input is supplied — the bar is never an empty frame.
|
|
2293
|
+
*/
|
|
2294
|
+
interface ChatSessionBarProps {
|
|
2295
|
+
sessionStats?: SessionStats;
|
|
2296
|
+
contextUsage?: ContextUsage;
|
|
2297
|
+
usageLimits?: UsageLimit[];
|
|
2298
|
+
onClear?: () => void;
|
|
2299
|
+
extras?: React.ReactNode;
|
|
2300
|
+
}
|
|
2301
|
+
/**
|
|
2302
|
+
* The strip above the composer. Every part is prop-fed: supply none of
|
|
2303
|
+
* sessionStats / contextUsage / usageLimits and no bar renders at all.
|
|
2304
|
+
*/
|
|
2305
|
+
declare function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }: ChatSessionBarProps): React.JSX.Element | null;
|
|
2306
|
+
/**
|
|
2307
|
+
* Model picker, effort level and fast mode. `null` when no models are given.
|
|
2308
|
+
* A model's `shortcut` renders a key cap and binds nothing.
|
|
2309
|
+
*/
|
|
2310
|
+
interface ModelControlsProps {
|
|
2311
|
+
models?: Model[];
|
|
2312
|
+
model?: string;
|
|
2313
|
+
onModelChange?: (id: string) => void;
|
|
2314
|
+
effortLevels?: string[];
|
|
2315
|
+
effort?: string;
|
|
2316
|
+
onEffortChange?: (level: string) => void;
|
|
2317
|
+
fastMode?: boolean;
|
|
2318
|
+
onFastModeChange?: (on: boolean) => void;
|
|
2319
|
+
fastModeLabel?: string;
|
|
2320
|
+
/** Collapsed into a submenu. */
|
|
2321
|
+
moreModels?: Model[];
|
|
2322
|
+
narrow?: boolean;
|
|
2323
|
+
}
|
|
2324
|
+
/**
|
|
2325
|
+
* Model picker (+ optional effort and fast-mode). Renders only when `models` is
|
|
2326
|
+
* supplied. Shortcuts are hints; the app registers the real keys.
|
|
2327
|
+
*/
|
|
2328
|
+
declare function ModelControls({ models, model, onModelChange, effortLevels, effort, onEffortChange, fastMode, onFastModeChange, fastModeLabel, moreModels, narrow: _narrow, }: ModelControlsProps): React.JSX.Element | null;
|
|
2329
|
+
|
|
2330
|
+
/**
|
|
2331
|
+
* Render context, built once by AgentChatPanel and threaded into every part.
|
|
2332
|
+
* Assembling your own shell means supplying this yourself.
|
|
2333
|
+
*/
|
|
2334
|
+
interface ChatRenderContext {
|
|
2335
|
+
assistantName?: string;
|
|
2336
|
+
userName?: string;
|
|
2337
|
+
assistantAvatar?: string;
|
|
2338
|
+
userAvatar?: string;
|
|
2339
|
+
/** Phosphor name for the assistant mark. Default "sparkle". */
|
|
2340
|
+
assistantIcon?: string;
|
|
2341
|
+
showAvatars?: boolean;
|
|
2342
|
+
markdown?: boolean;
|
|
2343
|
+
/** Clamp threshold before Show more. 0 disables. */
|
|
2344
|
+
maxVisibleChars?: number;
|
|
2345
|
+
messageActions?: boolean;
|
|
2346
|
+
showSteps?: boolean;
|
|
2347
|
+
showThinking?: boolean;
|
|
2348
|
+
packetSchemas?: Record<string, PacketSchema>;
|
|
2349
|
+
packetRenderers?: Record<string, (p: Packet) => React.ReactNode>;
|
|
2350
|
+
appliedPackets?: Record<string, boolean>;
|
|
2351
|
+
/** Sidebar-width layout: tighter padding, stacked controls. */
|
|
2352
|
+
narrow?: boolean;
|
|
2353
|
+
onOpenAttachment?: (a: Attachment) => void;
|
|
2354
|
+
/** Max height (px) of the attachment grid before it scrolls. Default 220. */
|
|
2355
|
+
attachmentHeight?: number;
|
|
2356
|
+
/** Renderer for `[^n]` citation markers inside markdown bodies. */
|
|
2357
|
+
renderCitation?: (marker: string) => React.ReactNode;
|
|
2358
|
+
/** Map a backend error code to human copy. */
|
|
2359
|
+
errorCopy?: (code: string) => React.ReactNode;
|
|
2360
|
+
onRetry?: () => void;
|
|
2361
|
+
onFeedback?: (id: string, value: "up" | "down") => void;
|
|
2362
|
+
onEdit?: (m: ChatMessage) => void;
|
|
2363
|
+
onApplyPacket?: (p: Packet) => void;
|
|
2364
|
+
onOpenCitation?: (c: Citation, e: React.MouseEvent) => void;
|
|
2365
|
+
extraActions?: (group: MessageGroup) => React.ReactNode;
|
|
2366
|
+
/** Disconnected-state heading. Default "The assistant isn't reachable". */
|
|
2367
|
+
deadTitle?: string;
|
|
2368
|
+
onReconnect?: () => void;
|
|
2369
|
+
}
|
|
2370
|
+
/** A run of consecutive messages from one sender — see groupMessages. */
|
|
2371
|
+
interface MessageGroup {
|
|
2372
|
+
role: string;
|
|
2373
|
+
author?: string;
|
|
2374
|
+
messages: ChatMessage[];
|
|
2375
|
+
/** A single-message run; affects spacing and the author label. */
|
|
2376
|
+
solo: boolean;
|
|
2377
|
+
key: string;
|
|
2378
|
+
}
|
|
2379
|
+
/** A structured result. `valid: false` blocks Apply; undefined does not. */
|
|
2380
|
+
interface PacketCardProps {
|
|
2381
|
+
packet: Packet;
|
|
2382
|
+
schema?: PacketSchema;
|
|
2383
|
+
render?: (p: Packet) => React.ReactNode;
|
|
2384
|
+
onApply?: (p: Packet) => void;
|
|
2385
|
+
applied?: boolean;
|
|
2386
|
+
}
|
|
2387
|
+
/**
|
|
2388
|
+
* A structured result carried by a message. `valid` is tri-state: only an
|
|
2389
|
+
* explicit `false` blocks Apply — an unvalidated packet is a legitimate result.
|
|
2390
|
+
*/
|
|
2391
|
+
declare function PacketCard({ packet, schema, render, onApply, applied }: PacketCardProps): React.JSX.Element | null;
|
|
2392
|
+
interface ThinkingBlockProps {
|
|
2393
|
+
text?: string;
|
|
2394
|
+
durationMs?: number;
|
|
2395
|
+
streaming?: boolean;
|
|
2396
|
+
defaultOpen?: boolean;
|
|
2397
|
+
}
|
|
2398
|
+
declare function ThinkingBlock({ text, durationMs, streaming, defaultOpen }: ThinkingBlockProps): React.JSX.Element | null;
|
|
2399
|
+
interface CitationsProps {
|
|
2400
|
+
items?: Citation[];
|
|
2401
|
+
onOpen?: (c: Citation, e: React.MouseEvent) => void;
|
|
2402
|
+
}
|
|
2403
|
+
declare function Citations({ items, onOpen }: CitationsProps): React.JSX.Element | null;
|
|
2404
|
+
/** Message content only — markdown, code blocks, clamping. No chrome. */
|
|
2405
|
+
interface MessageBodyProps {
|
|
2406
|
+
message: ChatMessage;
|
|
2407
|
+
ctx: ChatRenderContext;
|
|
2408
|
+
}
|
|
2409
|
+
declare function MessageBody({ message: m, ctx }: MessageBodyProps): React.JSX.Element;
|
|
2410
|
+
/** Copy / retry / feedback / edit. Real buttons; the hover reveal is CSS only. */
|
|
2411
|
+
interface RunActionsProps {
|
|
2412
|
+
group: MessageGroup;
|
|
2413
|
+
ctx: ChatRenderContext;
|
|
2414
|
+
}
|
|
2415
|
+
/**
|
|
2416
|
+
* One toolbar per run — copying the whole run, not one message. Buttons stay in
|
|
2417
|
+
* the DOM (the reveal is CSS only), so keyboard and screen-reader users, who
|
|
2418
|
+
* never produce a hover, can still reach them.
|
|
2419
|
+
*/
|
|
2420
|
+
declare function RunActions({ group, ctx }: RunActionsProps): React.JSX.Element | null;
|
|
2421
|
+
/** One turn: author label, body, attachments, packet, thinking, citations, actions. */
|
|
2422
|
+
interface ChatTurnProps {
|
|
2423
|
+
group: MessageGroup;
|
|
2424
|
+
ctx: ChatRenderContext;
|
|
2425
|
+
}
|
|
2426
|
+
declare function ChatTurn({ group, ctx }: ChatTurnProps): React.JSX.Element;
|
|
2427
|
+
|
|
2428
|
+
/** Consecutive messages from one sender inside 60s share a head. Anything
|
|
2429
|
+
* carrying a packet, steps or an error always starts its own group. */
|
|
2430
|
+
declare function groupMessages(list: ChatMessage[]): MessageGroup[];
|
|
2431
|
+
/**
|
|
2432
|
+
* Runs of consecutive messages from one sender, for run-vs-turn spacing.
|
|
2433
|
+
* `role="log"` and deliberately NOT also aria-live — a live region on a
|
|
2434
|
+
* transcript makes a screen reader announce every token of a streaming reply.
|
|
2435
|
+
*/
|
|
2436
|
+
interface ChatTranscriptProps {
|
|
2437
|
+
messages?: ChatMessage[];
|
|
2438
|
+
ctx: ChatRenderContext;
|
|
2439
|
+
status?: "idle" | "resolving" | "loading" | "ready" | "disconnected";
|
|
2440
|
+
/** Terminal error code; renders the disconnected state. */
|
|
2441
|
+
fatal?: string | null;
|
|
2442
|
+
suggestions?: Array<Suggestion | string>;
|
|
2443
|
+
onPick?: (s: Suggestion | string) => void;
|
|
2444
|
+
emptyTitle?: string;
|
|
2445
|
+
emptyDescription?: string;
|
|
2446
|
+
emptyIcon?: string;
|
|
2447
|
+
renderEmpty?: () => React.ReactNode;
|
|
2448
|
+
className?: string;
|
|
2449
|
+
}
|
|
2450
|
+
/**
|
|
2451
|
+
* The transcript: a role="log" region (and deliberately not aria-live too —
|
|
2452
|
+
* that doubles announcements). Grouping, day dividers, and an auto-scroll that
|
|
2453
|
+
* follows only while the reader is already at the bottom.
|
|
2454
|
+
*/
|
|
2455
|
+
declare function ChatTranscript({ messages, ctx, status, fatal, suggestions, onPick, emptyTitle, emptyDescription, emptyIcon, renderEmpty, className, }: ChatTranscriptProps): React.JSX.Element;
|
|
2456
|
+
/** Namespace export — grouping helper, for consumers rendering their own transcript. */
|
|
2457
|
+
declare const TranscriptKit: {
|
|
2458
|
+
groupMessages: typeof groupMessages;
|
|
2459
|
+
};
|
|
2460
|
+
|
|
2461
|
+
interface TableQuery {
|
|
2462
|
+
/** 1-indexed. */
|
|
2463
|
+
page: number;
|
|
2464
|
+
pageSize: number;
|
|
2465
|
+
sort: string | null;
|
|
2466
|
+
dir: "asc" | "desc";
|
|
2467
|
+
/** Free-text search across the endpoint's searchable columns. */
|
|
2468
|
+
q: string;
|
|
2469
|
+
filters: Record<string, unknown>;
|
|
2470
|
+
}
|
|
2471
|
+
interface Pager {
|
|
2472
|
+
page: number;
|
|
2473
|
+
pageSize: number;
|
|
2474
|
+
total: number;
|
|
2475
|
+
totalPages: number;
|
|
2476
|
+
from: number;
|
|
2477
|
+
to: number;
|
|
2478
|
+
hasPrev: boolean;
|
|
2479
|
+
hasNext: boolean;
|
|
2480
|
+
}
|
|
2481
|
+
/** The envelope EVERY paged list endpoint returns. */
|
|
2482
|
+
interface ListEnvelope<Row = unknown> {
|
|
2483
|
+
rows: Row[];
|
|
2484
|
+
pager: Pager;
|
|
2485
|
+
sort: {
|
|
2486
|
+
key: string;
|
|
2487
|
+
dir: "asc" | "desc";
|
|
2488
|
+
} | null;
|
|
2489
|
+
filter: {
|
|
2490
|
+
applied: Record<string, unknown>;
|
|
2491
|
+
q: string;
|
|
2492
|
+
facets: Record<string, Record<string, number>>;
|
|
2493
|
+
};
|
|
2494
|
+
aggregate?: Record<string, unknown>;
|
|
2495
|
+
}
|
|
2496
|
+
interface RunConfig<Row = unknown> {
|
|
2497
|
+
searchKeys?: string[];
|
|
2498
|
+
filters?: Record<string, (row: Row, value: any) => boolean>;
|
|
2499
|
+
/** Sort value for a column whose key isn't a plain field. */
|
|
2500
|
+
accessors?: Record<string, (row: Row) => unknown>;
|
|
2501
|
+
/** Option counts, computed over the SEARCHED set so a filter never erases its own alternatives. */
|
|
2502
|
+
facets?: Record<string, (row: Row) => string | number | null>;
|
|
2503
|
+
defaultSort?: {
|
|
2504
|
+
key: string;
|
|
2505
|
+
dir: "asc" | "desc";
|
|
2506
|
+
};
|
|
2507
|
+
defaultPageSize?: number;
|
|
2508
|
+
}
|
|
2509
|
+
interface ServerTable<Row = unknown> {
|
|
2510
|
+
rows: Row[];
|
|
2511
|
+
pager: Pager;
|
|
2512
|
+
total: number;
|
|
2513
|
+
page: number;
|
|
2514
|
+
pageSize: number;
|
|
2515
|
+
totalPages: number;
|
|
2516
|
+
from: number;
|
|
2517
|
+
to: number;
|
|
2518
|
+
facets: Record<string, Record<string, number>>;
|
|
2519
|
+
aggregate: Record<string, unknown>;
|
|
2520
|
+
query: TableQuery;
|
|
2521
|
+
sort?: {
|
|
2522
|
+
key: string;
|
|
2523
|
+
dir: "asc" | "desc";
|
|
2524
|
+
};
|
|
2525
|
+
q: string;
|
|
2526
|
+
filters: Record<string, unknown>;
|
|
2527
|
+
loading: boolean;
|
|
2528
|
+
/** True only on the FIRST load — later loads keep showing stale rows. */
|
|
2529
|
+
initialLoading: boolean;
|
|
2530
|
+
/** True while re-querying with previous rows still on screen. Pass straight to
|
|
2531
|
+
* DataTable's `refreshing`, or to LoadingRegion as state="refreshing". */
|
|
2532
|
+
refreshing: boolean;
|
|
2533
|
+
activeFilterCount: number;
|
|
2534
|
+
setPage(p: number): void;
|
|
2535
|
+
setPageSize(n: number): void;
|
|
2536
|
+
setQ(q: string): void;
|
|
2537
|
+
toggleSort(key: string): void;
|
|
2538
|
+
setSortKey(key: string, dir?: "asc" | "desc"): void;
|
|
2539
|
+
/** Same query, unpaged — for CSV export and select-all-matching. */
|
|
2540
|
+
fetchAll(): Promise<Row[]>;
|
|
2541
|
+
/** value may be an updater fn — REQUIRED for toggles, or two fast clicks read the same
|
|
2542
|
+
* stale array and the first is lost. */
|
|
2543
|
+
setFilter(id: string, value: unknown | ((prev: unknown) => unknown)): void;
|
|
2544
|
+
clearFilter(id: string): void;
|
|
2545
|
+
setFilters(filters: Record<string, unknown>): void;
|
|
2546
|
+
clearFilters(): void;
|
|
2547
|
+
refresh(): void;
|
|
2548
|
+
queryString: string;
|
|
2549
|
+
}
|
|
2550
|
+
interface UseServerTableOptions {
|
|
2551
|
+
endpoint: string;
|
|
2552
|
+
params?: Record<string, unknown>;
|
|
2553
|
+
defaults?: Partial<TableQuery>;
|
|
2554
|
+
/** Extra values that should trigger a refetch. */
|
|
2555
|
+
deps?: unknown[];
|
|
2556
|
+
/** Opts into durable rows-per-page and default sort. Page/search/filters are deliberately
|
|
2557
|
+
* NOT persisted — they describe one task, and restoring them hides rows the user expects. */
|
|
2558
|
+
prefsKey?: string;
|
|
2559
|
+
}
|
|
2560
|
+
declare function eqFilter<Row>(get: (row: Row) => unknown): (row: Row, value: unknown) => boolean;
|
|
2561
|
+
declare function anyOfFilter<Row>(get: (row: Row) => string[]): (row: Row, value: unknown) => boolean;
|
|
2562
|
+
declare function rangeFilter<Row>(get: (row: Row) => number): (row: Row, value: unknown) => boolean;
|
|
2563
|
+
type Api = {
|
|
2564
|
+
request(id: string, params?: unknown): Promise<unknown>;
|
|
2565
|
+
};
|
|
2566
|
+
type PrefsStore = {
|
|
2567
|
+
getTable(key: string): any;
|
|
2568
|
+
setTable(key: string, patch: any): void;
|
|
2569
|
+
};
|
|
2570
|
+
declare global {
|
|
2571
|
+
interface Window {
|
|
2572
|
+
PlannerPrefs?: PrefsStore;
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
/** React hook — the only way a screen should talk to a list endpoint. */
|
|
2576
|
+
declare function useServerTable<Row = unknown>({ endpoint, params, defaults, deps, prefsKey }: UseServerTableOptions): ServerTable<Row>;
|
|
2577
|
+
declare const QueryKit: {
|
|
2578
|
+
DEFAULTS: TableQuery;
|
|
2579
|
+
/** Register the app's simulated (or real) transport: an object with .request(id, params). */
|
|
2580
|
+
setApi<T extends Api>(api: T): T;
|
|
2581
|
+
getApi(): Api;
|
|
2582
|
+
/** Optional durable table preferences store: { getTable(key), setTable(key, patch) }. */
|
|
2583
|
+
setPrefs(p: PrefsStore): void;
|
|
2584
|
+
/**
|
|
2585
|
+
* @param rows the full static dataset (stands in for the table)
|
|
2586
|
+
* @param query { page, pageSize, sort, dir, q, filters }
|
|
2587
|
+
* @param cfg { searchKeys[], filters: {id: (row, value) => boolean},
|
|
2588
|
+
* accessors: {colKey: (row) => sortableValue},
|
|
2589
|
+
* facets: {id: (row) => facetValue} }
|
|
2590
|
+
*/
|
|
2591
|
+
run<Row>(rows: Row[], query: Partial<TableQuery>, cfg: RunConfig<Row>): ListEnvelope<Row>;
|
|
2592
|
+
/** Display form of a query, for the request log and the API spec screen. */
|
|
2593
|
+
toQueryString(query: Partial<TableQuery>): string;
|
|
2594
|
+
useServerTable: typeof useServerTable;
|
|
2595
|
+
};
|
|
2596
|
+
|
|
2597
|
+
/** Runtime mode and computed feature completeness. */
|
|
2598
|
+
interface FeatureStatus {
|
|
2599
|
+
/** Every endpoint this feature needs is served by the real backend. */
|
|
2600
|
+
complete: boolean;
|
|
2601
|
+
/** Endpoint ids still unserved. */
|
|
2602
|
+
missing: string[];
|
|
2603
|
+
/** No requirements were declared — treated as incomplete (fails closed). */
|
|
2604
|
+
undeclared?: boolean;
|
|
2605
|
+
/** Needs no backend at all. */
|
|
2606
|
+
clientOnly?: boolean;
|
|
2607
|
+
}
|
|
2608
|
+
interface RuntimeSummary {
|
|
2609
|
+
total: number;
|
|
2610
|
+
complete: number;
|
|
2611
|
+
incomplete: number;
|
|
2612
|
+
endpointsWired: number;
|
|
2613
|
+
}
|
|
2614
|
+
declare const RuntimeKit: {
|
|
2615
|
+
WIRED_ENDPOINTS: string[];
|
|
2616
|
+
CLIENT_ONLY: string[];
|
|
2617
|
+
FEATURE_NEEDS: Record<string, string[]>;
|
|
2618
|
+
/** Register what each feature needs: { "planner.flights": ["flights.list", "flights.shift"] } */
|
|
2619
|
+
declare(map: Record<string, string[]>): void;
|
|
2620
|
+
needsOf(key: string): string[];
|
|
2621
|
+
/** Every declared feature, for the admin surface. */
|
|
2622
|
+
declared(): string[];
|
|
2623
|
+
isEndpointWired(id: string): boolean;
|
|
2624
|
+
/** The whole point: complete is computed, never stored. */
|
|
2625
|
+
status(key: string): FeatureStatus;
|
|
2626
|
+
isComplete(key: string): boolean;
|
|
2627
|
+
getMode(): "live" | "test";
|
|
2628
|
+
isTest(): boolean;
|
|
2629
|
+
setMode(next: "live" | "test"): void;
|
|
2630
|
+
subscribe(fn: (mode: "live" | "test") => void): () => void;
|
|
2631
|
+
/** Counts for the admin surface and the mode popover. */
|
|
2632
|
+
summary(): RuntimeSummary;
|
|
2633
|
+
};
|
|
2634
|
+
declare function useRuntimeMode(): "live" | "test";
|
|
2635
|
+
/** True when this feature should be shown obstructed right now. */
|
|
2636
|
+
declare function useFeatureStatus(key: string): FeatureStatus & {
|
|
2637
|
+
mode: "live" | "test";
|
|
2638
|
+
obstruct: boolean;
|
|
2639
|
+
};
|
|
2640
|
+
declare const UseRuntimeMode: typeof useRuntimeMode;
|
|
2641
|
+
declare const UseFeatureStatus: typeof useFeatureStatus;
|
|
2642
|
+
|
|
2643
|
+
interface MockJobStatus<TResult = unknown> {
|
|
2644
|
+
id: string;
|
|
2645
|
+
status: "queued" | "running" | "done" | "failed";
|
|
2646
|
+
progress?: number;
|
|
2647
|
+
stage?: string;
|
|
2648
|
+
result?: TResult;
|
|
2649
|
+
/** Set only when status is "failed" — the thrown value's message. */
|
|
2650
|
+
error?: string;
|
|
2651
|
+
}
|
|
2652
|
+
interface PollOptions<TResult = unknown> {
|
|
2653
|
+
/** Delay between polls, ms. Default 400 — the rate every hand-written poller used. */
|
|
2654
|
+
intervalMs?: number;
|
|
2655
|
+
onTick?: (status: MockJobStatus<TResult>) => void;
|
|
2656
|
+
}
|
|
2657
|
+
/**
|
|
2658
|
+
* @param prefix id prefix, e.g. "imp" → "imp_1a2b3c"
|
|
2659
|
+
* @param latency [lo, hi] ms — total time across every stage, jittered within the range
|
|
2660
|
+
* @param stages progress labels in order, e.g. ["Uploading", "Parsing", "Validating"]
|
|
2661
|
+
* @param compute runs once, on the final stage, to produce the result — or throws to fail the job
|
|
2662
|
+
*/
|
|
2663
|
+
declare function schedule<TResult>(prefix: string, latency: [number, number], stages: string[], compute: () => TResult): string;
|
|
2664
|
+
/** Plain status lookup — what a jobs.get-style mock endpoint wraps. Throws if the id is unknown. */
|
|
2665
|
+
declare function get<TResult = unknown>(jobId: string): MockJobStatus<TResult>;
|
|
2666
|
+
/** Polls until the job resolves. Resolves with the result on "done", throws on "failed". */
|
|
2667
|
+
declare function poll<TResult = unknown>(jobId: string, opts?: PollOptions<TResult>): Promise<TResult>;
|
|
2668
|
+
declare const MockJobKit: {
|
|
2669
|
+
schedule: typeof schedule;
|
|
2670
|
+
get: typeof get;
|
|
2671
|
+
poll: typeof poll;
|
|
2672
|
+
};
|
|
2673
|
+
|
|
2674
|
+
/** One entry in the append-only history. Never mutated after it's appended,
|
|
2675
|
+
* except for `current` flipping to false when a later version is committed. */
|
|
2676
|
+
interface VersionRecord<TDiff = unknown> {
|
|
2677
|
+
v: number;
|
|
2678
|
+
current: boolean;
|
|
2679
|
+
who: string;
|
|
2680
|
+
when: string;
|
|
2681
|
+
action: string;
|
|
2682
|
+
/** Human-readable change descriptions, as returned by the mutator passed to commit(). */
|
|
2683
|
+
changes: string[];
|
|
2684
|
+
/** Present only when the store was created with a `diff` option. */
|
|
2685
|
+
diff?: TDiff;
|
|
2686
|
+
/** Open bag for caller-specific before/after figures (e.g. a computed total) that
|
|
2687
|
+
* don't belong in `diff` because they aren't part of the state comparison itself. */
|
|
2688
|
+
meta?: Record<string, unknown>;
|
|
2689
|
+
}
|
|
2690
|
+
interface CreateVersionStoreOptions<TState, TDiff = unknown> {
|
|
2691
|
+
/** Defaults to structuredClone. Override if TState holds something it can't clone
|
|
2692
|
+
* (a class instance, a function) — must still produce a deep, independent copy. */
|
|
2693
|
+
clone?: (state: TState) => TState;
|
|
2694
|
+
/** Computes what a version's `diff` field holds by comparing the state before and
|
|
2695
|
+
* after the mutation. Omit if the caller has no use for a structured diff — the
|
|
2696
|
+
* `changes` string list on every record still describes what happened. */
|
|
2697
|
+
diff?: (before: TState, after: TState) => TDiff;
|
|
2698
|
+
/** Timestamp/attribution stamp for `when`. Defaults to `new Date().toISOString()`. */
|
|
2699
|
+
now?: () => string;
|
|
2700
|
+
/** Version number the initial state is recorded under. Defaults to 0. */
|
|
2701
|
+
initialVersion?: number;
|
|
2702
|
+
who?: string;
|
|
2703
|
+
action?: string;
|
|
2704
|
+
}
|
|
2705
|
+
interface VersionStore<TState, TDiff = unknown> {
|
|
2706
|
+
/** The live state. Mutating it directly bypasses versioning — always go through commit(). */
|
|
2707
|
+
getState(): TState;
|
|
2708
|
+
/** Newest first, matching the order a version-history panel reads top to bottom. */
|
|
2709
|
+
getVersions(): VersionRecord<TDiff>[];
|
|
2710
|
+
getVersion(v: number): VersionRecord<TDiff> | undefined;
|
|
2711
|
+
/**
|
|
2712
|
+
* Snapshots the state before mutation, runs `mutate` against the live state, then
|
|
2713
|
+
* appends a new version recording what changed and keeps a full-state snapshot of
|
|
2714
|
+
* the result so this version can be restored to or undone past later.
|
|
2715
|
+
*/
|
|
2716
|
+
commit(who: string, action: string, mutate: (state: TState) => string[], meta?: Record<string, unknown>): VersionRecord<TDiff>;
|
|
2717
|
+
/** Makes version `v` current by committing its snapshot as a NEW head version —
|
|
2718
|
+
* history is append-only, so this never rewrites or truncates the list. */
|
|
2719
|
+
restoreTo(v: number, who?: string): VersionRecord<TDiff>;
|
|
2720
|
+
/** Restores the state one version behind the current head, committed as a new head
|
|
2721
|
+
* version (the inverse of the head's change, recorded rather than erased). Throws
|
|
2722
|
+
* if the head is already the oldest recorded version. */
|
|
2723
|
+
undo(who?: string): VersionRecord<TDiff>;
|
|
2724
|
+
}
|
|
2725
|
+
declare function createVersionStore<TState, TDiff = unknown>(initialState: TState, options?: CreateVersionStoreOptions<TState, TDiff>): VersionStore<TState, TDiff>;
|
|
2726
|
+
|
|
2727
|
+
export { AccountMenu, type AccountMenuLink, type AccountMenuProps, AgentChatPanel, type AgentChatPanelProps, ApiSpecBrowser, type ApiSpecBrowserProps, type Attachment, Avatar, type AvatarProps, AvatarStack, type AvatarStackProps, Badge, type BadgeProps, BudgetReallocator, type BudgetReallocatorProps, Button, type ButtonProps, CHANNELS, CHANNEL_WEIGHTS, CHAT_UNAVAILABLE, Calendar, type CalendarProps, Card, type CardProps, CardRow, type CardRowProps, ChannelContribution, type ChannelContributionChannel, type ChannelContributionProps, ChannelMeta, type ChannelMetaRecord, ChannelTag, type ChannelTagProps, ChannelWeightOf, type ChatAdapter, ChatComposer, type ChatComposerProps, type ChatEngine, type ChatEngineOptions, ChatKit, type ChatMessage, type ChatRenderContext, ChatSessionBar, type ChatSessionBarProps, ChatTranscript, type ChatTranscriptProps, ChatTurn, type ChatTurnProps, Checkbox, type CheckboxProps, type Citation, Citations, type CitationsProps, Clamp, type ClampProps, ClockFace, type ClockFaceProps, CodeBlock, type CodeBlockProps, Collapsible, type CollapsibleProps, ComingSoon, type ComingSoonProps, type ContextUsage, type CreateVersionStoreOptions, CsiBadge, type CsiBadgeProps, CsiHero, type CsiHeroProps, CsiMeter, type CsiMeterProps, DataTable, type DataTableColumn, type DataTableProps, DatePicker, type DatePickerProps, type DeviceSession, DiffBlock, type DiffBlockProps, Drawer, type DrawerProps, Dropzone, DropzoneKit, type DropzoneProps, type EditorTrigger, EmptyState, type EmptyStateProps, type EndpointSpec, FeatureGate, type FeatureGateProps, type FeatureStatus, FileChip, type FileChipProps, FileGrid, type FileGridProps, FileKit, FilePickButton, type FilePickButtonProps, type FileRejection, FileStrip, type FileStripProps, FileTile, type FileTileProps, type FileUploadHandler, Flag$1 as Flag, type FlagExplanation, type FlagProps, FormatEta, Gate, type GateProps, IconButton, type IconButtonProps, Input, type InputProps, type JobStatus, Json, KeyHint, type KeyHintProps, type ListEnvelope, LoadingRegion, type LoadingRegionProps, Markdown, MarkdownEditor, type MarkdownEditorHandle, type MarkdownEditorProps, MarkdownInline, type MarkdownProps, type MentionSource, Menu, MenuButton, type MenuButtonProps, type MenuItem, type MenuProps, MessageBody, type MessageBodyProps, type MessageGroup, Meter, type MeterProps, type MeterSegment, type MeterSegmentInput, MixGap, type MixGapProps, type MixGapRow, MockJobKit, type MockJobStatus, Modal, type ModalProps, ModeSwitch, type ModeSwitchProps, type Model, ModelControls, type ModelControlsProps, NumberInput, type NumberInputProps, type Packet, PacketCard, type PacketCardProps, type PacketSchema, type Pager, Pagination, type PaginationProps, PermissionDenied, type PermissionDeniedProps, type PermissionGroup, PermissionHint, type PermissionItem, type Placement, type PollOptions, Popover, type PopoverProps, ProfilePage, type ProfilePageProps, ProgressBar, type ProgressBarProps, QueryKit, type QueuedTurn, QuotaRow, type QuotaRowProps, Radio, type RadioProps, RangeSlider, type RangeSliderProps, RelativeTime, type RelativeTimeProps, type RoadmapItem, type RoadmapResult, RoadmapTimeline, type RoadmapTimelineProps, type Role, RunActions, type RunActionsProps, type RunConfig, RuntimeKit, type RuntimeSummary, SaturationDistribution, type SaturationDistributionCampus, type SaturationDistributionProps, SegmentedControl, type SegmentedControlProps, SegmentedMeter, type SegmentedMeterProps, Select, type SelectOption, type SelectProps, type SendMessageResult, type ServerTable, type Session, type Flag as SessionFlag, SessionKit, type SessionStats, SidebarNav, type SidebarNavItem, type SidebarNavProps, Skeleton, type SkeletonProps, SkeletonText, type SkeletonTextProps, type SlashCommand, Slider, type SliderProps, SortMenu, type SortMenuField, type SortMenuProps, type SpecModule, Spinner, type SpinnerProps, StatTile, type StatTileProps, type Step, StepList, type StepListProps, type StreamHandlers, type Suggestion, SurroundSound, type SurroundSoundProps, Switch, type SwitchProps, type TableQuery, Tabs, type TabsProps, Tag, type TagProps, TestModeBar, type TestModeBarProps, Textarea, type TextareaProps, ThinkingBlock, type ThinkingBlockProps, type ThreadSummary, TimePicker, type TimePickerProps, Toast, type ToastProps, Tooltip, type TooltipProps, Topbar, type TopbarProps, TranscriptKit, type UsageLimit, UseFeatureStatus, UseRuntimeMode, type UseServerTableOptions, type User, type VersionRecord, type VersionStore, type VoiceHandler, acceptMatches, anyOfFilter, channelWeightOf, createVersionStore, eqFilter, extensionOf, extractClipboardFiles, filterFiles, formatAbsolute, formatBytes, formatClock, formatDuration, formatEta, formatRelative, groupMessages, hasModifier, iconForMime, isImage, isSystemMessage, jobBucket, languageLabel, markdownToText, meterFormats, modifierLabel, pastedTextName, rangeFilter, revokeAttachment, toAttachment, tokenize, useChatEngine, useFeatureStatus, usePopoverPosition, useRuntimeMode, useServerTable, useStagedFiles, visibleMessages };
|