@alexkroman1/aai-ui 5.13.1 → 5.14.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.
Files changed (27) hide show
  1. package/dist/{chat-view-DadZOvJO.js → chat-view-CgFytvGy.js} +3 -3
  2. package/dist/components/auto-scroll.d.ts +63 -0
  3. package/dist/components/chat-view.js +1 -1
  4. package/dist/components/controls.js +1 -1
  5. package/dist/components/message-list.js +1 -1
  6. package/dist/components/start-screen.js +1 -1
  7. package/dist/components/tool-call-block.js +1 -1
  8. package/dist/default-client/assets/{audio-BAAzYW28.js → audio-CsQVQn3f.js} +1 -1
  9. package/dist/default-client/assets/{capture-processor-vx5IW_Rd.js → capture-processor-B_5Ive8e.js} +1 -1
  10. package/dist/default-client/assets/{index-Cn-zI5ic.js → index-D35_z2WM.js} +26 -26
  11. package/dist/default-client/assets/index-DCjB3qtb.css +2 -0
  12. package/dist/default-client/assets/{playback-processor-2p9HLz_W.js → playback-processor-6L8SIQ_l.js} +1 -1
  13. package/dist/default-client/index.html +2 -2
  14. package/dist/define-client.js +4 -4
  15. package/dist/hooks.d.ts +30 -0
  16. package/dist/hooks.js +4 -26
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +6 -6
  19. package/dist/{message-list-YdLocGoT.js → message-list-CcjgWRVZ.js} +90 -26
  20. package/dist/{session-core-BoB7kTzL.js → session-core-BA8H3qtF.js} +38 -15
  21. package/dist/session-core-types.d.ts +22 -0
  22. package/dist/session-core.js +1 -1
  23. package/package.json +2 -2
  24. package/dist/default-client/assets/index-Ctjrde3-.css +0 -2
  25. package/dist/{aai-logo-B8lDmsut.js → aai-logo-9xRBGVFl.js} +1 -1
  26. package/dist/{controls-DzQEKq9c.js → controls-Cy_YVfsa.js} +1 -1
  27. package/dist/{tool-call-block-CAscLGFy.js → tool-call-block-CrLN7xlI.js} +1 -1
@@ -1,9 +1,9 @@
1
+ import { t as MessageList } from "./message-list-CcjgWRVZ.js";
1
2
  import { useSessionSelector, useTheme } from "./context.js";
2
3
  import { n as THINKING_COLOR, r as inkTint, t as ERROR_COLOR } from "./_colors-CcAi2FOU.js";
3
- import { t as AaiLogo } from "./aai-logo-B8lDmsut.js";
4
+ import { t as AaiLogo } from "./aai-logo-9xRBGVFl.js";
4
5
  import { t as Eyebrow } from "./eyebrow-C6ZFuiz6.js";
5
- import { t as Controls } from "./controls-DzQEKq9c.js";
6
- import { t as MessageList } from "./message-list-YdLocGoT.js";
6
+ import { t as Controls } from "./controls-Cy_YVfsa.js";
7
7
  import clsx from "clsx";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region components/console-shell.tsx
