@camstack/ui-library 0.1.55 → 0.1.57

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.
@@ -19,6 +19,24 @@ export interface SignalingResult {
19
19
  /** Server-offer mode: session ID + SDP offer from server */
20
20
  sessionId: string;
21
21
  sdpOffer: string;
22
+ /**
23
+ * ICE servers (STUN/TURN) the BROWSER should use, supplied by the server
24
+ * from its `turn-provider` cap. Critical for remote viewing: behind CGNAT
25
+ * (e.g. mobile data) the browser needs a TURN relay candidate or it has no
26
+ * reachable address and the stream stays black. Falls back to public STUN.
27
+ */
28
+ iceServers?: readonly RTCIceServer[];
29
+ }
30
+ /**
31
+ * Client-offer mode result: the server's SDP answer to a browser-built
32
+ * offer. The browser creates the offer advertising its FULL decode caps
33
+ * (incl. H.264 High on iOS 18), the server answers by echoing the matching
34
+ * codec, and the camera's native bytes pass through untouched — no
35
+ * transcode. This is the dual of `SignalingResult`'s server-offer flow.
36
+ */
37
+ export interface ClientOfferResult {
38
+ sessionId: string;
39
+ sdpAnswer: string;
22
40
  }
23
41
  /**
24
42
  * Client-side capabilities sent to the server at session creation.
@@ -75,6 +93,28 @@ export interface CameraStreamPlayerProps {
75
93
  * Required when createSession is provided.
76
94
  */
77
95
  sendAnswer?: (sessionId: string, sdpAnswer: string) => Promise<void>;
96
+ /**
97
+ * Client-offer signaling — PREFERRED over `createSession` when supplied.
98
+ * The player builds the SDP offer (advertising the browser's full native
99
+ * decode caps, incl. H.264 High on iOS 18) and this posts it to the
100
+ * server, which answers by echoing the matching codec. That lets a
101
+ * Main/High (incl. 4K) source pass through with NO transcode — the only
102
+ * path that lets iOS decode High. The caller closes over the target.
103
+ */
104
+ handleOffer?: (sdpOffer: string) => Promise<ClientOfferResult>;
105
+ /**
106
+ * Fetch the browser-side ICE servers (STUN + TURN) for client-offer mode.
107
+ * Called BEFORE the offer is built so a remote/CGNAT viewer can gather a
108
+ * relay candidate. Best-effort — resolve `undefined` to use public STUN.
109
+ */
110
+ getIceServers?: () => Promise<readonly RTCIceServer[] | undefined>;
111
+ /** Trickle ICE — push a client-gathered candidate to the server. */
112
+ addIceCandidate?: (sessionId: string, candidate: RTCIceCandidateInit) => Promise<void>;
113
+ /** Trickle ICE — poll the server's gathered candidates for a session. */
114
+ getIceCandidates?: (sessionId: string) => Promise<{
115
+ candidates: RTCIceCandidateInit[];
116
+ done: boolean;
117
+ }>;
78
118
  /** Explicitly close the WebRTC session on server (cleanup on unmount). */
79
119
  closeSession?: (sessionId: string) => Promise<void>;
80
120
  /**
@@ -84,4 +124,4 @@ export interface CameraStreamPlayerProps {
84
124
  */
85
125
  hintsOverride?: Partial<ClientStreamHints>;
86
126
  }
