@ksp-gonogo/kerbcast-react 1.10.2 → 1.11.0-rc.4
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/dist/CameraFeed.d.ts +21 -0
- package/dist/CameraFeed.js +106 -5
- package/dist/CommandBrackets.d.ts +110 -0
- package/dist/CommandBrackets.js +167 -0
- package/dist/hooks/useCommandDisplayUt.d.ts +22 -0
- package/dist/hooks/useCommandDisplayUt.js +104 -0
- package/dist/hooks/useDisplayedFrameUt.d.ts +11 -0
- package/dist/hooks/useDisplayedFrameUt.js +22 -0
- package/dist/hooks/useReportDisplaySize.d.ts +5 -0
- package/dist/hooks/useReportDisplaySize.js +18 -7
- package/dist/hooks/useTargetBracketsPreference.d.ts +6 -0
- package/dist/hooks/useTargetBracketsPreference.js +39 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +2 -2
- package/src/CameraFeed.test.tsx +291 -2
- package/src/CameraFeed.tsx +234 -4
- package/src/CommandBrackets.test.tsx +259 -0
- package/src/CommandBrackets.tsx +341 -0
- package/src/hooks/useCommandDisplayUt.test.tsx +156 -0
- package/src/hooks/useCommandDisplayUt.ts +135 -0
- package/src/hooks/useDisplayedFrameUt.ts +26 -0
- package/src/hooks/useReportDisplaySize.test.tsx +10 -4
- package/src/hooks/useReportDisplaySize.ts +18 -7
- package/src/hooks/useTargetBracketsPreference.ts +38 -0
- package/src/index.test.ts +2 -1
- package/src/index.ts +15 -0
|
@@ -4,10 +4,12 @@ import { useKerbcastDisplaySizes } from "../context";
|
|
|
4
4
|
/*
|
|
5
5
|
* Debounce + bucket for self-measured display-size reporting. The first
|
|
6
6
|
* measurement after mount reports promptly (so a feed never sits at a stale
|
|
7
|
-
* resolution until it is resized); later churn is debounced.
|
|
8
|
-
* to a BUCKET_PX multiple: that meets-the-minimum-need (never
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* resolution until it is resized); later churn is debounced. The width is
|
|
8
|
+
* ceiled to a BUCKET_PX multiple: that meets-the-minimum-need (never
|
|
9
|
+
* under-serve) and gives a material-change threshold so a feed re-reports
|
|
10
|
+
* only when a resize crosses a bucket boundary. The height follows from the
|
|
11
|
+
* box's own aspect, rounded to even for H.264, so the frame fills the box
|
|
12
|
+
* instead of letterboxing inside it.
|
|
11
13
|
*/
|
|
12
14
|
const DEBOUNCE_MS = 200;
|
|
13
15
|
const BUCKET_PX = 16;
|
|
@@ -17,6 +19,14 @@ function bucket(px: number): number {
|
|
|
17
19
|
return b < BUCKET_PX ? BUCKET_PX : b;
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
/** Report size for a `width` x `height` box: bucketed width, height at the box's aspect. */
|
|
23
|
+
export function displayReportSize(width: number, height: number): { width: number; height: number } {
|
|
24
|
+
const w = bucket(width);
|
|
25
|
+
if (!(width > 0) || !(height > 0)) return { width: w, height: bucket(height) };
|
|
26
|
+
const h = Math.max(2, Math.round((w * height) / width / 2) * 2);
|
|
27
|
+
return { width: w, height: h };
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
export interface ReportDisplaySizeOptions {
|
|
21
31
|
/**
|
|
22
32
|
* Report while mounted. Default true. Set false for a fixed-resolution feed
|
|
@@ -70,9 +80,10 @@ export function useReportDisplaySize(
|
|
|
70
80
|
let firstReported = false;
|
|
71
81
|
|
|
72
82
|
const report = (width: number, height: number) => {
|
|
73
|
-
const side =
|
|
74
|
-
const bw
|
|
75
|
-
|
|
83
|
+
const side = bucket(Math.min(width, height));
|
|
84
|
+
const { width: bw, height: bh } = square
|
|
85
|
+
? { width: side, height: side }
|
|
86
|
+
: displayReportSize(width, height);
|
|
76
87
|
if (bw === lastW && bh === lastH) return;
|
|
77
88
|
lastW = bw;
|
|
78
89
|
lastH = bh;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
const STORAGE_PREFIX = "kerbcast:target-brackets:";
|
|
4
|
+
|
|
5
|
+
function readPreference(key: string): boolean {
|
|
6
|
+
try {
|
|
7
|
+
return globalThis.localStorage?.getItem(STORAGE_PREFIX + key) !== "0";
|
|
8
|
+
} catch {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function writePreference(key: string, on: boolean): void {
|
|
14
|
+
try {
|
|
15
|
+
if (on) globalThis.localStorage?.removeItem(STORAGE_PREFIX + key);
|
|
16
|
+
else globalThis.localStorage?.setItem(STORAGE_PREFIX + key, "0");
|
|
17
|
+
} catch {
|
|
18
|
+
/* Storage blocked or full: the toggle still works for this session. */
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Whether target brackets are shown for one command camera, remembered per
|
|
24
|
+
* camera in this browser. Defaults on. `key` is null for a feed with no
|
|
25
|
+
* command camera, which reads as on and ignores toggles.
|
|
26
|
+
*/
|
|
27
|
+
export function useTargetBracketsPreference(key: string | null): [boolean, () => void] {
|
|
28
|
+
const [overrides, setOverrides] = useState<Record<string, boolean>>({});
|
|
29
|
+
const stored = useMemo(() => (key === null ? true : readPreference(key)), [key]);
|
|
30
|
+
const on = key !== null && key in overrides ? overrides[key]! : stored;
|
|
31
|
+
const toggle = useCallback(() => {
|
|
32
|
+
if (key === null) return;
|
|
33
|
+
const next = !on;
|
|
34
|
+
writePreference(key, next);
|
|
35
|
+
setOverrides((o) => ({ ...o, [key]: next }));
|
|
36
|
+
}, [key, on]);
|
|
37
|
+
return [on, toggle];
|
|
38
|
+
}
|
package/src/index.test.ts
CHANGED
|
@@ -3,8 +3,9 @@ import { describe, it, expect } from "vitest";
|
|
|
3
3
|
// Smoke test: the package module resolves and its shape is what consumers
|
|
4
4
|
// expect. Real component tests land with the CameraFeed extraction.
|
|
5
5
|
describe("@ksp-gonogo/kerbcast-react", () => {
|
|
6
|
+
/* A cold import transforms the whole package, which can exceed the default timeout on a busy machine. */
|
|
6
7
|
it("module resolves", async () => {
|
|
7
8
|
const mod = await import("./index");
|
|
8
9
|
expect(mod).toBeDefined();
|
|
9
|
-
});
|
|
10
|
+
}, 30_000);
|
|
10
11
|
});
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ export type {
|
|
|
25
25
|
export { useKerbcastCameras } from "./hooks/useKerbcastCameras";
|
|
26
26
|
export { useKerbcastStream } from "./hooks/useKerbcastStream";
|
|
27
27
|
export { useKerbcastClock } from "./hooks/useKerbcastClock";
|
|
28
|
+
export { useDisplayedFrameUt } from "./hooks/useDisplayedFrameUt";
|
|
28
29
|
export { useKerbcastInFlight } from "./hooks/useKerbcastInFlight";
|
|
29
30
|
export { useReportDisplaySize } from "./hooks/useReportDisplaySize";
|
|
30
31
|
export { useRecordings } from "./hooks/useRecordings";
|
|
@@ -53,6 +54,20 @@ export type {
|
|
|
53
54
|
FeedAction,
|
|
54
55
|
} from "./CameraFeed";
|
|
55
56
|
|
|
57
|
+
// Target brackets geometry and the overlay API for drawing on top of them
|
|
58
|
+
export { bracketGeometry, containedContentRect, COMMAND_BRACKET_COLOURS } from "./CommandBrackets";
|
|
59
|
+
export type {
|
|
60
|
+
BracketGeometry,
|
|
61
|
+
BracketPoint,
|
|
62
|
+
CommandBracketContext,
|
|
63
|
+
CommandBracketCorner,
|
|
64
|
+
CommandBracketCornerOverride,
|
|
65
|
+
CommandBracketCornerOverrides,
|
|
66
|
+
CommandBracketCornerStyle,
|
|
67
|
+
CommandBracketOverlay,
|
|
68
|
+
ContentRect,
|
|
69
|
+
} from "./CommandBrackets";
|
|
70
|
+
|
|
56
71
|
// FeedActionBar: the shared action row (tracking, fullscreen, PiP, quality,
|
|
57
72
|
// close, REC) both CameraFeed and KerbalFaceFeed render, with the issue-#6
|
|
58
73
|
// overflow (⋮) engine.
|