@@ -0,0 +1,63 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ /**
3
+ * A scroll container that stays pinned to the bottom as its content grows,
4
+ * releases when the reader scrolls up, and re-engages once they return to the
5
+ * bottom.
6
+ *
7
+ * For clients that render their own chat chrome instead of using
8
+ * {@link MessageList} — a terminal, a dispatch board, a themed transcript.
9
+ * `MessageList` already behaves this way; this is the same mechanism with no
10
+ * opinion about what goes inside it.
11
+ *
12
+ * @remarks
13
+ * The pattern this replaces is a `useEffect` that calls
14
+ * `ref.current?.scrollIntoView()` on every message change. That version has
15
+ * three faults, and they compound: it fights the reader, since scrolling up to
16
+ * re-read is undone by the next transcript delta; it misses growth that is not
17
+ * a new message, because a streamed reply, an expanding tool block or a
18
+ * markdown reflow changes height without changing the dependency array; and it
19
+ * needs a synthetic dependency (`messages.length + transcript.length`) to fire
20
+ * at all, which is where the dead `if (version < 0) return;` line comes from.
21
+ * A `ResizeObserver` on the content — what this uses — has none of those.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * import { AutoScroll, useSession } from "@alexkroman1/aai-ui";
26
+ *
27
+ * function Transcript() {
28
+ * const session = useSession();
29
+ * return (
30
+ * <AutoScroll className="flex-1 min-h-0" contentClassName="flex flex-col gap-2 p-4">
31
+ * {session.messages.map((m) => (
32
+ * <div key={m.id}>{m.content}</div>
33
+ * ))}
34
+ * </AutoScroll>
35
+ * );
36
+ * }
37
+ * ```
38
+ *
39
+ * @param children - The scrollable content.
40
+ * @param className - Classes for the outer container. It must be given a
41
+ * bounded height (`flex-1 min-h-0`, `h-full`, a fixed height) — an unbounded
42
+ * one grows with its content and never scrolls, so nothing pins.
43
+ * @param contentClassName - Classes for the inner content element, where
44
+ * padding and the children's own layout belong.
45
+ * @param scrollClassName - Classes for the scrolling element itself. Defaults
46
+ * to hiding the scrollbar; pass `"overflow-y-auto"` to show a native one.
47
+ * @param style - Inline styles for the outer container.
48
+ * @param initial - Scroll behavior on mount. Defaults to `"instant"` (start at
49
+ * the latest content without animating a scroll the reader did not ask for).
50
+ * @param resize - Scroll behavior when pinned content grows. Defaults to
51
+ * `"smooth"`.
52
+ *
53
+ * @public
54
+ */
55
+ export declare function AutoScroll({ children, className, contentClassName, scrollClassName, style, initial, resize, }: {
56
+ children: ReactNode;
57
+ className?: string | undefined;
58
+ contentClassName?: string | undefined;
59
+ scrollClassName?: string | undefined;
60
+ style?: CSSProperties | undefined;
61
+ initial?: "instant" | "smooth" | undefined;
62
+ resize?: "instant" | "smooth" | undefined;
63
+ }): ReactNode;
@@ -1,3 +1,3 @@
1
1
  import "../context.js";
2
- import { t as ChatView } from "../chat-view-DadZOvJO.js";
2
+ import { t as ChatView } from "../chat-view-CgFytvGy.js";
3
3
  export { ChatView };
@@ -1,3 +1,3 @@
1
1
  import "../context.js";
2
- import { t as Controls } from "../controls-DzQEKq9c.js";
2
+ import { t as Controls } from "../controls-Cy_YVfsa.js";
3
3
  export { Controls };
@@ -1,3 +1,3 @@
1
+ import { t as MessageList } from "../message-list-CcjgWRVZ.js";
1
2
  import "../context.js";
2
- import { t as MessageList } from "../message-list-YdLocGoT.js";
3
3
  export { MessageList };
@@ -1,7 +1,7 @@
1
1
  import { useSessionCore, useSessionSelector, useTheme } from "../context.js";
2
2
  import { r as inkTint } from "../_colors-CcAi2FOU.js";
3
3
  import { Button } from "./button.js";
4
- import { t as AaiLogo } from "../aai-logo-B8lDmsut.js";
4
+ import { t as AaiLogo } from "../aai-logo-9xRBGVFl.js";
5
5
  import { t as Eyebrow } from "../eyebrow-C6ZFuiz6.js";
6
6
  import clsx from "clsx";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,3 +1,3 @@
1
1
  import "../context.js";
2
- import { t as ToolCallBlock } from "../tool-call-block-CAscLGFy.js";
2
+ import { t as ToolCallBlock } from "../tool-call-block-CrLN7xlI.js";
3
3
  export { ToolCallBlock };