87
- export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, className, onStateChange, onError, overlay, createSession, sendAnswer, closeSession, hintsOverride, }: CameraStreamPlayerProps): import("react/jsx-runtime").JSX.Element;
127
+ export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, className, onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, hintsOverride, }: CameraStreamPlayerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import { ReactNode } from 'react';
2
+ import { MaskShape, MaskShapeKind } from '@camstack/types';
3
+ /** One editable item on the drawing plane. */
4
+ export interface MaskShapeItem {
5
+ readonly id: string | number;
6
+ readonly shape: MaskShape;
7
+ /** Small text label rendered near the shape. */
8
+ readonly label?: string;
9
+ /** Stroke/fill color. Falls back to a sensible per-kind default. */
10
+ readonly color?: string;
11
+ /** `false` → rendered dimmed (and skipped for hit-testing of editing chrome). */
12
+ readonly enabled?: boolean;
13
+ }
14
+ export interface MaskShapeCanvasProps {
15
+ readonly items: MaskShapeItem[];
16
+ readonly selectedId: string | number | null;
17
+ readonly onSelect: (id: string | number | null) => void;
18
+ /** Fired on every edit (controlled — parent owns truth, immutable updates). */
19
+ readonly onShapeChange: (id: string | number, shape: MaskShape) => void;
20
+ /** Fired when a new shape is finished while drawing. */
21
+ readonly onDrawComplete: (shape: MaskShape) => void;
22
+ /** When set, clicks draw a new shape of that kind. */
23
+ readonly drawingKind: MaskShapeKind | null;
24
+ /** Which kinds this mount allows. */
25
+ readonly supportedShapes: readonly MaskShapeKind[];
26
+ /** Transparent absolute-fill overlay mode (layer over a stream player). */
27
+ readonly transparent?: boolean;
28
+ /** Backdrop element rendered behind the Stage in standalone mode. */
29
+ readonly backdrop?: ReactNode;
30
+ /** Gate polygon vertex add/remove (e.g. Hikvision {min:4,max:4}). */
31
+ readonly polygonVertices?: {
32
+ readonly min: number;
33
+ readonly max: number;
34
+ };
35
+ /** Grid dims when drawing/seeding a `grid` shape. */
36
+ readonly grid?: {
37
+ readonly width: number;
38
+ readonly height: number;
39
+ };
40
+ }
41
+ declare function MaskShapeCanvasImpl({ items, selectedId, onSelect, onShapeChange, onDrawComplete, drawingKind, supportedShapes, transparent, backdrop, polygonVertices, grid, }: MaskShapeCanvasProps): import("react/jsx-runtime").JSX.Element;
42
+ /**
43
+ * Memoised export. The Konva scene is expensive to re-evaluate (many nodes per
44
+ * item + per-item drag-bound closures) and overlay parents re-render on every
45
+ * detection/motion event. Mirrors ZoneCanvas's `memo`.
46
+ */
47
+ export declare const MaskShapeCanvas: import('react').MemoExoticComponent<typeof MaskShapeCanvasImpl>;
48
+ export {};
@@ -1,2 +1,2 @@
1
1
  import { CapSettingsComponentProps } from './index';
