@touchcastllc/napster-companion-api-dev 2.0.0-alpha.1 → 2.0.0-alpha.11

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.
@@ -0,0 +1,33 @@
1
+ export interface MicRecoveryController {
2
+ destroy(): void;
3
+ }
4
+ export interface MicRecoveryOptions {
5
+ /** Element the card mounts into — `#np_companion-sdk-root`. */
6
+ root: HTMLElement;
7
+ /**
8
+ * The avatar shape to render the card as, read when the card opens rather
9
+ * than once at creation: this controller lives as long as the widget, and
10
+ * the view can change under it (`updateStyle()`).
11
+ */
12
+ getView: () => string | undefined;
13
+ }
14
+ /**
15
+ * A click on the badged mute button: ask for the microphone back.
16
+ *
17
+ * Where the browser can still ask (Firefox reports "prompt" after a
18
+ * mid-session revoke) the retry puts the browser's own Allow/Block prompt on
19
+ * screen, and that prompt is the whole interaction — no card on top of it,
20
+ * and none after the user answers it. Only "blocked" opens the card: the
21
+ * browser will not ask (Chrome, "denied"), so the user has to be told how to
22
+ * unblock it themselves.
23
+ */
24
+ export declare function requestMicRecovery(): Promise<void>;
25
+ /**
26
+ * The recovery card for a microphone lost MID-session (CA-1076) — the same
27
+ * `MicPermission` card micGate.ts mounts at session start, but driven purely
28
+ * by `avatar.micRecoveryOpen` instead of by a session attempt: the session is
29
+ * live here, `session.status` stays ready, and the card can be closed back to
30
+ * it. requestMicRecovery() above sets the flag; it drops by itself once the
31
+ * microphone is available again (avatarSlice).
32
+ */
33
+ export declare function createMicRecovery(options: MicRecoveryOptions): MicRecoveryController;
@@ -1,8 +1,12 @@
1
+ export type MicAvailability = "available" | "unavailable";
2
+ export type MicRetryResult = "granted" | "refused" | "blocked";
1
3
  export type MicManagerEventMap = {
2
4
  stream: MediaStream | null;
3
5
  muted: boolean;
4
6
  device: string | undefined;
5
7
  error: Error;
8
+ availability: MicAvailability;
9
+ sourceMuted: boolean;
6
10
  };
7
11
  export type MicManagerEvent = keyof MicManagerEventMap;
8
12
  type Handler<E extends MicManagerEvent> = (payload: MicManagerEventMap[E]) => void;
@@ -19,6 +23,15 @@ export interface MicrophoneManager {
19
23
  getTrack(): MediaStreamTrack | null;
20
24
  getAnalyser(): AnalyserNode | null;
21
25
  getCurrentDeviceId(): string | undefined;
26
+ /** False while the microphone is lost and cannot be re-acquired unprompted. */
27
+ isAvailable(): boolean;
28
+ /** True while the browser itself is holding the capture muted. */
29
+ isSourceMuted(): boolean;
30
+ /**
31
+ * Ask for a lost microphone again, on the user's initiative. The only path
32
+ * that may put a browser permission prompt on screen mid-session.
33
+ */
34
+ retry(): Promise<MicRetryResult>;
22
35
  on<E extends MicManagerEvent>(event: E, handler: Handler<E>): () => void;
23
36
  }
24
37
  export declare const DEFAULT_AUDIO_CONSTRAINTS: MediaTrackConstraints;
@@ -2,7 +2,7 @@ export { store } from "./store";
2
2
  export type { RootState } from "./store";
3
3
  export { setOnFeaturesUpdateCallback } from "./middleware/featuresUpdateMiddleware";
4
4
  export * from "./selectors";
5
- export { setSessionId, setTransport, setStopInteraction, setMuted, setAutoMicActive, setVolume, setAvatarSpeaking, setScreenSharing, setUserTalking, setConnectionToken, setCloseConnectionHandler, setSessionTerminatedByServer, resetAvatar, } from "./slices/avatarSlice";
5
+ export { setSessionId, setTransport, setStopInteraction, setMuted, setMicUnavailable, setMicRecoveryOpen, setMicSourceMuted, setAutoMicActive, setVolume, setAvatarSpeaking, setScreenSharing, setUserTalking, setConnectionToken, setCloseConnectionHandler, setSessionTerminatedByServer, resetAvatar, } from "./slices/avatarSlice";
6
6
  export { setSessionStatus, resetSession } from "./slices/sessionSlice";
