@touchcastllc/napster-companion-api-dev 2.0.0-alpha.8 → 2.1.0-alpha.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.
@@ -26,8 +26,8 @@ export declare function requestMicRecovery(): Promise<void>;
26
26
  * The recovery card for a microphone lost MID-session (CA-1076) — the same
27
27
  * `MicPermission` card micGate.ts mounts at session start, but driven purely
28
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).
29
+ * live here and `session.status` stays ready. The card has no close control
30
+ * (removed in CA-1114): requestMicRecovery() above sets the flag, and it drops
31
+ * by itself once the microphone is available again (avatarSlice).
32
32
  */
33
33
  export declare function createMicRecovery(options: MicRecoveryOptions): MicRecoveryController;
@@ -19,6 +19,12 @@ export interface MicrophoneManager {
19
19
  switchDevice(deviceId: string): Promise<MediaStream>;
20
20
  setMuted(muted: boolean): void;
21
21
  isMuted(): boolean;
22
+ /**
23
+ * Hold the capture silent regardless of the user's mute (the inactivity
24
+ * warning). Not reflected in `isMuted()` or the `muted` event, so releasing
25
+ * it gives back exactly the user's own choice.
26
+ */
27
+ setHeld(held: boolean): void;
22
28
  getStream(): MediaStream | null;
23
29
  getTrack(): MediaStreamTrack | null;
24
30
  getAnalyser(): AnalyserNode | null;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Turns session, microphone, sound and screen-share changes into short
3
+ * screen-reader announcements (CA-1145, WCAG 4.1.3). Texts proposed by the
4
+ * developer, approved by the manager in review.
5
+ *
6
+ * The two end texts are not watched for here: teardown resets the avatar and
7
+ * the session before anything could tell an end from a reset, so
8
+ * `destroyInstance()` stops this watcher first and announces the end itself
9
+ * ("Connection lost…" when a live session lost its connection, CA-1146).
10
+ *
11
+ * Everything is spoken through the widget's announcer (utils/announcer.ts).
12
+ */
13
+ export declare const ANNOUNCEMENTS: {
14
+ readonly connecting: "Connecting";
15
+ readonly ready: "Avatar is ready";
16
+ readonly muted: "Microphone muted";
17
+ readonly unmuted: "Microphone unmuted";
18
+ readonly micUnavailable: "Microphone unavailable";
19
+ readonly micAvailable: "Microphone available again";
20
+ readonly soundMuted: "Sound muted";
21
+ readonly soundUnmuted: "Sound unmuted";
22
+ readonly screenShareStarted: "Screen sharing started";
23
+ readonly screenShareStopped: "Screen sharing stopped";
24
+ readonly sessionEnded: "Session ended";
25
+ readonly connectionLost: "Connection lost. Session ended.";
26
+ };
27
+ export declare function watchSessionAnnouncements(announce: (text: string) => void): () => void;
28
+ /**
29
+ * Speaks `text` from a short-lived region on the page body, `delayMs` from
30
+ * now. Only for the end of a session whose root is removed (no click-to-start
31
+ * button): the widget's announcer lives in that root and goes with it.
32
+ * Returns a function that removes the region early.
33
+ */
34
+ export declare function announceOutsideWidget(text: string, delayMs?: number): () => void;
@@ -13,6 +13,12 @@ interface WebRTCParams {
13
13
  onChatMessage?: (data: EventMessage) => void;
14
14
  onConnectionSuccess?: () => void;
15
15
  onConnectionError?: (error: Error) => void;
16
+ /**
17
+ * The connection is gone without an error: signaling closed cleanly over a
18
+ * peer that was already dead. The transport is released by then; the SDK
19
+ * still has to end the session (CA-1146).
20
+ */
21
+ onConnectionLost?: () => void;
16
22
  onStreams?: (streams: {
17
23
  videoStream?: MediaStream | null;
18
24
  audioStream?: MediaStream | null;
@@ -186,6 +186,8 @@ export interface FeatureConfig {
186
186
  */
187
187
  duration?: number;
188
188
  /** Countdown duration before close connection in seconds, shown to users when inactive timeout is triggered
189
+ *
190
+ * Min: 20 seconds (WCAG 2.2.1)
189
191
  *
190
192
  * Max: 60 seconds
191
193
  *
@@ -15,16 +15,12 @@ export interface InactiveOverlayTrigger {
15
15
  type: InactiveOverlayTriggerType;
16
16
  /** Duration in seconds for the countdown */
17
17
  duration: number;
18
- /** Optional custom message to display */
19
- message?: string;
20
- /** Whether user interaction is needed to continue */
21
- isInteractionNeeded: boolean;
22
18
  }
23
19
  /**
24
20
  * Callbacks for overlay lifecycle events
25
21
  */
26
22
  export interface InactiveOverlayCallbacks {
27
- /** Called when user clicks "Continue" */
23
+ /** Called when user clicks "I'm here" */
28
24
  onContinue?: () => void;
29
25
  /** Called when countdown ends */
30
26
  onEnd?: () => void;
@@ -43,6 +39,10 @@ export declare class InactiveOverlayManager {
43
39
  private isVisible;
44
40
  private readonly mountElement;
45
41
  private pipTarget;
42
+ /** Where keyboard focus was when the overlay took it (CA-1139). */
43
+ private returnFocusTo;
44
+ /** Switches back on what the overlay covers (CA-1139 review). */
45
+ private restoreCovered;
46
46
  private readonly callbacks;
47
47
  constructor(mountElement: HTMLElement, callbacks?: InactiveOverlayCallbacks);
48
48
  /**
@@ -55,6 +55,12 @@ export declare class InactiveOverlayManager {
55
55
  * This is the single entry point for hiding the overlay
56
56
  */
57
57
  hide(): void;
58
+ /**
59
+ * restoreFocus is false when the session is ending (countdown run out,
60
+ * teardown): destroyInstance() moves focus once, to the entry point or
61
+ * mount — restoring here first would move it twice (Serhii's review).
62
+ */
63
+ private hideOverlay;
58
64
  /**
59
65
  * Check if overlay is currently visible
60
66
  */
@@ -64,7 +70,7 @@ export declare class InactiveOverlayManager {
64
70
  */
65
71
  getCurrentTrigger(): InactiveOverlayTrigger | null;
66
72
  /**
67
- * Set the PiP window body as the overlay target (or null when PiP closes).
73
+ * Set the PiP window's stage as the overlay target (or null when PiP closes).
68
74
  * Relocates a visible overlay so it stays in front of the user.
69
75
  */
70
76
  setPipTarget(target: HTMLElement | null): void;
@@ -19,8 +19,11 @@ export declare function createPiPManager(handlers: {
19
19
  onScreenShareToggle: () => void;
20
20
  isScreenShareSupported: () => boolean;
21
21
  onInteraction?: () => void;
22
- /** PiP window opened — receives its body so callers can render into it. */
23
- onOpen?: (pipBody: HTMLElement) => void;
22
+ /**
23
+ * PiP window opened — receives its #np_companion-sdk-root stage so callers
24
+ * can render into it with the SDK's root-anchored CSS applying.
25
+ */
26
+ onOpen?: (pipStage: HTMLElement) => void;
24
27
  /** PiP window closed (by user or programmatically). */
25
28
  onClose?: () => void;
26
29
  }): PiPController;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The widget's one screen-reader announcer (CA-1139).
3
+ *
4
+ * A single visually hidden `aria-live="polite"` region per root, shared by
5
+ * everything that needs to speak — several regions would talk over each
6
+ * other. The PiP document gets its own, since a live region only speaks for
7
+ * the document it lives in. Styled in App.css.
8
+ */
9
+ /**
10
+ * Text is written this long after the region is cleared. Screen readers
11
+ * announce a *change* to a region that already exists; clearing and writing
12
+ * in the same tick is no change at all, and a region created in the same tick
13
+ * as its text is often not read.
14
+ */
15
+ export declare const ANNOUNCE_DELAY_MS = 100;
16
+ /**
17
+ * Adds the announcer to a root, or returns the one it has. Call this when the
18
+ * root is created, so the region already exists by the time anything speaks.
19
+ */
20
+ export declare function createAnnouncer(root: HTMLElement): HTMLElement;
21
+ /**
22
+ * Reads `text` out through the announcer of the root `from` belongs to.
23
+ * `from` may be inside the root (main page) or contain it (the PiP body,
24
+ * which holds the PiP stage).
25
+ */
26
+ export declare function announce(from: HTMLElement, text: string): void;
@@ -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;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Focuses an element that may not be focusable on its own — a container the
3
+ * widget was in, or the widget root — so focus has somewhere sensible to land
4
+ * instead of <body> (CA-1139). A plain <div> gets a temporary tabindex="-1",
5
+ * dropped again once focus moves on; a tabindex the host set is left as is.
6
+ * <body> is skipped: focus is already there, and it must not gain a tabindex.
7
+ */
8
+ export declare function focusWithTemporaryTabindex(el: HTMLElement): void;
@@ -24,8 +24,8 @@ export interface WrapWithGlassFrameOptions {
24
24
  *
25
25
  * Used to carry the translucent glass-frame ring every card in this SDK
26
26
  * once rendered (avatar shape, control-bar pill, Entry Point card,
27
- * MicPermission card) — removed per manager feedback ("треба прибрати всі
28
- * скляні бордери, не тільки навколо шейпів"), so this now just wraps;
27
+ * MicPermission card) — removed per manager feedback ("remove all the glass
28
+ * borders, not only around the shapes"), so this now just wraps;
29
29
  * kept as the one place every caller's page-position class lands, since
30
30
  * that part of the contract is unrelated to the frame that used to live
31
31
  * here.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Green-screen removal on the GPU (CA-1185). Same rule as
3
+ * GreenScreenProcessor — a pixel inside the threshold box becomes
4
+ * transparent, every other pixel gets the same green-spill reduction — but
5
+ * run by a fragment shader, so no frame is read back into JavaScript. The
6
+ * CPU path cost ~8 ms per frame on a desktop (drawImage into a
7
+ * `willReadFrequently` canvas, the pixel loop, get/putImageData) and did
8
+ * not fit a frame on an iPhone.
9
+ *
10
+ * Differs from the CPU path in one way only: GreenScreenProcessor leaves a
11
+ * frame without a single green pixel untouched (no spill reduction either);
12
+ * checking that here would need the readback this exists to avoid. A
13
+ * silhouette is only ever set up for a green-screen agent.
14
+ */
15
+ export interface WebGLKeyer {
16
+ /** Draws the keyed `video` frame onto the canvas. False when the context
17
+ * is gone (lost or destroyed) or the upload failed — nothing was drawn. */
18
+ render(video: HTMLVideoElement): boolean;
19
+ destroy(): void;
20
+ }
21
+ /**
22
+ * Takes the canvas's one context as WebGL. Returns null if WebGL is
23
+ * unavailable or the shaders do not build; the caller then keys on the CPU.
24
+ * Even then the canvas may already hold a WebGL context and can never get a
25
+ * 2D one, so pass a canvas that can be thrown away.
26
+ */
27
+ export declare function createWebGLKeyer(canvas: HTMLCanvasElement): WebGLKeyer | null;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The widget's own texts are English and the SDK root says so (`lang="en"`,
3
+ * CA-1148). Texts the integrator supplies through the config (button label
4
+ * and description, disclaimer) are written in the host page's language
5
+ * instead, so they carry the page's `lang` — otherwise a screen reader would
6
+ * read, say, a German label with the English voice.
7
+ */
8
+ /** The host page's declared language, or "" when it declares none. */
9
+ export declare function hostLanguage(): string;
10
+ /**
11
+ * Marks an element holding integrator text with the host page's language.
12
+ * With no page language there is nothing to mark it with: the attribute is
13
+ * removed and the element inherits the root's English.
14
+ */
15
+ export declare function applyHostLanguage(el: HTMLElement): void;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Makes everything next to `el` inert — not focusable, not read by screen
3
+ * readers — and returns a function that undoes it. Used while an overlay
4
+ * covers its siblings, so Tab cannot reach a control hidden under it
5
+ * (CA-1139). Not `aria-modal`: that would claim the whole host page.
6
+ *
7
+ * A sibling holding the widget's announcer is not made inert itself — inert
8
+ * content is not read out, so the warning would go silent (the PiP stage
9
+ * holds both the announcer and the controls). Its other children are, instead.
10
+ * Elements that were already inert are left out, so the undo keeps them so.
11
+ */
12
+ export declare function makeSiblingsInert(el: HTMLElement): () => void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The widget root `from` belongs to: the root around it on the main page, or
3
+ * the stage inside it when `from` is the PiP body. Falls back to `from`.
4
+ */
5
+ export declare function findWidgetRoot(from: HTMLElement): HTMLElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@touchcastllc/napster-companion-api-dev",
3
- "version": "2.0.0-alpha.8",
3
+ "version": "2.1.0-alpha.0",
4
4
  "keywords": [
5
5
  "napster",
6
6
  "companion-api",