2
- export declare function MotionZonesSettings({ deviceId }: CapSettingsComponentProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function MotionZonesSettings({ deviceId }: CapSettingsComponentProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,2 @@
1
+ import { CapSettingsComponentProps } from './index';
2
+ export declare function PrivacyMaskSettings({ deviceId }: CapSettingsComponentProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,6 +1,9 @@
1
1
  export { PtzPanel } from './PtzPanel';
2
2
  export { AutotrackSection } from './AutotrackSection';
3
3
  export { MotionZonesSettings } from './MotionZonesSettings';
4
+ export { PrivacyMaskSettings } from './PrivacyMaskSettings';
5
+ export { MaskShapeCanvas } from './MaskShapeCanvas';
6
+ export type { MaskShapeItem, MaskShapeCanvasProps } from './MaskShapeCanvas';
4
7
  /**
5
8
  * Props every cap-settings component receives.
6
9
  *
@@ -1,4 +1,5 @@
1
1
  import { ReactElement } from 'react';
2
+ import { UseDeviceProxyTrpc } from '../hooks/use-device-proxy';
2
3
  export interface DeviceExportPanelProps {
3
4
  /**
4
5
  * Addon id of the export addon whose surface is rendered. Used for
@@ -6,6 +7,12 @@ export interface DeviceExportPanelProps {
6
7
  * router resolves the (hub-resident) provider by `nodeId`.
7
8
  */
8
9
  readonly addonId: string;
10
+ /**
11
+ * tRPC client passed straight to the shared `<DeviceList>` so its
12
+ * per-row capability atoms (snapshot, switch, …) work. Supply
13
+ * `system.trpcClient` from `useSystem()` at the mount site.
14
+ */
15
+ readonly trpc: UseDeviceProxyTrpc;
9
16
  /** Optional callback to open a device-details page. */
10
17
  readonly onOpenDevice?: (deviceId: string) => void;
11
18
  }
@@ -13,4 +20,4 @@ export interface DeviceExportPanelProps {
13
20
  * Generic device-export panel. Addon-agnostic — only the `device-export`
14
21
  * cap drives it.
15
22
  */
16
- export declare function DeviceExportPanel({ addonId, onOpenDevice }: DeviceExportPanelProps): ReactElement;
23
+ export declare function DeviceExportPanel({ addonId, trpc, onOpenDevice }: DeviceExportPanelProps): ReactElement;
@@ -96,6 +96,15 @@ export interface DeviceItemProps {
96
96
  readonly children?: readonly DeviceItemDevice[];
97
97
  readonly onNavigate?: (deviceId: number) => void;
98
98
  readonly className?: string;
99
+ /**
100
+ * Optional trailing per-row control rendered alongside the built-in
101
+ * `<DeviceItemActions>` (both card + table views). Lets a caller
102
+ * inject a context-specific affordance — e.g. the device-export
103
+ * panel's per-row Expose/Exposed toggle — without the composite
104
+ * knowing anything about that domain. Omitted ⇒ no extra control,
105
+ * so every existing call site renders unchanged.
106
+ */
107
+ readonly renderRowAction?: (device: DeviceItemDevice) => ReactNode;
99
108
  }
100
109
  export interface VariantDefaults {
101
110
  readonly showIntegrationIcon: boolean;
@@ -1,19 +1,21 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { DeviceListView } from './url-state';
3
+ export interface FilterChipsAddonOption {
4
+ readonly id: string;
5
+ readonly name: string;
6
+ readonly icon?: ReactNode;
7
+ }
3
8
  export interface FilterChipsProps {
4
9
  readonly view: DeviceListView;
5
10
  readonly onViewChange: (view: DeviceListView) => void;
6
11
  readonly types?: readonly string[];
7
12
  readonly currentType: string | null;
8
13
  readonly onTypeChange: (type: string | null) => void;
9
- readonly addons?: readonly {
10
- readonly id: string;
11
- readonly name: string;
12
- readonly icon?: ReactNode;
13
- }[];
14
- readonly currentAddon: string | null;
15
- readonly onAddonChange: (addon: string | null) => void;
14
+ readonly addons?: readonly FilterChipsAddonOption[];
15
+ /** Currently-selected integration ids (multi-select). */
16
+ readonly selectedAddons: readonly string[];
17
+ readonly onAddonsChange: (addons: readonly string[]) => void;
16
18
  /** When true, the addon group is hidden (used by Integrations expand-row inner DeviceList). */
17
19
  readonly hideAddons?: boolean;
18
20
  }
19
- export declare function FilterChips({ view, onViewChange, types, currentType, onTypeChange, addons, currentAddon, onAddonChange, hideAddons, }: FilterChipsProps): ReactNode;
21
+ export declare function FilterChips({ view, onViewChange, types, currentType, onTypeChange, addons, selectedAddons, onAddonsChange, hideAddons, }: FilterChipsProps): ReactNode;
@@ -27,5 +27,14 @@ export interface DeviceListProps {
27
27
  readonly urlScope?: 'root' | 'nested';
28
28
  /** LocalStorage key prefix. Default 'devices' for root scope, 'integrations:nested' for nested. */
29
29
  readonly lsKey?: string;
30
+ /**
31
+ * Optional trailing per-row control rendered beside each row's
32
+ * built-in actions (both card + table views, top-level rows only —
33
+ * accessory sub-rows are excluded). Threaded straight through to
34
+ * `<DeviceItem renderRowAction>`. Omitted ⇒ every current call site
35
+ * (Devices, IntegrationDetail, Integrations nested) renders unchanged.
36
+ * Used by `<DeviceExportPanel>` to surface a per-device Expose toggle.
37
+ */
38
+ readonly renderRowAction?: (device: DeviceItemDevice) => ReactNode;
30
39
  }
31
40
  export declare function DeviceList(props: DeviceListProps): ReactNode;
@@ -2,7 +2,12 @@ export type DeviceListView = 'cards' | 'table';
2
2
  export interface DeviceListUrlState {
3
3
  readonly view: DeviceListView;
4
4
  readonly page: number;
5
- readonly addon: string | null;
5
+ /**
6
+ * Selected integration ids. Multi-select: empty array = no integration
7
+ * filter (show all). Serialised in the URL / localStorage as a single
8
+ * comma-joined `addon` value so existing single-value links keep working.
9
+ */
10
+ readonly addons: readonly string[];
6
11
  readonly type: string | null;
7
12
  readonly q: string;
8
13
  }
@@ -16,7 +21,7 @@ export interface UseDeviceListUrlStateResult {
16
21
  readonly state: DeviceListUrlState;
17
22
  readonly setView: (view: DeviceListView) => void;
18
23
  readonly setPage: (page: number) => void;
19
- readonly setAddon: (addon: string | null) => void;
24
+ readonly setAddons: (addons: readonly string[]) => void;
20
25
  readonly setType: (type: string | null) => void;
21
26
  readonly setQ: (q: string) => void;
22
27
  readonly clearFilters: () => void;
@@ -108,5 +108,5 @@ export { WidgetSlot } from './widget-slot';
108
108
  export type { WidgetSlotProps } from './widget-slot';
109
109
  export { ScopePicker, validateScopes } from './scope-picker';
110
110
  export type { ScopeAccess } from './scope-picker';
111
- export { PtzPanel, AutotrackSection, MotionZonesSettings } from './cap-settings';
112
- export type { CapSettingsComponentProps } from './cap-settings';
111
+ export { PtzPanel, AutotrackSection, MotionZonesSettings, MaskShapeCanvas } from './cap-settings';
112
+ export type { CapSettingsComponentProps, MaskShapeItem, MaskShapeCanvasProps } from './cap-settings';
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
- import { SignalingResult, ClientStreamHints } from './camera-stream-player';
2
+ import { SignalingResult, ClientOfferResult, ClientStreamHints } from './camera-stream-player';
3
3
  import { DeviceDetections } from '../hooks/use-device-detections';
4
4
  /**
5
5
  * Discriminated WebRTC target — same shape as the backend cap. Kept
@@ -45,6 +45,22 @@ export interface StreamPanelProps {
45
45
  readonly serverUrl: string;
46
46
  readonly createSession: (target: StreamChoiceTarget, hints?: ClientStreamHints) => Promise<SignalingResult>;
47
47
  readonly sendAnswer: (sessionId: string, sdpAnswer: string) => Promise<void>;
48
+ /**
49
+ * Client-offer signaling — PREFERRED over `createSession` for browsers.
50
+ * Closes over the selected target; the player builds the offer and this
51
+ * posts it. Lets iOS pass H.264 High/Main (incl. 4K) through untouched.
52
+ * Optional so non-browser callers (benchmark) can stay on server-offer.
53
+ */
54
+ readonly handleOffer?: (target: StreamChoiceTarget, sdpOffer: string) => Promise<ClientOfferResult>;
55
+ /** Browser-side ICE servers (STUN + TURN) for client-offer mode. */
56
+ readonly getIceServers?: () => Promise<readonly RTCIceServer[] | undefined>;
57
+ /** Trickle ICE — push a client candidate to the server (sessionId-scoped). */
58
+ readonly addIceCandidate?: (sessionId: string, candidate: RTCIceCandidateInit) => Promise<void>;
59
+ /** Trickle ICE — poll the server's gathered candidates (sessionId-scoped). */
60
+ readonly getIceCandidates?: (sessionId: string) => Promise<{
61
+ candidates: RTCIceCandidateInit[];
62
+ done: boolean;
63
+ }>;
48
64
  readonly closeSession?: (sessionId: string) => Promise<void>;
49
65
  /** Available streams. If omitted or single entry, no dropdown shown. */
50
66
  readonly streams?: readonly StreamChoice[];
@@ -140,4 +156,4 @@ export interface StreamPanelProps {
140
156
  readonly chromeless?: boolean;
141
157
  readonly className?: string;
142
158
  }
143
- export declare function StreamPanel({ serverUrl, createSession, sendAnswer, closeSession, streams, activeStreamId: controlledStreamId, onStreamChange, deviceName, phase, pipelineMetrics, detections, defaultShowDetections, defaultShowMotion, streamStats, autoPlay, showPlayStop, snapshotSrc, snapshotLoading, onRefreshSnapshot, showStreamStats, children, extraOverlay, ptzAvailable, ptzShown, ptzOverlay, onPtzToggle, intercomAvailable, intercomShown, onIntercomToggle, chromeless, className, }: StreamPanelProps): import("react/jsx-runtime").JSX.Element;
159
+ export declare function StreamPanel({ serverUrl, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, streams, activeStreamId: controlledStreamId, onStreamChange, deviceName, phase, pipelineMetrics, detections, defaultShowDetections, defaultShowMotion, streamStats, autoPlay, showPlayStop, snapshotSrc, snapshotLoading, onRefreshSnapshot, showStreamStats, children, extraOverlay, ptzAvailable, ptzShown, ptzOverlay, onPtzToggle, intercomAvailable, intercomShown, onIntercomToggle, chromeless, className, }: StreamPanelProps): import("react/jsx-runtime").JSX.Element;
@@ -43,6 +43,14 @@ export declare const useAddonsListCapabilityProviders: typeof trpc.addons.listCa
43
43
  export declare const useAddonsSetCapabilityProviderEnabled: typeof trpc.addons.setCapabilityProviderEnabled.useMutation;
44
44
  /** Generated alias around `trpc.addons.updateFrameworkPackage.useMutation`. */
45
45
  export declare const useAddonsUpdateFrameworkPackage: typeof trpc.addons.updateFrameworkPackage.useMutation;
46
+ /** Generated alias around `trpc.addons.startBulkUpdate.useMutation`. */
47
+ export declare const useAddonsStartBulkUpdate: typeof trpc.addons.startBulkUpdate.useMutation;
48
+ /** Generated alias around `trpc.addons.getBulkUpdateState.useQuery`. */
49
+ export declare const useAddonsGetBulkUpdateState: typeof trpc.addons.getBulkUpdateState.useQuery;
50
+ /** Generated alias around `trpc.addons.cancelBulkUpdate.useMutation`. */
51
+ export declare const useAddonsCancelBulkUpdate: typeof trpc.addons.cancelBulkUpdate.useMutation;
52
+ /** Generated alias around `trpc.addons.listActiveBulkUpdates.useQuery`. */
53
+ export declare const useAddonsListActiveBulkUpdates: typeof trpc.addons.listActiveBulkUpdates.useQuery;
46
54
  /** Generated alias around `trpc.addons.getVersions.useQuery`. */
47
55
  export declare const useAddonsGetVersions: typeof trpc.addons.getVersions.useQuery;
48
56
  /** Generated alias around `trpc.addons.restartAddon.useMutation`. */
@@ -161,6 +169,12 @@ export declare const useBrightnessGetStatus: typeof trpc.brightness.getStatus.us
161
169
  export declare const useCameraCredentialsGetCredentials: typeof trpc.cameraCredentials.getCredentials.useQuery;
162
170
  /** Generated alias around `trpc.cameraCredentials.getStatus.useQuery`. */
163
171
  export declare const useCameraCredentialsGetStatus: typeof trpc.cameraCredentials.getStatus.useQuery;
172
+ /** Generated alias around `trpc.cameraPipelineConfig.getDeviceSettingsContribution.useQuery`. */
173
+ export declare const useCameraPipelineConfigGetDeviceSettingsContribution: typeof trpc.cameraPipelineConfig.getDeviceSettingsContribution.useQuery;
174
+ /** Generated alias around `trpc.cameraPipelineConfig.getDeviceLiveContribution.useQuery`. */
175
+ export declare const useCameraPipelineConfigGetDeviceLiveContribution: typeof trpc.cameraPipelineConfig.getDeviceLiveContribution.useQuery;
176
+ /** Generated alias around `trpc.cameraPipelineConfig.applyDeviceSettingsPatch.useMutation`. */
177
+ export declare const useCameraPipelineConfigApplyDeviceSettingsPatch: typeof trpc.cameraPipelineConfig.applyDeviceSettingsPatch.useMutation;
164
178
  /** Generated alias around `trpc.cameraStreams.getCameraStreams.useQuery`. */
165
179
  export declare const useCameraStreamsGetCameraStreams: typeof trpc.cameraStreams.getCameraStreams.useQuery;
166
180
  /** Generated alias around `trpc.cameraStreams.getBrokerStreams.useQuery`. */
@@ -493,6 +507,8 @@ export declare const useMqttBrokerStartEmbeddedBroker: typeof trpc.mqttBroker.st
493
507
  export declare const useMqttBrokerStopEmbeddedBroker: typeof trpc.mqttBroker.stopEmbeddedBroker.useMutation;
494
508
  /** Generated alias around `trpc.mqttBroker.getStatus.useQuery`. */
495
509
  export declare const useMqttBrokerGetStatus: typeof trpc.mqttBroker.getStatus.useQuery;
510
+ /** Generated alias around `trpc.nativeObjectDetection.setEnabled.useMutation`. */
511
+ export declare const useNativeObjectDetectionSetEnabled: typeof trpc.nativeObjectDetection.setEnabled.useMutation;
496
512
  /** Generated alias around `trpc.nativeObjectDetection.getStatus.useQuery`. */
497
513
  export declare const useNativeObjectDetectionGetStatus: typeof trpc.nativeObjectDetection.getStatus.useQuery;
498
514
  /** Generated alias around `trpc.networkAccess.start.useMutation`. */
@@ -739,6 +755,12 @@ export declare const usePlatformProbeResolveHwAccel: typeof trpc.platformProbe.r
739
755
  export declare const usePlatformProbeGetHardwareEncoders: typeof trpc.platformProbe.getHardwareEncoders.useQuery;
740
756
  /** Generated alias around `trpc.platformProbe.refreshHardwareEncoders.useMutation`. */
741
757
  export declare const usePlatformProbeRefreshHardwareEncoders: typeof trpc.platformProbe.refreshHardwareEncoders.useMutation;
758
+ /** Generated alias around `trpc.privacyMask.getOptions.useQuery`. */
759
+ export declare const usePrivacyMaskGetOptions: typeof trpc.privacyMask.getOptions.useQuery;
760
+ /** Generated alias around `trpc.privacyMask.setMask.useMutation`. */
761
+ export declare const usePrivacyMaskSetMask: typeof trpc.privacyMask.setMask.useMutation;
762
+ /** Generated alias around `trpc.privacyMask.getStatus.useQuery`. */
763
+ export declare const usePrivacyMaskGetStatus: typeof trpc.privacyMask.getStatus.useQuery;
742
764
  /** Generated alias around `trpc.ptz.move.useMutation`. */
743
765
  export declare const usePtzMove: typeof trpc.ptz.move.useMutation;
744
766
  /** Generated alias around `trpc.ptz.continuousMove.useMutation`. */
@@ -951,6 +973,8 @@ export declare const useStreamBrokerGetDeviceSettingsContribution: typeof trpc.s
951
973
  export declare const useStreamBrokerGetDeviceLiveContribution: typeof trpc.streamBroker.getDeviceLiveContribution.useQuery;
952
974
  /** Generated alias around `trpc.streamBroker.applyDeviceSettingsPatch.useMutation`. */
953
975
  export declare const useStreamBrokerApplyDeviceSettingsPatch: typeof trpc.streamBroker.applyDeviceSettingsPatch.useMutation;
976
+ /** Generated alias around `trpc.streamCatalog.getCatalog.useQuery`. */
977
+ export declare const useStreamCatalogGetCatalog: typeof trpc.streamCatalog.getCatalog.useQuery;
954
978
  /** Generated alias around `trpc.streamParams.getOptions.useQuery`. */
955
979
  export declare const useStreamParamsGetOptions: typeof trpc.streamParams.getOptions.useQuery;
956
980
  /** Generated alias around `trpc.streamParams.setProfile.useMutation`. */
@@ -1041,6 +1065,10 @@ export declare const useWebrtcSessionCreateSession: typeof trpc.webrtcSession.cr
1041
1065
  export declare const useWebrtcSessionHandleOffer: typeof trpc.webrtcSession.handleOffer.useMutation;
1042
1066
  /** Generated alias around `trpc.webrtcSession.handleAnswer.useMutation`. */
1043
1067
  export declare const useWebrtcSessionHandleAnswer: typeof trpc.webrtcSession.handleAnswer.useMutation;
1068
+ /** Generated alias around `trpc.webrtcSession.addIceCandidate.useMutation`. */
1069
+ export declare const useWebrtcSessionAddIceCandidate: typeof trpc.webrtcSession.addIceCandidate.useMutation;
1070
+ /** Generated alias around `trpc.webrtcSession.getIceCandidates.useQuery`. */
1071
+ export declare const useWebrtcSessionGetIceCandidates: typeof trpc.webrtcSession.getIceCandidates.useQuery;
1044
1072
  /** Generated alias around `trpc.webrtcSession.closeSession.useMutation`. */
1045
1073
  export declare const useWebrtcSessionCloseSession: typeof trpc.webrtcSession.closeSession.useMutation;
1046
1074
  /** Generated alias around `trpc.webrtcSession.hasAdaptiveBitrate.useQuery`. */
@@ -1,5 +1,5 @@
1
1
  import { StreamChoice } from '../composites/stream-panel';
2
- import { ClientStreamHints, SignalingResult } from '../composites/camera-stream-player';
2
+ import { ClientStreamHints, SignalingResult, ClientOfferResult } from '../composites/camera-stream-player';
3
3
  /**
4
4
  * Discriminated WebRTC target — same shape as `WebrtcStreamTarget` on
5
5
  * the backend cap. Repeated locally to keep the hook self-contained
@@ -54,11 +54,44 @@ export interface UseDeviceWebrtcTrpc {
54
54
  sessionId: string;
55
55
  sdpOffer: string;
56
56
  }>;
57
+ /**
58
+ * Client-offer signaling: the browser builds the SDP offer (advertising
59
+ * its native decode caps) and the server answers. The dual of
60
+ * `createSession`; lets iOS pass H.264 High/Main through untouched.
61
+ */
62
+ handleOffer: MutateFn<{
63
+ deviceId: number;
64
+ target: WebrtcTarget;
65
+ sdpOffer: string;
66
+ }, {
67
+ sessionId: string;
68
+ sdpAnswer: string;
69
+ }>;
57
70
  handleAnswer: MutateFn<{
58
71
  deviceId: number;
59
72
  sessionId: string;
60
73
  sdpAnswer: string;
61
74
  }, void>;
75
+ /** Trickle ICE — push a client candidate to the server. */
76
+ addIceCandidate: MutateFn<{
77
+ deviceId: number;
78
+ sessionId: string;
79
+ candidate: string;
80
+ sdpMid?: string | null;
81
+ sdpMLineIndex?: number | null;
82
+ }, void>;
83
+ /** Trickle ICE — poll the server's gathered candidates. */
84
+ getIceCandidates: QueryFn<{
85
+ deviceId: number;
86
+ sessionId: string;
87
+ }, {
88
+ candidates: {
89
+ candidate: string;
90
+ sdpMid: string | null;
91
+ sdpMLineIndex: number | null;
92
+ }[];
93
+ done: boolean;
94
+ }>;
62
95
  closeSession: MutateFn<{
63
96
  deviceId: number;
64
97
  sessionId: string;
@@ -75,6 +108,16 @@ export interface UseDeviceWebrtcTrpc {
75
108
  configuredFps: number;
76
109
  } | null>;
77
110
  };
111
+ /** Optional: aggregated STUN/TURN servers (e.g. cloudflare-turn). When
112
+ * present, the client uses them so it can gather a reachable relay
113
+ * candidate for remote (CGNAT/mobile) viewing. */
114
+ turnProvider?: {
115
+ getTurnServers: QueryFn<void, readonly {
116
+ urls: string | readonly string[];
117
+ username?: string;
118
+ credential?: string;
119
+ }[]>;
120
+ };
78
121
  }
79
122
  /** Pipeline detection metrics for a device. */
80
123
  export interface DevicePipelineMetrics {
@@ -94,6 +137,28 @@ export interface DeviceWebrtcResult {
94
137
  readonly createSession: (target: WebrtcTarget, hints?: ClientStreamHints) => Promise<SignalingResult>;
95
138
  /** Server-offer signaling: send the client's SDP answer. */
96
139
  readonly sendAnswer: (sessionId: string, sdpAnswer: string) => Promise<void>;
140
+ /**
141
+ * Client-offer signaling (preferred for browsers): post a browser-built
142
+ * SDP offer and get the server's answer. The browser advertises its full
143
+ * H.264 decode caps in the offer, so a Main/High (incl. 4K) source passes
144
+ * through with no transcode — the only path that lets iOS decode High.
145
+ */
146
+ readonly handleOffer: (target: WebrtcTarget, sdpOffer: string) => Promise<ClientOfferResult>;
147
+ /**
148
+ * Fetch the browser-side ICE servers (STUN + TURN from the `turn-provider`
149
+ * cap). Needed for client-offer mode, where the PeerConnection must be
150
+ * built with TURN BEFORE the offer is created so a remote/CGNAT viewer can
151
+ * gather a relay candidate. Best-effort — resolves `undefined` on failure
152
+ * (the player falls back to public STUN).
153
+ */
154
+ readonly getIceServers: () => Promise<readonly RTCIceServer[] | undefined>;
155
+ /** Trickle ICE — push a client-gathered candidate to the server. */
156
+ readonly addIceCandidate: (sessionId: string, candidate: RTCIceCandidateInit) => Promise<void>;
157
+ /** Trickle ICE — poll the server's gathered candidates for a session. */
158
+ readonly getIceCandidates: (sessionId: string) => Promise<{
159
+ candidates: RTCIceCandidateInit[];
160
+ done: boolean;
161
+ }>;
97
162
  /** Close a WebRTC session on the server. */
98
163
  readonly closeSession: (sessionId: string) => Promise<void>;
99
164
  }