@burdenoff/fe-libs 2026.1024.3 → 2026.1024.4

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 (29) 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 -73
  4. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +6 -0
  5. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +195 -171
  6. package/dist/shared/assistant/avatar/live/avatarSessionClient.d.ts +77 -1
  7. package/dist/shared/assistant/avatar/live/avatarSessionClient.js +193 -72
  8. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +14 -1
  9. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +65 -40
  10. package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +64 -2
  11. package/dist/shared/assistant/avatar/live/liveAvatarController.js +403 -242
  12. package/dist/shared/assistant/avatar/live/liveAvatarMinutesView.d.ts +127 -0
  13. package/dist/shared/assistant/avatar/live/liveAvatarMinutesView.js +168 -0
  14. package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +3 -0
  15. package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -7
  16. package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +127 -2
  17. package/dist/shared/assistant/avatar/useAvatarCopy.js +116 -7
  18. package/dist/shared/assistant/i18nManifest.d.ts +32 -1
  19. package/dist/shared/assistant/i18nManifest.js +55 -24
  20. package/dist/shared/assistant/index.d.ts +2 -0
  21. package/dist/shared/assistant/ui/AssistantChatArea.js +17 -17
  22. package/dist/shared/assistant/ui/AssistantMarkdownRenderer.d.ts +5 -2
  23. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +10 -2
  24. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +78 -76
  25. package/dist/shared/assistant/ui/avatar/avatarAskGate.d.ts +2 -0
  26. package/dist/shared/assistant/ui/avatar/avatarAskGate.js +1 -0
  27. package/dist/shared/events/index.d.ts +2 -1
  28. package/dist/shared-assistant.js +227 -225
  29. 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,167 @@
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,
35
+ /* @__PURE__ */ f("video", {
36
+ ref: _.attachVideo,
37
+ className: `size-full object-cover ${A.videoReady ? "" : "invisible"}`,
38
+ "aria-label": x.stageLabel,
30
39
  autoPlay: !0,
31
40
  muted: !0,
32
41
  playsInline: !0,
33
42
  "data-testid": "assistant-live-avatar-video"
34
43
  }),
