@buoy-gg/core 7.0.41 → 7.0.43

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 (270) hide show
  1. package/README.md +14 -95
  2. package/bin/buoy.js +19 -340
  3. package/lib/commonjs/Buoy.js +1 -107
  4. package/lib/commonjs/browserRegister.js +1 -48
  5. package/lib/commonjs/browserRegister.server.js +1 -10
  6. package/lib/commonjs/earlyHookScript.js +1 -0
  7. package/lib/commonjs/earlyNetwork.js +1 -0
  8. package/lib/commonjs/floatingMenu/AppHost.js +1 -460
  9. package/lib/commonjs/floatingMenu/AppHost.web.js +1 -376
  10. package/lib/commonjs/floatingMenu/AppHostLogic.js +1 -50
  11. package/lib/commonjs/floatingMenu/AppIcon.js +1 -91
  12. package/lib/commonjs/floatingMenu/AppIcon.web.js +1 -53
  13. package/lib/commonjs/floatingMenu/BrowserToolRuntime.js +1 -150
  14. package/lib/commonjs/floatingMenu/DefaultConfigContext.js +1 -45
  15. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +1 -18
  16. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.shared.js +1 -1114
  17. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +1 -48
  18. package/lib/commonjs/floatingMenu/DevToolsVisibilityContext.js +1 -49
  19. package/lib/commonjs/floatingMenu/FloatingDevTools.js +1 -559
  20. package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +1 -415
  21. package/lib/commonjs/floatingMenu/FloatingMenu.js +1 -371
  22. package/lib/commonjs/floatingMenu/MinimizedToolsContext.js +1 -274
  23. package/lib/commonjs/floatingMenu/MinimizedToolsStack.js +1 -288
  24. package/lib/commonjs/floatingMenu/MinimizedToolsStack.web.js +1 -377
  25. package/lib/commonjs/floatingMenu/ToggleStateManager.js +1 -36
  26. package/lib/commonjs/floatingMenu/ToolHost.js +1 -281
  27. package/lib/commonjs/floatingMenu/ToolOpenerBridge.js +1 -41
  28. package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +1 -471
  29. package/lib/commonjs/floatingMenu/autoExternalSync.js +1 -443
  30. package/lib/commonjs/floatingMenu/defaultConfig.js +1 -167
  31. package/lib/commonjs/floatingMenu/deviceIdentity.js +1 -173
  32. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +1 -904
  33. package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +1 -895
  34. package/lib/commonjs/floatingMenu/dial/DialHyperspace.js +1 -177
  35. package/lib/commonjs/floatingMenu/dial/DialIcon.js +1 -304
  36. package/lib/commonjs/floatingMenu/dial/DialPagination.js +1 -165
  37. package/lib/commonjs/floatingMenu/dial/DialSnow.js +1 -92
  38. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +1 -246
  39. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.web.js +2 -147
  40. package/lib/commonjs/floatingMenu/dial/dialUsageStore.js +1 -93
  41. package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +1 -42
  42. package/lib/commonjs/floatingMenu/dial/onboardingConstants.web.js +1 -42
  43. package/lib/commonjs/floatingMenu/externalSyncGate.js +1 -49
  44. package/lib/commonjs/floatingMenu/floatingTools.js +1 -42
  45. package/lib/commonjs/floatingMenu/floatingTools.web.js +1 -30
  46. package/lib/commonjs/floatingMenu/optionalRequire.js +1 -37
  47. package/lib/commonjs/floatingMenu/settingsBus.js +1 -12
  48. package/lib/commonjs/floatingMenu/sync/appSyncAdapter.js +1 -115
  49. package/lib/commonjs/floatingMenu/types.js +1 -5
  50. package/lib/commonjs/floatingMenu/useDesktopSyncStatus.js +1 -28
  51. package/lib/commonjs/floatingMenu/useDesktopSyncStatus.web.js +1 -14
  52. package/lib/commonjs/index.js +1 -291
  53. package/lib/commonjs/index.web.js +1 -241
  54. package/lib/commonjs/reactDevtoolsTypes.js +1 -1
  55. package/lib/commonjs/releaseOptIn.js +1 -0
  56. package/lib/commonjs/telemetry/installPing.js +1 -131
  57. package/lib/commonjs/toolkit.js +1 -584
  58. package/lib/commonjs/toolkitComponents.js +1 -241
  59. package/lib/commonjs/utils/autoDiscoverPresets.web.js +1 -46
  60. package/lib/commonjs/utils/createWebTool.js +1 -20
  61. package/lib/commonjs/utils/createWebTools.js +1 -30
  62. package/lib/commonjs/version.js +1 -18
  63. package/lib/module/Buoy.js +1 -102
  64. package/lib/module/browserRegister.js +1 -44
  65. package/lib/module/browserRegister.server.js +1 -6
  66. package/lib/module/earlyHookScript.js +1 -0
  67. package/lib/module/earlyNetwork.js +1 -0
  68. package/lib/module/floatingMenu/AppHost.js +1 -452
  69. package/lib/module/floatingMenu/AppHost.web.js +1 -368
  70. package/lib/module/floatingMenu/AppHostLogic.js +1 -45
  71. package/lib/module/floatingMenu/AppIcon.js +1 -84
  72. package/lib/module/floatingMenu/AppIcon.web.js +1 -47
  73. package/lib/module/floatingMenu/BrowserToolRuntime.js +1 -146
  74. package/lib/module/floatingMenu/DefaultConfigContext.js +1 -39
  75. package/lib/module/floatingMenu/DevToolsSettingsModal.js +1 -3
  76. package/lib/module/floatingMenu/DevToolsSettingsModal.shared.js +1 -1110
  77. package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +1 -44
  78. package/lib/module/floatingMenu/DevToolsVisibilityContext.js +1 -44
  79. package/lib/module/floatingMenu/FloatingDevTools.js +1 -556
  80. package/lib/module/floatingMenu/FloatingDevTools.web.js +1 -412
  81. package/lib/module/floatingMenu/FloatingMenu.js +1 -366
  82. package/lib/module/floatingMenu/MinimizedToolsContext.js +1 -266
  83. package/lib/module/floatingMenu/MinimizedToolsStack.js +1 -285
  84. package/lib/module/floatingMenu/MinimizedToolsStack.web.js +1 -374
  85. package/lib/module/floatingMenu/ToggleStateManager.js +1 -32
  86. package/lib/module/floatingMenu/ToolHost.js +1 -273
  87. package/lib/module/floatingMenu/ToolOpenerBridge.js +1 -37
  88. package/lib/module/floatingMenu/autoDiscoverPresets.js +1 -467
  89. package/lib/module/floatingMenu/autoExternalSync.js +1 -436
  90. package/lib/module/floatingMenu/defaultConfig.js +1 -158
  91. package/lib/module/floatingMenu/deviceIdentity.js +1 -163
  92. package/lib/module/floatingMenu/dial/DialDevTools.js +1 -900
  93. package/lib/module/floatingMenu/dial/DialDevTools.web.js +1 -891
  94. package/lib/module/floatingMenu/dial/DialHyperspace.js +1 -173
  95. package/lib/module/floatingMenu/dial/DialIcon.js +1 -301
  96. package/lib/module/floatingMenu/dial/DialPagination.js +1 -160
  97. package/lib/module/floatingMenu/dial/DialSnow.js +1 -90
  98. package/lib/module/floatingMenu/dial/OnboardingTooltip.js +1 -240
  99. package/lib/module/floatingMenu/dial/OnboardingTooltip.web.js +2 -142
  100. package/lib/module/floatingMenu/dial/dialUsageStore.js +1 -85
  101. package/lib/module/floatingMenu/dial/onboardingConstants.js +1 -3
  102. package/lib/module/floatingMenu/dial/onboardingConstants.web.js +1 -3
  103. package/lib/module/floatingMenu/externalSyncGate.js +1 -44
  104. package/lib/module/floatingMenu/floatingTools.js +1 -27
  105. package/lib/module/floatingMenu/floatingTools.web.js +1 -3
  106. package/lib/module/floatingMenu/optionalRequire.js +1 -33
  107. package/lib/module/floatingMenu/settingsBus.js +1 -3
  108. package/lib/module/floatingMenu/sync/appSyncAdapter.js +1 -112
  109. package/lib/module/floatingMenu/types.js +1 -3
  110. package/lib/module/floatingMenu/useDesktopSyncStatus.js +1 -25
  111. package/lib/module/floatingMenu/useDesktopSyncStatus.web.js +1 -10
  112. package/lib/module/index.js +1 -41
  113. package/lib/module/index.web.js +1 -36
  114. package/lib/module/reactDevtoolsTypes.js +1 -1
  115. package/lib/module/releaseOptIn.js +1 -0
  116. package/lib/module/telemetry/installPing.js +1 -127
  117. package/lib/module/toolkit.js +1 -62
  118. package/lib/module/toolkitComponents.js +1 -230
  119. package/lib/module/utils/autoDiscoverPresets.web.js +1 -38
  120. package/lib/module/utils/createWebTool.js +1 -17
  121. package/lib/module/utils/createWebTools.js +1 -25
  122. package/lib/module/version.js +1 -14
  123. package/lib/typescript/commonjs/browserRegister.d.ts +9 -1
  124. package/lib/typescript/commonjs/browserRegister.server.d.ts +1 -0
  125. package/lib/typescript/commonjs/earlyHookScript.d.ts +12 -0
  126. package/lib/typescript/commonjs/earlyNetwork.d.ts +52 -0
  127. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +1 -1
  128. package/lib/typescript/commonjs/index.web.d.ts +1 -0
  129. package/lib/typescript/commonjs/releaseOptIn.d.ts +4 -0
  130. package/lib/typescript/commonjs/version.d.ts +1 -1
  131. package/lib/typescript/module/browserRegister.d.ts +9 -1
  132. package/lib/typescript/module/browserRegister.server.d.ts +1 -0
  133. package/lib/typescript/module/earlyHookScript.d.ts +12 -0
  134. package/lib/typescript/module/earlyNetwork.d.ts +52 -0
  135. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +1 -1
  136. package/lib/typescript/module/index.web.d.ts +1 -0
  137. package/lib/typescript/module/releaseOptIn.d.ts +4 -0
  138. package/lib/typescript/module/version.d.ts +1 -1
  139. package/lib/web/chunks/chunk-5SGL6QL4.mjs +1 -0
  140. package/lib/web/chunks/chunk-FROELTJH.mjs +1 -0
  141. package/lib/web/chunks/chunk-QZDVCMCT.mjs +1 -2
  142. package/lib/web/chunks/chunk-R7RGRTY5.mjs +1 -2
  143. package/lib/web/index.mjs +2 -3
  144. package/lib/web/register-server.mjs +1 -2
  145. package/lib/web/register.mjs +1 -2
  146. package/lib/web/toolkit.mjs +1 -2
  147. package/package.json +6 -9
  148. package/lib/typescript/commonjs/Buoy.d.ts.map +0 -1
  149. package/lib/typescript/commonjs/browserRegister.d.ts.map +0 -1
  150. package/lib/typescript/commonjs/browserRegister.server.d.ts.map +0 -1
  151. package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +0 -1
  152. package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts.map +0 -1
  153. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +0 -1
  154. package/lib/typescript/commonjs/floatingMenu/AppIcon.d.ts.map +0 -1
  155. package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts.map +0 -1
  156. package/lib/typescript/commonjs/floatingMenu/BrowserToolRuntime.d.ts.map +0 -1
  157. package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts.map +0 -1
  158. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +0 -1
  159. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.shared.d.ts.map +0 -1
  160. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +0 -1
  161. package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts.map +0 -1
  162. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +0 -1
  163. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +0 -1
  164. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +0 -1
  165. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts.map +0 -1
  166. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts.map +0 -1
  167. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.web.d.ts.map +0 -1
  168. package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts.map +0 -1
  169. package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +0 -1
  170. package/lib/typescript/commonjs/floatingMenu/ToolOpenerBridge.d.ts.map +0 -1
  171. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +0 -1
  172. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +0 -1
  173. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +0 -1
  174. package/lib/typescript/commonjs/floatingMenu/deviceIdentity.d.ts.map +0 -1
  175. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +0 -1
  176. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts.map +0 -1
  177. package/lib/typescript/commonjs/floatingMenu/dial/DialHyperspace.d.ts.map +0 -1
  178. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +0 -1
  179. package/lib/typescript/commonjs/floatingMenu/dial/DialPagination.d.ts.map +0 -1
  180. package/lib/typescript/commonjs/floatingMenu/dial/DialSnow.d.ts.map +0 -1
  181. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +0 -1
  182. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.web.d.ts.map +0 -1
  183. package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts.map +0 -1
  184. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +0 -1
  185. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts.map +0 -1
  186. package/lib/typescript/commonjs/floatingMenu/externalSyncGate.d.ts.map +0 -1
  187. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +0 -1
  188. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts.map +0 -1
  189. package/lib/typescript/commonjs/floatingMenu/optionalRequire.d.ts.map +0 -1
  190. package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts.map +0 -1
  191. package/lib/typescript/commonjs/floatingMenu/sync/appSyncAdapter.d.ts.map +0 -1
  192. package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +0 -1
  193. package/lib/typescript/commonjs/floatingMenu/useDesktopSyncStatus.d.ts.map +0 -1
  194. package/lib/typescript/commonjs/floatingMenu/useDesktopSyncStatus.web.d.ts.map +0 -1
  195. package/lib/typescript/commonjs/index.d.ts.map +0 -1
  196. package/lib/typescript/commonjs/index.web.d.ts.map +0 -1
  197. package/lib/typescript/commonjs/reactDevtoolsTypes.d.ts.map +0 -1
  198. package/lib/typescript/commonjs/telemetry/installPing.d.ts.map +0 -1
  199. package/lib/typescript/commonjs/toolkit.d.ts.map +0 -1
  200. package/lib/typescript/commonjs/toolkitComponents.d.ts.map +0 -1
  201. package/lib/typescript/commonjs/utils/autoDiscoverPresets.web.d.ts.map +0 -1
  202. package/lib/typescript/commonjs/utils/createWebTool.d.ts.map +0 -1
  203. package/lib/typescript/commonjs/utils/createWebTools.d.ts.map +0 -1
  204. package/lib/typescript/commonjs/version.d.ts.map +0 -1
  205. package/lib/typescript/module/Buoy.d.ts.map +0 -1
  206. package/lib/typescript/module/browserRegister.d.ts.map +0 -1
  207. package/lib/typescript/module/browserRegister.server.d.ts.map +0 -1
  208. package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +0 -1
  209. package/lib/typescript/module/floatingMenu/AppHost.web.d.ts.map +0 -1
  210. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +0 -1
  211. package/lib/typescript/module/floatingMenu/AppIcon.d.ts.map +0 -1
  212. package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts.map +0 -1
  213. package/lib/typescript/module/floatingMenu/BrowserToolRuntime.d.ts.map +0 -1
  214. package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts.map +0 -1
  215. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +0 -1
  216. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.shared.d.ts.map +0 -1
  217. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +0 -1
  218. package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts.map +0 -1
  219. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +0 -1
  220. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +0 -1
  221. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +0 -1
  222. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts.map +0 -1
  223. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts.map +0 -1
  224. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.web.d.ts.map +0 -1
  225. package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts.map +0 -1
  226. package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +0 -1
  227. package/lib/typescript/module/floatingMenu/ToolOpenerBridge.d.ts.map +0 -1
  228. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +0 -1
  229. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +0 -1
  230. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +0 -1
  231. package/lib/typescript/module/floatingMenu/deviceIdentity.d.ts.map +0 -1
  232. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +0 -1
  233. package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts.map +0 -1
  234. package/lib/typescript/module/floatingMenu/dial/DialHyperspace.d.ts.map +0 -1
  235. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +0 -1
  236. package/lib/typescript/module/floatingMenu/dial/DialPagination.d.ts.map +0 -1
  237. package/lib/typescript/module/floatingMenu/dial/DialSnow.d.ts.map +0 -1
  238. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +0 -1
  239. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.web.d.ts.map +0 -1
  240. package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts.map +0 -1
  241. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +0 -1
  242. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts.map +0 -1
  243. package/lib/typescript/module/floatingMenu/externalSyncGate.d.ts.map +0 -1
  244. package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +0 -1
  245. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts.map +0 -1
  246. package/lib/typescript/module/floatingMenu/optionalRequire.d.ts.map +0 -1
  247. package/lib/typescript/module/floatingMenu/settingsBus.d.ts.map +0 -1
  248. package/lib/typescript/module/floatingMenu/sync/appSyncAdapter.d.ts.map +0 -1
  249. package/lib/typescript/module/floatingMenu/types.d.ts.map +0 -1
  250. package/lib/typescript/module/floatingMenu/useDesktopSyncStatus.d.ts.map +0 -1
  251. package/lib/typescript/module/floatingMenu/useDesktopSyncStatus.web.d.ts.map +0 -1
  252. package/lib/typescript/module/index.d.ts.map +0 -1
  253. package/lib/typescript/module/index.web.d.ts.map +0 -1
  254. package/lib/typescript/module/reactDevtoolsTypes.d.ts.map +0 -1
  255. package/lib/typescript/module/telemetry/installPing.d.ts.map +0 -1
  256. package/lib/typescript/module/toolkit.d.ts.map +0 -1
  257. package/lib/typescript/module/toolkitComponents.d.ts.map +0 -1
  258. package/lib/typescript/module/utils/autoDiscoverPresets.web.d.ts.map +0 -1
  259. package/lib/typescript/module/utils/createWebTool.d.ts.map +0 -1
  260. package/lib/typescript/module/utils/createWebTools.d.ts.map +0 -1
  261. package/lib/typescript/module/version.d.ts.map +0 -1
  262. package/lib/web/chunks/chunk-5NZOQR6Q.mjs +0 -2
  263. package/lib/web/chunks/chunk-CGDSLLZF.mjs +0 -2
  264. package/lib/web/chunks/chunk-DCJDUSNR.mjs +0 -2
  265. package/lib/web/chunks/chunk-IDSHFS6G.mjs +0 -2
  266. package/lib/web/chunks/chunk-JOAMJGCS.mjs +0 -2
  267. package/lib/web/chunks/chunk-KYKZ2N52.mjs +0 -2
  268. package/lib/web/chunks/chunk-NGM7JGGS.mjs +0 -2
  269. package/lib/web/chunks/chunk-Z3XWDDIO.mjs +0 -2
  270. package/lib/web/chunks/chunk-ZTGQ6TTY.mjs +0 -2
