@alexkroman1/aai-ui 0.12.2 → 0.13.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/README.md +257 -0
- package/dist/_colors-CpZO-88A.js +78 -0
- package/dist/_module-url-C13kAJ87.js +15 -0
- package/dist/_page-shell.d.ts +11 -0
- package/dist/_recover-run.d.ts +79 -0
- package/dist/_repeat-until.d.ts +30 -0
- package/dist/_run-controls.d.ts +33 -0
- package/dist/_submission-state.d.ts +92 -0
- package/dist/_tap-to-talk-state.d.ts +88 -0
- package/dist/_utils-Mi-SCFA1.js +47 -0
- package/dist/_utils.d.ts +26 -0
- package/dist/_web-storage.d.ts +43 -0
- package/dist/_workflow-api-ref.d.ts +37 -0
- package/dist/_workflow-files.d.ts +34 -0
- package/dist/_workflow-test-defs.d.ts +38 -0
- package/dist/aai-logo-D8pMVn9_.js +85 -0
- package/dist/agent-state-labels.d.ts +65 -0
- package/dist/agent-state.d.ts +108 -0
- package/dist/audio/capture.d.ts +44 -0
- package/dist/audio/index.d.ts +18 -0
- package/dist/audio/index.js +431 -0
- package/dist/audio/preconnect.d.ts +87 -0
- package/dist/audio/voice-io.d.ts +112 -0
- package/dist/build-default-client.d.ts +1 -0
- package/dist/chat-view-BCbI7gD4.js +396 -0
- package/dist/client-config-C7fQrioM.js +139 -0
- package/dist/client-config.d.ts +119 -0
- package/dist/client-dir.d.ts +44 -0
- package/dist/client-dir.js +58 -0
- package/dist/client-identity.d.ts +131 -0
- package/dist/client-tool.d.ts +51 -0
- package/dist/components/_colors.d.ts +73 -0
- package/dist/components/_form-readiness.d.ts +56 -0
- package/dist/components/_form-values.d.ts +19 -0
- package/dist/components/aai-logo.d.ts +12 -0
- package/dist/components/audio-result.d.ts +94 -0
- package/dist/components/auto-scroll.d.ts +74 -0
- package/dist/components/bullet-list.d.ts +74 -0
- package/dist/components/button.d.ts +58 -0
- package/dist/components/button.js +85 -0
- package/dist/components/chat-view.d.ts +35 -0
- package/dist/components/chat-view.js +3 -0
- package/dist/components/console-shell.d.ts +85 -0
- package/dist/components/controls.d.ts +33 -0
- package/dist/components/controls.js +49 -0
- package/dist/components/conversation-view.d.ts +130 -0
- package/dist/components/eyebrow.d.ts +12 -0
- package/dist/components/facts.d.ts +81 -0
- package/dist/components/form-fields.d.ts +142 -0
- package/dist/components/form-types.d.ts +73 -0
- package/dist/components/form.d.ts +96 -0
- package/dist/components/markdown.d.ts +57 -0
- package/dist/components/message-list.d.ts +48 -0
- package/dist/components/message-list.js +3 -0
- package/dist/components/session-controls.d.ts +125 -0
- package/dist/components/session-error-banner.d.ts +69 -0
- package/dist/components/session-state-dot.d.ts +111 -0
- package/dist/components/sidebar-layout.d.ts +42 -0
- package/dist/components/sidebar-layout.js +56 -0
- package/dist/components/start-screen.d.ts +37 -0
- package/dist/components/start-screen.js +71 -0
- package/dist/components/tool-call-block.d.ts +28 -0
- package/dist/components/tool-call-block.js +3 -0
- package/dist/components/tool-call-row.d.ts +75 -0
- package/dist/components/tool-config-context.d.ts +22 -0
- package/dist/components/upload-progress.d.ts +76 -0
- package/dist/components/url-chips.d.ts +29 -0
- package/dist/components/workflow-fields.d.ts +46 -0
- package/dist/components/workflow-pending-note.d.ts +81 -0
- package/dist/components/workflow-progress.d.ts +81 -0
- package/dist/components/workflow-run-error.d.ts +48 -0
- package/dist/components/workflow-run-panel.d.ts +79 -0
- package/dist/context.d.ts +288 -0
- package/dist/context.js +382 -0
- package/dist/conversation-log.d.ts +88 -0
- package/dist/default-client/assets/_module-url-BX0RuRU2.js +1 -0
- package/dist/default-client/assets/audio-cGFGID-2.js +1 -0
- package/dist/default-client/assets/capture-processor-BUABoo3_.js +90 -0
- package/dist/default-client/assets/index-8ihb1Urm.css +2 -0
- package/dist/default-client/assets/index-BQURVARl.js +126 -0
- package/dist/default-client/assets/playback-processor-ABOa11j0.js +314 -0
- package/dist/default-client/favicon.ico +0 -0
- package/dist/default-client/index.html +15 -0
- package/dist/default-client.d.ts +23 -0
- package/dist/define-client.d.ts +162 -26
- package/dist/define-client.js +201 -55
- package/dist/eyebrow-UfmSz9yy.js +27 -0
- package/dist/hooks-DN4gHYsj.js +182 -0
- package/dist/hooks.d.ts +172 -0
- package/dist/hooks.js +3 -0
- package/dist/hooks.test-d.d.ts +14 -0
- package/dist/inbox-protocol.d.ts +128 -0
- package/dist/inbox.d.ts +132 -0
- package/dist/index.d.ts +129 -32
- package/dist/index.js +5503 -18
- package/dist/internal.d.ts +43 -0
- package/dist/internal.js +8 -0
- package/dist/linked-client.d.ts +70 -0
- package/dist/message-list-DHZcLhRA.js +682 -0
- package/dist/notice-player.d.ts +70 -0
- package/dist/page.d.ts +114 -0
- package/dist/phone.d.ts +50 -0
- package/dist/route-fetch.d.ts +79 -0
- package/dist/session/audio-effects.d.ts +31 -0
- package/dist/session/audio-setup.d.ts +57 -0
- package/dist/session/audio-state.d.ts +157 -0
- package/dist/session/browser-session.d.ts +30 -0
- package/dist/session/close.d.ts +26 -0
- package/dist/session/dial.d.ts +65 -0
- package/dist/session/handshake.d.ts +60 -0
- package/dist/session/index.d.ts +17 -0
- package/dist/session/index.js +2 -0
- package/dist/session/messages.d.ts +63 -0
- package/dist/session/mic.d.ts +64 -0
- package/dist/session/preconnect.d.ts +48 -0
- package/dist/session/reconnect.d.ts +25 -0
- package/dist/session/resume-store.d.ts +57 -0
- package/dist/session/state.d.ts +146 -0
- package/dist/session/ticket.d.ts +44 -0
- package/dist/session/types.d.ts +476 -0
- package/dist/session/url.d.ts +17 -0
- package/dist/session/user-turn.d.ts +55 -0
- package/dist/session-CE0Ipsb3.js +2129 -0
- package/dist/stored-value.d.ts +103 -0
- package/dist/tool-call-block-uXtMOUWN.js +204 -0
- package/dist/tool-config-context-Es4YUzV2.js +19 -0
- package/dist/types.d.ts +272 -34
- package/dist/types.js +35 -4
- package/dist/upload/files.d.ts +83 -0
- package/dist/upload/index.d.ts +15 -0
- package/dist/upload/pause.d.ts +31 -0
- package/dist/upload/recall.d.ts +67 -0
- package/dist/upload/report.d.ts +26 -0
- package/dist/upload/session.d.ts +99 -0
- package/dist/url-chips-oyy_Eiqh.js +239 -0
- package/dist/use-client-runs.d.ts +87 -0
- package/dist/use-conversation-log.d.ts +86 -0
- package/dist/use-conversation.d.ts +131 -0
- package/dist/use-copy.d.ts +74 -0
- package/dist/use-download-url.d.ts +84 -0
- package/dist/use-flash.d.ts +61 -0
- package/dist/use-inbox.d.ts +101 -0
- package/dist/use-push-to-talk.d.ts +105 -0
- package/dist/use-route-mutation.d.ts +113 -0
- package/dist/use-route.d.ts +82 -0
- package/dist/use-run-key.d.ts +133 -0
- package/dist/use-session-controls.d.ts +69 -0
- package/dist/use-session-id.d.ts +57 -0
- package/dist/use-tap-to-talk.d.ts +138 -0
- package/dist/use-user-transcript-uyHhzy4d.js +73 -0
- package/dist/use-user-transcript.d.ts +75 -0
- package/dist/use-workflow-form.d.ts +285 -0
- package/dist/use-workflow-progress.d.ts +137 -0
- package/dist/use-workflow-run-DdhWmbx-.js +697 -0
- package/dist/use-workflow-run.d.ts +100 -0
- package/dist/use-workflow-runs.d.ts +71 -0
- package/dist/use-workflow-stream.d.ts +154 -0
- package/dist/use-workflows.d.ts +77 -0
- package/dist/workflow-client.d.ts +136 -0
- package/dist/workflow-def-types.d.ts +31 -0
- package/dist/workflow-events.d.ts +39 -0
- package/dist/workflow-field-kind.d.ts +95 -0
- package/dist/workflow-status-labels.d.ts +35 -0
- package/dist/worklets/_module-url.d.ts +10 -0
- package/dist/worklets/_playback-bench-harness.d.ts +181 -0
- package/dist/worklets/_playback-bench-host.d.ts +63 -0
- package/dist/worklets/_playback-bench-page.d.ts +65 -0
- package/dist/worklets/_tts-trace-harness.d.ts +142 -0
- package/dist/worklets/_worklet-test-utils.d.ts +27 -0
- package/dist/worklets/capture-processor.d.ts +4 -2
- package/dist/worklets/capture-processor.js +73 -33
- package/dist/worklets/playback-processor.d.ts +4 -2
- package/dist/worklets/playback-processor.js +258 -42
- package/package.json +58 -31
- package/styles.css +152 -30
- package/dist/_components/app.d.ts +0 -23
- package/dist/_components/app.js +0 -41
- package/dist/_components/button.d.ts +0 -50
- package/dist/_components/button.js +0 -46
- package/dist/_components/chat-view.d.ts +0 -29
- package/dist/_components/chat-view.js +0 -61
- package/dist/_components/controls.d.ts +0 -18
- package/dist/_components/controls.js +0 -37
- package/dist/_components/error-banner.d.ts +0 -21
- package/dist/_components/error-banner.js +0 -27
- package/dist/_components/message-bubble.d.ts +0 -23
- package/dist/_components/message-bubble.js +0 -35
- package/dist/_components/message-list.d.ts +0 -21
- package/dist/_components/message-list.js +0 -76
- package/dist/_components/sidebar-layout.d.ts +0 -21
- package/dist/_components/sidebar-layout.js +0 -36
- package/dist/_components/start-screen.d.ts +0 -26
- package/dist/_components/start-screen.js +0 -51
- package/dist/_components/state-indicator.d.ts +0 -29
- package/dist/_components/state-indicator.js +0 -37
- package/dist/_components/thinking-indicator.d.ts +0 -16
- package/dist/_components/thinking-indicator.js +0 -34
- package/dist/_components/tool-call-block.d.ts +0 -27
- package/dist/_components/tool-call-block.js +0 -118
- package/dist/_components/tool-icons.d.ts +0 -17
- package/dist/_components/tool-icons.js +0 -128
- package/dist/_components/transcript.d.ts +0 -25
- package/dist/_components/transcript.js +0 -35
- package/dist/audio.d.ts +0 -45
- package/dist/audio.js +0 -121
- package/dist/client-context.d.ts +0 -33
- package/dist/client-context.js +0 -15
- package/dist/client-handler.d.ts +0 -43
- package/dist/session-CWB7vmqz.js +0 -429
- package/dist/session.d.ts +0 -69
- package/dist/session.js +0 -2
- package/dist/signals.d.ts +0 -120
- package/dist/signals.js +0 -161
- package/dist/tsdown.config.d.ts +0 -2
- package/dist/types.test-d.d.ts +0 -7
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Props for {@link BulletList}.
|
|
4
|
+
*
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export type BulletListProps = {
|
|
8
|
+
/**
|
|
9
|
+
* The bullets, in the order they should read.
|
|
10
|
+
*
|
|
11
|
+
* TEXT rather than `ReactNode`, deliberately: every list this replaced was a
|
|
12
|
+
* string array straight off a run's output, and taking strings is what lets
|
|
13
|
+
* this component key them (see the component doc). A page that needs a link
|
|
14
|
+
* inside a bullet wants its own `<ul>`, not a prop here.
|
|
15
|
+
*/
|
|
16
|
+
items: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Rendered as a heading above the list, inside a wrapping `<section>`.
|
|
19
|
+
*
|
|
20
|
+
* Omitted (or `null`/`false`, so `title={cond && "Risks"}` means what it
|
|
21
|
+
* looks like) renders the bare `<ul>` with no wrapper — which is what four of
|
|
22
|
+
* the five lists this replaced were.
|
|
23
|
+
*/
|
|
24
|
+
title?: ReactNode | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* `"sm"` adds `text-sm`, which two of the five copies carried and three did
|
|
27
|
+
* not. `"base"` is the default and adds nothing.
|
|
28
|
+
*/
|
|
29
|
+
size?: "sm" | "base" | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* ADDED to the list's own classes rather than replacing them. There is no
|
|
32
|
+
* `tailwind-merge` in this package, so a class that CONFLICTS with a base one
|
|
33
|
+
* is not reliably the winner — use this for additions, not overrides.
|
|
34
|
+
*/
|
|
35
|
+
className?: string | undefined;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* A disc-bulleted list of short strings — a run's key points, findings, risks.
|
|
39
|
+
*
|
|
40
|
+
* Five pages had written this, byte-identical apart from a `text-sm` suffix on
|
|
41
|
+
* two of them, and all five had the same two defects. Both are the reason this
|
|
42
|
+
* is a component rather than four lines a page repeats:
|
|
43
|
+
*
|
|
44
|
+
* - **All five keyed by the string itself, and these lists are MODEL OUTPUT.**
|
|
45
|
+
* Two identical bullets are entirely plausible — a summariser that repeats
|
|
46
|
+
* itself is a bad summary, not a bad program — and a repeated string is then
|
|
47
|
+
* a duplicate `key`: React warns, and the two `<li>`s contend for one slot in
|
|
48
|
+
* the reconciliation. What is keyed here instead is the content PLUS how many
|
|
49
|
+
* times that content has already appeared in this list, which is unique by
|
|
50
|
+
* construction and unchanged by a re-render that did not change the text.
|
|
51
|
+
* (Position alone would also be sound — these lists are replaced wholesale by
|
|
52
|
+
* each new output and never reordered — but it is what `noArrayIndexKey`
|
|
53
|
+
* exists to talk you out of, and a unique key is one `Map` away, so there is
|
|
54
|
+
* no reason to spend a lint suppression on it.)
|
|
55
|
+
* - **Three of the five rendered an empty `<ul>` under a heading.** Two had
|
|
56
|
+
* hand-rolled `if (items.length === 0) return null` and three had not, so the
|
|
57
|
+
* same absent field was "nothing" on two pages and a stray heading with a
|
|
58
|
+
* void under it on three. Emptiness renders NOTHING here, `title` included:
|
|
59
|
+
* a heading over no bullets is a claim the run did not make.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```tsx
|
|
63
|
+
* import { BulletList } from "@alexkroman1/aai-ui";
|
|
64
|
+
*
|
|
65
|
+
* function Findings({ risks }: { risks: string[] }) {
|
|
66
|
+
* return <BulletList title="Risks" items={risks} size="sm" />;
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @param props - Bullet-list props.
|
|
71
|
+
*
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
export declare function BulletList({ items, title, size, className }: BulletListProps): ReactNode;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Visual style of a {@link Button} (design-system "website refresh":
|
|
4
|
+
* rectangular, ALL-CAPS, tracked labels).
|
|
5
|
+
*
|
|
6
|
+
* - `"default"` — Primary filled button (indigo background).
|
|
7
|
+
* - `"secondary"` — Outlined primary (transparent background, primary border).
|
|
8
|
+
* - `"ghost"` — Raised neutral (surface background with hairline border).
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export type ButtonVariant = "default" | "secondary" | "ghost";
|
|
13
|
+
/**
|
|
14
|
+
* Size preset for a {@link Button}.
|
|
15
|
+
*
|
|
16
|
+
* - `"default"` — Compact control (height 36 px).
|
|
17
|
+
* - `"lg"` — Primary CTA (height 44 px, generous padding).
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export type ButtonSize = "default" | "lg";
|
|
22
|
+
/**
|
|
23
|
+
* A styled button with variant and size presets.
|
|
24
|
+
*
|
|
25
|
+
* Accepts all standard `<button>` HTML attributes in addition to the props
|
|
26
|
+
* listed below.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* import { Button } from "@alexkroman1/aai-ui";
|
|
31
|
+
*
|
|
32
|
+
* function Actions({ onStop }: { onStop: () => void }) {
|
|
33
|
+
* return (
|
|
34
|
+
* <>
|
|
35
|
+
* <Button variant="secondary" onClick={onStop}>Stop</Button>
|
|
36
|
+
* <Button size="lg" className="w-full">Start Conversation</Button>
|
|
37
|
+
* </>
|
|
38
|
+
* );
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @param props - Button props: `variant` (visual style — see
|
|
43
|
+
* {@link ButtonVariant}, defaults to `"default"`), `size` (see
|
|
44
|
+
* {@link ButtonSize}, defaults to `"default"`), `className` (appended to the
|
|
45
|
+
* button's own classes), `children` (the label), and any `<button>` attribute.
|
|
46
|
+
*
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
export declare function Button({ variant, size, className, children, style, ...rest }: {
|
|
50
|
+
/** Visual style. Defaults to `"default"`. */
|
|
51
|
+
variant?: ButtonVariant;
|
|
52
|
+
/** Size preset. Defaults to `"default"`. */
|
|
53
|
+
size?: ButtonSize;
|
|
54
|
+
/** Additional CSS class names, appended to the button's own. */
|
|
55
|
+
className?: string;
|
|
56
|
+
/** Button label / content. */
|
|
57
|
+
children?: ReactNode;
|
|
58
|
+
} & Omit<ButtonHTMLAttributes<HTMLButtonElement>, "className">): import("react").JSX.Element;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useTheme } from "../context.js";
|
|
2
|
+
import { a as inkTint, i as focusRingStyle, n as FOCUS_RING, o as primaryTint } from "../_colors-CpZO-88A.js";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/button.tsx
|
|
6
|
+
/** @jsxImportSource react */
|
|
7
|
+
/** One variant's rest and hover colors, derived from the theme. */
|
|
8
|
+
function variantColors(variant, theme) {
|
|
9
|
+
switch (variant) {
|
|
10
|
+
case "default": return {
|
|
11
|
+
bg: theme.primary,
|
|
12
|
+
fg: theme.surface,
|
|
13
|
+
border: "transparent",
|
|
14
|
+
hoverBg: inkTint(theme.text, theme.primary, 14),
|
|
15
|
+
hoverFg: theme.surface,
|
|
16
|
+
hoverBorder: "transparent"
|
|
17
|
+
};
|
|
18
|
+
case "secondary": return {
|
|
19
|
+
bg: "transparent",
|
|
20
|
+
fg: theme.primary,
|
|
21
|
+
border: theme.primary,
|
|
22
|
+
hoverBg: primaryTint(theme.primary, theme.surface, 8),
|
|
23
|
+
hoverFg: theme.primary,
|
|
24
|
+
hoverBorder: theme.primary
|
|
25
|
+
};
|
|
26
|
+
default: return {
|
|
27
|
+
bg: theme.surface,
|
|
28
|
+
fg: theme.text,
|
|
29
|
+
border: theme.border,
|
|
30
|
+
hoverBg: inkTint(theme.text, theme.surface, 5),
|
|
31
|
+
hoverFg: theme.text,
|
|
32
|
+
hoverBorder: theme.border
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A styled button with variant and size presets.
|
|
38
|
+
*
|
|
39
|
+
* Accepts all standard `<button>` HTML attributes in addition to the props
|
|
40
|
+
* listed below.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```tsx
|
|
44
|
+
* import { Button } from "@alexkroman1/aai-ui";
|
|
45
|
+
*
|
|
46
|
+
* function Actions({ onStop }: { onStop: () => void }) {
|
|
47
|
+
* return (
|
|
48
|
+
* <>
|
|
49
|
+
* <Button variant="secondary" onClick={onStop}>Stop</Button>
|
|
50
|
+
* <Button size="lg" className="w-full">Start Conversation</Button>
|
|
51
|
+
* </>
|
|
52
|
+
* );
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @param props - Button props: `variant` (visual style — see
|
|
57
|
+
* {@link ButtonVariant}, defaults to `"default"`), `size` (see
|
|
58
|
+
* {@link ButtonSize}, defaults to `"default"`), `className` (appended to the
|
|
59
|
+
* button's own classes), `children` (the label), and any `<button>` attribute.
|
|
60
|
+
*
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
function Button({ variant = "default", size = "default", className, children, style, ...rest }) {
|
|
64
|
+
const theme = useTheme();
|
|
65
|
+
const colors = variantColors(variant, theme);
|
|
66
|
+
const vars = {
|
|
67
|
+
"--aai-btn-bg": colors.bg,
|
|
68
|
+
"--aai-btn-fg": colors.fg,
|
|
69
|
+
"--aai-btn-bd": colors.border,
|
|
70
|
+
"--aai-btn-bg-hover": colors.hoverBg,
|
|
71
|
+
"--aai-btn-fg-hover": colors.hoverFg,
|
|
72
|
+
"--aai-btn-bd-hover": colors.hoverBorder,
|
|
73
|
+
...focusRingStyle(theme.primary),
|
|
74
|
+
...style
|
|
75
|
+
};
|
|
76
|
+
return /* @__PURE__ */ jsx("button", {
|
|
77
|
+
type: "button",
|
|
78
|
+
style: vars,
|
|
79
|
+
className: clsx("inline-flex items-center justify-center appearance-none m-0 w-fit whitespace-nowrap", size === "lg" ? "h-11 px-7 text-xs" : "h-9 px-5 text-[11px]", "rounded-aai font-aai font-medium tracking-[1.4px] uppercase leading-none", "cursor-pointer border transition-colors duration-150", "bg-(--aai-btn-bg) text-(--aai-btn-fg) border-(--aai-btn-bd)", "enabled:hover:bg-(--aai-btn-bg-hover) enabled:hover:text-(--aai-btn-fg-hover)", "enabled:hover:border-(--aai-btn-bd-hover)", FOCUS_RING, "disabled:cursor-not-allowed disabled:opacity-50", className),
|
|
80
|
+
...rest,
|
|
81
|
+
children
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
export { Button };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** @jsxImportSource react */
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* The main chat interface for a voice agent session — the design-system
|
|
5
|
+
* "voice agent console": a 760px column on the cream page with a header
|
|
6
|
+
* (logo + live-status eyebrow), the conversation on a raised white card,
|
|
7
|
+
* and the session controls beneath it.
|
|
8
|
+
*
|
|
9
|
+
* Must be rendered inside a `SessionProvider`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* import { ChatView, StartScreen } from "@alexkroman1/aai-ui";
|
|
14
|
+
*
|
|
15
|
+
* function App() {
|
|
16
|
+
* return (
|
|
17
|
+
* <StartScreen icon="🍕" title="Pizza Palace">
|
|
18
|
+
* <ChatView />
|
|
19
|
+
* </StartScreen>
|
|
20
|
+
* );
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @param props - Chat surface props.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export declare function ChatView({ icon, title, className, }: {
|
|
29
|
+
/** Element rendered in place of the logo in the header. */
|
|
30
|
+
icon?: ReactNode | undefined;
|
|
31
|
+
/** Title string for the header. Defaults to the agent's declared name. */
|
|
32
|
+
title?: string | undefined;
|
|
33
|
+
/** Additional CSS class names for the root element, appended to its own. */
|
|
34
|
+
className?: string | undefined;
|
|
35
|
+
}): ReactNode;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { AgentState } from "../types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Props of {@link ConsoleShell}.
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export type ConsoleShellProps = {
|
|
9
|
+
/** Element rendered in place of the logo in the header. */
|
|
10
|
+
icon?: ReactNode | undefined;
|
|
11
|
+
/** Title string for the header. */
|
|
12
|
+
title?: string | undefined;
|
|
13
|
+
/** Live status shown in the header eyebrow. */
|
|
14
|
+
state: AgentState;
|
|
15
|
+
/** Whether the status dot pulses. */
|
|
16
|
+
pulsing: boolean;
|
|
17
|
+
/** Card content — normally a {@link MessageList}. */
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
/** Row rendered beneath the card (controls). */
|
|
20
|
+
footer: ReactNode;
|
|
21
|
+
/** Additional CSS class names for the root element, appended to its own. */
|
|
22
|
+
className?: string | undefined;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The design-system "console" chrome: a 760px column on the themed page with a
|
|
26
|
+
* header (icon + live-status eyebrow), an announced error banner, the main
|
|
27
|
+
* content on a raised card, and a footer row beneath it.
|
|
28
|
+
*
|
|
29
|
+
* {@link ChatView} is this shell with `<MessageList>` inside it and
|
|
30
|
+
* `<Controls>` under it, and until now that was the only way to get it — the
|
|
31
|
+
* shell itself was internal, so a client wanting its own conversation markup
|
|
32
|
+
* had to rebuild the chrome as well. Each one that did re-derived the error
|
|
33
|
+
* banner WITHOUT `role="alert"`.
|
|
34
|
+
*
|
|
35
|
+
* **The banner is {@link SessionErrorBanner} now, composed here rather than
|
|
36
|
+
* spelled out.** It was four lines of this file, and this file is a whole
|
|
37
|
+
* FRAME — a centred `max-w-190` column — so the full-bleed chromes that needed
|
|
38
|
+
* the announced banner could not take it without taking a layout that would
|
|
39
|
+
* replace the design they exist to demonstrate. Composing means there is one
|
|
40
|
+
* banner, and it means this component no longer takes an `error` prop: the
|
|
41
|
+
* banner reads the session itself, which is one fewer thing a caller can wire
|
|
42
|
+
* up wrong.
|
|
43
|
+
*
|
|
44
|
+
* Reach for it when the conversation is yours and the frame is not. Reach for
|
|
45
|
+
* `<ChatView>` when both are ours. Reach for `<SessionErrorBanner>` alone when
|
|
46
|
+
* neither is.
|
|
47
|
+
*
|
|
48
|
+
* Must be rendered inside the providers `mountClient()` installs.
|
|
49
|
+
*
|
|
50
|
+
* @example A custom conversation in the stock chrome
|
|
51
|
+
* ```tsx
|
|
52
|
+
* import {
|
|
53
|
+
* ConsoleShell,
|
|
54
|
+
* Controls,
|
|
55
|
+
* useConversation,
|
|
56
|
+
* useSessionStatus,
|
|
57
|
+
* } from "@alexkroman1/aai-ui";
|
|
58
|
+
*
|
|
59
|
+
* function Console() {
|
|
60
|
+
* const state = useSessionStatus();
|
|
61
|
+
* const { items } = useConversation();
|
|
62
|
+
* return (
|
|
63
|
+
* <ConsoleShell
|
|
64
|
+
* title="Dispatch"
|
|
65
|
+
* state={state}
|
|
66
|
+
* pulsing={state === "listening"}
|
|
67
|
+
* footer={<Controls />}
|
|
68
|
+
* >
|
|
69
|
+
* <ul>
|
|
70
|
+
* {items.map((item) => (
|
|
71
|
+
* <li key={item.kind === "message" ? item.message.id : item.toolCall.callId}>
|
|
72
|
+
* {item.kind === "message" ? item.message.content : item.toolCall.name}
|
|
73
|
+
* </li>
|
|
74
|
+
* ))}
|
|
75
|
+
* </ul>
|
|
76
|
+
* </ConsoleShell>
|
|
77
|
+
* );
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @param props - See {@link ConsoleShellProps}.
|
|
82
|
+
*
|
|
83
|
+
* @public
|
|
84
|
+
*/
|
|
85
|
+
export declare function ConsoleShell({ icon, title, state, pulsing, children, footer, className, }: ConsoleShellProps): ReactNode;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type FunctionComponent, type MemoExoticComponent } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Props of {@link Controls}.
|
|
4
|
+
*
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export type ControlsProps = {
|
|
8
|
+
/**
|
|
9
|
+
* Additional CSS class names, appended to the container's own layout
|
|
10
|
+
* classes rather than replacing them.
|
|
11
|
+
*/
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Session control buttons: **Stop / Resume** and **New Conversation**.
|
|
16
|
+
*
|
|
17
|
+
* Reads session state from {@link useSession}. Must be rendered inside a
|
|
18
|
+
* `SessionProvider`.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* import { Controls } from "@alexkroman1/aai-ui";
|
|
23
|
+
*
|
|
24
|
+
* function Footer() {
|
|
25
|
+
* return <Controls className="justify-end" />;
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @param props - Container props.
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export declare const Controls: MemoExoticComponent<FunctionComponent<ControlsProps>>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useSessionCore, useSessionSelector } from "../context.js";
|
|
2
|
+
import { Button } from "./button.js";
|
|
3
|
+
import { n as SessionUrlChips } from "../url-chips-oyy_Eiqh.js";
|
|
4
|
+
import { memo } from "react";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/controls.tsx
|
|
8
|
+
/** @jsxImportSource react */
|
|
9
|
+
/**
|
|
10
|
+
* Session control buttons: **Stop / Resume** and **New Conversation**.
|
|
11
|
+
*
|
|
12
|
+
* Reads session state from {@link useSession}. Must be rendered inside a
|
|
13
|
+
* `SessionProvider`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* import { Controls } from "@alexkroman1/aai-ui";
|
|
18
|
+
*
|
|
19
|
+
* function Footer() {
|
|
20
|
+
* return <Controls className="justify-end" />;
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @param props - Container props.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
const Controls = memo(function Controls({ className }) {
|
|
29
|
+
const running = useSessionSelector((s) => s.running);
|
|
30
|
+
const { toggle, restart } = useSessionCore();
|
|
31
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
32
|
+
className: clsx("flex flex-wrap items-center gap-3 shrink-0", className),
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ jsx(Button, {
|
|
35
|
+
variant: "secondary",
|
|
36
|
+
onClick: toggle,
|
|
37
|
+
children: running ? "Stop" : "Resume"
|
|
38
|
+
}),
|
|
39
|
+
/* @__PURE__ */ jsx(Button, {
|
|
40
|
+
variant: "ghost",
|
|
41
|
+
onClick: restart,
|
|
42
|
+
children: "New Conversation"
|
|
43
|
+
}),
|
|
44
|
+
/* @__PURE__ */ jsx(SessionUrlChips, { className: "basis-full sm:basis-auto sm:ml-auto sm:max-w-[60%]" })
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
//#endregion
|
|
49
|
+
export { Controls };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** @jsxImportSource react */
|
|
2
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
3
|
+
import type { ConversationLogEntry } from "../conversation-log.ts";
|
|
4
|
+
import type { ChatMessage, ToolCallInfo } from "../types.ts";
|
|
5
|
+
import type { UseUserTranscriptResult } from "../use-user-transcript.ts";
|
|
6
|
+
/**
|
|
7
|
+
* Props of {@link ConversationView}.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export type ConversationViewProps = {
|
|
12
|
+
/** One finalized message, in this chrome's own markup. */
|
|
13
|
+
renderMessage: (message: ChatMessage) => ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* One tool invocation. Absent, a compact {@link ToolCallRow} naming the tool,
|
|
16
|
+
* shimmering while it is pending.
|
|
17
|
+
*/
|
|
18
|
+
renderTool?: ((toolCall: ToolCallInfo) => ReactNode) | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* The agent's reply as it arrives. Absent, `renderMessage` is called with a
|
|
21
|
+
* synthetic assistant message carrying the text so far (its `id` is `-1`,
|
|
22
|
+
* which no real message has) — every chrome so far rendered the two the same
|
|
23
|
+
* way, and this keeps them from drifting.
|
|
24
|
+
*/
|
|
25
|
+
renderStreaming?: ((text: string) => ReactNode) | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The caller's in-progress turn. Rendered only while `transcript.speaking`,
|
|
28
|
+
* which is the `null`-vs-`""` distinction {@link useUserTranscript} makes
|
|
29
|
+
* (`""` is speech detected with no words yet — render on it, and read
|
|
30
|
+
* `transcript.text` for the placeholder). Absent, a muted italic line.
|
|
31
|
+
*/
|
|
32
|
+
renderTranscript?: ((transcript: UseUserTranscriptResult) => ReactNode) | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Where the transcript row goes. `"inline"` (the default) is the last row
|
|
35
|
+
* inside the scroll region, as `MessageList` places it; `"below"` renders it
|
|
36
|
+
* after the scroll region as a sibling — the strip a two-pane board pins to
|
|
37
|
+
* the bottom of its conversation column, outside the scroll.
|
|
38
|
+
*/
|
|
39
|
+
transcriptPosition?: "inline" | "below" | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* A persisted transcript to render IN PLACE of the live session's items —
|
|
42
|
+
* `useConversationLog().entries`, whose newest session entry already is the
|
|
43
|
+
* live conversation. Session entries render through `renderMessage` /
|
|
44
|
+
* `renderTool`, each after `renderSessionHeader`; a `spoken` entry is
|
|
45
|
+
* `renderMessage` over an assistant message (id `-2`); a `note` is
|
|
46
|
+
* `renderNote`. The streaming reply, the transcript and the thinking row are
|
|
47
|
+
* rendered after it as usual.
|
|
48
|
+
*/
|
|
49
|
+
log?: readonly ConversationLogEntry[] | undefined;
|
|
50
|
+
/** A log `note`. Absent, a small centred muted line. */
|
|
51
|
+
renderNote?: ((entry: Extract<ConversationLogEntry, {
|
|
52
|
+
kind: "note";
|
|
53
|
+
}>) => ReactNode) | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* What goes above each log `session` entry — a timestamp, a "continue"
|
|
56
|
+
* button. Absent, nothing.
|
|
57
|
+
*/
|
|
58
|
+
renderSessionHeader?: ((entry: Extract<ConversationLogEntry, {
|
|
59
|
+
kind: "session";
|
|
60
|
+
}>) => ReactNode) | undefined;
|
|
61
|
+
/** Rendered inside the scroll region while there is nothing to show at all. */
|
|
62
|
+
empty?: ReactNode | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* The `aria-label` of the thinking row. Default `"Thinking"`. Say who: the
|
|
65
|
+
* dots are the only sign the agent is working, and to a screen reader they
|
|
66
|
+
* are punctuation.
|
|
67
|
+
*/
|
|
68
|
+
thinkingLabel?: string | undefined;
|
|
69
|
+
/** What the thinking row shows. Default: three pulsing dots. */
|
|
70
|
+
thinkingIndicator?: ReactNode | undefined;
|
|
71
|
+
/** CSS class names for the thinking row itself (the `role="status"` element). */
|
|
72
|
+
thinkingClassName?: string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Classes for the {@link AutoScroll} container. It must end up with a
|
|
75
|
+
* bounded height (`flex-1 min-h-0`, `h-full`) or nothing pins.
|
|
76
|
+
*/
|
|
77
|
+
className?: string | undefined;
|
|
78
|
+
/** Classes for the scroll region's content element — padding, gap, direction. */
|
|
79
|
+
contentClassName?: string | undefined;
|
|
80
|
+
/** Classes for the scrolling element itself. See {@link AutoScroll}. */
|
|
81
|
+
scrollClassName?: string | undefined;
|
|
82
|
+
/** Inline styles for the scroll container. */
|
|
83
|
+
style?: CSSProperties | undefined;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The conversation's skeleton over {@link useConversation}, with every row a
|
|
87
|
+
* render slot: a pinned scroll region holding the empty state, the interleaved
|
|
88
|
+
* messages and tool calls, the streaming reply and the announced thinking row,
|
|
89
|
+
* plus the live transcript — inside the scroll or pinned beneath it.
|
|
90
|
+
*
|
|
91
|
+
* `useConversation()` already made the DATA one thing: the interleave, the
|
|
92
|
+
* streaming utterance, the `null`-vs-`""` transcript distinction and the
|
|
93
|
+
* thinking-suppression rule. What three custom chromes then each wrote around
|
|
94
|
+
* it was the same fifty lines of STRUCTURE: an `AutoScroll` with a bounded
|
|
95
|
+
* height, an empty-state guard on `items.length === 0 && streaming === null`,
|
|
96
|
+
* the map with its keys, the streaming row, the thinking row with its
|
|
97
|
+
* `role="status"` and `aria-label` (and the same comment about screen readers
|
|
98
|
+
* hearing punctuation), the transcript guarded on `speaking`. The bubbles are
|
|
99
|
+
* the part each template exists to show, so those are slots; the order and the
|
|
100
|
+
* accessibility contract are this component's.
|
|
101
|
+
*
|
|
102
|
+
* {@link MessageList} is this with the stock bubbles filled in.
|
|
103
|
+
*
|
|
104
|
+
* Must be rendered inside the providers `mountClient()` installs.
|
|
105
|
+
*
|
|
106
|
+
* @example A board's radio log: its own bubbles, the stock tool row, the transcript pinned below
|
|
107
|
+
* ```tsx
|
|
108
|
+
* import { ConversationView } from "@alexkroman1/aai-ui";
|
|
109
|
+
*
|
|
110
|
+
* function RadioLog() {
|
|
111
|
+
* return (
|
|
112
|
+
* <ConversationView
|
|
113
|
+
* contentClassName="p-4 flex flex-col gap-2"
|
|
114
|
+
* empty={<p className="text-center opacity-60">Standing by.</p>}
|
|
115
|
+
* renderMessage={({ role, content }) => (
|
|
116
|
+
* <div className={role === "assistant" ? "self-start" : "self-end"}>{content}</div>
|
|
117
|
+
* )}
|
|
118
|
+
* renderTranscript={({ text }) => <div className="px-4 py-2 italic">{text}</div>}
|
|
119
|
+
* transcriptPosition="below"
|
|
120
|
+
* thinkingLabel="Dispatch is thinking"
|
|
121
|
+
* />
|
|
122
|
+
* );
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @param props - See {@link ConversationViewProps}.
|
|
127
|
+
*
|
|
128
|
+
* @public
|
|
129
|
+
*/
|
|
130
|
+
export declare function ConversationView({ renderMessage, renderTool, renderStreaming, renderTranscript, transcriptPosition, log, renderNote, renderSessionHeader, empty, thinkingLabel, thinkingIndicator, thinkingClassName, className, contentClassName, scrollClassName, style, }: ConversationViewProps): ReactNode;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Eyebrow label — the design system's small outlined pill with ALL-CAPS,
|
|
4
|
+
* tracked text ("VOICE AGENT", "LISTENING", …). Used as a section label on
|
|
5
|
+
* the start screen and as the live status chip in the chat header.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare function Eyebrow({ children, className, style, ...rest }: {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
className?: string | undefined;
|
|
12
|
+
} & Omit<React.HTMLAttributes<HTMLSpanElement>, "className">): ReactNode;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Props for {@link Facts}.
|
|
4
|
+
*
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export type FactsProps = {
|
|
8
|
+
/**
|
|
9
|
+
* The facts, in reading order. Anything `false`, `null`, `undefined` or the
|
|
10
|
+
* empty string is DROPPED, so a page writes `cond && \`${n} skipped\`` inline
|
|
11
|
+
* instead of splicing a separator into a conditional string.
|
|
12
|
+
*
|
|
13
|
+
* `0` is NOT dropped — it is a fact ("0 words"), and treating it as absent is
|
|
14
|
+
* the bug a plain truthiness filter would ship.
|
|
15
|
+
*
|
|
16
|
+
* They are TEXT rather than `ReactNode`: every one of the nine lines this
|
|
17
|
+
* replaced was a string, and taking strings is what lets this JOIN them (see
|
|
18
|
+
* the component doc) instead of interleaving keyed separator elements.
|
|
19
|
+
*/
|
|
20
|
+
items: readonly (string | number | false | null | undefined)[];
|
|
21
|
+
/**
|
|
22
|
+
* Which of the two muted typographies the pages use. `"sm"` is
|
|
23
|
+
* `text-sm opacity-70`, `"xs"` is `text-xs opacity-60` — the size and the
|
|
24
|
+
* muting move together, because that is the pair every site had.
|
|
25
|
+
*/
|
|
26
|
+
size?: "sm" | "xs" | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* The element to render. `"p"` by default; `"span"` for a line that sits
|
|
29
|
+
* inside phrasing content, where a `<p>` is invalid nesting the browser will
|
|
30
|
+
* reparent out from under React.
|
|
31
|
+
*/
|
|
32
|
+
as?: "p" | "span" | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* ADDED to the base classes rather than replacing them — `tabular-nums`,
|
|
35
|
+
* `uppercase tracking-[1.2px]`. There is no `tailwind-merge` in this package,
|
|
36
|
+
* so a class that CONFLICTS with a base one is not reliably the winner.
|
|
37
|
+
*/
|
|
38
|
+
className?: string | undefined;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* A muted line of run facts, joined by `·` — "6 segments · 12:04 of audio ·
|
|
42
|
+
* 1,840 words".
|
|
43
|
+
*
|
|
44
|
+
* Nine pages had written this by hand under four different typographies for
|
|
45
|
+
* one role, two of them (`call-audit-workflow` and `spoken-summary-workflow`) byte-identical down
|
|
46
|
+
* to the payload. Three things it takes off the caller:
|
|
47
|
+
*
|
|
48
|
+
* - **The separator cannot be forgotten, and neither can the space around it.**
|
|
49
|
+
* Four of the nine carried a literal `{" "}` at the end of a line, because
|
|
50
|
+
* Prettier's wrap ate the space that made `· ` read as a separator rather
|
|
51
|
+
* than as punctuation glued to the next word. A line that is correct only
|
|
52
|
+
* because somebody remembered an invisible JSX expression is exactly the
|
|
53
|
+
* thing a component should own.
|
|
54
|
+
* - **A fact worth omitting is omitted, and by the caller's own condition.**
|
|
55
|
+
* The hand-written shape for a conditional fact was to splice the separator
|
|
56
|
+
* into the string — `{x ? \` · budget exhausted\` : ""}` — which puts the
|
|
57
|
+
* punctuation in two places and gets the leading separator wrong the moment
|
|
58
|
+
* the fact before it also disappears. Passing the condition and letting this
|
|
59
|
+
* drop it keeps the separator in one place.
|
|
60
|
+
* - **A line with nothing left to say renders NOTHING.** With every fact
|
|
61
|
+
* conditional, the alternative is a muted empty row, or a bare `·`.
|
|
62
|
+
*
|
|
63
|
+
* The facts are JOINED into one string rather than interleaved as elements, and
|
|
64
|
+
* that is why the prop is text: joined, there is no per-fact `key` to invent —
|
|
65
|
+
* the same reasoning `WorkflowProgress` gives for its log lines. A line that
|
|
66
|
+
* genuinely needs an element in it (a link) wants its own markup.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* import { Facts } from "@alexkroman1/aai-ui";
|
|
71
|
+
*
|
|
72
|
+
* function RunFacts({ words, cut }: { words: number; cut: number }) {
|
|
73
|
+
* return <Facts size="xs" items={[`${words} words`, cut > 0 && `${cut} blind cuts`]} />;
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @param props - Facts-line props.
|
|
78
|
+
*
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
81
|
+
export declare function Facts({ items, size, as, className }: FactsProps): ReactNode;
|