@burdenoff/fe-libs 2026.1024.3 → 2026.1026.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/shared/assistant/avatar/live/LiveAvatarMinutes.d.ts +24 -0
  2. package/dist/shared/assistant/avatar/live/LiveAvatarMinutes.js +161 -0
  3. package/dist/shared/assistant/avatar/live/LiveAvatarStage.js +95 -74
  4. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +6 -0
  5. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +195 -172
  6. package/dist/shared/assistant/avatar/live/avatarPorts.d.ts +9 -0
  7. package/dist/shared/assistant/avatar/live/avatarSessionClient.d.ts +77 -1
  8. package/dist/shared/assistant/avatar/live/avatarSessionClient.js +193 -72
  9. package/dist/shared/assistant/avatar/live/avatarTransport.d.ts +25 -3
  10. package/dist/shared/assistant/avatar/live/avatarTransport.js +29 -2
  11. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +35 -4
  12. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +136 -79
  13. package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +79 -3
  14. package/dist/shared/assistant/avatar/live/liveAvatarController.js +424 -245
  15. package/dist/shared/assistant/avatar/live/liveAvatarMinutesView.d.ts +127 -0
  16. package/dist/shared/assistant/avatar/live/liveAvatarMinutesView.js +168 -0
  17. package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +3 -0
  18. package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -7
  19. package/dist/shared/assistant/avatar/live/liveKitAvatarRoom.js +6 -1
  20. package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +127 -2
  21. package/dist/shared/assistant/avatar/useAvatarCopy.js +116 -7
  22. package/dist/shared/assistant/i18nManifest.d.ts +32 -1
  23. package/dist/shared/assistant/i18nManifest.js +55 -24
  24. package/dist/shared/assistant/index.d.ts +2 -0
  25. package/dist/shared/assistant/ui/AssistantChatArea.js +17 -17
  26. package/dist/shared/assistant/ui/AssistantMarkdownRenderer.d.ts +5 -2
  27. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +11 -3
  28. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +78 -76
  29. package/dist/shared/assistant/ui/avatar/avatarAskGate.d.ts +2 -0
  30. package/dist/shared/assistant/ui/avatar/avatarAskGate.js +1 -0
  31. package/dist/shared/events/index.d.ts +2 -1
  32. package/dist/shared-assistant.js +227 -225
  33. package/package.json +1 -1