@@ -1,900 +1 @@
1
- "use strict";
2
-
3
- import { useEffect, useMemo, useRef, useState } from "react";
4
- import { Pressable, StyleSheet, TouchableOpacity, View, useWindowDimensions, Text, Animated, Easing } from "react-native";
5
- import { absoluteFill, monoFont } from "@buoy-gg/shared-ui";
6
- // Icons are provided by installedApps; no direct icon imports here.
7
- import { DialIcon } from "./DialIcon.js";
8
- import { DialHyperspace } from "./DialHyperspace.js";
9
- import { DialPagination } from "./DialPagination.js";
10
- import { getRankedToolIds, isDialUsageLoaded, loadDialUsage, recordToolUsage } from "./dialUsageStore.js";
11
- import { persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors, ToolBackground, useToolBackground } from "@buoy-gg/shared-ui";
12
- import { DevToolsSettingsModal, useDevToolsSettings } from "../DevToolsSettingsModal";
13
- import { useProAccess } from "@buoy-gg/license";
14
- import { renderAppIcon } from "../AppIcon";
15
- import { useAppHost } from "../AppHost";
16
- import { OnboardingTooltip } from "./OnboardingTooltip";
17
- import { getDialLayout, MAX_DIAL_SLOTS, DIAL_BUTTON_SIZE, dialAnimationConfig, dialColors } from "@buoy-gg/floating-tools-core";
18
-
19
- // The circle size depends on the live window width, so it's computed inside
20
- // the component via useWindowDimensions — a module-scope Dimensions.get
21
- // snapshot goes stale when the window resizes after load (web/desktop).
22
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
23
- const BUTTON_SIZE = DIAL_BUTTON_SIZE;
24
- /** The dial face is a circle, so its centre is the middle of the measured square. */
25
- const DIAL_BACKGROUND_FOCUS = {
26
- x: 0.5,
27
- y: 0.5
28
- };
29
- // Radius kept clear of grid spokes around the glass hub (its outer disc is
30
- // 0.75 × BUTTON_SIZE, plus a breath).
31
- const HUB_CLEARANCE = BUTTON_SIZE * 0.75 + 6;
32
-
33
- /**
34
- * The centre button's idle pulse, off by design.
35
- *
36
- * It is built as `Animated.loop(Animated.sequence([...]))`, and that pairing is
37
- * NOT handed to the native driver even though every child sets
38
- * `useNativeDriver: true` — so it ticked on the JS thread for as long as the
39
- * dial stayed open, taxing every interaction launched from the dial.
40
- *
41
- * Re-enabling it means rewriting the loop so it genuinely runs natively;
42
- * flipping this alone just puts the JS-thread cost back.
43
- */
44
- const DIAL_IDLE_PULSE = false;
45
- const ONBOARDING_STORAGE_KEY = "@react_buoy_settings_tooltip_shown";
46
- /** A non-interactive placeholder used to fill out the last dial page. */
47
- const createEmptySlot = slotIndex => ({
48
- id: `empty-${slotIndex}`,
49
- name: `empty-${slotIndex}`,
50
- icon: null,
51
- color: "transparent",
52
- onPress: () => {}
53
- });
54
- export const DialDevTools = ({
55
- onClose,
56
- onSettingsPress,
57
- autoOpenSettings = false,
58
- apps,
59
- state,
60
- actions
61
- }) => {
62
- const [selectedIcon, setSelectedIcon] = useState(-1);
63
- const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
64
- const [settingsModalStateLoaded, setSettingsModalStateLoaded] = useState(false);
65
- const [showOnboardingTooltip, setShowOnboardingTooltip] = useState(false);
66
- const onboardingDismissedRef = useRef(false); // Track if onboarding was dismissed
67
- const hintsDisabled = useHintsDisabled();
68
- const {
69
- refreshSettings
70
- } = useDevToolsSettings();
71
- const {
72
- open
73
- } = useAppHost();
74
- const {
75
- isPro,
76
- isWeekendFree
77
- } = useProAccess();
78
-
79
- // Live window size — keeps the dial centered and sized correctly when the
80
- // window resizes (Electron/web) or the device rotates.
81
- const {
82
- width: screenWidth,
83
- height: screenHeight
84
- } = useWindowDimensions();
85
- const circleSize = getDialLayout({
86
- screenWidth
87
- }).circleSize;
88
- // The dial face shows the same backdrop the tools use (persisted shared
89
- // selection). Hyperspace keeps its hand-tuned dial port below.
90
- const {
91
- id: backgroundId
92
- } = useToolBackground();
93
- const sizeStyles = useMemo(() => ({
94
- parent: {
95
- width: circleSize,
96
- height: circleSize
97
- },
98
- circle: {
99
- width: circleSize,
100
- height: circleSize,
101
- borderRadius: circleSize / 2
102
- },
103
- rounded: {
104
- borderRadius: circleSize / 2
105
- },
106
- gridLine: {
107
- width: circleSize
108
- },
109
- // Each spoke is two segments that stop short of the glass hub, so the
110
- // six lines never meet in the middle where the background shows through.
111
- spoke: {
112
- width: circleSize / 2 - HUB_CLEARANCE
113
- }
114
- }), [circleSize]);
115
-
116
- // Load persisted settings modal state on mount
117
- useEffect(() => {
118
- const loadSettingsModalState = async () => {
119
- try {
120
- const savedModalOpen = await persistentStorage.getItem(devToolsStorageKeys.settings.modalOpen());
121
- if (savedModalOpen === "true") {
122
- setIsSettingsModalOpen(true);
123
- }
124
- } catch (error) {
125
- // Failed to load settings modal state - use default (closed)
126
- } finally {
127
- setSettingsModalStateLoaded(true);
128
- }
129
- };
130
- loadSettingsModalState();
131
- }, []);
132
-
133
- // Persist settings modal state when it changes
134
- useEffect(() => {
135
- // Only persist after initial state is loaded to avoid overwriting with default
136
- if (!settingsModalStateLoaded) return;
137
- persistentStorage.setItem(devToolsStorageKeys.settings.modalOpen(), isSettingsModalOpen ? "true" : "false").catch(error => {
138
- // Failed to save settings modal state - continue without persistence
139
- console.warn("Failed to save settings modal state:", error);
140
- });
141
- }, [isSettingsModalOpen, settingsModalStateLoaded]);
142
-
143
- // Auto-open settings modal when prop is true
144
- useEffect(() => {
145
- if (autoOpenSettings && !isSettingsModalOpen) {
146
- setIsSettingsModalOpen(true);
147
- }
148
- }, [autoOpenSettings, isSettingsModalOpen]);
149
-
150
- // Check if we should show the onboarding tooltip
151
- useEffect(() => {
152
- // Skip onboarding if hints are disabled
153
- if (hintsDisabled) {
154
- return;
155
- }
156
- const checkOnboarding = async () => {
157
- try {
158
- const hasSeenTooltip = await persistentStorage.getItem(ONBOARDING_STORAGE_KEY);
159
- if (!hasSeenTooltip) {
160
- // Small delay to let the entrance animations play first
161
- setTimeout(() => {
162
- setShowOnboardingTooltip(true);
163
- }, 1200);
164
- }
165
- } catch (error) {
166
- // If there's an error reading storage, don't show the tooltip
167
- // to avoid annoying the user repeatedly
168
- }
169
- };
170
- checkOnboarding();
171
- }, [hintsDisabled]);
172
-
173
- // React Native Animated values
174
- const backdropOpacity = useRef(new Animated.Value(0)).current;
175
- const dialScale = useRef(new Animated.Value(0)).current;
176
- const dialRotation = useRef(new Animated.Value(0)).current;
177
- const centerButtonScale = useRef(new Animated.Value(0)).current;
178
- const iconsProgress = useRef(new Animated.Value(0)).current;
179
- const pulseScale = useRef(new Animated.Value(1)).current;
180
- const availableApps = useMemo(() => apps.map(({
181
- id,
182
- name,
183
- slot,
184
- description,
185
- icon,
186
- color
187
- }) => ({
188
- id,
189
- name,
190
- slot,
191
- description,
192
- // Settings rows fall back to the app's own icon when the modal has no
193
- // hardcoded glyph for the tool.
194
- icon,
195
- color
196
- })), [apps]);
197
-
198
- // Animation tracking refs
199
- const pulseAnimationRef = useRef(null);
200
- const toggleSettingsModal = () => {
201
- if (isSettingsModalOpen) {
202
- setIsSettingsModalOpen(false);
203
- return;
204
- }
205
- setIsSettingsModalOpen(true);
206
- // Dismiss onboarding tooltip when user opens settings
207
- if (showOnboardingTooltip && !onboardingDismissedRef.current) {
208
- handleOnboardingDismiss();
209
- }
210
- // Also call external handler if provided
211
- onSettingsPress?.();
212
- };
213
-
214
- // Dial-eligible apps: everything except row-only tools. All of them are
215
- // shown — paginated across pages of MAX_DIAL_SLOTS — so there is no longer
216
- // a per-tool show/hide setting.
217
- const dialApps = useMemo(() => apps.filter(a => (a.slot ?? "both") !== "row"), [apps]);
218
-
219
- // Build a stable IconType for every dial-eligible app, keyed by id.
220
- const iconsById = useMemo(() => {
221
- const map = new Map();
222
- for (const app of dialApps) {
223
- map.set(app.id, {
224
- id: app.id,
225
- name: app.name,
226
- // Pass both the pre-rendered icon (for non-function icons; strings
227
- // resolve to auto-fitting text icons) and the component (for dynamic
228
- // rendering).
229
- icon: typeof app.icon === "function" ? null : renderAppIcon(app.icon, {
230
- slot: "dial",
231
- size: 20,
232
- color: app.color
233
- }),
234
- // Cast to the expected type - the signature is compatible at runtime.
235
- iconComponent: typeof app.icon === "function" ? app.icon : undefined,
236
- color: app.color ?? buoyColors.primary,
237
- onPress: () => {
238
- // Put this tool first the next time the dial opens.
239
- void recordToolUsage(app.id);
240
-
241
- // Call the app's onPress callback if provided, passing actions for
242
- // toggle tools.
243
- app?.onPress?.(actions);
244
-
245
- // Only open modal if not a toggle-only tool.
246
- if (app.launchMode !== "toggle-only") {
247
- const resolvedIcon = renderAppIcon(app.icon, {
248
- slot: "dial",
249
- size: 20,
250
- color: app.color
251
- });
252
- open({
253
- id: app.id,
254
- title: app.name,
255
- component: app.component,
256
- props: app.props,
257
- launchMode: app.launchMode ?? "self-modal",
258
- singleton: app.singleton ?? true,
259
- icon: resolvedIcon,
260
- color: app.color,
261
- modal: app.modal
262
- });
263
- }
264
-
265
- // Close the dial.
266
- onClose?.();
267
- }
268
- });
269
- }
270
- return map;
271
- }, [dialApps, actions, open, onClose]);
272
-
273
- // Snapshot the usage-ranked order when the dial opens. It stays stable while
274
- // open so icons don't jump positions mid-interaction.
275
- const [rankedIds, setRankedIds] = useState(() => getRankedToolIds(dialApps.map(a => a.id)));
276
- useEffect(() => {
277
- const ids = dialApps.map(a => a.id);
278
- if (isDialUsageLoaded()) {
279
- setRankedIds(getRankedToolIds(ids));
280
- return;
281
- }
282
- // Usage data not loaded yet — show default order, then re-rank once ready.
283
- let cancelled = false;
284
- loadDialUsage().then(() => {
285
- if (!cancelled) setRankedIds(getRankedToolIds(ids));
286
- });
287
- return () => {
288
- cancelled = true;
289
- };
290
- }, [dialApps]);
291
-
292
- // Page count spans the installed tools (usage-ranked). Only tools the app
293
- // actually has appear in the dial — nothing is shown for uninstalled ones.
294
- const pageCount = Math.max(1, Math.ceil(rankedIds.length / MAX_DIAL_SLOTS));
295
- const [currentPage, setCurrentPage] = useState(0);
296
- const safePage = Math.min(currentPage, pageCount - 1);
297
-
298
- // Every dial-eligible icon, with the page/slot it occupies. The ranking is
299
- // snapshotted on open, so each icon's page and slot are fixed for the
300
- // session — which lets us mount all icons once and paginate purely by
301
- // toggling visibility (no remounts on page change).
302
- const allDialIcons = useMemo(() => {
303
- return rankedIds.map(id => iconsById.get(id)).filter(icon => Boolean(icon)).map((icon, i) => ({
304
- icon,
305
- page: Math.floor(i / MAX_DIAL_SLOTS),
306
- slot: i % MAX_DIAL_SLOTS
307
- }));
308
- }, [rankedIds, iconsById]);
309
-
310
- // Empty slot indices for the current page (only the last page can be
311
- // partial). These are cheap placeholder dots.
312
- const emptySlots = useMemo(() => {
313
- const onThisPage = allDialIcons.filter(d => d.page === safePage).length;
314
- const slots = [];
315
- for (let s = onThisPage; s < MAX_DIAL_SLOTS; s += 1) slots.push(s);
316
- return slots;
317
- }, [allDialIcons, safePage]);
318
-
319
- // Swap to another page. Every icon is already mounted, so this only toggles
320
- // which ones are visible — no remount, no re-animation — keeping page
321
- // changes instant.
322
- const handlePageChange = next => {
323
- const clamped = Math.max(0, Math.min(next, pageCount - 1));
324
- if (clamped !== safePage) setCurrentPage(clamped);
325
- };
326
-
327
- // Initialize animations on mount - using shared config from core
328
- useEffect(() => {
329
- // Fallback config in case dialAnimationConfig hasn't loaded
330
- const config = dialAnimationConfig ?? {
331
- entrance: {
332
- backdrop: {
333
- duration: 400
334
- },
335
- dial: {
336
- scale: {
337
- damping: 15,
338
- stiffness: 150,
339
- mass: 1
340
- },
341
- rotation: {
342
- duration: 800
343
- }
344
- },
345
- centerButton: {
346
- delay: 150,
347
- damping: 10,
348
- stiffness: 200
349
- },
350
- icons: {
351
- delay: 200,
352
- duration: 400
353
- },
354
- circuitTraces: {
355
- delay: 600,
356
- duration: 1000
357
- }
358
- },
359
- continuous: {
360
- glitch: {
361
- interval: 3000,
362
- offset: 2,
363
- stepDuration: 50
364
- },
365
- pulse: {
366
- maxScale: 1.02,
367
- minScale: 0.98,
368
- duration: 1000
369
- },
370
- floating: {
371
- maxY: -8,
372
- minY: 0,
373
- duration: 3000
374
- },
375
- breathing: {
376
- maxScale: 1.05,
377
- minScale: 0.98,
378
- duration: 2500
379
- }
380
- }
381
- };
382
- const {
383
- entrance,
384
- continuous
385
- } = config;
386
-
387
- // Entrance animation sequence
388
- Animated.timing(backdropOpacity, {
389
- toValue: 1,
390
- duration: entrance.backdrop.duration,
391
- useNativeDriver: true
392
- }).start();
393
- Animated.spring(dialScale, {
394
- toValue: 1,
395
- damping: entrance.dial.scale.damping,
396
- stiffness: entrance.dial.scale.stiffness,
397
- mass: entrance.dial.scale.mass,
398
- useNativeDriver: true
399
- }).start();
400
- Animated.sequence([Animated.timing(dialRotation, {
401
- toValue: 1,
402
- duration: entrance.dial.rotation.duration,
403
- easing: Easing.out(Easing.cubic),
404
- useNativeDriver: true
405
- }), Animated.timing(dialRotation, {
406
- toValue: 0,
407
- duration: 0,
408
- useNativeDriver: true
409
- })]).start();
410
- Animated.sequence([Animated.delay(entrance.centerButton.delay), Animated.spring(centerButtonScale, {
411
- toValue: 1,
412
- damping: entrance.centerButton.damping,
413
- stiffness: entrance.centerButton.stiffness,
414
- useNativeDriver: true
415
- })]).start();
416
- Animated.sequence([Animated.delay(entrance.icons.delay), Animated.timing(iconsProgress, {
417
- toValue: 1,
418
- duration: entrance.icons.duration,
419
- easing: Easing.out(Easing.cubic),
420
- useNativeDriver: true
421
- })]).start();
422
-
423
- // Pulse animation - only continuous effect kept for performance
424
- if (!DIAL_IDLE_PULSE) return;
425
- pulseAnimationRef.current = Animated.loop(Animated.sequence([Animated.timing(pulseScale, {
426
- toValue: continuous.pulse.maxScale,
427
- duration: continuous.pulse.duration,
428
- easing: Easing.inOut(Easing.ease),
429
- useNativeDriver: true
430
- }), Animated.timing(pulseScale, {
431
- toValue: continuous.pulse.minScale,
432
- duration: continuous.pulse.duration,
433
- easing: Easing.inOut(Easing.ease),
434
- useNativeDriver: true
435
- })]));
436
- pulseAnimationRef.current.start();
437
- return () => {
438
- pulseAnimationRef.current?.stop();
439
- };
440
- }, []);
441
- const handleOnboardingDismiss = () => {
442
- // Mark as dismissed immediately in ref (synchronous, no re-render needed)
443
- onboardingDismissedRef.current = true;
444
-
445
- // Hide the tooltip
446
- setShowOnboardingTooltip(false);
447
-
448
- // Save to storage asynchronously in the background
449
- persistentStorage.setItem(ONBOARDING_STORAGE_KEY, "true").catch(error => {
450
- // Silently fail - user already saw onboarding, just won't persist
451
- console.warn("Failed to save dial onboarding state:", error);
452
- });
453
- };
454
- const handleClose = () => {
455
- // Stop any ongoing animations first
456
- if (pulseAnimationRef.current) {
457
- pulseAnimationRef.current.stop();
458
- }
459
- const exit = dialAnimationConfig?.exit ?? {
460
- icons: {
461
- duration: 300
462
- },
463
- centerButton: {
464
- duration: 200
465
- },
466
- dialScale: {
467
- duration: 250
468
- },
469
- backdrop: {
470
- duration: 200
471
- }
472
- };
473
-
474
- // Exit animation sequence - reverse order of entrance, using shared config
475
- Animated.sequence([
476
- // First animate icons back to center
477
- Animated.timing(iconsProgress, {
478
- toValue: 0,
479
- duration: exit.icons.duration,
480
- easing: Easing.in(Easing.cubic),
481
- useNativeDriver: true
482
- }),
483
- // Then scale down center button and dial
484
- Animated.parallel([Animated.timing(centerButtonScale, {
485
- toValue: 0,
486
- duration: exit.centerButton.duration,
487
- easing: Easing.in(Easing.cubic),
488
- useNativeDriver: true
489
- }), Animated.timing(dialScale, {
490
- toValue: 0,
491
- duration: exit.dialScale.duration,
492
- easing: Easing.in(Easing.cubic),
493
- useNativeDriver: true
494
- })]),
495
- // Finally fade out backdrop
496
- Animated.timing(backdropOpacity, {
497
- toValue: 0,
498
- duration: exit.backdrop.duration,
499
- useNativeDriver: true
500
- })]).start(() => {
501
- // Use setTimeout to defer the state update to the next tick
502
- // This avoids the useInsertionEffect warning
503
- if (onClose) {
504
- setTimeout(() => {
505
- onClose();
506
- }, 0);
507
- }
508
- });
509
- };
510
- const handleIconPress = icon => {
511
- setSelectedIcon(1);
512
- const interaction = dialAnimationConfig?.interaction ?? {
513
- iconSelect: {
514
- pulse: [{
515
- scale: 0.9,
516
- damping: 15,
517
- stiffness: 500
518
- }, {
519
- scale: 1,
520
- damping: 10,
521
- stiffness: 200
522
- }],
523
- actionDelay: 50
524
- }
525
- };
526
- const [pulseIn, pulseOut] = interaction.iconSelect.pulse;
527
-
528
- // Pulse animation on selection - using shared config
529
- Animated.sequence([Animated.spring(centerButtonScale, {
530
- toValue: pulseIn.scale,
531
- damping: pulseIn.damping,
532
- stiffness: pulseIn.stiffness,
533
- useNativeDriver: true
534
- }), Animated.spring(centerButtonScale, {
535
- toValue: pulseOut.scale,
536
- damping: pulseOut.damping,
537
- stiffness: pulseOut.stiffness,
538
- useNativeDriver: true
539
- })]).start();
540
-
541
- // Trigger action - using shared delay
542
- setTimeout(() => {
543
- icon.onPress();
544
- // Only close if it's not the WiFi toggle (by id)
545
- if (icon.id !== "wifi") {
546
- handleClose();
547
- }
548
- }, interaction.iconSelect.actionDelay);
549
- };
550
-
551
- // Animated styles
552
- const backdropAnimatedStyle = {
553
- opacity: backdropOpacity
554
- };
555
- const centerButtonAnimatedStyle = {
556
- transform: [{
557
- scale: centerButtonScale
558
- }]
559
- };
560
- const pulseAnimatedStyle = {
561
- transform: [{
562
- scale: selectedIcon >= 0 ? 1 : pulseScale
563
- }]
564
- };
565
- return /*#__PURE__*/_jsxs(View, {
566
- style: styles.container,
567
- nativeID: "dial-devtools-root",
568
- children: [/*#__PURE__*/_jsx(Animated.View, {
569
- style: [styles.backdrop, backdropAnimatedStyle],
570
- children: /*#__PURE__*/_jsx(Pressable, {
571
- style: absoluteFill,
572
- onPress: handleClose
573
- })
574
- }), /*#__PURE__*/_jsxs(Animated.View, {
575
- style: [styles.parent, sizeStyles.parent, {
576
- position: "absolute",
577
- left: (screenWidth - circleSize) / 2,
578
- bottom: 80,
579
- transform: [{
580
- scale: dialScale
581
- }, {
582
- rotate: dialRotation.interpolate({
583
- inputRange: [0, 1],
584
- outputRange: ["0deg", "360deg"]
585
- })
586
- }]
587
- }],
588
- children: [/*#__PURE__*/_jsxs(Animated.View, {
589
- style: [styles.circle, sizeStyles.circle],
590
- children: [/*#__PURE__*/_jsxs(View, {
591
- style: [styles.gradientBackground, sizeStyles.rounded],
592
- children: [/*#__PURE__*/_jsx(View, {
593
- style: [styles.gradientLayer1, sizeStyles.rounded]
594
- }), /*#__PURE__*/_jsx(View, {
595
- style: [styles.gradientLayer2, sizeStyles.rounded]
596
- }), /*#__PURE__*/_jsx(View, {
597
- style: [styles.gradientLayer3, sizeStyles.rounded]
598
- }), /*#__PURE__*/_jsx(View, {
599
- style: styles.gridPattern,
600
- children: Array.from({
601
- length: 6
602
- }).map((_, i) => /*#__PURE__*/_jsxs(View, {
603
- style: [styles.gridLine, sizeStyles.gridLine, {
604
- transform: [{
605
- rotate: `${i * 60}deg`
606
- }]
607
- }],
608
- children: [/*#__PURE__*/_jsx(View, {
609
- style: [styles.spoke, sizeStyles.spoke, styles.spokeLeft]
610
- }), /*#__PURE__*/_jsx(View, {
611
- style: [styles.spoke, sizeStyles.spoke, styles.spokeRight]
612
- })]
613
- }, i))
614
- }), backgroundId === "hyperspace" ? /*#__PURE__*/_jsx(DialHyperspace, {
615
- size: circleSize
616
- }) : /*#__PURE__*/_jsx(ToolBackground, {
617
- focus: DIAL_BACKGROUND_FOCUS
618
- })]
619
- }), allDialIcons.filter(({
620
- page
621
- }) => page === safePage).map(({
622
- icon,
623
- slot
624
- }) => /*#__PURE__*/_jsx(DialIcon, {
625
- onPress: handleIconPress,
626
- iconsProgress: iconsProgress,
627
- icon: icon,
628
- index: slot,
629
- totalIcons: MAX_DIAL_SLOTS,
630
- active: true
631
- }, icon.id ?? `page${safePage}-${slot}`)), emptySlots.map(slot => /*#__PURE__*/_jsx(DialIcon, {
632
- onPress: handleIconPress,
633
- iconsProgress: iconsProgress,
634
- icon: createEmptySlot(slot),
635
- index: slot,
636
- totalIcons: MAX_DIAL_SLOTS,
637
- active: true
638
- }, `empty-${slot}`))]
639
- }), /*#__PURE__*/_jsx(Animated.View, {
640
- style: [styles.buttonContainer, centerButtonAnimatedStyle],
641
- children: /*#__PURE__*/_jsxs(View, {
642
- style: styles.buttonGradient,
643
- children: [/*#__PURE__*/_jsx(View, {
644
- style: styles.buttonGradientLayer1
645
- }), /*#__PURE__*/_jsx(View, {
646
- style: styles.buttonGradientLayer2
647
- }), /*#__PURE__*/_jsx(View, {
648
- style: styles.buttonGradientLayer3
649
- }), /*#__PURE__*/_jsx(View, {
650
- style: styles.buttonBorder,
651
- children: /*#__PURE__*/_jsx(Animated.View, {
652
- style: [styles.button, pulseAnimatedStyle],
653
- children: /*#__PURE__*/_jsx(TouchableOpacity, {
654
- accessibilityRole: "button",
655
- accessibilityLabel: isSettingsModalOpen ? "Close Settings" : "Open Dev Tools Settings",
656
- onPress: toggleSettingsModal
657
- // Dims on touch-down so the press registers visually straight
658
- // away, even though the panel only opens on release.
659
- ,
660
- activeOpacity: 0.5,
661
- style: styles.buttonPressable,
662
- children: isSettingsModalOpen ? /*#__PURE__*/_jsxs(_Fragment, {
663
- children: [/*#__PURE__*/_jsx(Text, {
664
- style: [styles.centerText, styles.closeTextTop],
665
- children: "CLOSE"
666
- }), /*#__PURE__*/_jsx(Text, {
667
- style: [styles.centerText, styles.closeTextBottom],
668
- children: "SETTINGS"
669
- })]
670
- }) : /*#__PURE__*/_jsxs(_Fragment, {
671
- children: [/*#__PURE__*/_jsx(Text, {
672
- style: styles.centerText,
673
- children: "BUOY"
674
- }), isPro && /*#__PURE__*/_jsx(Text, {
675
- style: [styles.proText, isWeekendFree && styles.weekendText],
676
- children: isWeekendFree ? "WEEKEND" : "PRO"
677
- })]
678
- })
679
- })
680
- })
681
- })]
682
- })
683
- })]
684
- }), pageCount > 1 && /*#__PURE__*/_jsx(DialPagination, {
685
- page: safePage,
686
- pageCount: pageCount,
687
- onPrev: () => handlePageChange(safePage - 1),
688
- onNext: () => handlePageChange(safePage + 1),
689
- animatedStyle: {
690
- position: "absolute",
691
- left: (screenWidth - circleSize) / 2,
692
- // Circle's bottom edge sits at bottom: 80 -> screenHeight - 80
693
- // from the top. Place the pager 16px below that edge.
694
- top: screenHeight - 80 + 16,
695
- width: circleSize,
696
- opacity: dialScale,
697
- transform: [{
698
- scale: dialScale
699
- }]
700
- }
701
- }), isSettingsModalOpen && /*#__PURE__*/_jsx(DevToolsSettingsModal, {
702
- visible: true,
703
- onClose: () => {
704
- setIsSettingsModalOpen(false);
705
- refreshSettings(); // Refresh from storage
706
- },
707
- availableApps: availableApps
708
- }), showOnboardingTooltip && !isSettingsModalOpen && !onboardingDismissedRef.current && /*#__PURE__*/_jsx(OnboardingTooltip, {
709
- visible: true,
710
- onDismiss: handleOnboardingDismiss
711
- })]
712
- });
713
- };
714
- const styles = StyleSheet.create({
715
- container: {
716
- ...absoluteFill,
717
- zIndex: 9999
718
- },
719
- backdrop: {
720
- ...absoluteFill,
721
- backgroundColor: dialColors.dialBackdrop
722
- },
723
- // width/height/borderRadius for the circle pieces come from sizeStyles —
724
- // they track the live window width.
725
- parent: {
726
- alignItems: "center",
727
- justifyContent: "center"
728
- },
729
- circle: {
730
- position: "absolute",
731
- backgroundColor: "transparent",
732
- borderWidth: 1,
733
- borderColor: dialColors.dialBorder,
734
- shadowColor: dialColors.dialShadow,
735
- shadowOffset: {
736
- width: 0,
737
- height: 0
738
- },
739
- shadowOpacity: 0.5,
740
- shadowRadius: 20,
741
- elevation: 10
742
- },
743
- gradientBackground: {
744
- width: "100%",
745
- height: "100%",
746
- position: "relative",
747
- backgroundColor: dialColors.dialBackground,
748
- overflow: "hidden"
749
- },
750
- gradientLayer1: {
751
- ...absoluteFill,
752
- backgroundColor: dialColors.dialGradient1,
753
- opacity: 0.6
754
- },
755
- gradientLayer2: {
756
- ...absoluteFill,
757
- backgroundColor: dialColors.dialGradient2,
758
- opacity: 0.4,
759
- top: "30%",
760
- left: "30%"
761
- },
762
- gradientLayer3: {
763
- ...absoluteFill,
764
- backgroundColor: dialColors.dialGradient3,
765
- opacity: 0.3,
766
- top: "50%",
767
- left: "50%"
768
- },
769
- gridPattern: {
770
- ...absoluteFill,
771
- alignItems: "center",
772
- justifyContent: "center"
773
- },
774
- gridLine: {
775
- position: "absolute",
776
- height: 1
777
- },
778
- spoke: {
779
- position: "absolute",
780
- top: 0,
781
- height: 1,
782
- backgroundColor: dialColors.dialGridLine
783
- },
784
- spokeLeft: {
785
- left: 0
786
- },
787
- spokeRight: {
788
- right: 0
789
- },
790
- buttonContainer: {
791
- zIndex: 1,
792
- backgroundColor: "transparent",
793
- alignItems: "center",
794
- justifyContent: "center",
795
- position: "absolute",
796
- width: BUTTON_SIZE * 1.5,
797
- height: BUTTON_SIZE * 1.5,
798
- borderRadius: BUTTON_SIZE
799
- },
800
- // The hub is glass: no rings, no opaque fill. The outer disc is a barely-
801
- // there shade and the inner button a soft puck, so the tool background runs
802
- // through the middle of the dial while BUOY / PRO stay legible. The spokes
803
- // stop at HUB_CLEARANCE so nothing draws under it.
804
- buttonGradient: {
805
- width: "100%",
806
- height: "100%",
807
- borderRadius: BUTTON_SIZE,
808
- alignItems: "center",
809
- justifyContent: "center",
810
- padding: 4,
811
- backgroundColor: dialColors.hubHalo,
812
- position: "relative",
813
- overflow: "hidden"
814
- },
815
- buttonGradientLayer1: {
816
- ...absoluteFill,
817
- backgroundColor: dialColors.dialGradient1,
818
- opacity: 0.5,
819
- borderRadius: BUTTON_SIZE
820
- },
821
- buttonGradientLayer2: {
822
- ...absoluteFill,
823
- backgroundColor: dialColors.dialGradient2,
824
- opacity: 0.3,
825
- top: "20%",
826
- left: "20%",
827
- borderRadius: BUTTON_SIZE
828
- },
829
- buttonGradientLayer3: {
830
- ...absoluteFill,
831
- backgroundColor: dialColors.dialGradient3,
832
- opacity: 0.2,
833
- top: "40%",
834
- left: "40%",
835
- borderRadius: BUTTON_SIZE
836
- },
837
- buttonBorder: {
838
- backgroundColor: dialColors.hubFill,
839
- alignItems: "center",
840
- justifyContent: "center",
841
- width: BUTTON_SIZE * 1.2,
842
- height: BUTTON_SIZE * 1.2,
843
- borderRadius: BUTTON_SIZE * 0.6
844
- },
845
- button: {
846
- width: BUTTON_SIZE,
847
- height: BUTTON_SIZE,
848
- borderRadius: BUTTON_SIZE / 2,
849
- justifyContent: "center",
850
- alignItems: "center",
851
- position: "relative",
852
- overflow: "hidden"
853
- },
854
- buttonPressable: {
855
- width: "100%",
856
- height: "100%",
857
- justifyContent: "center",
858
- alignItems: "center"
859
- },
860
- centerText: {
861
- color: dialColors.centerText,
862
- fontSize: 10,
863
- fontWeight: "900",
864
- fontFamily: monoFont,
865
- letterSpacing: 1,
866
- textAlign: "center",
867
- textTransform: "uppercase",
868
- textShadowColor: dialColors.centerTextGlow,
869
- textShadowOffset: {
870
- width: 0,
871
- height: 0
872
- },
873
- textShadowRadius: 4
874
- },
875
- closeTextTop: {
876
- marginBottom: -2
877
- },
878
- closeTextBottom: {
879
- marginTop: -2
880
- },
881
- proText: {
882
- color: buoyColors.warning,
883
- fontSize: 8,
884
- fontWeight: "900",
885
- fontFamily: monoFont,
886
- letterSpacing: 2,
887
- textAlign: "center",
888
- textShadowColor: dialColors.centerTextGlow,
889
- textShadowOffset: {
890
- width: 0,
891
- height: 0
892
- },
893
- textShadowRadius: 4
894
- },
895
- // Weekend Pass variant — violet, tighter spacing so "WEEKEND" fits the dial.
896
- weekendText: {
897
- color: "#BF5AF2",
898
- letterSpacing: 1
899
- }
900
- });
1
+ "use strict";import{useEffect as b,useMemo as S,useRef as f,useState as w}from"react";import{Pressable as Ie,StyleSheet as Le,TouchableOpacity as Oe,View as s,useWindowDimensions as Be,Text as P,Animated as o,Easing as l}from"react-native";import{absoluteFill as u,monoFont as oe}from"@buoy-gg/shared-ui";import{DialIcon as ne}from"./DialIcon.js";import{DialHyperspace as Pe}from"./DialHyperspace.js";import{DialPagination as Re}from"./DialPagination.js";import{getRankedToolIds as F,isDialUsageLoaded as Ae,loadDialUsage as Ve,recordToolUsage as Ne}from"./dialUsageStore.js";import{persistentStorage as R,useHintsDisabled as Ge,devToolsStorageKeys as ie,buoyColors as re,ToolBackground as Me,useToolBackground as _e}from"@buoy-gg/shared-ui";import{DevToolsSettingsModal as Ee,useDevToolsSettings as Fe}from"../DevToolsSettingsModal";import{useProAccess as je}from"@buoy-gg/license";import{renderAppIcon as ae}from"../AppIcon";import{useAppHost as ze}from"../AppHost";import{OnboardingTooltip as Ue}from"./OnboardingTooltip";import{getDialLayout as We,MAX_DIAL_SLOTS as v,DIAL_BUTTON_SIZE as qe,dialAnimationConfig as j,dialColors as a}from"@buoy-gg/floating-tools-core";import{jsx as i,jsxs as h,Fragment as se}from"react/jsx-runtime";const c=qe;const He={x:.5,y:.5};const $e=c*.75+6;const Ke=false;const le="@react_buoy_settings_tooltip_shown";const Ye=y=>({id:`empty-${y}`,name:`empty-${y}`,icon:null,color:"transparent",onPress:()=>{}});export const DialDevTools=({onClose:y,onSettingsPress:ce,autoOpenSettings:z=false,apps:L,state:Ze,actions:U})=>{const[de,ue]=w(-1);const[g,T]=w(false);const[W,ge]=w(false);const[q,H]=w(false);const A=f(false);const $=Ge();const{refreshSettings:pe}=Fe();const{open:K}=ze();const{isPro:me,isWeekendFree:Y}=je();const{width:V,height:fe}=Be();const d=We({screenWidth:V}).circleSize;const{id:he}=_e();const p=S(()=>({parent:{width:d,height:d},circle:{width:d,height:d,borderRadius:d/2},rounded:{borderRadius:d/2},gridLine:{width:d},spoke:{width:d/2-$e}}),[d]);b(()=>{const t=async()=>{try{const e=await R.getItem(ie.settings.modalOpen());if(e==="true"){T(true)}}catch(e){}finally{ge(true)}};t()},[]);b(()=>{if(!W)return;R.setItem(ie.settings.modalOpen(),g?"true":"false").catch(t=>{console.warn("Failed to save settings modal state:",t)})},[g,W]);b(()=>{if(z&&!g){T(true)}},[z,g]);b(()=>{if($){return}const t=async()=>{try{const e=await R.getItem(le);if(!e){setTimeout(()=>{H(true)},1200)}}catch(e){}};t()},[$]);const N=f(new o.Value(0)).current;const k=f(new o.Value(0)).current;const G=f(new o.Value(0)).current;const D=f(new o.Value(0)).current;const O=f(new o.Value(0)).current;const M=f(new o.Value(1)).current;const ye=S(()=>L.map(({id:t,name:e,slot:r,description:I,icon:xe,color:Ce})=>({id:t,name:e,slot:r,description:I,icon:xe,color:Ce})),[L]);const x=f(null);const be=()=>{if(g){T(false);return}T(true);if(q&&!A.current){Q()}ce?.()};const C=S(()=>L.filter(t=>(t.slot??"both")!=="row"),[L]);const Z=S(()=>{const t=new Map;for(const e of C){t.set(e.id,{id:e.id,name:e.name,icon:typeof e.icon==="function"?null:ae(e.icon,{slot:"dial",size:20,color:e.color}),iconComponent:typeof e.icon==="function"?e.icon:void 0,color:e.color??re.primary,onPress:()=>{void Ne(e.id);e?.onPress?.(U);if(e.launchMode!=="toggle-only"){const r=ae(e.icon,{slot:"dial",size:20,color:e.color});K({id:e.id,title:e.name,component:e.component,props:e.props,launchMode:e.launchMode??"self-modal",singleton:e.singleton??true,icon:r,color:e.color,modal:e.modal})}y?.()}})}return t},[C,U,K,y]);const[_,X]=w(()=>F(C.map(t=>t.id)));b(()=>{const t=C.map(r=>r.id);if(Ae()){X(F(t));return}let e=false;Ve().then(()=>{if(!e)X(F(t))});return()=>{e=true}},[C]);const B=Math.max(1,Math.ceil(_.length/v));const[Se,we]=w(0);const m=Math.min(Se,B-1);const E=S(()=>{return _.map(t=>Z.get(t)).filter(t=>Boolean(t)).map((t,e)=>({icon:t,page:Math.floor(e/v),slot:e%v}))},[_,Z]);const ve=S(()=>{const t=E.filter(r=>r.page===m).length;const e=[];for(let r=t;r<v;r+=1)e.push(r);return e},[E,m]);const J=t=>{const e=Math.max(0,Math.min(t,B-1));if(e!==m)we(e)};b(()=>{const t=j??{entrance:{backdrop:{duration:400},dial:{scale:{damping:15,stiffness:150,mass:1},rotation:{duration:800}},centerButton:{delay:150,damping:10,stiffness:200},icons:{delay:200,duration:400},circuitTraces:{delay:600,duration:1e3}},continuous:{glitch:{interval:3e3,offset:2,stepDuration:50},pulse:{maxScale:1.02,minScale:.98,duration:1e3},floating:{maxY:-8,minY:0,duration:3e3},breathing:{maxScale:1.05,minScale:.98,duration:2500}}};const{entrance:e,continuous:r}=t;o.timing(N,{toValue:1,duration:e.backdrop.duration,useNativeDriver:true}).start();o.spring(k,{toValue:1,damping:e.dial.scale.damping,stiffness:e.dial.scale.stiffness,mass:e.dial.scale.mass,useNativeDriver:true}).start();o.sequence([o.timing(G,{toValue:1,duration:e.dial.rotation.duration,easing:l.out(l.cubic),useNativeDriver:true}),o.timing(G,{toValue:0,duration:0,useNativeDriver:true})]).start();o.sequence([o.delay(e.centerButton.delay),o.spring(D,{toValue:1,damping:e.centerButton.damping,stiffness:e.centerButton.stiffness,useNativeDriver:true})]).start();o.sequence([o.delay(e.icons.delay),o.timing(O,{toValue:1,duration:e.icons.duration,easing:l.out(l.cubic),useNativeDriver:true})]).start();if(!Ke)return;x.current=o.loop(o.sequence([o.timing(M,{toValue:r.pulse.maxScale,duration:r.pulse.duration,easing:l.inOut(l.ease),useNativeDriver:true}),o.timing(M,{toValue:r.pulse.minScale,duration:r.pulse.duration,easing:l.inOut(l.ease),useNativeDriver:true})]));x.current.start();return()=>{x.current?.stop()}},[]);const Q=()=>{A.current=true;H(false);R.setItem(le,"true").catch(t=>{console.warn("Failed to save dial onboarding state:",t)})};const ee=()=>{if(x.current){x.current.stop()}const t=j?.exit??{icons:{duration:300},centerButton:{duration:200},dialScale:{duration:250},backdrop:{duration:200}};o.sequence([o.timing(O,{toValue:0,duration:t.icons.duration,easing:l.in(l.cubic),useNativeDriver:true}),o.parallel([o.timing(D,{toValue:0,duration:t.centerButton.duration,easing:l.in(l.cubic),useNativeDriver:true}),o.timing(k,{toValue:0,duration:t.dialScale.duration,easing:l.in(l.cubic),useNativeDriver:true})]),o.timing(N,{toValue:0,duration:t.backdrop.duration,useNativeDriver:true})]).start(()=>{if(y){setTimeout(()=>{y()},0)}})};const te=t=>{ue(1);const e=j?.interaction??{iconSelect:{pulse:[{scale:.9,damping:15,stiffness:500},{scale:1,damping:10,stiffness:200}],actionDelay:50}};const[r,I]=e.iconSelect.pulse;o.sequence([o.spring(D,{toValue:r.scale,damping:r.damping,stiffness:r.stiffness,useNativeDriver:true}),o.spring(D,{toValue:I.scale,damping:I.damping,stiffness:I.stiffness,useNativeDriver:true})]).start();setTimeout(()=>{t.onPress();if(t.id!=="wifi"){ee()}},e.iconSelect.actionDelay)};const Te={opacity:N};const ke={transform:[{scale:D}]};const De={transform:[{scale:de>=0?1:M}]};return h(s,{style:n.container,nativeID:"dial-devtools-root",children:[i(o.View,{style:[n.backdrop,Te],children:i(Ie,{style:u,onPress:ee})}),h(o.View,{style:[n.parent,p.parent,{position:"absolute",left:(V-d)/2,bottom:80,transform:[{scale:k},{rotate:G.interpolate({inputRange:[0,1],outputRange:["0deg","360deg"]})}]}],children:[h(o.View,{style:[n.circle,p.circle],children:[h(s,{style:[n.gradientBackground,p.rounded],children:[i(s,{style:[n.gradientLayer1,p.rounded]}),i(s,{style:[n.gradientLayer2,p.rounded]}),i(s,{style:[n.gradientLayer3,p.rounded]}),i(s,{style:n.gridPattern,children:Array.from({length:6}).map((t,e)=>h(s,{style:[n.gridLine,p.gridLine,{transform:[{rotate:`${e*60}deg`}]}],children:[i(s,{style:[n.spoke,p.spoke,n.spokeLeft]}),i(s,{style:[n.spoke,p.spoke,n.spokeRight]})]},e))}),he==="hyperspace"?i(Pe,{size:d}):i(Me,{focus:He})]}),E.filter(({page:t})=>t===m).map(({icon:t,slot:e})=>i(ne,{onPress:te,iconsProgress:O,icon:t,index:e,totalIcons:v,active:true},t.id??`page${m}-${e}`)),ve.map(t=>i(ne,{onPress:te,iconsProgress:O,icon:Ye(t),index:t,totalIcons:v,active:true},`empty-${t}`))]}),i(o.View,{style:[n.buttonContainer,ke],children:h(s,{style:n.buttonGradient,children:[i(s,{style:n.buttonGradientLayer1}),i(s,{style:n.buttonGradientLayer2}),i(s,{style:n.buttonGradientLayer3}),i(s,{style:n.buttonBorder,children:i(o.View,{style:[n.button,De],children:i(Oe,{accessibilityRole:"button",accessibilityLabel:g?"Close Settings":"Open Dev Tools Settings",onPress:be,activeOpacity:.5,style:n.buttonPressable,children:g?h(se,{children:[i(P,{style:[n.centerText,n.closeTextTop],children:"CLOSE"}),i(P,{style:[n.centerText,n.closeTextBottom],children:"SETTINGS"})]}):h(se,{children:[i(P,{style:n.centerText,children:"BUOY"}),me&&i(P,{style:[n.proText,Y&&n.weekendText],children:Y?"WEEKEND":"PRO"})]})})})})]})})]}),B>1&&i(Re,{page:m,pageCount:B,onPrev:()=>J(m-1),onNext:()=>J(m+1),animatedStyle:{position:"absolute",left:(V-d)/2,top:fe-80+16,width:d,opacity:k,transform:[{scale:k}]}}),g&&i(Ee,{visible:true,onClose:()=>{T(false);pe()},availableApps:ye}),q&&!g&&!A.current&&i(Ue,{visible:true,onDismiss:Q})]})};const n=Le.create({container:{...u,zIndex:9999},backdrop:{...u,backgroundColor:a.dialBackdrop},parent:{alignItems:"center",justifyContent:"center"},circle:{position:"absolute",backgroundColor:"transparent",borderWidth:1,borderColor:a.dialBorder,shadowColor:a.dialShadow,shadowOffset:{width:0,height:0},shadowOpacity:.5,shadowRadius:20,elevation:10},gradientBackground:{width:"100%",height:"100%",position:"relative",backgroundColor:a.dialBackground,overflow:"hidden"},gradientLayer1:{...u,backgroundColor:a.dialGradient1,opacity:.6},gradientLayer2:{...u,backgroundColor:a.dialGradient2,opacity:.4,top:"30%",left:"30%"},gradientLayer3:{...u,backgroundColor:a.dialGradient3,opacity:.3,top:"50%",left:"50%"},gridPattern:{...u,alignItems:"center",justifyContent:"center"},gridLine:{position:"absolute",height:1},spoke:{position:"absolute",top:0,height:1,backgroundColor:a.dialGridLine},spokeLeft:{left:0},spokeRight:{right:0},buttonContainer:{zIndex:1,backgroundColor:"transparent",alignItems:"center",justifyContent:"center",position:"absolute",width:c*1.5,height:c*1.5,borderRadius:c},buttonGradient:{width:"100%",height:"100%",borderRadius:c,alignItems:"center",justifyContent:"center",padding:4,backgroundColor:a.hubHalo,position:"relative",overflow:"hidden"},buttonGradientLayer1:{...u,backgroundColor:a.dialGradient1,opacity:.5,borderRadius:c},buttonGradientLayer2:{...u,backgroundColor:a.dialGradient2,opacity:.3,top:"20%",left:"20%",borderRadius:c},buttonGradientLayer3:{...u,backgroundColor:a.dialGradient3,opacity:.2,top:"40%",left:"40%",borderRadius:c},buttonBorder:{backgroundColor:a.hubFill,alignItems:"center",justifyContent:"center",width:c*1.2,height:c*1.2,borderRadius:c*.6},button:{width:c,height:c,borderRadius:c/2,justifyContent:"center",alignItems:"center",position:"relative",overflow:"hidden"},buttonPressable:{width:"100%",height:"100%",justifyContent:"center",alignItems:"center"},centerText:{color:a.centerText,fontSize:10,fontWeight:"900",fontFamily:oe,letterSpacing:1,textAlign:"center",textTransform:"uppercase",textShadowColor:a.centerTextGlow,textShadowOffset:{width:0,height:0},textShadowRadius:4},closeTextTop:{marginBottom:-2},closeTextBottom:{marginTop:-2},proText:{color:re.warning,fontSize:8,fontWeight:"900",fontFamily:oe,letterSpacing:2,textAlign:"center",textShadowColor:a.centerTextGlow,textShadowOffset:{width:0,height:0},textShadowRadius:4},weekendText:{color:"#BF5AF2",letterSpacing:1}});