@q9labsai/chalk-react-native 5.2.2 → 5.2.3

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.
Files changed (35) hide show
  1. package/embedded/chalk-whiteboard/asset-manifest.json +2 -2
  2. package/embedded/chalk-whiteboard/renderer.js +473 -473
  3. package/package.json +4 -4
  4. package/src/avatar/FaceAvatar.tsx +2 -0
  5. package/src/components/EmbeddedWhiteboard.tsx +5 -0
  6. package/src/components/useAndroidCallService.ts +2 -0
  7. package/src/dock/Dock.tsx +1 -0
  8. package/src/dock/MoreSheet.tsx +1 -0
  9. package/src/dock/ReactionBurst.tsx +1 -0
  10. package/src/dock/audio-route.ts +1 -0
  11. package/src/frame/CallFrame.tsx +3 -0
  12. package/src/frame/Chalk.tsx +1 -0
  13. package/src/frame/back-handler.ts +1 -0
  14. package/src/frame/measurement.ts +1 -0
  15. package/src/frame/use-landscape-chrome.ts +1 -0
  16. package/src/header/Header.tsx +1 -0
  17. package/src/limbo/Limbo.tsx +2 -0
  18. package/src/limbo/Veil.tsx +1 -0
  19. package/src/motion/MovingFrame.tsx +1 -0
  20. package/src/primitives/BottomSheet.tsx +2 -0
  21. package/src/primitives/CopyButton.tsx +2 -0
  22. package/src/primitives/Menu.tsx +5 -0
  23. package/src/primitives/Switch.tsx +1 -0
  24. package/src/primitives/TileShell.tsx +1 -0
  25. package/src/sheets/people-chat/ChatNotices.tsx +1 -0
  26. package/src/sheets/people-chat/PeopleChatSheet.tsx +1 -0
  27. package/src/sheets/people-chat/PhotoViewer.tsx +1 -0
  28. package/src/sheets/people-chat/attachments.tsx +1 -0
  29. package/src/stage/ContentTile.tsx +1 -0
  30. package/src/stage/ParticipantTile.tsx +1 -0
  31. package/src/stage/Stage.tsx +3 -0
  32. package/src/stage/stage-view.tsx +1 -0
  33. package/src/status/CallOverlays.tsx +1 -0
  34. package/src/status/StatusPage.tsx +1 -0
  35. package/src/toasts/CallEventToasts.tsx +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@q9labsai/chalk-react-native",
3
- "version": "5.2.2",
3
+ "version": "5.2.3",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -106,9 +106,9 @@
106
106
  "registry": "https://registry.npmjs.org/"
107
107
  },
108
108
  "dependencies": {
109
- "@q9labsai/chalk-client": "^5.2.2",
110
- "@q9labsai/chalk-whiteboard": "^5.2.2",
111
- "@q9labsai/facehash": "^5.2.2",
109
+ "@q9labsai/chalk-client": "^5.2.3",
110
+ "@q9labsai/chalk-whiteboard": "^5.2.3",
111
+ "@q9labsai/facehash": "^5.2.3",
112
112
  "uqr": "^0.1.3"
113
113
  },
