@ksp-gonogo/kerbcast-react 1.6.3 → 1.7.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.
- package/CHANGELOG.md +41 -0
- package/README.md +19 -0
- package/dist/CameraFeed.d.ts +31 -3
- package/dist/CameraFeed.js +86 -49
- package/dist/KerbalFaceFeed.d.ts +82 -0
- package/dist/KerbalFaceFeed.js +118 -0
- package/dist/context.d.ts +27 -1
- package/dist/context.js +68 -2
- package/dist/hooks/useReportDisplaySize.d.ts +23 -0
- package/dist/hooks/useReportDisplaySize.js +71 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/package.json +2 -2
- package/src/CameraFeed.test.tsx +176 -12
- package/src/CameraFeed.tsx +196 -56
- package/src/KerbalFaceFeed.test.tsx +203 -0
- package/src/KerbalFaceFeed.tsx +236 -0
- package/src/context.test.tsx +94 -0
- package/src/context.tsx +98 -1
- package/src/hooks/useReportDisplaySize.test.tsx +166 -0
- package/src/hooks/useReportDisplaySize.ts +91 -0
- package/src/index.ts +12 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { useEffect, useId } from "react";
|
|
2
|
+
import { useKerbcastDisplaySizes } from "../context";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Debounce + bucket for self-measured display-size reporting. The first
|
|
6
|
+
* measurement after mount reports promptly (so a feed never sits at a stale
|
|
7
|
+
* resolution until it is resized); later churn is debounced. Sizes are ceiled
|
|
8
|
+
* to a BUCKET_PX multiple: that meets-the-minimum-need (never under-serve),
|
|
9
|
+
* yields an even value (H.264-friendly), and gives a material-change threshold
|
|
10
|
+
* so a feed re-reports only when a resize crosses a bucket boundary.
|
|
11
|
+
*/
|
|
12
|
+
const DEBOUNCE_MS = 200;
|
|
13
|
+
const BUCKET_PX = 16;
|
|
14
|
+
|
|
15
|
+
function bucket(px: number): number {
|
|
16
|
+
const b = Math.ceil(px / BUCKET_PX) * BUCKET_PX;
|
|
17
|
+
return b < BUCKET_PX ? BUCKET_PX : b;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ReportDisplaySizeOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Report while mounted. Default true. Set false for a fixed-resolution feed
|
|
23
|
+
* that should not drive auto-resolution. This is a RESOLUTION concern only,
|
|
24
|
+
* independent of whether the feed shows its action UI.
|
|
25
|
+
*/
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Report a square (side x side, the min edge) rather than the raw w x h.
|
|
29
|
+
* Face cams are square; part cams are not.
|
|
30
|
+
*/
|
|
31
|
+
square?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Self-measure a feed element and drive the client's per-consumer
|
|
36
|
+
* `reportDisplaySize` (via the context display-size registry, which collapses
|
|
37
|
+
* multiple feeds of the same camera to one MAX report). Attach `ref` to the
|
|
38
|
+
* element whose rendered pixel box is the display size. Reporting follows the
|
|
39
|
+
* mount lifecycle: on unmount / `flightId` change / `enabled` flip it
|
|
40
|
+
* deregisters, and the sidecar's clear-on-departure (unsubscribe) does the
|
|
41
|
+
* server-side clear.
|
|
42
|
+
*/
|
|
43
|
+
export function useReportDisplaySize(
|
|
44
|
+
flightId: number | null,
|
|
45
|
+
ref: React.RefObject<Element | null>,
|
|
46
|
+
{ enabled = true, square = false }: ReportDisplaySizeOptions = {},
|
|
47
|
+
): void {
|
|
48
|
+
const displaySizes = useKerbcastDisplaySizes();
|
|
49
|
+
const instanceId = useId();
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (flightId === null || !enabled) return;
|
|
53
|
+
const el = ref.current;
|
|
54
|
+
if (!el) return;
|
|
55
|
+
|
|
56
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
57
|
+
let lastW = -1;
|
|
58
|
+
let lastH = -1;
|
|
59
|
+
let firstReported = false;
|
|
60
|
+
|
|
61
|
+
const report = (width: number, height: number) => {
|
|
62
|
+
const side = square ? Math.min(width, height) : 0;
|
|
63
|
+
const bw = bucket(square ? side : width);
|
|
64
|
+
const bh = bucket(square ? side : height);
|
|
65
|
+
if (bw === lastW && bh === lastH) return;
|
|
66
|
+
lastW = bw;
|
|
67
|
+
lastH = bh;
|
|
68
|
+
displaySizes.report(flightId, instanceId, bw, bh);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const observer = new ResizeObserver((entries) => {
|
|
72
|
+
const entry = entries[0];
|
|
73
|
+
if (!entry) return;
|
|
74
|
+
const { width, height } = entry.contentRect;
|
|
75
|
+
if (!firstReported) {
|
|
76
|
+
firstReported = true;
|
|
77
|
+
report(width, height);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (timer !== null) clearTimeout(timer);
|
|
81
|
+
timer = setTimeout(() => report(width, height), DEBOUNCE_MS);
|
|
82
|
+
});
|
|
83
|
+
observer.observe(el);
|
|
84
|
+
|
|
85
|
+
return () => {
|
|
86
|
+
observer.disconnect();
|
|
87
|
+
if (timer !== null) clearTimeout(timer);
|
|
88
|
+
displaySizes.clear(flightId, instanceId);
|
|
89
|
+
};
|
|
90
|
+
}, [displaySizes, flightId, ref, enabled, square, instanceId]);
|
|
91
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,16 +10,23 @@
|
|
|
10
10
|
export {
|
|
11
11
|
KerbcastProvider,
|
|
12
12
|
createClientSubscriptions,
|
|
13
|
+
createClientDisplaySizes,
|
|
13
14
|
useKerbcastClient,
|
|
14
15
|
useKerbcastSubscriptions,
|
|
16
|
+
useKerbcastDisplaySizes,
|
|
17
|
+
} from "./context";
|
|
18
|
+
export type {
|
|
19
|
+
KerbcastProviderProps,
|
|
20
|
+
KerbcastSubscriptions,
|
|
21
|
+
KerbcastDisplaySizes,
|
|
15
22
|
} from "./context";
|
|
16
|
-
export type { KerbcastProviderProps, KerbcastSubscriptions } from "./context";
|
|
17
23
|
|
|
18
24
|
// Hooks
|
|
19
25
|
export { useKerbcastCameras } from "./hooks/useKerbcastCameras";
|
|
20
26
|
export { useKerbcastStream } from "./hooks/useKerbcastStream";
|
|
21
27
|
export { useKerbcastClock } from "./hooks/useKerbcastClock";
|
|
22
28
|
export { useKerbcastInFlight } from "./hooks/useKerbcastInFlight";
|
|
29
|
+
export { useReportDisplaySize } from "./hooks/useReportDisplaySize";
|
|
23
30
|
|
|
24
31
|
// Shared standby glyph (used by CameraFeed + downstream dashboards)
|
|
25
32
|
export { StandbyIcon } from "./StandbyIcon";
|
|
@@ -40,3 +47,7 @@ export type {
|
|
|
40
47
|
CameraStreamHook,
|
|
41
48
|
FeedAction,
|
|
42
49
|
} from "./CameraFeed";
|
|
50
|
+
|
|
51
|
+
// KerbalFaceFeed — shared single-kerbal-face primitive
|
|
52
|
+
export { KerbalFaceFeed } from "./KerbalFaceFeed";
|
|
53
|
+
export type { KerbalFaceFeedProps, KerbalFeedState } from "./KerbalFaceFeed";
|