@q9labsai/chalk-react-native 4.0.0 → 4.1.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/dist/android.js +1 -1
- package/dist/{chunk-BXIZIHIO.js → chunk-7HVDPCPM.js} +42 -5
- package/dist/components/Chalk.d.ts +1 -0
- package/dist/components/Chalk.d.ts.map +1 -1
- package/dist/components/SpaceView.d.ts +1 -0
- package/dist/components/SpaceView.d.ts.map +1 -1
- package/dist/components/native-space-view/SpaceActionMenu.d.ts +2 -1
- package/dist/components/native-space-view/SpaceActionMenu.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/ios-pad.js +1 -1
- package/dist/ios-phone.js +1 -1
- package/dist/ios.js +1 -1
- package/dist/macos.js +1 -1
- package/dist/test-support/test-renderer.d.ts.map +1 -1
- package/embedded/chalk-whiteboard/asset-manifest.json +2 -2
- package/embedded/chalk-whiteboard/renderer.js +382 -382
- package/package.json +7 -7
- package/src/components/Chalk.test.tsx +11 -1
- package/src/components/Chalk.tsx +29 -3
- package/src/components/SpaceView.shared.test.tsx +2 -1
- package/src/components/SpaceView.shared.tsx +1 -1
- package/src/components/SpaceView.tsx +1 -0
- package/src/components/native-space-view/SpaceActionMenu.test.tsx +16 -0
- package/src/components/native-space-view/SpaceActionMenu.tsx +12 -1
- package/src/public-surface.test.ts +1 -0
- package/src/test-support/test-renderer.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@q9labsai/chalk-react-native",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -150,15 +150,15 @@
|
|
|
150
150
|
"registry": "https://registry.npmjs.org/"
|
|
151
151
|
},
|
|
152
152
|
"dependencies": {
|
|
153
|
-
"@q9labsai/chalk-client": "^4.
|
|
154
|
-
"@q9labsai/chalk-whiteboard": "^4.
|
|
155
|
-
"@q9labsai/facehash": "^4.
|
|
153
|
+
"@q9labsai/chalk-client": "^4.1.0",
|
|
154
|
+
"@q9labsai/chalk-whiteboard": "^4.1.0",
|
|
155
|
+
"@q9labsai/facehash": "^4.1.0"
|
|
156
156
|
},
|
|
157
157
|
"devDependencies": {
|
|
158
|
-
"@types/node": "^
|
|
159
|
-
"@types/react": "^19.
|
|
158
|
+
"@types/node": "^26.2.0",
|
|
159
|
+
"@types/react": "^19.2.18",
|
|
160
160
|
"react-native-webview": "13.16.1",
|
|
161
|
-
"typescript": "^
|
|
161
|
+
"typescript": "^6.0.3"
|
|
162
162
|
},
|
|
163
163
|
"peerDependencies": {
|
|
164
164
|
"@cloudflare/react-native-webrtc": "~125.0.1 || ~125.0.3",
|
|
@@ -13,6 +13,7 @@ const testingLibrary = createRequire(import.meta.url)(join(process.cwd(), "../re
|
|
|
13
13
|
const { act, cleanup, render } = testingLibrary;
|
|
14
14
|
|
|
15
15
|
const nativeClientFactory = vi.hoisted(() => vi.fn());
|
|
16
|
+
const spaceViewSpy = vi.hoisted(() => vi.fn(() => <div data-testid="space" />));
|
|
16
17
|
|
|
17
18
|
vi.mock("react-native", () => ({
|
|
18
19
|
StyleSheet: { create: <T,>(styles: T) => styles },
|
|
@@ -21,7 +22,7 @@ vi.mock("react-native", () => ({
|
|
|
21
22
|
Pressable: ({ children }: { readonly children?: ReactNode }) => children,
|
|
22
23
|
}));
|
|
23
24
|
vi.mock("../space-client/create-native-space-client", () => ({ createNativeSpaceClient: nativeClientFactory }));
|
|
24
|
-
vi.mock("./SpaceView", () => ({ SpaceView:
|
|
25
|
+
vi.mock("./SpaceView", () => ({ SpaceView: spaceViewSpy }));
|
|
25
26
|
vi.mock("./Entrance", () => ({ Entrance: () => <div data-testid="entrance" /> }));
|
|
26
27
|
|
|
27
28
|
import { Chalk } from "./Chalk";
|
|
@@ -29,6 +30,7 @@ import { Chalk } from "./Chalk";
|
|
|
29
30
|
afterEach(() => {
|
|
30
31
|
cleanup();
|
|
31
32
|
nativeClientFactory.mockReset();
|
|
33
|
+
spaceViewSpy.mockClear();
|
|
32
34
|
});
|
|
33
35
|
|
|
34
36
|
describe("Chalk", () => {
|
|
@@ -55,6 +57,14 @@ describe("Chalk", () => {
|
|
|
55
57
|
expect(onJoined).not.toHaveBeenCalled();
|
|
56
58
|
});
|
|
57
59
|
|
|
60
|
+
it("forwards the optional diagnostics action to the live Space", () => {
|
|
61
|
+
const client = createClient("live");
|
|
62
|
+
const onOpenDiagnostics = vi.fn();
|
|
63
|
+
render(<Chalk client={client.client} onOpenDiagnostics={onOpenDiagnostics} />);
|
|
64
|
+
|
|
65
|
+
expect(spaceViewSpy).toHaveBeenCalledWith(expect.objectContaining({ onOpenDiagnostics }), undefined);
|
|
66
|
+
});
|
|
67
|
+
|
|
58
68
|
it("forwards canonical client events and cleans their subscriptions up", () => {
|
|
59
69
|
const client = createClient("live");
|
|
60
70
|
const handlers = new Map<string, (event: unknown) => void>();
|
package/src/components/Chalk.tsx
CHANGED
|
@@ -64,6 +64,7 @@ type ChalkCommonProps = ChalkCallbacks & {
|
|
|
64
64
|
readonly inviteLink?: string;
|
|
65
65
|
readonly layout?: SpaceLayout;
|
|
66
66
|
readonly onLayoutChange?: (layout: SpaceLayout) => void;
|
|
67
|
+
readonly onOpenDiagnostics?: () => void;
|
|
67
68
|
};
|
|
68
69
|
|
|
69
70
|
export type ChalkProps = ChalkCommonProps &
|
|
@@ -264,7 +265,19 @@ function SpaceExperience(props: ChalkProps): React.JSX.Element {
|
|
|
264
265
|
</AppearanceLayer>
|
|
265
266
|
);
|
|
266
267
|
|
|
267
|
-
return
|
|
268
|
+
return (
|
|
269
|
+
<NativeSpaceView
|
|
270
|
+
features={props.features}
|
|
271
|
+
inviteLink={props.inviteLink}
|
|
272
|
+
layout={props.layout}
|
|
273
|
+
logoUrl={props.logoUrl}
|
|
274
|
+
onLayoutChange={props.onLayoutChange}
|
|
275
|
+
onOpenDiagnostics={props.onOpenDiagnostics}
|
|
276
|
+
pickChatFiles={props.pickChatFiles}
|
|
277
|
+
reconnecting={connection.status === "reconnecting"}
|
|
278
|
+
spaceName={spaceName}
|
|
279
|
+
/>
|
|
280
|
+
);
|
|
268
281
|
}
|
|
269
282
|
|
|
270
283
|
function defaultJoinOptions(props: ChalkProps): Parameters<SpaceClient["join"]>[0] {
|
|
@@ -282,18 +295,31 @@ type NativeSpaceViewProps = {
|
|
|
282
295
|
readonly layout?: SpaceLayout;
|
|
283
296
|
readonly logoUrl?: string;
|
|
284
297
|
readonly onLayoutChange?: (layout: SpaceLayout) => void;
|
|
298
|
+
readonly onOpenDiagnostics?: () => void;
|
|
285
299
|
readonly pickChatFiles?: () => Promise<readonly ChatUploadFile[]>;
|
|
286
300
|
readonly reconnecting: boolean;
|
|
287
301
|
readonly spaceName: string;
|
|
288
302
|
};
|
|
289
303
|
|
|
290
|
-
function NativeSpaceView({ features, inviteLink, layout, logoUrl, onLayoutChange, pickChatFiles, reconnecting, spaceName }: NativeSpaceViewProps): React.JSX.Element {
|
|
304
|
+
function NativeSpaceView({ features, inviteLink, layout, logoUrl, onLayoutChange, onOpenDiagnostics, pickChatFiles, reconnecting, spaceName }: NativeSpaceViewProps): React.JSX.Element {
|
|
291
305
|
const client = useSpaceClient();
|
|
292
306
|
const theme = useNativeTheme();
|
|
293
307
|
|
|
294
308
|
return (
|
|
295
309
|
<View style={[styles.space, { backgroundColor: theme.colors.background }]}>
|
|
296
|
-
<SpaceView
|
|
310
|
+
<SpaceView
|
|
311
|
+
features={features}
|
|
312
|
+
inviteLink={inviteLink}
|
|
313
|
+
layout={layout}
|
|
314
|
+
logoUrl={logoUrl}
|
|
315
|
+
onEndEpisode={() => client.endEpisode()}
|
|
316
|
+
onLayoutChange={onLayoutChange}
|
|
317
|
+
onLeave={() => client.leave()}
|
|
318
|
+
onOpenDiagnostics={onOpenDiagnostics}
|
|
319
|
+
pickChatFiles={pickChatFiles}
|
|
320
|
+
reconnecting={reconnecting}
|
|
321
|
+
spaceName={spaceName}
|
|
322
|
+
/>
|
|
297
323
|
<NativeTextureOverlay />
|
|
298
324
|
</View>
|
|
299
325
|
);
|
|
@@ -67,13 +67,14 @@ describe("SpaceViewShared", () => {
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
it("passes reconnecting state and capability-gated controls to the native surface", () => {
|
|
70
|
-
const props = { onLeave: vi.fn(), reconnecting: true, logoUrl: "https://cdn.example.test/logo.png", spaceName: "Design space" };
|
|
70
|
+
const props = { onLeave: vi.fn(), onOpenDiagnostics: vi.fn(), reconnecting: true, logoUrl: "https://cdn.example.test/logo.png", spaceName: "Design space" };
|
|
71
71
|
const tree = SpaceViewShared(props);
|
|
72
72
|
|
|
73
73
|
expect(state.useSpaceViewController).toHaveBeenCalledWith(props);
|
|
74
74
|
expect(findText(tree)).toContain("Reconnecting… controls will resume shortly.");
|
|
75
75
|
expect(findElement(tree, "SpaceTopBarAndroid")).toMatchObject({ props: { logoUrl: props.logoUrl, spaceName: props.spaceName } });
|
|
76
76
|
expect(findElement(tree, "SpaceBottomDockAndroid")).toMatchObject({ props: { onOpenChat: expect.any(Function), unreadChatCount: 0 } });
|
|
77
|
+
expect(findElement(tree, "SpaceActionMenu")).toMatchObject({ props: { onOpenDiagnostics: props.onOpenDiagnostics } });
|
|
77
78
|
});
|
|
78
79
|
|
|
79
80
|
it("renders the whiteboard surface instead of a stage or grid when it is open", () => {
|
|
@@ -72,7 +72,7 @@ export function SpaceViewShared(props: SpaceViewProps): React.JSX.Element {
|
|
|
72
72
|
simulatorMediaDisabled={controller.simulatorMediaDisabled}
|
|
73
73
|
unreadChatCount={controller.chat.unreadCount}
|
|
74
74
|
/>
|
|
75
|
-
<SpaceActionMenu controller={controller} />
|
|
75
|
+
<SpaceActionMenu controller={controller} onOpenDiagnostics={props.onOpenDiagnostics} />
|
|
76
76
|
{controller.panel === "settings" ? <SettingsSheet controller={controller} isOpen onClose={controller.closePanel} /> : <SpacePanelSheet controller={controller} />}
|
|
77
77
|
<ReactionPicker isOpen={controller.reactionPickerOpen} onClose={() => controller.setReactionPickerOpen(false)} onSelect={(reaction) => selectSpaceReaction(controller, reaction)} />
|
|
78
78
|
</View>
|
|
@@ -29,6 +29,7 @@ export interface SpaceViewProps {
|
|
|
29
29
|
features?: SpaceViewFeatures;
|
|
30
30
|
onLeave: () => void | Promise<void>;
|
|
31
31
|
onEndEpisode?: () => void | Promise<void>;
|
|
32
|
+
onOpenDiagnostics?: () => void;
|
|
32
33
|
onDiagnosticsChange?: (snapshot: SpaceViewDiagnosticsSnapshot) => void;
|
|
33
34
|
/** Selects files; the SDK owns bounds, upload, and send sequencing. */
|
|
34
35
|
pickChatFiles?: () => Promise<readonly ChatUploadFile[]>;
|
|
@@ -59,6 +59,22 @@ describe("SpaceActionMenu", () => {
|
|
|
59
59
|
expect(sendReaction).toHaveBeenCalledWith("🎉");
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
it("shows diagnostics only when its open handler is provided", () => {
|
|
63
|
+
const setActionsOpen = vi.fn();
|
|
64
|
+
const onOpenDiagnostics = vi.fn();
|
|
65
|
+
const controller = actionController({ setActionsOpen });
|
|
66
|
+
|
|
67
|
+
expect(findText(SpaceActionMenu({ controller }))).not.toContain("Diagnostics");
|
|
68
|
+
|
|
69
|
+
const tree = SpaceActionMenu({ controller, onOpenDiagnostics });
|
|
70
|
+
const diagnostics = findElements(tree, "Pressable").find((element) => findText(element).length === 1 && findText(element).includes("Diagnostics"));
|
|
71
|
+
const onPress = diagnostics?.props?.onPress;
|
|
72
|
+
if (typeof onPress === "function") onPress();
|
|
73
|
+
|
|
74
|
+
expect(setActionsOpen).toHaveBeenCalledWith(false);
|
|
75
|
+
expect(onOpenDiagnostics).toHaveBeenCalledTimes(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
62
78
|
it("leaves chat and settings panel ownership to canonical mounted sheets", () => {
|
|
63
79
|
expect(source).not.toContain("ChatPanel");
|
|
64
80
|
expect(source).not.toContain("<SpacePanel");
|
|
@@ -7,7 +7,7 @@ import type { useSpaceViewController } from "./useSpaceViewController";
|
|
|
7
7
|
|
|
8
8
|
type Controller = ReturnType<typeof useSpaceViewController>;
|
|
9
9
|
|
|
10
|
-
export function SpaceActionMenu({ controller }: { readonly controller: Controller }): React.JSX.Element {
|
|
10
|
+
export function SpaceActionMenu({ controller, onOpenDiagnostics }: { readonly controller: Controller; readonly onOpenDiagnostics?: () => void }): React.JSX.Element {
|
|
11
11
|
const theme = useNativeTheme();
|
|
12
12
|
const actions = [
|
|
13
13
|
...(controller.canInvite ? [{ label: "Invite participants", onPress: controller.handleInviteParticipants }] : []),
|
|
@@ -28,6 +28,17 @@ export function SpaceActionMenu({ controller }: { readonly controller: Controlle
|
|
|
28
28
|
]
|
|
29
29
|
: []),
|
|
30
30
|
...(controller.canSettings ? [{ label: "Settings", onPress: () => controller.openPanel("settings") }] : []),
|
|
31
|
+
...(onOpenDiagnostics
|
|
32
|
+
? [
|
|
33
|
+
{
|
|
34
|
+
label: "Diagnostics",
|
|
35
|
+
onPress: () => {
|
|
36
|
+
controller.setActionsOpen(false);
|
|
37
|
+
onOpenDiagnostics();
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
]
|
|
41
|
+
: []),
|
|
31
42
|
...(["grid", "focus", "presentation"] as const).map((layout) => ({
|
|
32
43
|
label: `${layout === "grid" ? "Grid" : layout === "focus" ? "Focus" : "Presentation"} layout${controller.layout.layout === layout ? " (selected)" : ""}`,
|
|
33
44
|
onPress: () => {
|
|
@@ -11,6 +11,7 @@ test("the turnkey and entrance props use the ratified names", () => {
|
|
|
11
11
|
readonly defaults?: { readonly microphone?: boolean; readonly camera?: boolean };
|
|
12
12
|
readonly features?: object;
|
|
13
13
|
readonly layout?: SpaceLayout;
|
|
14
|
+
readonly onOpenDiagnostics?: () => void;
|
|
14
15
|
}>();
|
|
15
16
|
expectTypeOf<EntranceProps>().toMatchTypeOf<{
|
|
16
17
|
readonly defaults?: { readonly microphone?: boolean; readonly camera?: boolean };
|