@@ -1 +1 @@
1
- import{a as e,o as t,t as n}from"./index-Cn-zI5ic.js";function r(e,t,n){if(e!==t)throw Error(`Browser refused the ${n} sample rate: asked for ${t} Hz, got ${e} Hz`)}function i(e){e.then(e=>{for(let t of e.getTracks())t.stop()}).catch(()=>{})}function a(e,t,n){let r=new AudioWorkletNode(e,`capture-processor`,{channelCount:1,channelCountMode:`explicit`}),i=null;return r.port.onmessage=e=>{let r=e.data;r.event===`chunk`&&r.buffer?t(r.buffer):r.event===`silent`?n?.():r.event===`stopped`&&(i?.(),i=null)},{node:r,start(){r.port.postMessage({event:`start`})},stop(){return new Promise(e=>{let t=setTimeout(e,250);i=()=>{clearTimeout(t),e()},r.port.postMessage({event:`stop`})})}}}async function o(o){let{sttSampleRate:s,ttsSampleRate:c,captureWorkletSrc:l,playbackWorkletSrc:u,onMicData:d,onError:f,onPlaybackStats:p,onPlaybackProgress:m,onMicSilent:h}=o,g=new AudioContext({sampleRate:c,latencyHint:`playback`}),_=s===c,v=_?g:new AudioContext({sampleRate:s,latencyHint:`interactive`});async function y(){let e=_?[g]:[g,v];await Promise.all(e.map(e=>e.close().catch(e=>{console.warn(`AudioContext close failed:`,e)})))}let b=navigator.mediaDevices.getUserMedia({audio:{deviceId:{ideal:`default`},...n}}),x;try{[x]=await Promise.all([b,g.resume(),v.resume(),v.audioWorklet.addModule(l),g.audioWorklet.addModule(u)]),r(v.sampleRate,s,`capture`),r(g.sampleRate,c,`playback`)}catch(e){throw i(b),await y(),e}let S=v.createMediaStreamSource(x),C=a(v,d,h);S.connect(C.node),C.node.onprocessorerror=()=>{let e=Error(`Audio capture worklet crashed`);console.error(`[aai-ui]`,e.message),f?.(e)},C.start();let w=null,T=null,E=0,D=null,O=new AbortController;function k(){T?.(),T=null,D=null}function A(e){e.stats&&e.stats.concealedSamples>0&&p?.(e.stats),e.reason!==`interrupt`&&(D!==null&&e.turn!==D||k())}function j(){if(w)return w;let e=new AudioWorkletNode(g,`playback-processor`);return e.connect(g.destination),e.port.onmessage=e=>{e.data.event===`stop`?A(e.data):e.data.event===`progress`&&m?.(e.data.bufferedMs)},e.onprocessorerror=()=>{let e=Error(`Audio playback worklet crashed`);console.error(`[aai-ui]`,e.message),k(),f?.(e)},w=e,e}let M={enqueue(e){O.signal.aborted||e.byteLength!==0&&j().port.postMessage({event:`write`,buffer:new Uint8Array(e)},[e])},done(){if(!w)return Promise.resolve();let n=++E;return w.port.postMessage({event:`done`,turn:n}),g.state===`running`?new Promise(r=>{T?.();let i=()=>{clearInterval(a),clearTimeout(o),T===i&&(T=null,D=null),r()},a=setInterval(()=>{g.state!==`running`&&i()},t),o=setTimeout(i,e);T=i,D=n}):(D=null,Promise.resolve())},flush(){w&&(k(),w.port.postMessage({event:`interrupt`}))},async close(){if(!O.signal.aborted){O.abort(),await C.stop(),S.disconnect(),C.node.disconnect(),w&&w.disconnect();for(let e of x.getTracks())e.stop();await y()}},async[Symbol.asyncDispose](){await M.close()}};return M}export{o as createVoiceIO};
1
+ import{a as e,o as t,t as n}from"./index-D35_z2WM.js";function r(e,t,n){if(e!==t)throw Error(`Browser refused the ${n} sample rate: asked for ${t} Hz, got ${e} Hz`)}function i(e){e.then(e=>{for(let t of e.getTracks())t.stop()}).catch(()=>{})}function a(e,t,n){let r=new AudioWorkletNode(e,`capture-processor`,{channelCount:1,channelCountMode:`explicit`}),i=null;return r.port.onmessage=e=>{let r=e.data;r.event===`chunk`&&r.buffer?t(r.buffer):r.event===`silent`?n?.():r.event===`stopped`&&(i?.(),i=null)},{node:r,start(){r.port.postMessage({event:`start`})},stop(){return new Promise(e=>{let t=setTimeout(e,250);i=()=>{clearTimeout(t),e()},r.port.postMessage({event:`stop`})})}}}async function o(o){let{sttSampleRate:s,ttsSampleRate:c,captureWorkletSrc:l,playbackWorkletSrc:u,onMicData:d,onError:f,onPlaybackStats:p,onPlaybackProgress:m,onMicSilent:h}=o,g=new AudioContext({sampleRate:c,latencyHint:`playback`}),_=s===c,v=_?g:new AudioContext({sampleRate:s,latencyHint:`interactive`});async function y(){let e=_?[g]:[g,v];await Promise.all(e.map(e=>e.close().catch(e=>{console.warn(`AudioContext close failed:`,e)})))}let b=navigator.mediaDevices.getUserMedia({audio:{deviceId:{ideal:`default`},...n}}),x;try{[x]=await Promise.all([b,g.resume(),v.resume(),v.audioWorklet.addModule(l),g.audioWorklet.addModule(u)]),r(v.sampleRate,s,`capture`),r(g.sampleRate,c,`playback`)}catch(e){throw i(b),await y(),e}let S=v.createMediaStreamSource(x),C=a(v,d,h);S.connect(C.node),C.node.onprocessorerror=()=>{let e=Error(`Audio capture worklet crashed`);console.error(`[aai-ui]`,e.message),f?.(e)},C.start();let w=null,T=null,E=0,D=null,O=new AbortController;function k(){T?.(),T=null,D=null}function A(e){e.stats&&e.stats.concealedSamples>0&&p?.(e.stats),e.reason!==`interrupt`&&(D!==null&&e.turn!==D||k())}function j(){if(w)return w;let e=new AudioWorkletNode(g,`playback-processor`);return e.connect(g.destination),e.port.onmessage=e=>{e.data.event===`stop`?A(e.data):e.data.event===`progress`&&m?.(e.data.bufferedMs)},e.onprocessorerror=()=>{let e=Error(`Audio playback worklet crashed`);console.error(`[aai-ui]`,e.message),k(),f?.(e)},w=e,e}let M={enqueue(e){O.signal.aborted||e.byteLength!==0&&j().port.postMessage({event:`write`,buffer:new Uint8Array(e)},[e])},done(){if(!w)return Promise.resolve();let n=++E;return w.port.postMessage({event:`done`,turn:n}),g.state===`running`?new Promise(r=>{T?.();let i=()=>{clearInterval(a),clearTimeout(o),T===i&&(T=null,D=null),r()},a=setInterval(()=>{g.state!==`running`&&i()},t),o=setTimeout(i,e);T=i,D=n}):(D=null,Promise.resolve())},flush(){w&&(k(),w.port.postMessage({event:`interrupt`}))},async close(){if(!O.signal.aborted){O.abort(),await C.stop(),S.disconnect(),C.node.disconnect(),w&&w.disconnect();for(let e of x.getTracks())e.stop();await y()}},async[Symbol.asyncDispose](){await M.close()}};return M}export{o as createVoiceIO};
@@ -1,4 +1,4 @@
1
- import{n as e,r as t}from"./index-Cn-zI5ic.js";import{t as n}from"./_module-url-BX0RuRU2.js";var r=n(`
1
+ import{n as e,r as t}from"./index-D35_z2WM.js";import{t as n}from"./_module-url-BX0RuRU2.js";var r=n(`
2
2
  class CaptureProcessor extends AudioWorkletProcessor {
3
3
  constructor(options) {
4
4
  super();