7
7
  export type { SessionStatus } from "./slices/sessionSlice";
8
8
  export { setFeatures, resetFeatures, setDebugMode } from "./slices/appSlice";
@@ -8,6 +8,9 @@ import type { SessionStatus } from "./slices/sessionSlice";
8
8
  export declare const selectSessionId: (state: RootState) => string | undefined;
9
9
  export declare const selectTransport: (state: RootState) => Transport | null;
10
10
  export declare const selectMuted: (state: RootState) => boolean;
11
+ export declare const selectMicUnavailable: (state: RootState) => boolean;
12
+ export declare const selectMicRecoveryOpen: (state: RootState) => boolean;
13
+ export declare const selectMicSourceMuted: (state: RootState) => boolean;
11
14
  export declare const selectAutoMicActive: (state: RootState) => boolean;
12
15
  export declare const selectAvatarSpeaking: (state: RootState) => boolean;
13
16
  export declare const selectUserTalking: (state: RootState) => boolean;
@@ -4,6 +4,9 @@ export interface AvatarState {
4
4
  transport: Transport | null;
5
5
  stopInteraction: boolean;
6
6
  muted: boolean;
7
+ micUnavailable: boolean;
8
+ micRecoveryOpen: boolean;
9
+ micSourceMuted: boolean;
7
10
  autoMicActive: boolean;
8
11
  volume: number;
9
12
  avatarSpeaking: boolean;
@@ -13,5 +16,5 @@ export interface AvatarState {
13
16
  closeConnectionHandler?: () => void;
14
17
  sessionTerminatedByServer: boolean;
15
18
  }
16
- export declare const setSessionId: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<string | undefined, "avatar/setSessionId">, setTransport: import("@reduxjs/toolkit").ActionCreatorWithPayload<Transport | null, "avatar/setTransport">, setStopInteraction: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setStopInteraction">, setMuted: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setMuted">, setAutoMicActive: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setAutoMicActive">, setVolume: import("@reduxjs/toolkit").ActionCreatorWithPayload<number, "avatar/setVolume">, setAvatarSpeaking: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setAvatarSpeaking">, setScreenSharing: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setScreenSharing">, setUserTalking: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setUserTalking">, setConnectionToken: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<string | undefined, "avatar/setConnectionToken">, setCloseConnectionHandler: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<(() => void) | undefined, "avatar/setCloseConnectionHandler">, setSessionTerminatedByServer: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setSessionTerminatedByServer">, resetAvatar: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"avatar/resetAvatar">;
19
+ export declare const setSessionId: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<string | undefined, "avatar/setSessionId">, setTransport: import("@reduxjs/toolkit").ActionCreatorWithPayload<Transport | null, "avatar/setTransport">, setStopInteraction: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setStopInteraction">, setMuted: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setMuted">, setMicUnavailable: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setMicUnavailable">, setMicRecoveryOpen: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setMicRecoveryOpen">, setMicSourceMuted: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setMicSourceMuted">, setAutoMicActive: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setAutoMicActive">, setVolume: import("@reduxjs/toolkit").ActionCreatorWithPayload<number, "avatar/setVolume">, setAvatarSpeaking: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setAvatarSpeaking">, setScreenSharing: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setScreenSharing">, setUserTalking: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setUserTalking">, setConnectionToken: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<string | undefined, "avatar/setConnectionToken">, setCloseConnectionHandler: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<(() => void) | undefined, "avatar/setCloseConnectionHandler">, setSessionTerminatedByServer: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "avatar/setSessionTerminatedByServer">, resetAvatar: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"avatar/resetAvatar">;
17
20
  export declare const avatarReducer: import("redux").Reducer<AvatarState>;
@@ -107,8 +107,10 @@ export type StopVideoCommand = {
107
107
  export interface FunctionCallOutputData {
108
108
  /** Identifier the server uses to pair the call with its output. */
109
109
  call_id: string;
110
- /** JSON-encoded string of the function result. */
111
- output: string;
110
+ /** Function result. Any JSON value; the server accepts `Any`. */
111
+ output: unknown;
112
+ /** Wait for the avatar to finish its current speech before handling this. */
113
+ delay?: boolean;
112
114
  }
113
115
  export type FunctionCallOutputCommand = {
114
116
  type: DataChannelMessageType.FUNCTION_CALL_OUTPUT;
@@ -333,7 +335,11 @@ export interface LayoutConfig {
333
335
  * - `"inline"`: the avatar renders inside `mountContainer` and fills it. Size
334
336
  * and shape come from how you style that container; `position` is ignored.
335
337
  * When the container has no explicit height, an internal 4:5 ratio is used
336
- * so the avatar is never invisible.
338
+ * so the avatar is never invisible. The container's content area must be at
339
+ * least 176px wide (132px without screen share) and 80px tall (64px for
340
+ * `round`) or the controls clip — the SDK warns in the console when there
341
+ * is too little room. See the README, "Inline layout: minimum container
342
+ * size".
337
343
  */
338
344
  type?: "fixed" | "inline";
339
345
  /** Position of the avatar on screen. Meaningful only when `type` is
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether the primary input can hover — false on phones and tablets. This is
3
+ * the SDK's one "is this a touch device" signal, the same query as
4
+ * ControlBar.css's hover rule. No matchMedia (older engines) counts as able
5
+ * to hover.
6
+ */
7
+ export declare function canHover(win?: Window): boolean;
@@ -35,6 +35,8 @@ export declare const ControlClasses: {
35
35
  readonly stop: "np_companion-action-btn-container-stop";
36
36
  readonly questionBar: "np_companion-chat-question-bar-container";
37
37
  readonly muted: "np_companion-muted";
38
+ /** Mic lost mid-session — ControlBar.css draws the warning badge off this. */
39
+ readonly micUnavailable: "np_companion-mic-unavailable";
38
40
  readonly hidden: "np_companion-hidden";
39
41
  readonly expanded: "np_companion-action-btn-container-expanded";
40
42
  readonly collapse: "np_companion-action-btn-container-collapse";
@@ -0,0 +1,53 @@
1
+ /**
2
+ * CA-845 — in inline layout the host sizes the container, so it can be given
3
+ * less room than the control row needs, and the buttons then clip silently
4
+ * (WCAG 1.4.10 Reflow needs a stated, testable floor). This watches the
5
+ * container and warns once when something stops fitting.
6
+ *
7
+ * It detects the problem from geometry instead of comparing against a stored
8
+ * width: the numbers can't drift out of step with the CSS (button size, gaps,
9
+ * pill padding), and the warning can say exactly how much room is missing.
10
+ */
11
+ export type FitProblem = {
12
+ kind: "controls-width";
13
+ availablePx: number;
14
+ requiredPx: number;
15
+ } | {
16
+ kind: "controls-height";
17
+ availablePx: number;
18
+ requiredPx: number;
19
+ } | {
20
+ kind: "entry-text";
21
+ availablePx: number;
22
+ };
23
+ export interface ContainerSizeWatcher {
24
+ /** Schedule a measurement now (e.g. controls were just mounted). */
25
+ check(): void;
26
+ destroy(): void;
27
+ }
28
+ /**
29
+ * Measures how the SDK's content sits inside the room the host gave it.
30
+ *
31
+ * "Room" is the root's own box, not the container's outer box: the root fills
32
+ * the container's content area, so the host's border and padding are already
33
+ * subtracted — which is what the integrator has to account for.
34
+ *
35
+ * - `controls-width`: the control row sticks out of its pill. `requiredPx` is
36
+ * the available width plus the overflow, i.e. the smallest room the row fits
37
+ * in.
38
+ * - `controls-height`: the pill is cut off by the top or bottom of the room.
39
+ * - `entry-text`: the entry-point card's text is clipped.
40
+ *
41
+ * Returns `[]` before the controls exist.
42
+ */
43
+ export declare function measureFit(root: HTMLElement): FitProblem[];
44
+ /**
45
+ * Watches the host container of an inline-layout widget and warns (always —
46
+ * `critical_warn` ignores debug mode, like other genuine integrator
47
+ * misconfiguration) when the controls or the start card stop fitting.
48
+ *
49
+ * One warning per kind of problem, each time it appears: it re-arms once the
50
+ * container fits again. Does nothing in fixed layout, where the SDK sizes
51
+ * itself (240px, wider than any threshold).
52
+ */
53
+ export declare function createContainerSizeWatcher(root: HTMLElement): ContainerSizeWatcher;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@touchcastllc/napster-companion-api-dev",
3
- "version": "2.0.0-alpha.1",
3
+ "version": "2.0.0-alpha.11",
4
4
  "keywords": [
5
5
  "napster",
6
6
  "companion-api",