@ikonai/sdk-react-ui 1.4.0 → 1.6.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.
@@ -0,0 +1,21 @@
1
+ import { IkonAudioCaptureRequest, IkonClient, IkonVideoCaptureRequest } from '../../sdk/sdk/src/index.ts';
2
+ export interface UseMediaCaptureResult {
3
+ /** The start failure, if the last capture start failed (e.g. permission denied). */
4
+ error: Error | null;
5
+ }
6
+ /**
7
+ * Streams the microphone up the given connection while `active` is true. The request
8
+ * is applied when capture (re)starts; changing it alone does not restart capture.
9
+ * Pair with a mute flag: `useMicrophoneCapture(client, !muted)`.
10
+ */
11
+ export declare function useMicrophoneCapture(client: IkonClient | null | undefined, active: boolean, request?: IkonAudioCaptureRequest): UseMediaCaptureResult;
12
+ /**
13
+ * Streams the camera up the given connection while `active` is true. The request is
14
+ * applied when capture (re)starts; changing it alone does not restart capture.
15
+ */
16
+ export declare function useCameraCapture(client: IkonClient | null | undefined, active: boolean, request?: Omit<IkonVideoCaptureRequest, 'sourceType'>): UseMediaCaptureResult;
17
+ /**
18
+ * Streams a screenshare up the given connection while `active` is true. The request is
19
+ * applied when capture (re)starts; changing it alone does not restart capture.
20
+ */
21
+ export declare function useScreenCapture(client: IkonClient | null | undefined, active: boolean, request?: Omit<IkonVideoCaptureRequest, 'sourceType'>): UseMediaCaptureResult;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import { IkonClient, IkonClientConfig } from '../../sdk/sdk/src/index.ts';
2
+ export interface UseSiblingClientResult {
3
+ /** The connected sibling client, or null while connecting / when inputs are null. */
4
+ client: IkonClient | null;
5
+ /** The connect failure, if the last connection attempt failed. */
6
+ error: Error | null;
7
+ }
8
+ /**
9
+ * Opens a sibling connection next to an existing one — same deployment, auth and
10
+ * transport, with the given connection parameters replacing the owner's. This is how
11
+ * a page opens a media leg to a cell room (`ikon-cell-type` + the cell's identity
12
+ * fields) while keeping its normal app connection. The sibling connects while the
13
+ * inputs are non-null and disconnects on unmount or when they change — so a huddle's
14
+ * media leg tears down (and the room sees the participant leave) simply by the
15
+ * component unmounting or `parameters` becoming null.
16
+ *
17
+ * Reconnection is keyed on the CONTENT of `parameters` (order-insensitive), so an
18
+ * object literal recreated every render is fine. `overrides` is applied when a
19
+ * connection starts; changing it alone does not reconnect.
20
+ */
21
+ export declare function useSiblingClient(client: IkonClient | null | undefined, parameters: Record<string, string> | null | undefined, overrides?: Partial<IkonClientConfig>): UseSiblingClientResult;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ import { CSSProperties } from 'react';
2
+ import { IkonClient } from '../../sdk/sdk/src/index.ts';
3
+ export declare const ATTACH_CANVAS_RETRY_MS = 100;
4
+ export declare const ATTACH_CANVAS_MAX_ATTEMPTS = 50;
5
+ /**
6
+ * The slice of the custom (non-WebRTC) video playback pipeline this view needs.
7
+ * Structurally satisfied by `IkonVideoPlayback` from `@ikonai/sdk`.
8
+ */
9
+ export interface VideoStreamPlayback {
10
+ attachCanvas(streamId: string, canvas: HTMLCanvasElement): Promise<unknown>;
11
+ detachCanvas(streamId: string): void;
12
+ }
13
+ export interface VideoStreamViewProps {
14
+ /** The connection the stream arrives on — an app connection or a sibling media connection. */
15
+ client: IkonClient | null | undefined;
16
+ /** App-level stream id, as published by the server (e.g. from a `Video.SendFrameAsync` relay). */
17
+ streamId: string | null | undefined;
18
+ className?: string;
19
+ style?: CSSProperties;
20
+ width?: number;
21
+ height?: number;
22
+ /** Extra attributes for the rendered element (e.g. `data-*` markers). */
23
+ attributes?: Record<string, string>;
24
+ /**
25
+ * Renderer hook: where to take the custom playback pipeline from. Defaults to
26
+ * `client.media?.video`, which is right for standalone use — the UI-node renderer
27
+ * passes its own source so a context-level override keeps working.
28
+ */
29
+ getPlayback?: () => VideoStreamPlayback | null | undefined;
30
+ }
31
+ /**
32
+ * Renders one incoming video stream (camera or screenshare), handling both media
33
+ * transports: when WebRTC carries the video the stream is a `MediaStream` rendered
34
+ * through a `<video>` element, and on the protocol-channel fallback the frames are
35
+ * decoded into a `<canvas>` attached to the custom playback pipeline. Which transport
36
+ * is active is per connection and can only be known at runtime, so a component that
37
+ * implements just one of the two renders a permanently black tile on the other — use
38
+ * this view instead of calling `attachCanvas` directly.
39
+ */
40
+ export declare const VideoStreamView: import('react').MemoExoticComponent<({ client, streamId, className, style, width, height, attributes, getPlayback, }: VideoStreamViewProps) => import("react").JSX.Element>;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikonai/sdk-react-ui",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -158,6 +158,44 @@
158
158
  word-break: break-word;
159
159
  }
160
160
 
161
+ /* An installed PWA has no reload control the user can reach, so this button is the only way back
162
+ into the app — sized for a thumb rather than as a secondary affordance. */
163
+ .ikon-offline-action {
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ min-width: 160px;
168
+ min-height: 44px;
169
+ margin-top: 12px;
170
+ padding: 0 20px;
171
+ background: var(--ikon-bg-solid);
172
+ border: 1px solid transparent;
173
+ border-radius: var(--ikon-radius-md);
174
+ font-family: inherit;
175
+ font-size: 15px;
176
+ font-weight: 500;
177
+ line-height: 1;
178
+ cursor: pointer;
179
+ transition: background-color 0.15s ease;
180
+ }
181
+
182
+ /* Crosswind's preflight resets `button { color: inherit }` unlayered, and an unlayered rule beats
183
+ a layered one whatever its specificity — so colouring the button itself loses the contrast and
184
+ paints the label in the chip's text colour on the solid fill. The span is not something the
185
+ reset targets, which keeps this a normal cascade an app can still override without !important. */
186
+ .ikon-offline-action-label {
187
+ color: var(--ikon-text-on-solid);
188
+ }
189
+
190
+ .ikon-offline-action:hover {
191
+ background: var(--ikon-bg-solid-hover);
192
+ }
193
+
194
+ .ikon-offline-action:focus-visible {
195
+ outline: 2px solid var(--ikon-focus-ring);
196
+ outline-offset: 2px;
197
+ }
198
+
161
199
  @keyframes ikon-fade-in {
162
200
  from { opacity: 0; }
163
201
  to { opacity: 1; }