@buoy-gg/js-top 7.0.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 (168) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +56 -0
  3. package/lib/commonjs/index.js +97 -0
  4. package/lib/commonjs/index.js.map +1 -0
  5. package/lib/commonjs/js-top/components/BusySparkline.js +56 -0
  6. package/lib/commonjs/js-top/components/BusySparkline.js.map +1 -0
  7. package/lib/commonjs/js-top/components/JsTopHudOverlay.js +79 -0
  8. package/lib/commonjs/js-top/components/JsTopHudOverlay.js.map +1 -0
  9. package/lib/commonjs/js-top/components/JsTopModal.js +347 -0
  10. package/lib/commonjs/js-top/components/JsTopModal.js.map +1 -0
  11. package/lib/commonjs/js-top/components/TaskDetailView.js +329 -0
  12. package/lib/commonjs/js-top/components/TaskDetailView.js.map +1 -0
  13. package/lib/commonjs/js-top/components/TaskTable.js +360 -0
  14. package/lib/commonjs/js-top/components/TaskTable.js.map +1 -0
  15. package/lib/commonjs/js-top/engine/JsTopController.js +350 -0
  16. package/lib/commonjs/js-top/engine/JsTopController.js.map +1 -0
  17. package/lib/commonjs/js-top/engine/aggregator.js +148 -0
  18. package/lib/commonjs/js-top/engine/aggregator.js.map +1 -0
  19. package/lib/commonjs/js-top/engine/busyProbe.js +101 -0
  20. package/lib/commonjs/js-top/engine/busyProbe.js.map +1 -0
  21. package/lib/commonjs/js-top/engine/frameStack.js +133 -0
  22. package/lib/commonjs/js-top/engine/frameStack.js.map +1 -0
  23. package/lib/commonjs/js-top/engine/longtasks.js +141 -0
  24. package/lib/commonjs/js-top/engine/longtasks.js.map +1 -0
  25. package/lib/commonjs/js-top/engine/now.js +38 -0
  26. package/lib/commonjs/js-top/engine/now.js.map +1 -0
  27. package/lib/commonjs/js-top/engine/originRegistry.js +402 -0
  28. package/lib/commonjs/js-top/engine/originRegistry.js.map +1 -0
  29. package/lib/commonjs/js-top/engine/patches/bridge.js +60 -0
  30. package/lib/commonjs/js-top/engine/patches/bridge.js.map +1 -0
  31. package/lib/commonjs/js-top/engine/patches/microtasks.js +27 -0
  32. package/lib/commonjs/js-top/engine/patches/microtasks.js.map +1 -0
  33. package/lib/commonjs/js-top/engine/patches/promises.js +65 -0
  34. package/lib/commonjs/js-top/engine/patches/promises.js.map +1 -0
  35. package/lib/commonjs/js-top/engine/patches/timers.js +66 -0
  36. package/lib/commonjs/js-top/engine/patches/timers.js.map +1 -0
  37. package/lib/commonjs/js-top/engine/state.js +63 -0
  38. package/lib/commonjs/js-top/engine/state.js.map +1 -0
  39. package/lib/commonjs/js-top/engine/symbolicate.js +95 -0
  40. package/lib/commonjs/js-top/engine/symbolicate.js.map +1 -0
  41. package/lib/commonjs/js-top/sync/jsTopSyncAdapter.js +74 -0
  42. package/lib/commonjs/js-top/sync/jsTopSyncAdapter.js.map +1 -0
  43. package/lib/commonjs/js-top/types/index.js +46 -0
  44. package/lib/commonjs/js-top/types/index.js.map +1 -0
  45. package/lib/commonjs/js-top/utils/preferences.js +39 -0
  46. package/lib/commonjs/js-top/utils/preferences.js.map +1 -0
  47. package/lib/commonjs/package.json +1 -0
  48. package/lib/commonjs/preset.js +75 -0
  49. package/lib/commonjs/preset.js.map +1 -0
  50. package/lib/module/index.js +47 -0
  51. package/lib/module/index.js.map +1 -0
  52. package/lib/module/js-top/components/BusySparkline.js +52 -0
  53. package/lib/module/js-top/components/BusySparkline.js.map +1 -0
  54. package/lib/module/js-top/components/JsTopHudOverlay.js +75 -0
  55. package/lib/module/js-top/components/JsTopHudOverlay.js.map +1 -0
  56. package/lib/module/js-top/components/JsTopModal.js +343 -0
  57. package/lib/module/js-top/components/JsTopModal.js.map +1 -0
  58. package/lib/module/js-top/components/TaskDetailView.js +325 -0
  59. package/lib/module/js-top/components/TaskDetailView.js.map +1 -0
  60. package/lib/module/js-top/components/TaskTable.js +353 -0
  61. package/lib/module/js-top/components/TaskTable.js.map +1 -0
  62. package/lib/module/js-top/engine/JsTopController.js +347 -0
  63. package/lib/module/js-top/engine/JsTopController.js.map +1 -0
  64. package/lib/module/js-top/engine/aggregator.js +136 -0
  65. package/lib/module/js-top/engine/aggregator.js.map +1 -0
  66. package/lib/module/js-top/engine/busyProbe.js +94 -0
  67. package/lib/module/js-top/engine/busyProbe.js.map +1 -0
  68. package/lib/module/js-top/engine/frameStack.js +126 -0
  69. package/lib/module/js-top/engine/frameStack.js.map +1 -0
  70. package/lib/module/js-top/engine/longtasks.js +133 -0
  71. package/lib/module/js-top/engine/longtasks.js.map +1 -0
  72. package/lib/module/js-top/engine/now.js +33 -0
  73. package/lib/module/js-top/engine/now.js.map +1 -0
  74. package/lib/module/js-top/engine/originRegistry.js +388 -0
  75. package/lib/module/js-top/engine/originRegistry.js.map +1 -0
  76. package/lib/module/js-top/engine/patches/bridge.js +56 -0
  77. package/lib/module/js-top/engine/patches/bridge.js.map +1 -0
  78. package/lib/module/js-top/engine/patches/microtasks.js +23 -0
  79. package/lib/module/js-top/engine/patches/microtasks.js.map +1 -0
  80. package/lib/module/js-top/engine/patches/promises.js +62 -0
  81. package/lib/module/js-top/engine/patches/promises.js.map +1 -0
  82. package/lib/module/js-top/engine/patches/timers.js +62 -0
  83. package/lib/module/js-top/engine/patches/timers.js.map +1 -0
  84. package/lib/module/js-top/engine/state.js +54 -0
  85. package/lib/module/js-top/engine/state.js.map +1 -0
  86. package/lib/module/js-top/engine/symbolicate.js +91 -0
  87. package/lib/module/js-top/engine/symbolicate.js.map +1 -0
  88. package/lib/module/js-top/sync/jsTopSyncAdapter.js +70 -0
  89. package/lib/module/js-top/sync/jsTopSyncAdapter.js.map +1 -0
  90. package/lib/module/js-top/types/index.js +42 -0
  91. package/lib/module/js-top/types/index.js.map +1 -0
  92. package/lib/module/js-top/utils/preferences.js +34 -0
  93. package/lib/module/js-top/utils/preferences.js.map +1 -0
  94. package/lib/module/preset.js +71 -0
  95. package/lib/module/preset.js.map +1 -0
  96. package/lib/typescript/index.d.ts +21 -0
  97. package/lib/typescript/index.d.ts.map +1 -0
  98. package/lib/typescript/js-top/components/BusySparkline.d.ts +8 -0
  99. package/lib/typescript/js-top/components/BusySparkline.d.ts.map +1 -0
  100. package/lib/typescript/js-top/components/JsTopHudOverlay.d.ts +8 -0
  101. package/lib/typescript/js-top/components/JsTopHudOverlay.d.ts.map +1 -0
  102. package/lib/typescript/js-top/components/JsTopModal.d.ts +39 -0
  103. package/lib/typescript/js-top/components/JsTopModal.d.ts.map +1 -0
  104. package/lib/typescript/js-top/components/TaskDetailView.d.ts +15 -0
  105. package/lib/typescript/js-top/components/TaskDetailView.d.ts.map +1 -0
  106. package/lib/typescript/js-top/components/TaskTable.d.ts +38 -0
  107. package/lib/typescript/js-top/components/TaskTable.d.ts.map +1 -0
  108. package/lib/typescript/js-top/engine/JsTopController.d.ts +78 -0
  109. package/lib/typescript/js-top/engine/JsTopController.d.ts.map +1 -0
  110. package/lib/typescript/js-top/engine/__tests__/engine.test.d.ts +9 -0
  111. package/lib/typescript/js-top/engine/__tests__/engine.test.d.ts.map +1 -0
  112. package/lib/typescript/js-top/engine/aggregator.d.ts +33 -0
  113. package/lib/typescript/js-top/engine/aggregator.d.ts.map +1 -0
  114. package/lib/typescript/js-top/engine/busyProbe.d.ts +25 -0
  115. package/lib/typescript/js-top/engine/busyProbe.d.ts.map +1 -0
  116. package/lib/typescript/js-top/engine/frameStack.d.ts +44 -0
  117. package/lib/typescript/js-top/engine/frameStack.d.ts.map +1 -0
  118. package/lib/typescript/js-top/engine/longtasks.d.ts +37 -0
  119. package/lib/typescript/js-top/engine/longtasks.d.ts.map +1 -0
  120. package/lib/typescript/js-top/engine/now.d.ts +9 -0
  121. package/lib/typescript/js-top/engine/now.d.ts.map +1 -0
  122. package/lib/typescript/js-top/engine/originRegistry.d.ts +82 -0
  123. package/lib/typescript/js-top/engine/originRegistry.d.ts.map +1 -0
  124. package/lib/typescript/js-top/engine/patches/bridge.d.ts +17 -0
  125. package/lib/typescript/js-top/engine/patches/bridge.d.ts.map +1 -0
  126. package/lib/typescript/js-top/engine/patches/microtasks.d.ts +6 -0
  127. package/lib/typescript/js-top/engine/patches/microtasks.d.ts.map +1 -0
  128. package/lib/typescript/js-top/engine/patches/promises.d.ts +18 -0
  129. package/lib/typescript/js-top/engine/patches/promises.d.ts.map +1 -0
  130. package/lib/typescript/js-top/engine/patches/timers.d.ts +11 -0
  131. package/lib/typescript/js-top/engine/patches/timers.d.ts.map +1 -0
  132. package/lib/typescript/js-top/engine/state.d.ts +35 -0
  133. package/lib/typescript/js-top/engine/state.d.ts.map +1 -0
  134. package/lib/typescript/js-top/engine/symbolicate.d.ts +16 -0
  135. package/lib/typescript/js-top/engine/symbolicate.d.ts.map +1 -0
  136. package/lib/typescript/js-top/sync/jsTopSyncAdapter.d.ts +43 -0
  137. package/lib/typescript/js-top/sync/jsTopSyncAdapter.d.ts.map +1 -0
  138. package/lib/typescript/js-top/types/index.d.ts +130 -0
  139. package/lib/typescript/js-top/types/index.d.ts.map +1 -0
  140. package/lib/typescript/js-top/utils/preferences.d.ts +13 -0
  141. package/lib/typescript/js-top/utils/preferences.d.ts.map +1 -0
  142. package/lib/typescript/preset.d.ts +49 -0
  143. package/lib/typescript/preset.d.ts.map +1 -0
  144. package/package.json +67 -0
  145. package/src/index.tsx +69 -0
  146. package/src/js-top/components/BusySparkline.tsx +59 -0
  147. package/src/js-top/components/JsTopHudOverlay.tsx +78 -0
  148. package/src/js-top/components/JsTopModal.tsx +421 -0
  149. package/src/js-top/components/TaskDetailView.tsx +349 -0
  150. package/src/js-top/components/TaskTable.tsx +365 -0
  151. package/src/js-top/engine/JsTopController.ts +401 -0
  152. package/src/js-top/engine/__tests__/engine.test.ts +337 -0
  153. package/src/js-top/engine/aggregator.ts +148 -0
  154. package/src/js-top/engine/busyProbe.ts +100 -0
  155. package/src/js-top/engine/frameStack.ts +142 -0
  156. package/src/js-top/engine/longtasks.ts +137 -0
  157. package/src/js-top/engine/now.ts +33 -0
  158. package/src/js-top/engine/originRegistry.ts +495 -0
  159. package/src/js-top/engine/patches/bridge.ts +69 -0
  160. package/src/js-top/engine/patches/microtasks.ts +25 -0
  161. package/src/js-top/engine/patches/promises.ts +57 -0
  162. package/src/js-top/engine/patches/timers.ts +80 -0
  163. package/src/js-top/engine/state.ts +66 -0
  164. package/src/js-top/engine/symbolicate.ts +109 -0
  165. package/src/js-top/sync/jsTopSyncAdapter.ts +72 -0
  166. package/src/js-top/types/index.ts +144 -0
  167. package/src/js-top/utils/preferences.ts +35 -0
  168. package/src/preset.tsx +67 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Pre-configured JS TOP tools for FloatingDevTools.