@@ -0,0 +1,24 @@
1
+ import { FC, RefObject } from 'react';
2
+ import { AssistantNavigate } from '../../ui/AssistantMarkdownRenderer';
3
+ import { LiveAvatarController, LiveAvatarSnapshot } from './liveAvatarController';
4
+ import { LiveAvatarMinutesPurchase } from './liveAvatarMinutesView';
5
+ /** How often the session's time left is redrawn while it runs (DESIGN 3.10: every 15 s). */
6
+ export declare const LIVE_AVATAR_MINUTES_REDRAW_MS = 15000;
7
+ export interface LiveAvatarMinutesProps {
8
+ readonly controller: LiveAvatarController;
9
+ /** The snapshot the tab read (one subscription per view). */
10
+ readonly snapshot: LiveAvatarSnapshot;
11
+ /** The host's purchase access (`LiveAvatarSeamOptions.minutesPurchase`). Absent: nobody is offered a purchase here. */
12
+ readonly minutesPurchase?: LiveAvatarMinutesPurchase | undefined;
13
+ /** The panel's navigator. Absent or null: nothing opens billing. */
14
+ readonly navigate?: AssistantNavigate | null | undefined;
15
+ /** Wall-clock milliseconds. */
16
+ readonly now: () => number;
17
+ /** The id of the region's words: a Turn on the minutes hold back is described by them. */
18
+ readonly textId: string;
19
+ /** The tab's view, focusable: it takes the focus when a control here goes while it holds it. A ref the tab keeps (stable). */
20
+ readonly viewRef: RefObject<HTMLElement | null>;
21
+ /** How often the session's time left is redrawn. Tests pass their own. */
22
+ readonly redrawMs?: number;
23
+ }
24
+ export declare const LiveAvatarMinutes: FC<LiveAvatarMinutesProps>;
@@ -0,0 +1,161 @@
1
+ import { useAvatarCopy as e } from "../useAvatarCopy.js";
2
+ import { LIVE_AVATAR_MINUTES_REFRESH_MS as t, LIVE_AVATAR_REQUEST_TIMEOUT_MS as n } from "./liveAvatarController.js";
3
+ import { LIVE_AVATAR_MINUTES_PLANS_PATH as r, liveAvatarMinutesView as i } from "./liveAvatarMinutesView.js";
4
+ import { useCallback as a, useEffect as o, useId as ee, useRef as s, useState as c } from "react";
5
+ import { ChevronDown as l } from "lucide-react";
6
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
7
+ //#region src/shared/assistant/avatar/live/LiveAvatarMinutes.tsx
8
+ var f = 15e3, p = t + n, m = "text-xs text-text-muted", h = "focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong", g = `inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl border border-border-default bg-bg-elevated px-4 text-sm font-medium text-text-primary transition-colors hover:bg-bg-raised aria-disabled:cursor-default aria-disabled:opacity-60 aria-disabled:hover:bg-bg-elevated ${h}`, _ = `inline-flex min-h-11 items-center gap-1 self-start rounded-lg text-xs font-medium text-text-secondary transition-colors hover:text-text-primary ${h}`, te = { status: "absent" }, v = { status: "asking" }, y = { status: "failed" };
9
+ function b(e) {
10
+ if (typeof e != "object" || !e || !("canPurchase" in e)) return y;
11
+ let { canPurchase: t } = e;
12
+ return typeof t == "boolean" ? {
13
+ status: "answered",
14
+ access: { canPurchase: t }
15
+ } : y;
16
+ }
17
+ var x = { status: "idle" }, S = ({ controller: t, snapshot: n, minutesPurchase: h, navigate: S, now: C, textId: w, viewRef: T, redrawMs: E = f }) => {
18
+ let D = e(), O = ee(), [k, A] = c(!1), j = s(!0);
19
+ o(() => (j.current = !0, () => {
20
+ j.current = !1;
21
+ }), []);
22
+ let M = a((e) => {
23
+ if (e !== null) return () => {
24
+ e.contains(e.ownerDocument.activeElement) && T.current?.focus();
25
+ };
26
+ }, [T]), N = h !== void 0 && S != null, [P, F] = c(x), I = s(null), L = n.minutes === null;
27
+ o(() => {
28
+ L && (I.current = null, F(x));
29
+ }, [L]);
30
+ let [R, z] = c(null), B = s(n.minutes?.balance?.availableMinutes ?? 0), V = s(n.purchase);
31
+ o(() => {
32
+ let e = V.current;
33
+ V.current = n.purchase, n.purchase === "checking" && e !== "checking" ? (B.current = n.minutes?.balance?.availableMinutes ?? 0, z(null)) : n.purchase === "idle" && e === "checking" && z({
34
+ kind: (n.minutes?.balance?.availableMinutes ?? 0) > B.current ? "seen" : "not-seen",
35
+ minutes: n.minutes
36
+ });
37
+ }, [n.purchase, n.minutes]);
38
+ let H = R !== null && R.minutes === n.minutes ? R.kind : null, [U, W] = c(null), [G, K] = c(null), q = U !== null && U.minutes === n.minutes, J = G !== null && G.minutes === n.minutes;
39
+ o(() => {
40
+ if (U === null) return;
41
+ if (!q) {
42
+ W(null), K({ minutes: n.minutes });
43
+ return;
44
+ }
45
+ let e = setTimeout(() => W(null), p);
46
+ return () => clearTimeout(e);
47
+ }, [
48
+ U,
49
+ q,
50
+ n.minutes
51
+ ]);
52
+ let Y = n.phase === "ready" && !n.closing && n.sessionEndsAt !== null, [, X] = c(0);
53
+ o(() => {
54
+ if (!Y) return;
55
+ let e = setInterval(() => X((e) => e + 1), E);
56
+ return () => clearInterval(e);
57
+ }, [Y, E]);
58
+ let Z = i({
59
+ snapshot: n,
60
+ access: N ? P.status === "idle" ? v : P : te,
61
+ purchaseOutcome: H,
62
+ rechecked: J,
63
+ now: C()
64
+ }, D), Q = N && Z !== null && Z.wantsAccess, $ = Z?.action ?? null;
65
+ o(() => {
66
+ if (!Q || P.status !== "idle" || I.current !== null || h === void 0) return;
67
+ let e = Symbol("access");
68
+ I.current = e, F(v);
69
+ let t = (t) => {
70
+ !j.current || I.current !== e || (I.current = null, F(t));
71
+ };
72
+ Promise.resolve().then(() => h.access()).then((e) => t(b(e)), () => t(y));
73
+ }, [
74
+ Q,
75
+ P.status,
76
+ h
77
+ ]);
78
+ let ne = a((e) => {
79
+ S && (z(null), Promise.resolve(S(e, "person", { surface: "avatar-minutes" })).catch(() => void 0));
80
+ }, [S]);
81
+ if (Z === null) return null;
82
+ let re = (e) => {
83
+ switch (e) {
84
+ case "see-plans":
85
+ ne(r);
86
+ return;
87
+ case "check-again":
88
+ if (q) return;
89
+ W({ minutes: n.minutes }), K(null), t.refreshMinutes();
90
+ return;
91
+ case "check-purchase-again":
92
+ t.checkMinutesPurchase();
93
+ return;
94
+ }
95
+ }, ie = (e) => {
96
+ switch (e) {
97
+ case "see-plans": return D.minutes.seePlans;
98
+ case "check-again": return q ? D.minutes.checking : D.minutes.checkAgain;
99
+ case "check-purchase-again": return D.minutes.checkAgain;
100
+ }
101
+ }, ae = D.minutes.howCounted(Z.staleSeconds);
102
+ return /* @__PURE__ */ d("div", {
103
+ ref: M,
104
+ className: "flex flex-col gap-1.5",
105
+ "data-testid": "assistant-avatar-tab-minutes",
106
+ "data-state": Z.state,
107
+ children: [
108
+ /* @__PURE__ */ d("div", {
109
+ id: w,
110
+ className: "flex flex-col gap-0.5",
111
+ children: [Z.headline === null ? null : /* @__PURE__ */ u("p", {
112
+ className: "text-sm text-text-primary",
113
+ "data-testid": "assistant-avatar-tab-minutes-headline",
114
+ children: Z.headline
115
+ }), Z.details.map((e) => /* @__PURE__ */ u("p", {
116
+ className: m,
117
+ "data-testid": "assistant-avatar-tab-minutes-detail",
118
+ children: e
119
+ }, e))]
120
+ }),
121
+ $ === null ? null : /* @__PURE__ */ u("button", {
122
+ ref: M,
123
+ type: "button",
124
+ onClick: () => re($),
125
+ "aria-disabled": $ === "check-again" && q,
126
+ className: g,
127
+ "data-testid": "assistant-avatar-tab-minutes-action",
128
+ "data-action": $,
129
+ children: ie($)
130
+ }),
131
+ /* @__PURE__ */ d("button", {
132
+ type: "button",
133
+ onClick: () => A((e) => !e),
134
+ "aria-expanded": k,
135
+ "aria-controls": O,
136
+ className: _,
137
+ "data-testid": "assistant-avatar-tab-minutes-how",
138
+ children: [/* @__PURE__ */ u("span", { children: D.minutes.howCountedTitle }), /* @__PURE__ */ u(l, {
139
+ className: `size-4 shrink-0 transition-transform ${k ? "rotate-180" : ""}`,
140
+ "aria-hidden": "true"
141
+ })]
142
+ }),
143
+ /* @__PURE__ */ u("ul", {
144
+ id: O,
145
+ hidden: !k,
146
+ className: "flex list-disc flex-col gap-1 ps-5 text-xs text-text-secondary",
147
+ "data-testid": "assistant-avatar-tab-minutes-how-text",
148
+ children: ae.map((e) => /* @__PURE__ */ u("li", { children: e }, e))
149
+ }),
150
+ /* @__PURE__ */ u("p", {
151
+ className: "sr-only",
152
+ "aria-live": "polite",
153
+ "aria-atomic": "true",
154
+ "data-testid": "assistant-avatar-tab-minutes-live",
155
+ children: Z.announcement ?? ""
156
+ })
157
+ ]
158
+ });
159
+ };
160
+ //#endregion
161
+ export { f as LIVE_AVATAR_MINUTES_REDRAW_MS, S as LiveAvatarMinutes };
@@ -1,145 +1,166 @@
1
1
  import { useAvatarCopy as e } from "../useAvatarCopy.js";