35
- D.videoReady ? null : /* @__PURE__ */ u("p", {
44
+ A.videoReady ? null : /* @__PURE__ */ f("p", {
36
45
  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
46
+ children: j === "ending" ? x.stateEnding : j === "connecting" ? x.stageWaiting : x.stateStarting
38
47
  }),
39
- D.audioBlocked ? /* @__PURE__ */ d("button", {
48
+ A.audioBlocked ? /* @__PURE__ */ p("button", {
40
49
  type: "button",
41
- onClick: () => h.resumeAudio(),
50
+ onClick: () => _.resumeAudio(),
42
51
  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
52
  "data-testid": "assistant-live-avatar-sound",
44
- children: [/* @__PURE__ */ u(l, {
53
+ children: [/* @__PURE__ */ f(d, {
45
54
  className: "size-4 shrink-0",
46
55
  "aria-hidden": "true"
47
- }), /* @__PURE__ */ u("span", { children: y.problem("playback-blocked") })]
56
+ }), /* @__PURE__ */ f("span", { children: x.problem("playback-blocked") })]
48
57
  }) : null
49
58
  ]
50
- }) : _,
51
- D.offered || k ? /* @__PURE__ */ d("button", {
52
- ref: E,
59
+ }) : y,
60
+ A.offered || M ? /* @__PURE__ */ p("button", {
61
+ ref: k,
53
62
  type: "button",
54
63
  onClick: () => {
55
- A || (k ? h.switchOff() : O === "choosing" ? h.cancelChoice() : h.requestOn());
64
+ N || (M ? _.switchOff() : j === "choosing" ? _.cancelChoice() : _.requestOn());
56
65
  },
57
- className: f,
58
- "aria-pressed": k,
59
- title: y.toggleHint,
60
- "aria-disabled": A,
66
+ className: m,
67
+ "aria-pressed": M,
68
+ title: x.toggleHint,
69
+ "aria-disabled": N,
61
70
  "data-testid": "assistant-live-avatar-toggle",
62
- children: [u(k ? c : s, {
71
+ children: [f(M ? u : l, {
63
72
  className: "size-4",
64
73
  "aria-hidden": "true"
65
- }), /* @__PURE__ */ u("span", { children: y.toggle })]
74
+ }), /* @__PURE__ */ f("span", { children: x.toggle })]
66
75
  }) : null,
67
- O === "choosing" ? /* @__PURE__ */ d("fieldset", {
76
+ j === "choosing" ? /* @__PURE__ */ p("fieldset", {
68
77
  className: "flex max-w-xs flex-col gap-2 rounded-xl border border-border-default bg-bg-elevated p-3 text-start",
69
78
  "data-testid": "assistant-live-avatar-consent",
70
79
  children: [
71
- /* @__PURE__ */ u("legend", {
80
+ /* @__PURE__ */ f("legend", {
72
81
  className: "sr-only",
73
- children: y.toggle
82
+ children: x.toggle
74
83
  }),
75
- D.avatars.length > 1 ? /* @__PURE__ */ d("fieldset", {
84
+ A.avatars.length > 1 ? /* @__PURE__ */ p("fieldset", {
76
85
  className: "flex flex-wrap gap-1.5",
77
86
  "data-testid": "assistant-live-avatar-chooser",
78
- children: [/* @__PURE__ */ u("legend", {
87
+ children: [/* @__PURE__ */ f("legend", {
79
88
  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", {
89
+ children: x.chooserTitle
90
+ }), A.avatars.map((e, t) => {
91
+ let n = e === A.selectedAvatarId, r = V(e, t);
92
+ return /* @__PURE__ */ p("label", {
93
+ 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"}`,
94
+ title: n ? x.chooserSelected(r) : x.chooserOption(r),
95
+ children: [/* @__PURE__ */ f("input", {
87
96
  type: "radio",
88
- name: S,
97
+ name: w,
89
98
  value: e,
90
99
  checked: n,
91
- onChange: () => h.selectAvatar(e),
100
+ onChange: () => _.selectAvatar(e),
92
101
  className: "sr-only",
93
102
  "data-avatar-id": e
94
103
  }), r]
95
104
  }, e);
96
105
  })]
97
106
  }) : null,
98
- /* @__PURE__ */ u("p", {
99
- ref: T,
100
- id: b,
107
+ /* @__PURE__ */ f("p", {
108
+ ref: O,
109
+ id: S,
101
110
  tabIndex: -1,
102
111
  className: "text-xs text-text-secondary focus:outline-none",
103
112
  "data-testid": "assistant-live-avatar-disclosure",
104
- children: y.consent
113
+ children: x.consent
105
114
  }),
106
- /* @__PURE__ */ d("div", {
115
+ L.length > 0 ? /* @__PURE__ */ f("div", {
116
+ id: T,
117
+ className: "flex flex-col gap-0.5 text-xs text-text-secondary",
118
+ "data-testid": "assistant-live-avatar-minutes",
119
+ children: L.map((e) => /* @__PURE__ */ f("p", {
120
+ "data-testid": "assistant-live-avatar-minutes-line",
121
+ children: e
122
+ }, e))
123
+ }) : null,
124
+ /* @__PURE__ */ p("div", {
107
125
  className: "flex justify-end gap-2",
108
- children: [/* @__PURE__ */ u("button", {
126
+ children: [/* @__PURE__ */ f("button", {
109
127
  type: "button",
110
128
  onClick: () => {
111
- w.current?.focus(), h.cancelChoice();
129
+ D.current?.focus(), _.cancelChoice();
112
130
  },
113
- className: f,
114
- children: y.consentCancel
115
- }), /* @__PURE__ */ u("button", {
131
+ className: m,
132
+ children: x.consentCancel
133
+ }), /* @__PURE__ */ f("button", {
116
134
  type: "button",
117
135
  onClick: () => {
118
- if (M) {
119
- h.recheckRetry();
136
+ if (F) {
137
+ _.recheckRetry();
120
138
  return;
121
139
  }
122
- w.current?.focus(), h.confirm();
140
+ if (R) {
141
+ A.retryAt !== null && _.recheckRetry();
142
+ return;
143
+ }
144
+ D.current?.focus(), _.confirm();
123
145
  },
124
- "aria-describedby": M ? `${b} ${x}` : b,
125
- "aria-disabled": M,
126
- className: m,
127
- disabled: D.selectedAvatarId === null,
146
+ "aria-describedby": B,
147
+ "aria-disabled": z,
148
+ className: g,
149
+ disabled: A.selectedAvatarId === null,
128
150
  "data-testid": "assistant-live-avatar-confirm",
129
- children: y.consentConfirm
151
+ children: x.consentConfirm
130
152
  })]
131
153
  })
132
154
  ]
133
155
  }) : null,
134
- /* @__PURE__ */ u("p", {
135
- id: x,
136
- className: N === null ? "sr-only" : "max-w-xs text-center text-xs text-text-muted",
156
+ /* @__PURE__ */ f("p", {
157
+ id: C,
158
+ className: I === null ? "sr-only" : "max-w-xs text-center text-xs text-text-muted",
137
159
  role: "status",
138
160
  "data-testid": "assistant-live-avatar-status",
139
- children: N ?? ""
161
+ children: I ?? ""
140
162
  })
141
163
  ]
142
164
  });
143
165
  };
144
166
  //#endregion
145
- export { h as LiveAvatarStageView };
167
+ 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;