114
114
  "devDependencies": {
@@ -48,6 +48,7 @@ export const FaceAvatar = memo(function FaceAvatar({ name, size, style = "subtle
48
48
  const lookX = useSharedValue(0);
49
49
  const lookY = useSharedValue(0);
50
50
 
51
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native avatar fallback lifecycle.
51
52
  useEffect(() => {
52
53
  if (reduceMotion) {
53
54
  blink.value = 1;
@@ -60,6 +61,7 @@ export const FaceAvatar = memo(function FaceAvatar({ name, size, style = "subtle
60
61
  return () => cancelAnimation(blink);
61
62
  }, [blink, reduceMotion, scene]);
62
63
 
64
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native avatar fallback lifecycle.
63
65
  useEffect(() => {
64
66
  if (!lookAround || reduceMotion) return;
65
67
  const timer = setInterval(() => {
@@ -114,6 +114,7 @@ export function EmbeddedWhiteboard({
114
114
  await exportBoardImage(value);
115
115
  }, []);
116
116
 
117
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing embedded native board lifecycle and cleanup.
117
118
  useEffect(() => {
118
119
  let active = true;
119
120
  setLoadError(null);
@@ -132,6 +133,7 @@ export function EmbeddedWhiteboard({
132
133
  };
133
134
  }, [loadAttempt, onError, rendererURLOverride]);
134
135
 
136
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing embedded native board lifecycle and cleanup.
135
137
  useEffect(() => {
136
138
  setCompatibilityNotice(null);
137
139
  const controller = new ChalkWhiteboardController({
@@ -196,14 +198,17 @@ export function EmbeddedWhiteboard({
196
198
  };
197
199
  }, [clearNotices, toasts, handleControllerError, handleControllerMetric, handleControllerUserExport, journeyId, localParticipantColor, rendererPort, theme, traceparent, tracestate, transport]);
198
200
 
201
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing embedded native board lifecycle and cleanup.
199
202
  useEffect(() => {
200
203
  controllerRef.current?.setBoard(board);
201
204
  }, [board]);
202
205
 
206
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing embedded native board lifecycle and cleanup.
203
207
  useEffect(() => {
204
208
  controllerRef.current?.setCapabilities({ canDraw, canClear });
205
209
  }, [canClear, canDraw]);
206
210
 
211
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing embedded native board lifecycle and cleanup.
207
212
  useEffect(() => {
208
213
  if (viewport) controllerRef.current?.setViewport(viewport);
209
214
  }, [viewport]);
@@ -16,6 +16,7 @@ export function useAndroidCallService(): void {
16
16
  const microphoneOn = media.local.microphone.state === "enabled";
17
17
  const cameraOn = media.local.camera.state === "enabled";
18
18
 
19
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing Android call-service ownership and cleanup.
19
20
  useEffect(() => {
20
21
  if (!inCall) return;
21
22
  const start = () => {
@@ -30,6 +31,7 @@ export function useAndroidCallService(): void {
30
31
  return () => subscription.remove();
31
32
  }, [inCall, microphoneOn, cameraOn]);
32
33
 
34
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing Android call-service ownership and cleanup.
33
35
  useEffect(() => {
34
36
  if (!inCall) return;
35
37
  return () => {
package/src/dock/Dock.tsx CHANGED
@@ -67,6 +67,7 @@ function AudioOutputButton({ output, size, captioned }: { readonly output: Audio
67
67
  const ref = useRef<View>(null);
68
68
  const [open, setOpen] = useState(false);
69
69
  const devScenario = useDevScenario();
70
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native dock motion and event subscriptions.
70
71
  useEffect(() => {
71
72
  if (devScenario.sheet === "route" || devScenario.menu === "route") setOpen(true);
72
73
  }, [devScenario]);
@@ -164,6 +164,7 @@ function EmojiButton({ emoji, name, onPress, testID }: { readonly emoji: Reactio
164
164
  function CopyLinkRow({ link }: { readonly link: string }): React.JSX.Element {
165
165
  const [copied, setCopied] = useState(false);
166
166
  const reset = useRef<ReturnType<typeof setTimeout> | null>(null);
167
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native sheet focus lifecycle.
167
168
  useEffect(() => () => clearTimeout(reset.current ?? undefined), []);
168
169
  const copy = useCallback(async () => {
169
170
  await setStringAsync(link);
@@ -28,6 +28,7 @@ export const ReactionBurst = memo(function ReactionBurst({ participantId }: { re
28
28
  function FloatingEmoji({ reaction }: { readonly reaction: ActiveReaction }): React.JSX.Element {
29
29
  const reduceMotion = useReducedMotion();
30
30
  const progress = useSharedValue(0);
31
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing cancellable native reaction animation.
31
32
  useEffect(() => {
32
33
  progress.value = withTiming(1, { duration: DURATION, easing: easings.easeOut });
33
34
  }, [progress]);
@@ -44,6 +44,7 @@ export function useAudioOutput(): AudioOutput | null {
44
44
  const [state, setState] = useState<AudioRoutes | null>(null);
45
45
  // In the harness, picking a route moves the check like a real switch would.
46
46
  const [scenarioSelected, setScenarioSelected] = useState<string | null>(null);
47
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native audio-route subscription cleanup.
47
48
  useEffect(() => {
48
49
  if (!nativeModule || !emitter) return;
49
50
  let live = true;
@@ -76,6 +76,7 @@ export function CallFrame({ displayName, callName = "Call", inviteLink, onLeave,
76
76
  setLeftByChoice(false);
77
77
  client.join({ displayName, microphone: false, camera: false }).catch(() => undefined);
78
78
  }, [client, displayName]);
79
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native frame accessibility and layout lifecycle.
79
80
  useEffect(() => {
80
81
  if (client.getSnapshot().connection.status === "idle") join();
81
82
  }, [client, join]);
@@ -116,6 +117,7 @@ export function CallFrame({ displayName, callName = "Call", inviteLink, onLeave,
116
117
  }, [canEnd, exit]);
117
118
 
118
119
  const devScenario = useDevScenario();
120
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native frame accessibility and layout lifecycle.
119
121
  useEffect(() => {
120
122
  if (devScenario.sheet && devScenario.sheet !== "route") setSheet(devScenario.sheet);
121
123
  if (devScenario.menu === "leave") setLeaveMenuOpen(true);
@@ -125,6 +127,7 @@ export function CallFrame({ displayName, callName = "Call", inviteLink, onLeave,
125
127
  const boardVisible = useStageView().boardOpen ?? sharedBoard;
126
128
  const chrome = useLandscapeChrome({ fades: layout.arrangement === "landscape" && sheet === null && !layered, boardVisible });
127
129
  const chromeOpacity = useSharedValue(1);
130
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native frame accessibility and layout lifecycle.
128
131
  useEffect(() => {
129
132
  chromeOpacity.value = withTiming(chrome.hidden ? 0 : 1, timings.fade);
130
133
  }, [chrome.hidden, chromeOpacity]);
@@ -52,6 +52,7 @@ export type ChalkProps = {
52
52
  * off, and draws everything inside the safe area in portrait, landscape and on iPad.
53
53
  */
54
54
  export function Chalk({ client, colorScheme, onColorSchemeChange, soundsEnabled, onSoundsEnabledChange, ...frame }: ChalkProps): React.JSX.Element {
55
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native client ownership and event subscriptions unchanged.
55
56
  useEffect(() => releaseSounds, []);
56
57
  return (
57
58
  <GestureHandlerRootView style={styles.root}>
@@ -8,6 +8,7 @@ import { BackHandler } from "react-native";
8
8
  export function useBackHandler(active: boolean, onBack: () => void): void {
9
9
  const latest = useRef(onBack);
10
10
  latest.current = onBack;
11
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native hardware-back listener cleanup.
11
12
  useEffect(() => {
12
13
  if (!active) return;
13
14
  const subscription = BackHandler.addEventListener("hardwareBackPress", () => {
@@ -25,6 +25,7 @@ export function setMeasurementRegistry(next: MeasurementRegistry | null): void {
25
25
  /** A ref for the host view that carries `testID`; attach it to that view. */
26
26
  export function useMeasuredRef<TNode extends MeasurableNode>(testID: string | undefined, style: StyleProp<ViewStyle | TextStyle>): RefObject<TNode | null> {
27
27
  const ref = useRef<TNode>(null);
28
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native measurement registry lifetime.
28
29
  useEffect(() => {
29
30
  if (!registry || !testID) return;
30
31
  const declared = StyleSheet.flatten(style) ?? {};
@@ -6,6 +6,7 @@ export function useLandscapeChrome({ fades, boardVisible }: { readonly fades: bo
6
6
  const canHide = fades && !boardVisible;
7
7
  const [hidden, setHidden] = useState(false);
8
8
  const [shownAt, setShownAt] = useState(0);
9
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native landscape-chrome timer cleanup.
9
10
  useEffect(() => {
10
11
  if (!canHide) {
11
12
  setHidden(false);
@@ -68,6 +68,7 @@ function CallTimer(): React.JSX.Element | null {
68
68
  const { episode } = useConnection();
69
69
  const startedAt = episode?.startedAt ? Date.parse(episode.startedAt) : null;
70
70
  const [now, setNow] = useState(Date.now);
71
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native elapsed-call clock cleanup.
71
72
  useEffect(() => {
72
73
  if (startedAt === null) return;
73
74
  const timer = setInterval(() => setNow(Date.now()), 1000);
@@ -31,6 +31,7 @@ export function Limbo({ state, callName, recording }: LimboProps): React.JSX.Ele
31
31
  const actionRef = useRef<View>(null);
32
32
  const consent = recording && (state.kind === "joining" || state.kind === "knocking") ? <Consent /> : null;
33
33
  // The mockup moves focus to the card's button when a state with one appears.
34
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native accessibility focus and reduced-motion animation.
34
35
  useEffect(() => {
35
36
  const node = actionRef.current && findNodeHandle(actionRef.current);
36
37
  if (node) AccessibilityInfo.setAccessibilityFocus(node);
@@ -132,6 +133,7 @@ function Spinner(): React.JSX.Element {
132
133
  const { colors } = useTheme();
133
134
  const reduceMotion = useReducedMotion();
134
135
  const turn = useSharedValue(0);
136
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native accessibility focus and reduced-motion animation.
135
137
  useEffect(() => {
136
138
  if (reduceMotion) return;
137
139
  turn.value = withRepeat(withTiming(1, { duration: 900, easing: Easing.linear }), -1);
@@ -19,6 +19,7 @@ const LIFT_FROM = 0.6;
19
19
  export function Veil({ level }: { readonly level: VeilLevel }): React.JSX.Element {
20
20
  const { colors } = useTheme();
21
21
  const opacity = useSharedValue(OPACITY[level]);
22
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native veil transition lifecycle.
22
23
  useEffect(() => {
23
24
  if (level !== "none") {
24
25
  opacity.value = OPACITY[level];
@@ -45,6 +45,7 @@ export function MovingFrame({ frame, children, style, testID }: MovingFrameProps
45
45
  const mounted = useRef(false);
46
46
 
47
47
  const { x, y, width, height } = frame;
48
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native layout animation lifecycle.
48
49
  useEffect(() => {
49
50
  if (!mounted.current) {
50
51
  mounted.current = true;
@@ -94,6 +94,7 @@ export function BottomSheet({ open, onClose, accessibilityLabel, children, title
94
94
  }, [height, layout.height, offset, reduceMotion, shown]);
95
95
 
96
96
  const wasOpen = useRef(open);
97
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native sheet presentation and focus restoration.
97
98
  useEffect(() => {
98
99
  if (open === wasOpen.current) return;
99
100
  wasOpen.current = open;
@@ -103,6 +104,7 @@ export function BottomSheet({ open, onClose, accessibilityLabel, children, title
103
104
  }, [height, hide, open, show]);
104
105
 
105
106
  const wasMounted = useRef(mounted);
107
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native sheet presentation and focus restoration.
106
108
  useEffect(() => {
107
109
  const closed = wasMounted.current && !mounted;
108
110
  wasMounted.current = mounted;
@@ -25,9 +25,11 @@ export const CopyButton = memo(function CopyButton({ onCopy, accessibilityLabel,
25
25
  const done = useSharedValue(0);
26
26
  const reset = useRef<ReturnType<typeof setTimeout> | null>(null);
27
27
 
28
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native copy animation and feedback timer cleanup.
28
29
  useEffect(() => {
29
30
  done.value = withTiming(copied ? 1 : 0, timings.copyMorph);
30
31
  }, [copied, done]);
32
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native copy animation and feedback timer cleanup.
31
33
  useEffect(() => () => clearReset(reset), []);
32
34
 
33
35
  const copy = useCallback(async () => {
@@ -43,6 +43,7 @@ export function Menu({ open, onClose, anchorRef, accessibilityLabel, children, m
43
43
 
44
44
  useBackHandler(open, onClose);
45
45
 
46
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native menu focus, viewport, and keyboard cleanup.
46
47
  useEffect(() => {
47
48
  if (!open) {
48
49
  firstItem.current = null;
@@ -57,12 +58,14 @@ export function Menu({ open, onClose, anchorRef, accessibilityLabel, children, m
57
58
  // Like iOS menus, an open menu closes when the screen rotates or resizes, because its trigger moved.
58
59
  const viewport = `${layout.width}x${layout.height}`;
59
60
  const openedIn = useRef(viewport);
61
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native menu focus, viewport, and keyboard cleanup.
60
62
  useEffect(() => {
61
63
  if (!open) openedIn.current = viewport;
62
64
  else if (viewport !== openedIn.current) onClose();
63
65
  }, [onClose, open, viewport]);
64
66
 
65
67
  const wasOpen = useRef(open);
68
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native menu focus, viewport, and keyboard cleanup.
66
69
  useEffect(() => {
67
70
  const closed = wasOpen.current && !open;
68
71
  wasOpen.current = open;
@@ -81,6 +84,7 @@ export function Menu({ open, onClose, anchorRef, accessibilityLabel, children, m
81
84
  [keyboardHeight, layout, placement, reduceMotion, shown, trigger],
82
85
  );
83
86
 
87
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native menu focus, viewport, and keyboard cleanup.
84
88
  useEffect(() => {
85
89
  if (!placement || !firstItem.current) return;
86
90
  const node = findNodeHandle(firstItem.current);
@@ -190,6 +194,7 @@ export function MenuSeparator(): React.JSX.Element {
190
194
 
191
195
  function useKeyboardHeight(): number {
192
196
  const [height, setHeight] = useState(0);
197
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native menu focus, viewport, and keyboard cleanup.
193
198
  useEffect(() => {
194
199
  const show = Keyboard.addListener("keyboardDidShow", (event) => setHeight(event.endCoordinates.height));
195
200
  const hide = Keyboard.addListener("keyboardDidHide", () => setHeight(0));
@@ -16,6 +16,7 @@ export type SwitchProps = {
16
16
  export const Switch = memo(function Switch({ value, onValueChange, accessibilityLabel, testID }: SwitchProps): React.JSX.Element {
17
17
  const { colors } = useTheme();
18
18
  const on = useSharedValue(value ? 1 : 0);
19
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native switch animation.
19
20
  useEffect(() => {
20
21
  on.value = withTiming(value ? 1 : 0, timings.switch);
21
22
  }, [on, value]);
@@ -28,6 +28,7 @@ export const TileShell = memo(function TileShell({ children, selected = false, s
28
28
  const radius = phone ? radii.tilePhone : radii.tile;
29
29
  const live = useSharedValue(0);
30
30
  const showRing = speaking && cameraOn;
31
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native speaking-ring animation.
31
32
  useEffect(() => {
32
33
  live.value = withTiming(showRing ? 1 : 0, timings.scrim);
33
34
  }, [live, showRing]);
@@ -29,6 +29,7 @@ export function ChatNotices({ chatVisible, sheetOpen, onOpenChat }: ChatNoticesP
29
29
  const latest = useRef({ chatVisible, sheetOpen, onOpenChat, soundsEnabled });
30
30
  latest.current = { chatVisible, sheetOpen, onOpenChat, soundsEnabled };
31
31
 
32
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native chat-notice observation.
32
33
  useEffect(() => {
33
34
  if (status !== "ready") return;
34
35
  const previous = seen.current;
@@ -81,6 +81,7 @@ function useMarkChatRead(reading: boolean): void {
81
81
  const client = useSpaceClient();
82
82
  const { messages, unreadCount } = useChat();
83
83
  const last = messages[messages.length - 1]?.messageId;
84
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native read-receipt synchronization.
84
85
  useEffect(() => {
85
86
  if (!reading || !last || unreadCount === 0) return;
86
87
  // A lost read receipt only leaves the unread dot up; the next message marks it again.
@@ -37,6 +37,7 @@ export function PhotoViewer({ viewing, onChange, onClose }: PhotoViewerProps): R
37
37
  const photo = photos[index] ?? photos[0];
38
38
  const count = photos.length;
39
39
  useBackHandler(true, onClose);
40
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native photo-viewer accessibility focus.
40
41
  useEffect(() => {
41
42
  const node = closeRef.current && findNodeHandle(closeRef.current);
42
43
  if (node) AccessibilityInfo.setAccessibilityFocus(node);
@@ -29,6 +29,7 @@ export type AttachmentSource = { readonly state: "loading" } | { readonly state:
29
29
  export function useAttachmentSource(attachment: ChatAttachment): AttachmentSource {
30
30
  const client = useSpaceClient();
31
31
  const [source, setSource] = useState<AttachmentSource>({ state: "loading" });
32
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native attachment resolution and stale-result cleanup.
32
33
  useEffect(() => {
33
34
  let live = true;
34
35
  resolveUrl(client, attachment)
@@ -87,6 +87,7 @@ function Board({ mini, onShowInSpotlight }: { readonly mini: boolean; readonly o
87
87
  const toasts = useToasts();
88
88
  const transport = useMemo(() => client.whiteboard.transport(), [client, status]);
89
89
  const [capabilities, setCapabilities] = useState({ canDraw: false, canClear: false, viewsAvailable: false });
90
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native board-summary subscription cleanup.
90
91
  useEffect(() => transport?.subscribeSummary?.((summary) => setCapabilities({ canDraw: summary.canDraw, canClear: summary.canClear, viewsAvailable: transport.supportsViews === true })), [transport]);
91
92
  const board = useMemo(
92
93
  () => ({
@@ -73,6 +73,7 @@ export const ParticipantTile = memo(function ParticipantTile({ id, width, height
73
73
  const tile = useSpaceSelector(select, shallowEqual);
74
74
  const slot = useDevScenario().videoSlots?.[id];
75
75
  const level = useSharedValue(0);
76
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native speaking-level animation.
76
77
  useEffect(() => {
77
78
  level.value = withTiming(tile.speaking ? 0.6 : 0, timings.level);
78
79
  }, [level, tile.speaking]);
@@ -59,6 +59,7 @@ function selectStructure(snapshot: SpaceSnapshot): Structure {
59
59
  export const Stage = memo(function Stage({ frame, onOpenPeople }: StageProps): React.JSX.Element {
60
60
  const client = useSpaceClient();
61
61
  const toasts = useToasts();
62
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing stage/board subscriptions and layout reconciliation.
62
63
  useEffect(() => ownNativeBoardSession(client, () => toasts.show({ kind: "error", dismissible: true, message: "Couldn’t open the board. Try again from More." })), [client, toasts]);
63
64
  const structure = useSpaceSelector(selectStructure, shallowEqual);
64
65
  const selectViewsAvailable = useCallback(() => client.whiteboard.transport()?.supportsViews === true, [client]);
@@ -68,6 +69,7 @@ export const Stage = memo(function Stage({ frame, onOpenPeople }: StageProps): R
68
69
  const { shareAspect } = useDevScenario();
69
70
  const [sheet, setSheet] = useState<{ readonly id: string | null; readonly open: boolean }>({ id: null, open: false });
70
71
 
72
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing stage/board subscriptions and layout reconciliation.
71
73
  useEffect(() => {
72
74
  if (view.following && !structure.present.split("\n").includes(view.following)) view.setFollowing(null);
73
75
  }, [structure.present, view.following, view.setFollowing]);
@@ -77,6 +79,7 @@ export const Stage = memo(function Stage({ frame, onOpenPeople }: StageProps): R
77
79
  const next = nextStageOrder(order, present, structure.selfId, structure.speaker);
78
80
  if (next !== order) setOrder(next);
79
81
 
82
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing stage/board subscriptions and layout reconciliation.
80
83
  useEffect(() => {
81
84
  view.setBoardOpen(null);
82
85
  view.setFollowing(null);
@@ -34,6 +34,7 @@ export function StageViewProvider({ children }: { readonly children: ReactNode }
34
34
  const [mirror, setMirror] = useState(true);
35
35
 
36
36
  const dev = useDevScenario();
37
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native development-layout reconciliation.
37
38
  useEffect(() => {
38
39
  if (dev.layout) setLayout(dev.layout);
39
40
  if (dev.pinned) setPinned(dev.pinned);
@@ -38,6 +38,7 @@ export function CallOverlays({ screen, callName, rejoining, onHold, onRejoin, on
38
38
  // Notices belong to the live call; a status page or a fresh join starts without them.
39
39
  const { clear } = useToasts();
40
40
  const clearsNotices = screen.kind === "status" || screen.kind === "joining";
41
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native terminal-notice cleanup.
41
42
  useEffect(() => {
42
43
  if (clearsNotices) clear();
43
44
  }, [clear, clearsNotices]);
@@ -54,6 +54,7 @@ export function StatusPage({ outcome, callName, failure, onRejoin, onHome }: Sta
54
54
  const ref = useMeasuredRef<View>("status.page", pageStyle);
55
55
  const firstAction = useRef<View>(null);
56
56
  // The mockup moves focus to the first action when the page appears.
57
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing native status-page accessibility focus.
57
58
  useEffect(() => {
58
59
  const node = firstAction.current && findNodeHandle(firstAction.current);
59
60
  if (node) AccessibilityInfo.setAccessibilityFocus(node);
@@ -46,6 +46,7 @@ export function useCallEventToasts({ onOpenSettings, resumedAfterSettings }: Cal
46
46
  const latest = useRef({ colors, soundsEnabled });
47
47
  latest.current = { colors, soundsEnabled };
48
48
 
49
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing client-event and native permission subscriptions.
49
50
  useEffect(() => {
50
51
  let previous = client.getSnapshot();
51
52
  return client.subscribe(() => {
@@ -137,6 +138,7 @@ function useResumedAfterSettings(permission: Permission | undefined, toasts: Toa
137
138
  const client = useSpaceClient();
138
139
  const { colors } = useTheme();
139
140
  const shown = useRef(false);
141
+ // nosemgrep: chalk.forbid-sdk-use-effect -- Keep existing client-event and native permission subscriptions.
140
142
  useEffect(() => {
141
143
  if (!permission) return;
142
144
  const check = () => {