2
- import { liveAvatarStatusText as t } from "./liveAvatarStatus.js";
3
- import { useCallback as n, useEffect as r, useId as i, useRef as a, useSyncExternalStore as o } from "react";
4
- import { Video as s, VideoOff as c, Volume2 as l } from "lucide-react";
5
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
2
+ import { liveAvatarMinutesBlockStart as t, liveAvatarMinutesView as n } from "./liveAvatarMinutesView.js";
3
+ import { liveAvatarStatusText as r } from "./liveAvatarStatus.js";
4
+ import { useCallback as i, useEffect as a, useId as o, useRef as s, useSyncExternalStore as c } from "react";
5
+ import { Video as l, VideoOff as u, Volume2 as d } from "lucide-react";
6
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
6
7
  //#region src/shared/assistant/avatar/live/LiveAvatarStage.tsx
7
- var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-default bg-bg-elevated px-3 text-xs font-medium text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong aria-disabled:cursor-default aria-disabled:opacity-60", p = "inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border px-3 text-xs font-medium transition-colors focus-within:ring-2 focus-within:ring-border-strong", m = "inline-flex h-8 items-center rounded-full bg-action-primary-bg px-3 text-xs font-medium text-action-primary-text transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong aria-disabled:cursor-default aria-disabled:opacity-60", h = ({ controller: h, expanded: g, fallback: _, avatarNames: v }) => {
8
- let y = e(), b = i(), x = i(), S = i(), C = a(null), w = a(null), T = n((e) => {
8
+ var m = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-default bg-bg-elevated px-3 text-xs font-medium text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong aria-disabled:cursor-default aria-disabled:opacity-60", h = "inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border px-3 text-xs font-medium transition-colors focus-within:ring-2 focus-within:ring-border-strong", g = "inline-flex h-8 items-center rounded-full bg-action-primary-bg px-3 text-xs font-medium text-action-primary-text transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong aria-disabled:cursor-default aria-disabled:opacity-60", _ = ({ controller: _, expanded: v, fallback: y, avatarNames: b }) => {
9
+ let x = e(), S = o(), C = o(), w = o(), T = o(), E = s(null), D = s(null), O = i((e) => {
9
10
  e?.focus();
10
- }, []), E = n((e) => (w.current = e, () => {
11
- w.current = null, document.activeElement === e && C.current?.focus();
12
- }), []), D = o(h.subscribe, h.getSnapshot, h.getSnapshot);
13
- r(() => (h.setStageMounted(!0), () => h.setStageMounted(!1)), [h]);
14
- let { phase: O } = D, k = O === "starting" || O === "connecting" || O === "ready" || O === "ending", A = O === "checking" || O === "ending", j = Date.now(), M = D.retryAt !== null && D.retryAt > j, N = t(D, y, j), P = (e, t) => v?.[e] ?? `${y.chooserLabel} ${t + 1}`;
15
- return /* @__PURE__ */ d("div", {
16
- ref: C,
11
+ }, []), k = i((e) => (D.current = e, () => {
12
+ D.current = null, document.activeElement === e && E.current?.focus();
13
+ }), []), A = c(_.subscribe, _.getSnapshot, _.getSnapshot);
14
+ a(() => (_.setStageMounted(!0), () => _.setStageMounted(!1)), [_]);
15
+ let { phase: j } = A, M = j === "starting" || j === "connecting" || j === "ready" || j === "ending", N = j === "checking" || j === "ending", P = Date.now(), F = A.retryAt !== null && A.retryAt > P, I = r(A, x, P), L = n({
16
+ snapshot: A,
17
+ access: { status: "absent" },
18
+ now: P
19
+ }, x)?.stageLines ?? [], R = t(A, P), z = F || R, B = F ? `${S} ${C}` : R ? [
20
+ S,
21
+ I === null ? null : C,
22
+ L.length > 0 ? T : null
23
+ ].filter((e) => e !== null).join(" ") : S, V = (e, t) => b?.[e] ?? `${x.chooserLabel} ${t + 1}`;
24
+ return /* @__PURE__ */ p("div", {
25
+ ref: E,
17
26
  tabIndex: -1,
18
27
  className: "flex flex-col items-center gap-2 focus:outline-none",
19
28
  "data-testid": "assistant-live-avatar",
20
29
  children: [
21
- k ? /* @__PURE__ */ d("div", {
22
- className: `relative overflow-hidden rounded-2xl border border-border-subtle bg-bg-sunken ${g ? "size-64" : "size-40"}`,
30
+ M ? /* @__PURE__ */ p("div", {
31
+ className: `relative overflow-hidden rounded-2xl border border-border-subtle bg-bg-sunken ${v ? "size-64" : "size-40"}`,
23
32
  "data-testid": "assistant-live-avatar-frame",
24
- "data-phase": O,
33
+ "data-phase": j,
25
34
  children: [
26
- /* @__PURE__ */ u("video", {
27
- ref: h.attachVideo,
28
- className: `size-full object-cover ${D.videoReady ? "" : "invisible"}`,
29
- "aria-label": y.stageLabel,
30
- autoPlay: !0,
35
+ /* @__PURE__ */ f("video", {
36
+ ref: _.attachVideo,
37
+ className: `size-full object-cover ${A.videoReady && !A.videoBlocked ? "" : "invisible"}`,
38
+ "aria-label": x.stageLabel,
31
39
  muted: !0,
32
40
  playsInline: !0,
33
41
  "data-testid": "assistant-live-avatar-video"
34
42
  }),
35
- D.videoReady ? null : /* @__PURE__ */ u("p", {
43
+ A.videoReady && !A.videoBlocked || j === "ready" && A.videoBlocked ? null : /* @__PURE__ */ f("p", {
36
44
  className: "absolute inset-0 flex items-center justify-center p-3 text-center text-xs text-text-muted",
37
- children: O === "ending" ? y.stateEnding : O === "connecting" ? y.stageWaiting : y.stateStarting
45
+ children: j === "ending" ? x.stateEnding : j === "connecting" ? x.stageWaiting : x.stateStarting
38
46
  }),
39
- D.audioBlocked ? /* @__PURE__ */ d("button", {
47
+ A.audioBlocked ? /* @__PURE__ */ p("button", {
40
48
  type: "button",
41
- onClick: () => h.resumeAudio(),
49
+ onClick: () => _.resumeAudio(),
42
50
  className: "absolute inset-x-2 bottom-2 inline-flex items-center justify-center gap-1.5 rounded-xl bg-bg-elevated px-2 py-1.5 text-xs font-medium text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong",
43
51
  "data-testid": "assistant-live-avatar-sound",
44
- children: [/* @__PURE__ */ u(l, {
52
+ children: [/* @__PURE__ */ f(d, {
45
53
  className: "size-4 shrink-0",
46
54
  "aria-hidden": "true"
47
- }), /* @__PURE__ */ u("span", { children: y.problem("playback-blocked") })]
55
+ }), /* @__PURE__ */ f("span", { children: x.problem("playback-blocked") })]
48
56
  }) : null
49
57
  ]
50
- }) : _,
51
- D.offered || k ? /* @__PURE__ */ d("button", {
52
- ref: E,
58
+ }) : y,
59
+ A.offered || M ? /* @__PURE__ */ p("button", {
60
+ ref: k,
53
61
  type: "button",
54
62
  onClick: () => {
55
- A || (k ? h.switchOff() : O === "choosing" ? h.cancelChoice() : h.requestOn());
63
+ N || (M ? _.switchOff() : j === "choosing" ? _.cancelChoice() : _.requestOn());
56
64
  },
57
- className: f,
58
- "aria-pressed": k,
59
- title: y.toggleHint,
60
- "aria-disabled": A,
65
+ className: m,
66
+ "aria-pressed": M,
67
+ title: x.toggleHint,
68
+ "aria-disabled": N,
61
69
  "data-testid": "assistant-live-avatar-toggle",
62
- children: [u(k ? c : s, {
70
+ children: [f(M ? u : l, {
63
71
  className: "size-4",
64
72
  "aria-hidden": "true"
65
- }), /* @__PURE__ */ u("span", { children: y.toggle })]
73
+ }), /* @__PURE__ */ f("span", { children: x.toggle })]
66
74
  }) : null,
67
- O === "choosing" ? /* @__PURE__ */ d("fieldset", {
75
+ j === "choosing" ? /* @__PURE__ */ p("fieldset", {
68
76
  className: "flex max-w-xs flex-col gap-2 rounded-xl border border-border-default bg-bg-elevated p-3 text-start",
69
77
  "data-testid": "assistant-live-avatar-consent",
70
78
  children: [
71
- /* @__PURE__ */ u("legend", {
79
+ /* @__PURE__ */ f("legend", {
72
80
  className: "sr-only",
73
- children: y.toggle
81
+ children: x.toggle
74
82
  }),
75
- D.avatars.length > 1 ? /* @__PURE__ */ d("fieldset", {
83
+ A.avatars.length > 1 ? /* @__PURE__ */ p("fieldset", {
76
84
  className: "flex flex-wrap gap-1.5",
77
85
  "data-testid": "assistant-live-avatar-chooser",
78
- children: [/* @__PURE__ */ u("legend", {
86
+ children: [/* @__PURE__ */ f("legend", {
79
87
  className: "sr-only",
80
- children: y.chooserTitle
81
- }), D.avatars.map((e, t) => {
82
- let n = e === D.selectedAvatarId, r = P(e, t);
83
- return /* @__PURE__ */ d("label", {
84
- className: `${p} ${n ? "border-transparent bg-action-primary-bg text-action-primary-text" : "border-border-default bg-bg-elevated text-text-secondary hover:bg-bg-raised hover:text-text-primary"}`,
85
- title: n ? y.chooserSelected(r) : y.chooserOption(r),
86
- children: [/* @__PURE__ */ u("input", {
88
+ children: x.chooserTitle
89
+ }), A.avatars.map((e, t) => {
90
+ let n = e === A.selectedAvatarId, r = V(e, t);
91
+ return /* @__PURE__ */ p("label", {
92
+ className: `${h} ${n ? "border-transparent bg-action-primary-bg text-action-primary-text" : "border-border-default bg-bg-elevated text-text-secondary hover:bg-bg-raised hover:text-text-primary"}`,
93
+ title: n ? x.chooserSelected(r) : x.chooserOption(r),
94
+ children: [/* @__PURE__ */ f("input", {
87
95
  type: "radio",
88
- name: S,
96
+ name: w,
89
97
  value: e,
90
98
  checked: n,
91
- onChange: () => h.selectAvatar(e),
99
+ onChange: () => _.selectAvatar(e),
92
100
  className: "sr-only",
93
101
  "data-avatar-id": e
94
102
  }), r]
95
103
  }, e);
96
104
  })]
97
105
  }) : null,
98
- /* @__PURE__ */ u("p", {
99
- ref: T,
100
- id: b,
106
+ /* @__PURE__ */ f("p", {
107
+ ref: O,
108
+ id: S,
101
109
  tabIndex: -1,
102
110
  className: "text-xs text-text-secondary focus:outline-none",
103
111
  "data-testid": "assistant-live-avatar-disclosure",
104
- children: y.consent
112
+ children: x.consent
105
113
  }),
106
- /* @__PURE__ */ d("div", {
114
+ L.length > 0 ? /* @__PURE__ */ f("div", {
115
+ id: T,
116
+ className: "flex flex-col gap-0.5 text-xs text-text-secondary",
117
+ "data-testid": "assistant-live-avatar-minutes",
118
+ children: L.map((e) => /* @__PURE__ */ f("p", {
119
+ "data-testid": "assistant-live-avatar-minutes-line",
120
+ children: e
121
+ }, e))
122
+ }) : null,
123
+ /* @__PURE__ */ p("div", {
107
124
  className: "flex justify-end gap-2",
108
- children: [/* @__PURE__ */ u("button", {
125
+ children: [/* @__PURE__ */ f("button", {
109
126
  type: "button",
110
127
  onClick: () => {
111
- w.current?.focus(), h.cancelChoice();
128
+ D.current?.focus(), _.cancelChoice();
112
129
  },
113
- className: f,
114
- children: y.consentCancel
115
- }), /* @__PURE__ */ u("button", {
130
+ className: m,
131
+ children: x.consentCancel
132
+ }), /* @__PURE__ */ f("button", {
116
133
  type: "button",
117
134
  onClick: () => {
118
- if (M) {
119
- h.recheckRetry();
135
+ if (F) {
136
+ _.recheckRetry();
120
137
  return;
121
138
  }
122
- w.current?.focus(), h.confirm();
139
+ if (R) {
140
+ A.retryAt !== null && _.recheckRetry();
141
+ return;
142
+ }
143
+ D.current?.focus(), _.confirm();
123
144
  },
124
- "aria-describedby": M ? `${b} ${x}` : b,
125
- "aria-disabled": M,
126
- className: m,
127
- disabled: D.selectedAvatarId === null,
145
+ "aria-describedby": B,
146
+ "aria-disabled": z,
147
+ className: g,
148
+ disabled: A.selectedAvatarId === null,
128
149
  "data-testid": "assistant-live-avatar-confirm",
129
- children: y.consentConfirm
150
+ children: x.consentConfirm
130
151
  })]
131
152
  })
132
153
  ]
133
154
  }) : null,
134
- /* @__PURE__ */ u("p", {
135
- id: x,
136
- className: N === null ? "sr-only" : "max-w-xs text-center text-xs text-text-muted",
155
+ /* @__PURE__ */ f("p", {
156
+ id: C,
157
+ className: I === null ? "sr-only" : "max-w-xs text-center text-xs text-text-muted",
137
158
  role: "status",
138
159
  "data-testid": "assistant-live-avatar-status",
139
- children: N ?? ""
160
+ children: I ?? ""
140
161
  })
141
162
  ]
142
163
  });
143
164
  };
144
165
  //#endregion
145
- export { h as LiveAvatarStageView };
166
+ export { _ as LiveAvatarStageView };
@@ -5,6 +5,7 @@ import { LiveAvatarTabProps } from '../../ui/assistantLiveAvatarSeam';
5
5
  import { AvatarVoiceDefault, AvatarVoicePreference } from '../avatarPreferences';
6
6
  import { LiveAvatarVoices } from './avatarVoice';
7
7
  import { LiveAvatarController } from './liveAvatarController';
8
+ import { LiveAvatarMinutesPurchase } from './liveAvatarMinutesView';
8
9
  /** The person's avatar voice choice: read by the engine decorator per reply, written by the tab's select. STABLE. */
9
10
  export interface LiveAvatarVoiceStore {
10
11
  getState(): AvatarVoicePreference;
@@ -24,6 +25,11 @@ export interface LiveAvatarTabViewProps extends LiveAvatarTabProps {
24
25
  readonly voiceStore: LiveAvatarVoiceStore;
25
26
  /** Wall-clock milliseconds: the retry time and the Turn off guard. Tests pass their own. */
26
27
  readonly now?: () => number;
28
+ /**
29
+ * ★ BOFF-9446: the host's purchase access (`LiveAvatarSeamOptions.minutesPurchase`, copied once by the seam). Absent: nobody is
30
+ * offered "See plans" here.
31
+ */
32
+ readonly minutesPurchase?: LiveAvatarMinutesPurchase;
27
33
  }
28
34
  /** A voice name as the person reads it: a hosted voice by its speaker ("Kore"), any other by its own name. */
29
35
  export declare function avatarVoiceDisplayName(name: string): string;