@fiestaboard/ui 6.0.3 → 6.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/components/ai/actions.d.ts +17 -0
- package/dist/components/ai/actions.js +33 -0
- package/dist/components/ai/actions.js.map +1 -0
- package/dist/components/ai/conversation.d.ts +44 -0
- package/dist/components/ai/conversation.js +91 -0
- package/dist/components/ai/conversation.js.map +1 -0
- package/dist/components/ai/loader.d.ts +18 -0
- package/dist/components/ai/loader.js +26 -0
- package/dist/components/ai/loader.js.map +1 -0
- package/dist/components/ai/message.d.ts +51 -0
- package/dist/components/ai/message.js +48 -0
- package/dist/components/ai/message.js.map +1 -0
- package/dist/components/ai/prompt-input.d.ts +61 -0
- package/dist/components/ai/prompt-input.js +104 -0
- package/dist/components/ai/prompt-input.js.map +1 -0
- package/dist/components/ai/shimmer.d.ts +14 -0
- package/dist/components/ai/shimmer.js +16 -0
- package/dist/components/ai/shimmer.js.map +1 -0
- package/dist/components/ai/spotlight.d.ts +59 -0
- package/dist/components/ai/spotlight.js +74 -0
- package/dist/components/ai/spotlight.js.map +1 -0
- package/dist/components/ai/suggestion.d.ts +20 -0
- package/dist/components/ai/suggestion.js +27 -0
- package/dist/components/ai/suggestion.js.map +1 -0
- package/dist/components/ai/task.d.ts +36 -0
- package/dist/components/ai/task.js +97 -0
- package/dist/components/ai/task.js.map +1 -0
- package/dist/components/ai/tool.d.ts +59 -0
- package/dist/components/ai/tool.js +145 -0
- package/dist/components/ai/tool.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +11 -1
- package/dist/theme.css +93 -0
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "../forms/button";
|
|
3
|
+
/**
|
|
4
|
+
* Actions — the small icon affordances under an assistant turn: copy,
|
|
5
|
+
* retry, undo.
|
|
6
|
+
*
|
|
7
|
+
* Each {@link Action} is an icon-only button, so the `label` prop is
|
|
8
|
+
* required: it is the accessible name AND the tooltip. The 24px `icon-xs`
|
|
9
|
+
* size is the package's floor for a target (SC 2.5.8).
|
|
10
|
+
*/
|
|
11
|
+
declare function Actions({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
12
|
+
export interface ActionProps extends Omit<React.ComponentProps<typeof Button>, "aria-label"> {
|
|
13
|
+
/** Accessible name and tooltip text. */
|
|
14
|
+
label: string;
|
|
15
|
+
}
|
|
16
|
+
declare function Action({ className, label, children, ...props }: ActionProps): React.JSX.Element;
|
|
17
|
+
export { Action, Actions };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/utils.js";
|
|
3
|
+
import { Button as t } from "../forms/button.js";
|
|
4
|
+
import { Tooltip as n, TooltipContent as r, TooltipTrigger as i } from "../overlays/tooltip.js";
|
|
5
|
+
import "react";
|
|
6
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/ai/actions.tsx
|
|
8
|
+
function s({ className: t, ...n }) {
|
|
9
|
+
return /* @__PURE__ */ a("div", {
|
|
10
|
+
"data-slot": "actions",
|
|
11
|
+
className: e("flex items-center gap-0.5", t),
|
|
12
|
+
...n
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function c({ className: s, label: c, children: l, ...u }) {
|
|
16
|
+
return /* @__PURE__ */ o(n, { children: [/* @__PURE__ */ a(i, {
|
|
17
|
+
asChild: !0,
|
|
18
|
+
children: /* @__PURE__ */ a(t, {
|
|
19
|
+
"data-slot": "action",
|
|
20
|
+
type: "button",
|
|
21
|
+
variant: "ghost",
|
|
22
|
+
size: "icon-xs",
|
|
23
|
+
"aria-label": c,
|
|
24
|
+
className: e("text-muted-foreground hover:text-foreground", s),
|
|
25
|
+
...u,
|
|
26
|
+
children: l
|
|
27
|
+
})
|
|
28
|
+
}), /* @__PURE__ */ a(r, { children: c })] });
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { c as Action, s as Actions };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","names":[],"sources":["../../../src/components/ai/actions.tsx"],"mappings":";;;;;;;AAiBA,SAAS,EAAQ,EAAE,cAAW,GAAG,KAAsC;CACrE,OAAO,kBAAC,OAAD;EAAK,aAAU;EAAU,WAAW,EAAG,6BAA6B,CAAS;EAAG,GAAI;CAAQ,CAAA;AACrG;AAOA,SAAS,EAAO,EAAE,cAAW,UAAO,aAAU,GAAG,KAAsB;CACrE,OACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;EAAgB,SAAA;EACd,UAAA,kBAAC,GAAD;GACE,aAAU;GACV,MAAK;GACL,SAAQ;GACR,MAAK;GACL,cAAY;GACZ,WAAW,EAAG,+CAA+C,CAAS;GACtE,GAAI;GAEH;EACK,CAAA;CACM,CAAA,GAChB,kBAAC,GAAD,EAAA,UAAiB,EAAsB,CAAA,CAChC,EAAA,CAAA;AAEb"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "../forms/button";
|
|
3
|
+
/**
|
|
4
|
+
* Conversation — the scrolling transcript of a chat.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the shadcn "AI Elements" `Conversation` vocabulary (Conversation /
|
|
7
|
+
* ConversationContent / ConversationScrollButton) so an app written against
|
|
8
|
+
* that API reads the same here. Behaviour is what matters:
|
|
9
|
+
*
|
|
10
|
+
* - it is a `role="log"` region, which is how assistive technology is told
|
|
11
|
+
* "new entries append at the end" without re-announcing the whole list;
|
|
12
|
+
* - it sticks to the bottom while the user is at the bottom (a streaming
|
|
13
|
+
* reply keeps scrolling into view) and STOPS sticking the moment the user
|
|
14
|
+
* scrolls up to read — the one thing a naive `scrollIntoView` on every
|
|
15
|
+
* token gets wrong;
|
|
16
|
+
* - {@link ConversationScrollButton} appears only while the user is away
|
|
17
|
+
* from the bottom, and jumps back.
|
|
18
|
+
*
|
|
19
|
+
* "At the bottom" is measured, not assumed: the scroll handler updates
|
|
20
|
+
* state, and a ResizeObserver on the content re-pins the viewport only
|
|
21
|
+
* when the last measurement said the user was there. The observer runs in
|
|
22
|
+
* an effect but never calls setState — it scrolls the DOM — so this stays
|
|
23
|
+
* on the right side of the set-state-in-effect rule.
|
|
24
|
+
*/
|
|
25
|
+
export interface ConversationLabels {
|
|
26
|
+
/** Accessible name of the log region. */
|
|
27
|
+
conversation: string;
|
|
28
|
+
/** Accessible name of the jump-to-latest button. */
|
|
29
|
+
scrollToBottom: string;
|
|
30
|
+
}
|
|
31
|
+
export declare const DEFAULT_CONVERSATION_LABELS: ConversationLabels;
|
|
32
|
+
export interface ConversationProps extends React.ComponentProps<"div"> {
|
|
33
|
+
labels?: Partial<ConversationLabels>;
|
|
34
|
+
}
|
|
35
|
+
declare function Conversation({ className, children, labels, onScroll, ...props }: ConversationProps): React.JSX.Element;
|
|
36
|
+
/** The padded stack the messages live in. */
|
|
37
|
+
declare function ConversationContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
38
|
+
/**
|
|
39
|
+
* Appears while the user has scrolled away from the newest entry. Rendered
|
|
40
|
+
* as nothing at all — not merely hidden — while at the bottom, so it never
|
|
41
|
+
* sits in the tab order as a no-op.
|
|
42
|
+
*/
|
|
43
|
+
declare function ConversationScrollButton({ className, ...props }: Omit<React.ComponentProps<typeof Button>, "children">): React.JSX.Element | null;
|
|
44
|
+
export { Conversation, ConversationContent, ConversationScrollButton };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/utils.js";
|
|
3
|
+
import { Button as t } from "../forms/button.js";
|
|
4
|
+
import { ArrowDown as n } from "lucide-react";
|
|
5
|
+
import * as r from "react";
|
|
6
|
+
import { jsx as i } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/ai/conversation.tsx
|
|
8
|
+
var a = {
|
|
9
|
+
conversation: "Conversation",
|
|
10
|
+
scrollToBottom: "Jump to latest"
|
|
11
|
+
}, o = r.createContext(null);
|
|
12
|
+
function s(e) {
|
|
13
|
+
let t = r.useContext(o);
|
|
14
|
+
if (!t) throw Error(`${e} must be rendered inside <Conversation>`);
|
|
15
|
+
return t;
|
|
16
|
+
}
|
|
17
|
+
var c = 24;
|
|
18
|
+
function l({ className: t, children: n, labels: s, onScroll: l, ...u }) {
|
|
19
|
+
let d = {
|
|
20
|
+
...a,
|
|
21
|
+
...s
|
|
22
|
+
}, f = r.useRef(null), [p, m] = r.useState(!0), h = r.useRef(!0), g = r.useCallback(() => {
|
|
23
|
+
let e = f.current;
|
|
24
|
+
return !e || e.scrollHeight - e.scrollTop - e.clientHeight <= c;
|
|
25
|
+
}, []), _ = r.useCallback(() => {
|
|
26
|
+
let e = f.current;
|
|
27
|
+
e && (e.scrollTop = e.scrollHeight, h.current = !0, m(!0));
|
|
28
|
+
}, []), v = (e) => {
|
|
29
|
+
let t = g();
|
|
30
|
+
h.current = t, m(t), l?.(e);
|
|
31
|
+
};
|
|
32
|
+
r.useEffect(() => {
|
|
33
|
+
let e = f.current;
|
|
34
|
+
if (!e || typeof ResizeObserver > "u") return;
|
|
35
|
+
let t = new ResizeObserver(() => {
|
|
36
|
+
h.current && (e.scrollTop = e.scrollHeight);
|
|
37
|
+
});
|
|
38
|
+
for (let n of Array.from(e.children)) t.observe(n);
|
|
39
|
+
return () => t.disconnect();
|
|
40
|
+
}, []);
|
|
41
|
+
let y = {
|
|
42
|
+
isAtBottom: p,
|
|
43
|
+
scrollToBottom: _,
|
|
44
|
+
labels: d
|
|
45
|
+
};
|
|
46
|
+
return /* @__PURE__ */ i(o.Provider, {
|
|
47
|
+
value: y,
|
|
48
|
+
children: /* @__PURE__ */ i("div", {
|
|
49
|
+
"data-slot": "conversation",
|
|
50
|
+
className: e("relative flex min-h-0 flex-1 flex-col has-[[data-slot=conversation-viewport]:focus-visible]:shadow-[var(--focus-ring-shadow)]", t),
|
|
51
|
+
...u,
|
|
52
|
+
children: /* @__PURE__ */ i("div", {
|
|
53
|
+
ref: f,
|
|
54
|
+
"data-slot": "conversation-viewport",
|
|
55
|
+
role: "log",
|
|
56
|
+
"aria-label": d.conversation,
|
|
57
|
+
"aria-live": "polite",
|
|
58
|
+
"aria-relevant": "additions text",
|
|
59
|
+
tabIndex: 0,
|
|
60
|
+
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain rounded-[inherit] outline-none",
|
|
61
|
+
onScroll: v,
|
|
62
|
+
children: n
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function u({ className: t, ...n }) {
|
|
68
|
+
return /* @__PURE__ */ i("div", {
|
|
69
|
+
"data-slot": "conversation-content",
|
|
70
|
+
className: e("flex flex-col gap-3 p-3", t),
|
|
71
|
+
...n
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function d({ className: r, ...a }) {
|
|
75
|
+
let { isAtBottom: o, scrollToBottom: c, labels: l } = s("ConversationScrollButton");
|
|
76
|
+
return o ? null : /* @__PURE__ */ i(t, {
|
|
77
|
+
"data-slot": "conversation-scroll-button",
|
|
78
|
+
type: "button",
|
|
79
|
+
variant: "outline",
|
|
80
|
+
size: "icon-sm",
|
|
81
|
+
"aria-label": l.scrollToBottom,
|
|
82
|
+
className: e("absolute bottom-3 left-1/2 -translate-x-1/2 rounded-full shadow-card", r),
|
|
83
|
+
onClick: c,
|
|
84
|
+
...a,
|
|
85
|
+
children: /* @__PURE__ */ i(n, { "aria-hidden": "true" })
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { l as Conversation, u as ConversationContent, d as ConversationScrollButton, a as DEFAULT_CONVERSATION_LABELS };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=conversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.js","names":[],"sources":["../../../src/components/ai/conversation.tsx"],"mappings":";;;;;;;AAsCA,IAAa,IAAkD;CAC7D,cAAc;CACd,gBAAgB;AAClB,GAQM,IAAsB,EAAM,cAA+C,IAAI;AAErF,SAAS,EAAgB,GAA6C;CACpE,IAAM,IAAM,EAAM,WAAW,CAAmB;CAChD,IAAI,CAAC,GAAK,MAAU,MAAM,GAAG,EAAU,wCAAwC;CAC/E,OAAO;AACT;AAGA,IAAM,IAAmB;AAMzB,SAAS,EAAa,EAAE,cAAW,aAAU,WAAQ,aAAU,GAAG,KAA4B;CAC5F,IAAM,IAAI;EAAE,GAAG;EAA6B,GAAG;CAAO,GAChD,IAAc,EAAM,OAA8B,IAAI,GACtD,CAAC,GAAY,KAAiB,EAAM,SAAS,EAAI,GAEjD,IAAc,EAAM,OAAO,EAAI,GAE/B,IAAU,EAAM,kBAAkB;EACtC,IAAM,IAAK,EAAY;EAEvB,OADA,CAAK,KACE,EAAG,eAAe,EAAG,YAAY,EAAG,gBAAgB;CAC7D,GAAG,CAAC,CAAC,GAEC,IAAiB,EAAM,kBAAkB;EAC7C,IAAM,IAAK,EAAY;EAClB,MACL,EAAG,YAAY,EAAG,cAClB,EAAY,UAAU,IACtB,EAAc,EAAI;CACpB,GAAG,CAAC,CAAC,GAEC,KAAgB,MAAyC;EAC7D,IAAM,IAAO,EAAQ;EAGrB,AAFA,EAAY,UAAU,GACtB,EAAc,CAAI,GAClB,IAAW,CAAK;CAClB;CAEA,EAAM,gBAAgB;EACpB,IAAM,IAAK,EAAY;EACvB,IAAI,CAAC,KAAM,OAAO,iBAAmB,KAAa;EAClD,IAAM,IAAW,IAAI,qBAAqB;GAGxC,AAAI,EAAY,YAAS,EAAG,YAAY,EAAG;EAC7C,CAAC;EACD,KAAK,IAAM,KAAS,MAAM,KAAK,EAAG,QAAQ,GAAG,EAAS,QAAQ,CAAK;EACnE,aAAa,EAAS,WAAW;CACnC,GAAG,CAAC,CAAC;CAEL,IAAM,IAAkC;EAAE;EAAY;EAAgB,QAAQ;CAAE;CAEhF,OACE,kBAAC,EAAoB,UAArB;EAAqC;EACnC,UAAA,kBAAC,OAAD;GACE,aAAU;GACV,WAAW,EAKT,iIACA,CACF;GACA,GAAI;GAEJ,UAAA,kBAAC,OAAD;IACE,KAAK;IACL,aAAU;IACV,MAAK;IACL,cAAY,EAAE;IACd,aAAU;IACV,iBAAc;IAKd,UAAU;IACV,WAAU;IACV,UAAU;IAET;GACE,CAAA;EACF,CAAA;CACuB,CAAA;AAElC;AAGA,SAAS,EAAoB,EAAE,cAAW,GAAG,KAAsC;CACjF,OAAO,kBAAC,OAAD;EAAK,aAAU;EAAuB,WAAW,EAAG,2BAA2B,CAAS;EAAG,GAAI;CAAQ,CAAA;AAChH;AAOA,SAAS,EAAyB,EAAE,cAAW,GAAG,KAAgE;CAChH,IAAM,EAAE,eAAY,mBAAgB,cAAW,EAAgB,0BAA0B;CAEzF,OADI,IAAmB,OAErB,kBAAC,GAAD;EACE,aAAU;EACV,MAAK;EACL,SAAQ;EACR,MAAK;EACL,cAAY,EAAO;EACnB,WAAW,EAAG,wEAAwE,CAAS;EAC/F,SAAS;EACT,GAAI;EAEJ,UAAA,kBAAC,GAAD,EAAW,eAAY,OAAQ,CAAA;CACzB,CAAA;AAEZ"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Loader — "the assistant is working" with optional text.
|
|
4
|
+
*
|
|
5
|
+
* Composes {@link Spinner}, which already swaps its rotation for a static
|
|
6
|
+
* ring under reduced motion, so this inherits the WCAG 2.2.2 answer rather
|
|
7
|
+
* than restating it. The `role="status"` lives on the Spinner when there is
|
|
8
|
+
* no text and on the wrapper when there is, so the message is announced
|
|
9
|
+
* once, not twice.
|
|
10
|
+
*/
|
|
11
|
+
export interface LoaderProps extends React.ComponentProps<"div"> {
|
|
12
|
+
/** What is happening — "Thinking…". Announced politely. */
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
/** Announced when there is no visible text. */
|
|
15
|
+
label?: string;
|
|
16
|
+
}
|
|
17
|
+
declare function Loader({ className, children, label, ...props }: LoaderProps): React.JSX.Element;
|
|
18
|
+
export { Loader };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/utils.js";
|
|
3
|
+
import { Spinner as t } from "../feedback/spinner.js";
|
|
4
|
+
import "react";
|
|
5
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/ai/loader.tsx
|
|
7
|
+
function i({ className: i, children: a, label: o = "Loading", ...s }) {
|
|
8
|
+
let c = a != null;
|
|
9
|
+
return /* @__PURE__ */ r("div", {
|
|
10
|
+
"data-slot": "loader",
|
|
11
|
+
role: c ? "status" : void 0,
|
|
12
|
+
className: e("inline-flex items-center gap-2 text-sm text-muted-foreground", i),
|
|
13
|
+
...s,
|
|
14
|
+
children: [/* @__PURE__ */ n(t, {
|
|
15
|
+
size: "sm",
|
|
16
|
+
label: c ? null : o
|
|
17
|
+
}), c ? /* @__PURE__ */ n("span", {
|
|
18
|
+
"data-slot": "loader-text",
|
|
19
|
+
children: a
|
|
20
|
+
}) : null]
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { i as Loader };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","names":[],"sources":["../../../src/components/ai/loader.tsx"],"mappings":";;;;;;AAwBA,SAAS,EAAO,EAAE,cAAW,aAAU,WAAQ,WAAW,GAAG,KAAsB;CACjF,IAAM,IAAU,KAAuC;CACvD,OACE,kBAAC,OAAD;EACE,aAAU;EACV,MAAM,IAAU,WAAW,KAAA;EAC3B,WAAW,EAAG,gEAAgE,CAAS;EACvF,GAAI;EAJN,UAAA,CAME,kBAAC,GAAD;GAAS,MAAK;GAAK,OAAO,IAAU,OAAO;EAAQ,CAAA,GAClD,IAAU,kBAAC,QAAD;GAAM,aAAU;GAAe;EAAe,CAAA,IAAI,IAC1D;;AAET"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Message — one turn of a conversation, from the user or the assistant.
|
|
5
|
+
*
|
|
6
|
+
* `from` is the only axis. It is stamped as `data-from` on the row so the
|
|
7
|
+
* bubble and anything a consumer places inside can style off it
|
|
8
|
+
* (`group-data-[from=user]:…`) without a second prop threaded down.
|
|
9
|
+
*
|
|
10
|
+
* The assistant's turn is deliberately NOT a bubble: it is the page's own
|
|
11
|
+
* ground with the ink of body copy, because it carries markdown, tool
|
|
12
|
+
* cards and step timelines that need the full width. The user's turn is
|
|
13
|
+
* the bubble — short, right-aligned, on the accent surface.
|
|
14
|
+
*/
|
|
15
|
+
declare const messageVariants: (props?: ({
|
|
16
|
+
from?: "user" | "assistant" | null | undefined;
|
|
17
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
18
|
+
export interface MessageProps extends React.ComponentProps<"div">, VariantProps<typeof messageVariants> {
|
|
19
|
+
from: "user" | "assistant";
|
|
20
|
+
}
|
|
21
|
+
declare function Message({ className, from, ...props }: MessageProps): React.JSX.Element;
|
|
22
|
+
declare const messageContentVariants: (props?: ({
|
|
23
|
+
from?: "user" | "assistant" | null | undefined;
|
|
24
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
25
|
+
export interface MessageContentProps extends React.ComponentProps<"div">, VariantProps<typeof messageContentVariants> {
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The body of a turn. Reads `from` off the nearest {@link Message} when not
|
|
29
|
+
* given, so the common case is `<Message from="user"><MessageContent>…`.
|
|
30
|
+
*/
|
|
31
|
+
declare function MessageContent({ className, from, ...props }: MessageContentProps): React.JSX.Element;
|
|
32
|
+
export interface MessageAvatarProps extends React.ComponentProps<"span"> {
|
|
33
|
+
/**
|
|
34
|
+
* Fallback initial when no child glyph is given. One character is used.
|
|
35
|
+
*/
|
|
36
|
+
name?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Decorative by default: the turn's `from` is already conveyed by
|
|
39
|
+
* position and content, and a screen reader announcing "assistant
|
|
40
|
+
* avatar" before every reply is noise. Pass `decorative={false}` and an
|
|
41
|
+
* `aria-label` when the avatar is the only thing naming the speaker.
|
|
42
|
+
*/
|
|
43
|
+
decorative?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A small round mark beside a turn: an icon child, or the first letter of
|
|
47
|
+
* `name`. Not an image component — chat avatars here are glyphs, and the
|
|
48
|
+
* package ships no image loading.
|
|
49
|
+
*/
|
|
50
|
+
declare function MessageAvatar({ className, name, decorative, children, ...props }: MessageAvatarProps): React.JSX.Element;
|
|
51
|
+
export { Message, MessageAvatar, MessageContent, messageContentVariants, messageVariants };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/utils.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
5
|
+
import { cva as n } from "class-variance-authority";
|
|
6
|
+
//#region src/components/ai/message.tsx
|
|
7
|
+
var r = n("group flex w-full gap-2", {
|
|
8
|
+
variants: { from: {
|
|
9
|
+
user: "flex-row-reverse",
|
|
10
|
+
assistant: "flex-row"
|
|
11
|
+
} },
|
|
12
|
+
defaultVariants: { from: "assistant" }
|
|
13
|
+
});
|
|
14
|
+
function i({ className: n, from: i, ...a }) {
|
|
15
|
+
return /* @__PURE__ */ t("div", {
|
|
16
|
+
"data-slot": "message",
|
|
17
|
+
"data-from": i,
|
|
18
|
+
className: e(r({ from: i }), n),
|
|
19
|
+
...a
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
var a = n("min-w-0 text-sm leading-relaxed", {
|
|
23
|
+
variants: { from: {
|
|
24
|
+
user: "max-w-[85%] rounded-xl rounded-br-sm bg-accent px-3 py-2 text-accent-foreground",
|
|
25
|
+
assistant: "flex-1 text-foreground"
|
|
26
|
+
} },
|
|
27
|
+
defaultVariants: { from: "assistant" }
|
|
28
|
+
});
|
|
29
|
+
function o({ className: n, from: r, ...i }) {
|
|
30
|
+
return /* @__PURE__ */ t("div", {
|
|
31
|
+
"data-slot": "message-content",
|
|
32
|
+
className: e(a({ from: r }), r === void 0 && "group-data-[from=user]:max-w-[85%] group-data-[from=user]:rounded-xl group-data-[from=user]:rounded-br-sm group-data-[from=user]:bg-accent group-data-[from=user]:px-3 group-data-[from=user]:py-2 group-data-[from=user]:text-accent-foreground", n),
|
|
33
|
+
...i
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function s({ className: n, name: r, decorative: i = !0, children: a, ...o }) {
|
|
37
|
+
return /* @__PURE__ */ t("span", {
|
|
38
|
+
"data-slot": "message-avatar",
|
|
39
|
+
"aria-hidden": i || void 0,
|
|
40
|
+
className: e("inline-flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium text-muted-foreground", "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5", n),
|
|
41
|
+
...o,
|
|
42
|
+
children: a ?? (r ? r.trim().charAt(0).toUpperCase() : null)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { i as Message, s as MessageAvatar, o as MessageContent, a as messageContentVariants, r as messageVariants };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.js","names":[],"sources":["../../../src/components/ai/message.tsx"],"mappings":";;;;;;AAmBA,IAAM,IAAkB,EAAI,2BAA2B;CACrD,UAAU,EACR,MAAM;EACJ,MAAM;EACN,WAAW;CACb,EACF;CACA,iBAAiB,EACf,MAAM,YACR;AACF,CAAC;AAMD,SAAS,EAAQ,EAAE,cAAW,SAAM,GAAG,KAAuB;CAC5D,OAAO,kBAAC,OAAD;EAAK,aAAU;EAAU,aAAW;EAAM,WAAW,EAAG,EAAgB,EAAE,QAAK,CAAC,GAAG,CAAS;EAAG,GAAI;CAAQ,CAAA;AACpH;AAEA,IAAM,IAAyB,EAAI,mCAAmC;CACpE,UAAU,EACR,MAAM;EACJ,MAAM;EACN,WAAW;CACb,EACF;CACA,iBAAiB,EACf,MAAM,YACR;AACF,CAAC;AAQD,SAAS,EAAe,EAAE,cAAW,SAAM,GAAG,KAA8B;CAC1E,OACE,kBAAC,OAAD;EACE,aAAU;EACV,WAAW,EACT,EAAuB,EAAE,QAAK,CAAC,GAC/B,MAAS,KAAA,KACP,oPACF,CACF;EACA,GAAI;CACL,CAAA;AAEL;AAqBA,SAAS,EAAc,EAAE,cAAW,SAAM,gBAAa,IAAM,aAAU,GAAG,KAA6B;CACrG,OACE,kBAAC,QAAD;EACE,aAAU;EACV,eAAa,KAAc,KAAA;EAC3B,WAAW,EACT,2HACA,uFACA,CACF;EACA,GAAI;EAEH,UAAA,MAAa,IAAO,EAAK,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI;CACvD,CAAA;AAEV"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "../forms/button";
|
|
3
|
+
import { Textarea } from "../forms/textarea";
|
|
4
|
+
/**
|
|
5
|
+
* PromptInput — the composer at the bottom of a chat.
|
|
6
|
+
*
|
|
7
|
+
* Follows the "AI Elements" composition (PromptInput / PromptInputTextarea
|
|
8
|
+
* / PromptInputToolbar / PromptInputTools / PromptInputSubmit) with one
|
|
9
|
+
* shared `status`:
|
|
10
|
+
*
|
|
11
|
+
* ready the user may send
|
|
12
|
+
* submitted sent, nothing streamed yet
|
|
13
|
+
* streaming a reply is arriving — the submit button becomes Stop
|
|
14
|
+
* error the last send failed; sending is allowed again
|
|
15
|
+
*
|
|
16
|
+
* Keyboard: Enter sends, Shift+Enter inserts a newline (the convention
|
|
17
|
+
* every chat product has settled on). IME composition is respected — an
|
|
18
|
+
* Enter that commits a Japanese/Chinese candidate never sends.
|
|
19
|
+
*
|
|
20
|
+
* Data in, events out: the form's `onSubmit` fires with the native event;
|
|
21
|
+
* the consumer owns the value and clears it. The family never fetches.
|
|
22
|
+
*/
|
|
23
|
+
export type PromptInputStatus = "ready" | "submitted" | "streaming" | "error";
|
|
24
|
+
export interface PromptInputLabels {
|
|
25
|
+
/** Accessible name of the send button. */
|
|
26
|
+
send: string;
|
|
27
|
+
/** Accessible name of the stop button (shown while streaming). */
|
|
28
|
+
stop: string;
|
|
29
|
+
}
|
|
30
|
+
export declare const DEFAULT_PROMPT_INPUT_LABELS: PromptInputLabels;
|
|
31
|
+
export interface PromptInputProps extends React.ComponentProps<"form"> {
|
|
32
|
+
status?: PromptInputStatus;
|
|
33
|
+
labels?: Partial<PromptInputLabels>;
|
|
34
|
+
}
|
|
35
|
+
declare function PromptInput({ className, status, labels, ...props }: PromptInputProps): React.JSX.Element;
|
|
36
|
+
export interface PromptInputTextareaProps extends React.ComponentProps<typeof Textarea> {
|
|
37
|
+
/** Rows shown before the textarea grows with its content. */
|
|
38
|
+
minRows?: number;
|
|
39
|
+
/** Rows at which growth stops and the textarea scrolls. */
|
|
40
|
+
maxRows?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Enter submits the enclosing form; Shift+Enter is a newline. The textarea
|
|
44
|
+
* grows with its content between `minRows` and `maxRows`, measured from
|
|
45
|
+
* the real scroll height so a pasted paragraph is fully visible.
|
|
46
|
+
*/
|
|
47
|
+
declare function PromptInputTextarea({ className, minRows, maxRows, onKeyDown, onInput, ref, ...props }: PromptInputTextareaProps): React.JSX.Element;
|
|
48
|
+
/** The row under the textarea: tools on the left, submit on the right. */
|
|
49
|
+
declare function PromptInputToolbar({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
50
|
+
/** The left-hand group of the toolbar: model picker, attachments, modes. */
|
|
51
|
+
declare function PromptInputTools({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
52
|
+
export interface PromptInputSubmitProps extends Omit<React.ComponentProps<typeof Button>, "children" | "type"> {
|
|
53
|
+
/** Called instead of submitting while `status` is `streaming`. */
|
|
54
|
+
onStop?: () => void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Send while ready, Stop while streaming. The Stop form is `type="button"`
|
|
58
|
+
* so a click can never accidentally submit the form it sits in.
|
|
59
|
+
*/
|
|
60
|
+
declare function PromptInputSubmit({ className, onStop, disabled, ...props }: PromptInputSubmitProps): React.JSX.Element;
|
|
61
|
+
export { PromptInput, PromptInputSubmit, PromptInputTextarea, PromptInputToolbar, PromptInputTools };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/utils.js";
|
|
3
|
+
import { Button as t } from "../forms/button.js";
|
|
4
|
+
import { Textarea as n } from "../forms/textarea.js";
|
|
5
|
+
import { ArrowUp as r, Square as i } from "lucide-react";
|
|
6
|
+
import * as a from "react";
|
|
7
|
+
import { jsx as o } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/ai/prompt-input.tsx
|
|
9
|
+
var s = {
|
|
10
|
+
send: "Send",
|
|
11
|
+
stop: "Stop"
|
|
12
|
+
}, c = a.createContext({
|
|
13
|
+
status: "ready",
|
|
14
|
+
labels: s
|
|
15
|
+
});
|
|
16
|
+
function l({ className: t, status: n = "ready", labels: r, ...i }) {
|
|
17
|
+
let a = {
|
|
18
|
+
status: n,
|
|
19
|
+
labels: {
|
|
20
|
+
...s,
|
|
21
|
+
...r
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return /* @__PURE__ */ o(c.Provider, {
|
|
25
|
+
value: a,
|
|
26
|
+
children: /* @__PURE__ */ o("form", {
|
|
27
|
+
"data-slot": "prompt-input",
|
|
28
|
+
"data-status": n,
|
|
29
|
+
className: e("flex w-full flex-col rounded-xl border bg-card shadow-card transition-[box-shadow,border-color] duration-base", "focus-within:border-ring focus-within:shadow-[var(--focus-ring-shadow)]", t),
|
|
30
|
+
...i
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function u({ className: t, minRows: r = 1, maxRows: i = 6, onKeyDown: s, onInput: c, ref: l, ...u }) {
|
|
35
|
+
let d = a.useRef(null), f = (e) => {
|
|
36
|
+
d.current = e, typeof l == "function" ? l(e) : l && (l.current = e);
|
|
37
|
+
}, p = () => {
|
|
38
|
+
let e = d.current;
|
|
39
|
+
if (!e) return;
|
|
40
|
+
let t = Number.parseFloat(getComputedStyle(e).lineHeight) || 20;
|
|
41
|
+
e.style.height = "auto";
|
|
42
|
+
let n = t * i;
|
|
43
|
+
e.style.height = `${Math.min(e.scrollHeight, n)}px`, e.style.overflowY = e.scrollHeight > n ? "auto" : "hidden";
|
|
44
|
+
};
|
|
45
|
+
return /* @__PURE__ */ o(n, {
|
|
46
|
+
ref: f,
|
|
47
|
+
"data-slot": "prompt-input-textarea",
|
|
48
|
+
rows: r,
|
|
49
|
+
className: e("min-h-0 resize-none border-0 bg-transparent px-3 py-2.5 shadow-none", "focus-visible:border-transparent focus-visible:ring-0 hover:border-transparent", t),
|
|
50
|
+
onInput: (e) => {
|
|
51
|
+
p(), c?.(e);
|
|
52
|
+
},
|
|
53
|
+
onKeyDown: (e) => {
|
|
54
|
+
s?.(e), !e.defaultPrevented && e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing && (e.preventDefault(), e.currentTarget.form?.requestSubmit());
|
|
55
|
+
},
|
|
56
|
+
...u
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function d({ className: t, ...n }) {
|
|
60
|
+
return /* @__PURE__ */ o("div", {
|
|
61
|
+
"data-slot": "prompt-input-toolbar",
|
|
62
|
+
className: e("flex items-center justify-between gap-2 px-2 pb-2", t),
|
|
63
|
+
...n
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function f({ className: t, ...n }) {
|
|
67
|
+
return /* @__PURE__ */ o("div", {
|
|
68
|
+
"data-slot": "prompt-input-tools",
|
|
69
|
+
className: e("flex items-center gap-1", t),
|
|
70
|
+
...n
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function p({ className: n, onStop: s, disabled: l, ...u }) {
|
|
74
|
+
let { status: d, labels: f } = a.useContext(c);
|
|
75
|
+
return d === "streaming" ? /* @__PURE__ */ o(t, {
|
|
76
|
+
"data-slot": "prompt-input-submit",
|
|
77
|
+
"data-status": d,
|
|
78
|
+
type: "button",
|
|
79
|
+
size: "icon-sm",
|
|
80
|
+
variant: "outline",
|
|
81
|
+
"aria-label": f.stop,
|
|
82
|
+
className: e("rounded-full", n),
|
|
83
|
+
onClick: s,
|
|
84
|
+
...u,
|
|
85
|
+
children: /* @__PURE__ */ o(i, {
|
|
86
|
+
"aria-hidden": "true",
|
|
87
|
+
className: "size-3.5 fill-current"
|
|
88
|
+
})
|
|
89
|
+
}) : /* @__PURE__ */ o(t, {
|
|
90
|
+
"data-slot": "prompt-input-submit",
|
|
91
|
+
"data-status": d,
|
|
92
|
+
type: "submit",
|
|
93
|
+
size: "icon-sm",
|
|
94
|
+
"aria-label": f.send,
|
|
95
|
+
disabled: l || d === "submitted",
|
|
96
|
+
className: e("rounded-full", n),
|
|
97
|
+
...u,
|
|
98
|
+
children: /* @__PURE__ */ o(r, { "aria-hidden": "true" })
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { s as DEFAULT_PROMPT_INPUT_LABELS, l as PromptInput, p as PromptInputSubmit, u as PromptInputTextarea, d as PromptInputToolbar, f as PromptInputTools };
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=prompt-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-input.js","names":[],"sources":["../../../src/components/ai/prompt-input.tsx"],"mappings":";;;;;;;;AAsCA,IAAa,IAAiD;CAC5D,MAAM;CACN,MAAM;AACR,GAOM,IAAqB,EAAM,cAAuC;CACtE,QAAQ;CACR,QAAQ;AACV,CAAC;AAOD,SAAS,EAAY,EAAE,cAAW,YAAS,SAAS,WAAQ,GAAG,KAA2B;CAExF,IAAM,IAAiC;EAAE;EAAQ,QAAQ;GAD7C,GAAG;GAA6B,GAAG;EACU;CAAE;CAC3D,OACE,kBAAC,EAAmB,UAApB;EAAoC;EAClC,UAAA,kBAAC,QAAD;GACE,aAAU;GACV,eAAa;GACb,WAAW,EACT,iHACA,2EACA,CACF;GACA,GAAI;EACL,CAAA;CAC0B,CAAA;AAEjC;AAcA,SAAS,EAAoB,EAC3B,cACA,aAAU,GACV,aAAU,GACV,cACA,YACA,QACA,GAAG,KACwB;CAC3B,IAAM,IAAW,EAAM,OAAmC,IAAI,GAExD,KAAW,MAAqC;EAEpD,AADA,EAAS,UAAU,GACf,OAAO,KAAQ,aAAY,EAAI,CAAI,IAC9B,MAAK,EAA4D,UAAU;CACtF,GAEM,UAAe;EACnB,IAAM,IAAK,EAAS;EACpB,IAAI,CAAC,GAAI;EACT,IAAM,IAAO,OAAO,WAAW,iBAAiB,CAAE,CAAC,CAAC,UAAU,KAAK;EACnE,EAAG,MAAM,SAAS;EAClB,IAAM,IAAM,IAAO;EAEnB,AADA,EAAG,MAAM,SAAS,GAAG,KAAK,IAAI,EAAG,cAAc,CAAG,EAAE,KACpD,EAAG,MAAM,YAAY,EAAG,eAAe,IAAM,SAAS;CACxD;CAEA,OACE,kBAAC,GAAD;EACE,KAAK;EACL,aAAU;EACV,MAAM;EACN,WAAW,EACT,uEACA,kFACA,CACF;EACA,UAAU,MAAU;GAElB,AADA,EAAO,GACP,IAAU,CAAK;EACjB;EACA,YAAY,MAAU;GACpB,IAAY,CAAK,GACb,GAAM,oBACN,EAAM,QAAQ,WAAW,CAAC,EAAM,YAAY,CAAC,EAAM,YAAY,gBACjE,EAAM,eAAe,GACrB,EAAM,cAAc,MAAM,cAAc;EAE5C;EACA,GAAI;CACL,CAAA;AAEL;AAGA,SAAS,EAAmB,EAAE,cAAW,GAAG,KAAsC;CAChF,OACE,kBAAC,OAAD;EACE,aAAU;EACV,WAAW,EAAG,qDAAqD,CAAS;EAC5E,GAAI;CACL,CAAA;AAEL;AAGA,SAAS,EAAiB,EAAE,cAAW,GAAG,KAAsC;CAC9E,OAAO,kBAAC,OAAD;EAAK,aAAU;EAAqB,WAAW,EAAG,2BAA2B,CAAS;EAAG,GAAI;CAAQ,CAAA;AAC9G;AAWA,SAAS,EAAkB,EAAE,cAAW,WAAQ,aAAU,GAAG,KAAiC;CAC5F,IAAM,EAAE,WAAQ,cAAW,EAAM,WAAW,CAAkB;CAmB9D,OAlBkB,MAAW,cAGzB,kBAAC,GAAD;EACE,aAAU;EACV,eAAa;EACb,MAAK;EACL,MAAK;EACL,SAAQ;EACR,cAAY,EAAO;EACnB,WAAW,EAAG,gBAAgB,CAAS;EACvC,SAAS;EACT,GAAI;EAEJ,UAAA,kBAAC,GAAD;GAAQ,eAAY;GAAO,WAAU;EAAyB,CAAA;CACxD,CAAA,IAIV,kBAAC,GAAD;EACE,aAAU;EACV,eAAa;EACb,MAAK;EACL,MAAK;EACL,cAAY,EAAO;EACnB,UAAU,KAAY,MAAW;EACjC,WAAW,EAAG,gBAAgB,CAAS;EACvC,GAAI;EAEJ,UAAA,kBAAC,GAAD,EAAS,eAAY,OAAQ,CAAA;CACvB,CAAA;AAEZ"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Shimmer — text with a light sweep, for a status line that is still in
|
|
4
|
+
* progress ("Running create_page…").
|
|
5
|
+
*
|
|
6
|
+
* The sweep is `.ai-shimmer` in theme.css: a gradient clipped to the text,
|
|
7
|
+
* moving on `ai-shimmer` keyframes. Under reduced motion the class pins
|
|
8
|
+
* the gradient's position so the text is a plain, fully legible
|
|
9
|
+
* muted-foreground — an infinite sweep is exactly what WCAG 2.2.2 (Pause,
|
|
10
|
+
* Stop, Hide) exists for, and the global 1ms backstop alone would freeze
|
|
11
|
+
* it at an arbitrary frame.
|
|
12
|
+
*/
|
|
13
|
+
declare function Shimmer({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
|
|
14
|
+
export { Shimmer };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../../lib/utils.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/ai/shimmer.tsx
|
|
6
|
+
function n({ className: n, ...r }) {
|
|
7
|
+
return /* @__PURE__ */ t("span", {
|
|
8
|
+
"data-slot": "shimmer",
|
|
9
|
+
className: e("ai-shimmer text-sm", n),
|
|
10
|
+
...r
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { n as Shimmer };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=shimmer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shimmer.js","names":[],"sources":["../../../src/components/ai/shimmer.tsx"],"mappings":";;;;;AAiBA,SAAS,EAAQ,EAAE,cAAW,GAAG,KAAuC;CACtE,OAAO,kBAAC,QAAD;EAAM,aAAU;EAAU,WAAW,EAAG,sBAAsB,CAAS;EAAG,GAAI;CAAQ,CAAA;AAC/F"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Spotlight — the visual language for "the assistant is working HERE".
|
|
5
|
+
*
|
|
6
|
+
* Three presentational parts an app positions itself (the package knows
|
|
7
|
+
* nothing about the app's DOM, routes or which element is meant):
|
|
8
|
+
*
|
|
9
|
+
* - {@link SpotlightRing} — a ring drawn around a target rectangle, with a
|
|
10
|
+
* tone: `driving` (the assistant is acting on this), `landed` (the change
|
|
11
|
+
* arrived), `error`. `aria-hidden`: the ring is for sighted users; the
|
|
12
|
+
* caption and the app's own live regions carry the same information.
|
|
13
|
+
* - {@link SpotlightCaption} — a small bubble that says what is happening
|
|
14
|
+
* ("Typing the name…"), a polite live region, with an optional slot for
|
|
15
|
+
* controls (Stop, Approve/Deny).
|
|
16
|
+
* - {@link GhostValue} — a value being "typed into" a control the app must
|
|
17
|
+
* not actually mutate: renders the typed prefix with a caret at
|
|
18
|
+
* `progress`, or the whole value as a badge for controls with no text
|
|
19
|
+
* box (a switch, a select). `aria-hidden`: the real control keeps its
|
|
20
|
+
* accessible value; the caption narrates the change.
|
|
21
|
+
*
|
|
22
|
+
* The pulse and caret are `.ai-spotlight-pulse` / `.ai-caret` in
|
|
23
|
+
* theme.css, each with a reduced-motion pose (a static ring, a solid
|
|
24
|
+
* caret) rather than an arbitrary frozen frame.
|
|
25
|
+
*
|
|
26
|
+
* The app positions these with inline `style` (a measured rect), which is
|
|
27
|
+
* why none of them carries a position of its own: they are `absolute`
|
|
28
|
+
* boxes in whatever coordinate space the consumer puts them in.
|
|
29
|
+
*/
|
|
30
|
+
export type SpotlightTone = "driving" | "landed" | "error";
|
|
31
|
+
declare const spotlightRingVariants: (props?: ({
|
|
32
|
+
tone?: "error" | "driving" | "landed" | null | undefined;
|
|
33
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
34
|
+
export interface SpotlightRingProps extends React.ComponentProps<"div">, VariantProps<typeof spotlightRingVariants> {
|
|
35
|
+
tone?: SpotlightTone;
|
|
36
|
+
}
|
|
37
|
+
declare function SpotlightRing({ className, tone, ...props }: SpotlightRingProps): React.JSX.Element;
|
|
38
|
+
export interface SpotlightCaptionProps extends React.ComponentProps<"div"> {
|
|
39
|
+
tone?: SpotlightTone;
|
|
40
|
+
/** Controls rendered after the text: Stop, Approve, Deny. */
|
|
41
|
+
controls?: React.ReactNode;
|
|
42
|
+
}
|
|
43
|
+
declare function SpotlightCaption({ className, tone, controls, children, ...props }: SpotlightCaptionProps): React.JSX.Element;
|
|
44
|
+
export interface GhostValueProps extends Omit<React.ComponentProps<"span">, "children"> {
|
|
45
|
+
/** The full value being revealed. */
|
|
46
|
+
value: string;
|
|
47
|
+
/** 0..1 — how much of `value` is shown; 1 shows all of it. */
|
|
48
|
+
progress?: number;
|
|
49
|
+
/**
|
|
50
|
+
* `replica` overlays a text box and shows the typed prefix with a caret;
|
|
51
|
+
* `badge` sits beside a control with no text (a switch, a select) and
|
|
52
|
+
* shows the whole value at once.
|
|
53
|
+
*/
|
|
54
|
+
variant?: "replica" | "badge";
|
|
55
|
+
}
|
|
56
|
+
/** The number of characters visible at `progress`, never past the end. */
|
|
57
|
+
export declare function revealedLength(value: string, progress: number): number;
|
|
58
|
+
declare function GhostValue({ className, value, progress, variant, ...props }: GhostValueProps): React.JSX.Element;
|
|
59
|
+
export { GhostValue, SpotlightCaption, SpotlightRing, spotlightRingVariants };
|