3
+ *
4
+ * Two presets:
5
+ * - jsTopPreset: toggle-only chip that turns the floating busy% pill on/off.
6
+ * - jsTopModalPreset: opens the Task Manager window (ranked live table).
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * import { jsTopPreset, jsTopModalPreset } from '@buoy-gg/js-top';
11
+ *
12
+ * <FloatingDevTools apps={[jsTopPreset, jsTopModalPreset]} />
13
+ * ```
14
+ */
15
+ import { JsTopModal } from "./js-top/components/JsTopModal";
16
+ export declare const JS_TOP_ENABLED_COLOR = "#34D399";
17
+ export declare const JS_TOP_DISABLED_COLOR = "#6B7280";
18
+ declare function JsTopToggleIcon({ size }: {
19
+ size: number;
20
+ }): import("react").JSX.Element;
21
+ declare function EmptyComponent(): null;
22
+ /** Toggle-only preset: show/hide the floating busy% pill. */
23
+ export declare const jsTopPreset: {
24
+ id: string;
25
+ name: string;
26
+ description: string;
27
+ slot: "row";
28
+ icon: typeof JsTopToggleIcon;
29
+ component: typeof EmptyComponent;
30
+ props: {};
31
+ launchMode: "toggle-only";
32
+ onPress: () => void;
33
+ };
34
+ /** Modal preset: the live Task Manager window. */
35
+ export declare const jsTopModalPreset: {
36
+ id: string;
37
+ name: string;
38
+ description: string;
39
+ slot: "both";
40
+ icon: ({ size }: {
41
+ size: number;
42
+ }) => import("react").JSX.Element;
43
+ component: typeof JsTopModal;
44
+ props: {
45
+ enableSharedModalDimensions: boolean;
46
+ };
47
+ };
48
+ export {};
49
+ //# sourceMappingURL=preset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/preset.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAE5D,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAC9C,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,iBAAS,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,+BASlD;AAED,iBAAS,cAAc,SAEtB;AAED,6DAA6D;AAC7D,eAAO,MAAM,WAAW;;;;;;;;;;CAYvB,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,gBAAgB;;;;;qBAKV;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;;;;;CAOlC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@buoy-gg/js-top",
3
+ "version": "7.0.3",
4
+ "description": "Live Task Manager for the React Native JS thread — see which timers, Promises, and callbacks are eating your JS FPS, ranked in real time. Part of Buoy devtools.",
5
+ "main": "lib/commonjs/index",
6
+ "module": "lib/module/index",
7
+ "types": "lib/typescript/index.d.ts",
8
+ "react-native": "src/index.tsx",
9
+ "source": "src/index.tsx",
10
+ "files": [
11
+ "src",
12
+ "lib"
13
+ ],
14
+ "sideEffects": false,
15
+ "dependencies": {
16
+ "@buoy-gg/shared-ui": "7.0.3"
17
+ },
18
+ "peerDependencies": {
19
+ "react": "*",
20
+ "react-native": ">=0.76.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^19.1.0",
24
+ "@types/react-native": "^0.73.0",
25
+ "typescript": "~5.8.3"
26
+ },
27
+ "react-native-builder-bob": {
28
+ "source": "src",
29
+ "output": "lib",
30
+ "targets": [
31
+ "commonjs",
32
+ "module",
33
+ "typescript"
34
+ ]
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/Buoy-gg/buoy.git",
39
+ "directory": "packages/js-top"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/LovesWorking/react-native-buoy/issues"
43
+ },
44
+ "homepage": "https://buoy.gg/tools/js-top",
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "tag": "latest"
48
+ },
49
+ "keywords": [
50
+ "react-native",
51
+ "expo",
52
+ "devtools",
53
+ "debugging",
54
+ "react-native-devtools",
55
+ "flipper-alternative",
56
+ "performance",
57
+ "profiler",
58
+ "js-thread",
59
+ "task-manager"
60
+ ],
61
+ "scripts": {
62
+ "build": "bob build",
63
+ "typecheck": "tsc --noEmit",
64
+ "clean": "rimraf lib",
65
+ "test": "pnpm run typecheck"
66
+ }
67
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @buoy-gg/js-top — live Task Manager for the React Native JS thread.
3
+ *
4
+ * Pure JS (Expo Go + release compatible): wraps timers / rAF / microtasks /
5
+ * Promise reactions / legacy-bridge call-ins and books exclusive time per
6
+ * scheduling origin; a calibrated busy probe measures true thread occupancy
7
+ * so unattributable work is surfaced honestly instead of hidden.
8
+ *
9
+ * Coverage tiers beyond the core (promises, bridge, longtask correlation)
10
+ * are registered here at module scope — the controller installs them lazily
11
+ * on first engine start, so importing this package costs nothing until a
12
+ * viewer opens the tool.
13
+ */
14
+
15
+ import { JsTopController } from "./js-top/engine/JsTopController";
16
+ import { installPromisePatches } from "./js-top/engine/patches/promises";
17
+ import { installBridgePatch } from "./js-top/engine/patches/bridge";
18
+ import { installTimerPatches } from "./js-top/engine/patches/timers";
19
+ import { installMicrotaskPatches } from "./js-top/engine/patches/microtasks";
20
+ import {
21
+ clearLongtasks,
22
+ getFreezeStats,
23
+ getLongtasks,
24
+ installLongtaskObserver,
25
+ } from "./js-top/engine/longtasks";
26
+
27
+ JsTopController.registerTierInstaller(installPromisePatches);
28
+ JsTopController.registerTierInstaller(installBridgePatch);
29
+ JsTopController.registerTierInstaller(installLongtaskObserver);
30
+ JsTopController.registerLongtaskProvider(getLongtasks);
31
+ JsTopController.registerFreezeStatsProvider(getFreezeStats);
32
+ JsTopController.registerClearHook(clearLongtasks);
33
+
34
+ // Timer/microtask patches install at import so callbacks scheduled at APP
35
+ // BOOT — the classic forgotten setInterval, the tool's #1 target — are
36
+ // wrapped and attributable the moment any viewer opens the tool. Wrappers
37
+ // resolve their origin lazily and cost one boolean per fire while the
38
+ // engine is off. (Promise/bridge patches are more invasive and stay lazy —
39
+ // they install on first engine start and cover work scheduled after it.)
40
+ try {
41
+ installTimerPatches();
42
+ installMicrotaskPatches();
43
+ } catch {
44
+ // Never let instrumentation break an app at import time.
45
+ }
46
+
47
+ export { JsTopController } from "./js-top/engine/JsTopController";
48
+ export { jsTopSyncAdapter } from "./js-top/sync/jsTopSyncAdapter";
49
+ export { JsTopModal } from "./js-top/components/JsTopModal";
50
+ export type {
51
+ JsTopModalProps,
52
+ JsTopRemoteActions,
53
+ } from "./js-top/components/JsTopModal";
54
+ export { JsTopHudOverlay } from "./js-top/components/JsTopHudOverlay";
55
+ export {
56
+ jsTopPreset,
57
+ jsTopModalPreset,
58
+ JS_TOP_ENABLED_COLOR,
59
+ JS_TOP_DISABLED_COLOR,
60
+ } from "./preset";
61
+ export type {
62
+ JsTopSnapshot,
63
+ JsTopRow,
64
+ JsTopLongtask,
65
+ JsTopOriginDetail,
66
+ JsTopTiers,
67
+ JsTopStatus,
68
+ TaskApi,
69
+ } from "./js-top/types";
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Busy% history sparkline — plain View bars (no SVG dependency), tinted by
3
+ * severity. Renders at most the trailing 60 buckets (15s).
4
+ */
5
+
6
+ import { memo } from "react";
7
+ import { StyleSheet, View } from "react-native";
8
+ import { macOSColors } from "@buoy-gg/shared-ui";
9
+
10
+ const MAX_POINTS = 60;
11
+
12
+ function barColor(pct: number): string {
13
+ if (pct >= 80) return macOSColors.semantic.error;
14
+ if (pct >= 40) return macOSColors.semantic.warning;
15
+ return macOSColors.semantic.success;
16
+ }
17
+
18
+ export const BusySparkline = memo(function BusySparkline({
19
+ history,
20
+ }: {
21
+ history: number[];
22
+ }) {
23
+ const points = history.slice(-MAX_POINTS);
24
+ return (
25
+ <View style={styles.row} pointerEvents="none">
26
+ {points.map((pct, i) => (
27
+ <View
28
+ // Bars are positional by nature (a scrolling time axis); the array
29
+ // is fixed-length and rewritten wholesale each tick, so positional
30
+ // keys are correct here — this is not a prepend-list.
31
+ key={`b${i}`}
32
+ style={[
33
+ styles.bar,
34
+ {
35
+ height: Math.max(2, (pct / 100) * 28),
36
+ backgroundColor: barColor(pct),
37
+ },
38
+ ]}
39
+ />
40
+ ))}
41
+ </View>
42
+ );
43
+ });
44
+
45
+ const styles = StyleSheet.create({
46
+ row: {
47
+ flexDirection: "row",
48
+ alignItems: "flex-end",
49
+ gap: 1,
50
+ height: 28,
51
+ alignSelf: "stretch",
52
+ },
53
+ bar: {
54
+ flex: 1,
55
+ maxWidth: 6,
56
+ borderRadius: 1,
57
+ opacity: 0.9,
58
+ },
59
+ });
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Minimal floating HUD pill: busy% + the current top offender. Rendered by
3
+ * FloatingDevTools (guarded require) when the JS TOP toggle chip is on —
4
+ * same auto-mount pattern as the perf-monitor overlay. Display-only in v1
5
+ * (no drag; sits under the status bar on the left).
6
+ */
7
+
8
+ import { useEffect, useState } from "react";
9
+ import { StyleSheet, Text, View } from "react-native";
10
+ import { macOSColors } from "@buoy-gg/shared-ui";
11
+ import type { JsTopSnapshot } from "../types";
12
+ import { JsTopController } from "../engine/JsTopController";
13
+
14
+ function busyColor(pct: number): string {
15
+ if (pct >= 80) return macOSColors.semantic.error;
16
+ if (pct >= 40) return macOSColors.semantic.warning;
17
+ return macOSColors.semantic.success;
18
+ }
19
+
20
+ export function JsTopHudOverlay() {
21
+ const [enabled, setEnabled] = useState(() => JsTopController.isEnabled());
22
+ const [snapshot, setSnapshot] = useState<JsTopSnapshot | null>(null);
23
+
24
+ useEffect(() => JsTopController.subscribeEnabled(setEnabled), []);
25
+
26
+ useEffect(() => {
27
+ if (!enabled) {
28
+ setSnapshot(null);
29
+ return;
30
+ }
31
+ return JsTopController.subscribe(setSnapshot);
32
+ }, [enabled]);
33
+
34
+ if (!enabled || !snapshot) return null;
35
+
36
+ const top = snapshot.rows.find((r) => r.api !== "unattributed");
37
+
38
+ return (
39
+ <View style={styles.pill} pointerEvents="none">
40
+ <Text style={[styles.busy, { color: busyColor(snapshot.busyPct) }]}>
41
+ {snapshot.busyPct.toFixed(0)}%
42
+ </Text>
43
+ {top && (
44
+ <Text style={styles.label} numberOfLines={1}>
45
+ {top.label}
46
+ </Text>
47
+ )}
48
+ </View>
49
+ );
50
+ }
51
+
52
+ const styles = StyleSheet.create({
53
+ pill: {
54
+ position: "absolute",
55
+ top: 60,
56
+ left: 8,
57
+ flexDirection: "row",
58
+ alignItems: "center",
59
+ gap: 6,
60
+ backgroundColor: "rgba(10,10,12,0.85)",
61
+ borderWidth: 1,
62
+ borderColor: macOSColors.border.default,
63
+ borderRadius: 6,
64
+ paddingHorizontal: 8,
65
+ paddingVertical: 4,
66
+ maxWidth: 220,
67
+ },
68
+ busy: {
69
+ fontSize: 11,
70
+ fontWeight: "700",
71
+ fontVariant: ["tabular-nums"],
72
+ },
73
+ label: {
74
+ color: macOSColors.text.secondary ?? macOSColors.text.primary,
75
+ fontSize: 10,
76
+ flexShrink: 1,
77
+ },
78
+ });