hypervibes 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (362) hide show
  1. package/.build/client/assets/_authenticated-C87LKvH0.js +1 -0
  2. package/.build/client/assets/_authenticated.account-D-nTh_fC.js +1 -0
  3. package/.build/client/assets/_authenticated.agents-HwcveLyB.js +1 -0
  4. package/.build/client/assets/_authenticated.agents._agentId-DuE8Lv62.js +1 -0
  5. package/.build/client/assets/_authenticated.agents._agentId.chart-Bb_XkvQx.js +4 -0
  6. package/.build/client/assets/_authenticated.agents._agentId.chat-ClMMKw_N.js +1 -0
  7. package/.build/client/assets/_authenticated.agents._agentId.index-DNJwBwn9.js +1 -0
  8. package/.build/client/assets/_authenticated.agents._agentId.indicators-BmVjLtRI.js +1 -0
  9. package/.build/client/assets/_authenticated.agents._agentId.indicators_._indicatorId-CMqaaFQf.js +2 -0
  10. package/.build/client/assets/_authenticated.agents._agentId.indicators_._indicatorId-DtgkoNV5.js +6 -0
  11. package/.build/client/assets/_authenticated.agents._agentId.memories-O-4f6a4G.js +1 -0
  12. package/.build/client/assets/_authenticated.agents._agentId.memories_._memoryRef-BUPtUcj3.js +1 -0
  13. package/.build/client/assets/_authenticated.agents._agentId.notifications-2lyhXCSk.js +1 -0
  14. package/.build/client/assets/_authenticated.agents._agentId.notifications_._notificationId-Da3ZVT4v.js +1 -0
  15. package/.build/client/assets/_authenticated.agents._agentId.settings-C5cWHoYw.js +1 -0
  16. package/.build/client/assets/_authenticated.agents._agentId.sub-agents-HwcveLyB.js +1 -0
  17. package/.build/client/assets/_authenticated.agents._agentId.sub-agents._subagentId-CZ4MeRy4.js +1 -0
  18. package/.build/client/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-dVbo5kMU.js +3 -0
  19. package/.build/client/assets/_authenticated.agents._agentId.sub-agents.index-DeWTOYJD.js +1 -0
  20. package/.build/client/assets/_authenticated.agents._agentId.sub-agents.new-D02TflCE.js +37 -0
  21. package/.build/client/assets/_authenticated.agents._agentId.transactions-D3a8Glgj.js +1 -0
  22. package/.build/client/assets/_authenticated.agents.index-CEqeDEgP.js +1 -0
  23. package/.build/client/assets/_authenticated.agents.new-DLRWK5Uk.js +1 -0
  24. package/.build/client/assets/_authenticated.providers-D0iPLXra.js +1 -0
  25. package/.build/client/assets/agent-chat-page-C5eYFgxw.js +1 -0
  26. package/.build/client/assets/agent-enablement-control-CUzOp9ce.js +1 -0
  27. package/.build/client/assets/agent-events-DC8eDZf3.js +1 -0
  28. package/.build/client/assets/agent-input-aolvZ47U.js +1 -0
  29. package/.build/client/assets/agent-notifications-page-B0mr-Zz8.js +1 -0
  30. package/.build/client/assets/agent-queries-CNMBImun.js +1 -0
  31. package/.build/client/assets/app-DjmsvbUC.css +2 -0
  32. package/.build/client/assets/artifact-content-COO4VTSZ.js +1 -0
  33. package/.build/client/assets/auth-client-K13li9H1.js +3 -0
  34. package/.build/client/assets/call-DxlGie4_.js +31 -0
  35. package/.build/client/assets/ccip-BgPlVPYP.js +1 -0
  36. package/.build/client/assets/chart-contracts-Cr5SFCbE.js +1 -0
  37. package/.build/client/assets/chart-queries-Bpvrgd0-.js +1 -0
  38. package/.build/client/assets/chat-bubble-CAJkT-vL.js +1 -0
  39. package/.build/client/assets/config-dialog-C9x9gKW9.js +1 -0
  40. package/.build/client/assets/contracts-HhAL7Tt7.js +1 -0
  41. package/.build/client/assets/createServerFn-DfZIp2Fu.js +3 -0
  42. package/.build/client/assets/custom-BeqXuIIS.js +5 -0
  43. package/.build/client/assets/decimal-B0DEG-1V.js +1 -0
  44. package/.build/client/assets/esm-BTb2nBdW.js +1 -0
  45. package/.build/client/assets/identicon-Lb9pS4O1.js +1 -0
  46. package/.build/client/assets/index-B4hvjMmI.js +10 -0
  47. package/.build/client/assets/indicator-input-fields-CtmrfVfS.js +1 -0
  48. package/.build/client/assets/indicator-queries-CzZ6u1Mh.js +1 -0
  49. package/.build/client/assets/indicator-renderer-CfccsIt2.js +1 -0
  50. package/.build/client/assets/instrument-label-BZYlWXR5.js +1 -0
  51. package/.build/client/assets/instrument-picker-DLPpryOm.js +1 -0
  52. package/.build/client/assets/journal-decimal-DBvd_Ibi.js +1 -0
  53. package/.build/client/assets/jsx-runtime-CIazjfqY.js +1 -0
  54. package/.build/client/assets/lazyRouteComponent-CJgUa7_r.js +1 -0
  55. package/.build/client/assets/lightweight-charts.production-BiBiFZSH.js +1 -0
  56. package/.build/client/assets/login-MNgYxCOY.js +3 -0
  57. package/.build/client/assets/memory-badge-BbN-DJQi.js +1 -0
  58. package/.build/client/assets/memory-contracts-Doi_Q1XJ.js +1 -0
  59. package/.build/client/assets/memory-queries-BttL580R.js +1 -0
  60. package/.build/client/assets/model-picker-YCQNNmyw.js +1 -0
  61. package/.build/client/assets/notification-queries-B12DG3aS.js +1 -0
  62. package/.build/client/assets/preload-helper-Czpn1I53.js +1 -0
  63. package/.build/client/assets/provider-logo-Ddb77aBD.js +1 -0
  64. package/.build/client/assets/provider-queries-DxPwhDOU.js +1 -0
  65. package/.build/client/assets/query-nvK_95Jo.js +1 -0
  66. package/.build/client/assets/random-uuid-DJR3ygJS.js +1 -0
  67. package/.build/client/assets/react-dom-C6bOPxes.js +1 -0
  68. package/.build/client/assets/risk-queries-D_6PHwus.js +1 -0
  69. package/.build/client/assets/run-subagent-dialog-B4yQe9T9.js +1 -0
  70. package/.build/client/assets/secp256k1-BbZnIgDP.js +1 -0
  71. package/.build/client/assets/setup-checklist-CUW-K484.js +1 -0
  72. package/.build/client/assets/sha2-hT-Q4pur.js +1 -0
  73. package/.build/client/assets/subagent-enabled-toggle-Bk5wfScg.js +1 -0
  74. package/.build/client/assets/subagent-input-TQke90I1.js +1 -0
  75. package/.build/client/assets/subagent-mutations-DfsDbmFu.js +1 -0
  76. package/.build/client/assets/subagent-queries-DVv5JCfs.js +1 -0
  77. package/.build/client/assets/subagent-run-history-D55Uh5VK.js +1 -0
  78. package/.build/client/assets/toggle-timestamp-B50FTPmA.js +1 -0
  79. package/.build/client/assets/useBaseQuery-Bxruo_mV.js +1 -0
  80. package/.build/client/assets/useMutation-DrF53EQG.js +1 -0
  81. package/.build/client/assets/useQuery-BMSU16jY.js +1 -0
  82. package/.build/client/assets/useRouter-c0I1aURm.js +1 -0
  83. package/.build/client/assets/useSelector-ERD9RroG.js +2 -0
  84. package/.build/client/assets/useSuspenseQuery-vODFwDb8.js +1 -0
  85. package/.build/client/assets/utils-Bpv4vmSW.js +1 -0
  86. package/.build/client/assets/worker-triggers-DtXyn40O.js +1 -0
  87. package/.build/server/assets/_authenticated-Bch0_2QH.js +264 -0
  88. package/.build/server/assets/_authenticated-DGV5cZYE.js +22 -0
  89. package/.build/server/assets/_authenticated.account-6qu6oQ1y.js +685 -0
  90. package/.build/server/assets/_authenticated.agents-DYDnQxli.js +5 -0
  91. package/.build/server/assets/_authenticated.agents._agentId-4HvB3mhd.js +17 -0
  92. package/.build/server/assets/_authenticated.agents._agentId-Cl608F1T.js +260 -0
  93. package/.build/server/assets/_authenticated.agents._agentId-G2jAHjma.js +15 -0
  94. package/.build/server/assets/_authenticated.agents._agentId.chart-BIA8wy73.js +2644 -0
  95. package/.build/server/assets/_authenticated.agents._agentId.chart-D4aj8Qy9.js +22 -0
  96. package/.build/server/assets/_authenticated.agents._agentId.chat-ClsVle4g.js +13 -0
  97. package/.build/server/assets/_authenticated.agents._agentId.chat-JV3FDsTp.js +18 -0
  98. package/.build/server/assets/_authenticated.agents._agentId.index-Ct4kbu2f.js +1044 -0
  99. package/.build/server/assets/_authenticated.agents._agentId.index-DJgrupvW.js +18 -0
  100. package/.build/server/assets/_authenticated.agents._agentId.indicators-DrKd0aP5.js +184 -0
  101. package/.build/server/assets/_authenticated.agents._agentId.indicators-wiVaY8fW.js +18 -0
  102. package/.build/server/assets/_authenticated.agents._agentId.indicators_._indicatorId-C4Ox2R-a.js +25 -0
  103. package/.build/server/assets/_authenticated.agents._agentId.indicators_._indicatorId-D8Q0hCW0.js +489 -0
  104. package/.build/server/assets/_authenticated.agents._agentId.memories-BUTyVLYy.js +42 -0
  105. package/.build/server/assets/_authenticated.agents._agentId.memories-E8-nSEMT.js +362 -0
  106. package/.build/server/assets/_authenticated.agents._agentId.memories_._memoryRef-C-SC6wzq.js +14 -0
  107. package/.build/server/assets/_authenticated.agents._agentId.memories_._memoryRef-ZCrdrc8C.js +490 -0
  108. package/.build/server/assets/_authenticated.agents._agentId.notifications-DQeQqTzh.js +14 -0
  109. package/.build/server/assets/_authenticated.agents._agentId.notifications-DlFyYyHh.js +16 -0
  110. package/.build/server/assets/_authenticated.agents._agentId.notifications_._notificationId-B0RosY_R.js +15 -0
  111. package/.build/server/assets/_authenticated.agents._agentId.notifications_._notificationId-C1NikQ9e.js +13 -0
  112. package/.build/server/assets/_authenticated.agents._agentId.settings-DDwjT3Ui.js +42 -0
  113. package/.build/server/assets/_authenticated.agents._agentId.settings-PEzEvOfo.js +751 -0
  114. package/.build/server/assets/_authenticated.agents._agentId.sub-agents-z34-J3Tu.js +5 -0
  115. package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId-D-6TOi1U.js +1371 -0
  116. package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId-DipzVdnr.js +13 -0
  117. package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-BZR3VLV7.js +610 -0
  118. package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-DsYJKfGm.js +12 -0
  119. package/.build/server/assets/_authenticated.agents._agentId.sub-agents.index-BF8Cuzcr.js +231 -0
  120. package/.build/server/assets/_authenticated.agents._agentId.sub-agents.index-D2x0qCnt.js +12 -0
  121. package/.build/server/assets/_authenticated.agents._agentId.sub-agents.new-CYm_vuDD.js +13 -0
  122. package/.build/server/assets/_authenticated.agents._agentId.sub-agents.new-CiVShIka.js +497 -0
  123. package/.build/server/assets/_authenticated.agents._agentId.transactions-BC_XfGqw.js +95 -0
  124. package/.build/server/assets/_authenticated.agents._agentId.transactions-WtBW3xmh.js +495 -0
  125. package/.build/server/assets/_authenticated.agents.index-C8zmu8T8.js +78 -0
  126. package/.build/server/assets/_authenticated.agents.new-Bt-tBR47.js +302 -0
  127. package/.build/server/assets/_authenticated.providers-C0pb-ujV.js +672 -0
  128. package/.build/server/assets/_tanstack-start-manifest_v-Baw5u9sk.js +354 -0
  129. package/.build/server/assets/account-XvMwzRXh.js +66 -0
  130. package/.build/server/assets/account-queries-AJJVkLAV.js +32 -0
  131. package/.build/server/assets/agent-chat-page-DChiz8KQ.js +686 -0
  132. package/.build/server/assets/agent-enablement-control-C90f0uIb.js +143 -0
  133. package/.build/server/assets/agent-events-DK9Yz5__.js +217 -0
  134. package/.build/server/assets/agent-notifications-page-BdyKAyds.js +276 -0
  135. package/.build/server/assets/agent-queries-Cmx8RiUx.js +51 -0
  136. package/.build/server/assets/agents-CZq-0mna.js +106 -0
  137. package/.build/server/assets/agents-CfjmtW7A.js +278 -0
  138. package/.build/server/assets/artifact-content-B6uHmPz7.js +35 -0
  139. package/.build/server/assets/artifacts-HtLwHs8q.js +42 -0
  140. package/.build/server/assets/auth-DTikZAVz.js +12 -0
  141. package/.build/server/assets/auth-client-Vb3O0v7P.js +1897 -0
  142. package/.build/server/assets/auth-user-DkLo8ojH.js +108 -0
  143. package/.build/server/assets/chart-queries-Bc3AfsEb.js +109 -0
  144. package/.build/server/assets/charts-C4dKuwTJ.js +130 -0
  145. package/.build/server/assets/chat-bubble-ik3itVbx.js +109 -0
  146. package/.build/server/assets/config-dialog-9gSAjwwT.js +55 -0
  147. package/.build/server/assets/contracts-CG-nZuYJ.js +155 -0
  148. package/.build/server/assets/createServerRpc-DNqhdU4z.js +12 -0
  149. package/.build/server/assets/decimal-BthtZ77w.js +97 -0
  150. package/.build/server/assets/empty-plugin-adapters-MOWBbCMw.js +5 -0
  151. package/.build/server/assets/errors-DaZqUftX.js +151 -0
  152. package/.build/server/assets/gateway-CHkruO_a.js +193 -0
  153. package/.build/server/assets/identicon-CyawcVnx.js +41 -0
  154. package/.build/server/assets/indicator-input-fields-bzuhIh6t.js +75 -0
  155. package/.build/server/assets/indicator-queries-4dY1-LXx.js +171 -0
  156. package/.build/server/assets/indicator-renderer-B2muPS-8.js +395 -0
  157. package/.build/server/assets/indicators-ENxAHN42.js +192 -0
  158. package/.build/server/assets/insight-contracts-B5rmUDfO.js +14 -0
  159. package/.build/server/assets/instrument-label-C-tTGGsD.js +40 -0
  160. package/.build/server/assets/instrument-picker-CRroLwVq.js +381 -0
  161. package/.build/server/assets/journal-9aE-um9f.js +107 -0
  162. package/.build/server/assets/journal-decimal-Bh9EL029.js +66 -0
  163. package/.build/server/assets/login-BsEFUvTX.js +125 -0
  164. package/.build/server/assets/memories-2WF2fW1G.js +100 -0
  165. package/.build/server/assets/memory-badge-DVYLTtRR.js +53 -0
  166. package/.build/server/assets/memory-contracts-DQGN2IKV.js +22 -0
  167. package/.build/server/assets/memory-queries--4RbPRrk.js +92 -0
  168. package/.build/server/assets/model-picker-CA1uYb--.js +225 -0
  169. package/.build/server/assets/notification-queries-0CJUkBt9.js +52 -0
  170. package/.build/server/assets/notifications-Cn7BiCyw.js +72 -0
  171. package/.build/server/assets/provider-logo-BRdbOr9I.js +32 -0
  172. package/.build/server/assets/provider-queries-BaZWLw8J.js +78 -0
  173. package/.build/server/assets/providers-CyoN_jgW.js +89 -0
  174. package/.build/server/assets/risk-Bqrb6hKQ.js +34 -0
  175. package/.build/server/assets/risk-queries-JQpByHT2.js +18 -0
  176. package/.build/server/assets/router-Hkr5TH_I.js +419 -0
  177. package/.build/server/assets/run-subagent-dialog-C7iUOZa-.js +59 -0
  178. package/.build/server/assets/setup-DqMlwgNr.js +30 -0
  179. package/.build/server/assets/setup-checklist-BICTmBo2.js +60 -0
  180. package/.build/server/assets/start-C-GPoWRt.js +4 -0
  181. package/.build/server/assets/subagent-enabled-toggle-Djte00oo.js +22 -0
  182. package/.build/server/assets/subagent-input-BHQKs2cW.js +57 -0
  183. package/.build/server/assets/subagent-mutations-dUo3B_SG.js +32 -0
  184. package/.build/server/assets/subagent-queries-sAwBjFxC.js +146 -0
  185. package/.build/server/assets/subagent-run-history-C_xmucO-.js +186 -0
  186. package/.build/server/assets/subagents-CIoilC66.js +180 -0
  187. package/.build/server/assets/toggle-timestamp-CToUVn67.js +28 -0
  188. package/.build/server/assets/trading-contracts-zWq1Hxnb.js +26 -0
  189. package/.build/server/assets/validation-BX-krL0i.js +33 -0
  190. package/.build/server/assets/worker-triggers-CJF5HWzR.js +51 -0
  191. package/.build/server/server.js +7501 -0
  192. package/LICENSE +661 -0
  193. package/README.md +145 -2
  194. package/bin/hypervibes.js +24 -0
  195. package/dist/agents/agent-enablement.js +10 -0
  196. package/dist/agents/agent-input.js +38 -0
  197. package/dist/agents/agent-service.js +1004 -0
  198. package/dist/agents/cancel-task-scope.js +52 -0
  199. package/dist/agents/candle-intervals.js +1 -0
  200. package/dist/agents/default-subagents.js +36 -0
  201. package/dist/agents/execution-context-contracts.js +1 -0
  202. package/dist/agents/execution-context.js +206 -0
  203. package/dist/agents/execution-warnings.js +33 -0
  204. package/dist/agents/orchestrator-insights.js +14 -0
  205. package/dist/agents/run-inspection.js +99 -0
  206. package/dist/agents/subagent-dashboard.js +1 -0
  207. package/dist/agents/subagent-extension.js +156 -0
  208. package/dist/agents/subagent-history.js +5 -0
  209. package/dist/agents/subagent-input.js +73 -0
  210. package/dist/agents/subagent-service.js +1177 -0
  211. package/dist/agents/subagent-templates.js +20 -0
  212. package/dist/agents/template-manifest.js +34 -0
  213. package/dist/agents/templates/basic-analysis.json +40 -0
  214. package/dist/agents/templates/basic-review.json +48 -0
  215. package/dist/agents/templates/basic-trading.json +45 -0
  216. package/dist/agents/templates/indicator-developer.json +16 -0
  217. package/dist/agents/time-guidance.js +29 -0
  218. package/dist/agents/worker-events.js +18 -0
  219. package/dist/agents/worker-inbox.js +28 -0
  220. package/dist/agents/worker-triggers.js +23 -0
  221. package/dist/auth-options.js +98 -0
  222. package/dist/auth-user.js +15 -0
  223. package/dist/auth.js +28 -0
  224. package/dist/cache/svg-logo-cache.js +155 -0
  225. package/dist/canonical.js +13 -0
  226. package/dist/charts/chart-service.js +236 -0
  227. package/dist/charts/contracts.js +118 -0
  228. package/dist/charts/durable-extension.js +27 -0
  229. package/dist/charts/trading-contracts.js +14 -0
  230. package/dist/config.js +85 -0
  231. package/dist/db/agent-schema.js +26 -0
  232. package/dist/db/auth-schema.js +122 -0
  233. package/dist/db/database.js +31 -0
  234. package/dist/db/exchange-schema.js +23 -0
  235. package/dist/db/gateway-schema.js +24 -0
  236. package/dist/db/indicator-schema.js +19 -0
  237. package/dist/db/migrations/20261015221717_auth/migration.sql +62 -0
  238. package/dist/db/migrations/20261015231224_agents/migration.sql +35 -0
  239. package/dist/db/migrations/20261016040641_provider_connections/migration.sql +19 -0
  240. package/dist/db/migrations/20261016061831_trade_journal/migration.sql +48 -0
  241. package/dist/db/migrations/20261016080320_order_execution/migration.sql +62 -0
  242. package/dist/db/migrations/20261016181110_subagents/migration.sql +51 -0
  243. package/dist/db/migrations/20261017222959_research_artifacts/migration.sql +37 -0
  244. package/dist/db/migrations/20261018061531_memories_and_insights/migration.sql +162 -0
  245. package/dist/db/migrations/20261020091156_worker_event_inbox/migration.sql +54 -0
  246. package/dist/db/migrations/20261021051229_chart_workspace/migration.sql +33 -0
  247. package/dist/db/migrations/20261021222116_gateways/migration.sql +15 -0
  248. package/dist/db/migrations/20261022015813_pine_indicators/migration.sql +43 -0
  249. package/dist/db/migrations/20261022101559_notifications/migration.sql +51 -0
  250. package/dist/db/migrations.js +12 -0
  251. package/dist/db/order-provenance-schema.js +17 -0
  252. package/dist/db/order-schema.js +32 -0
  253. package/dist/db/profile-lock.js +12 -0
  254. package/dist/db/provider-schema.js +16 -0
  255. package/dist/db/shared-database.js +128 -0
  256. package/dist/db/subagent-schema.js +79 -0
  257. package/dist/gateway/bridge-service.js +223 -0
  258. package/dist/gateway/gateway-store.js +77 -0
  259. package/dist/gateway/handlers.js +122 -0
  260. package/dist/gateway/telegram/connector.js +115 -0
  261. package/dist/gateway/telegram/transport.js +96 -0
  262. package/dist/hyperliquid/account-info.js +117 -0
  263. package/dist/hyperliquid/account-status.js +64 -0
  264. package/dist/hyperliquid/agent-dashboard.js +56 -0
  265. package/dist/hyperliquid/chart-contracts.js +9 -0
  266. package/dist/hyperliquid/constants.js +25 -0
  267. package/dist/hyperliquid/crypto.js +29 -0
  268. package/dist/hyperliquid/currency-logo.js +7 -0
  269. package/dist/hyperliquid/decimal.js +98 -0
  270. package/dist/hyperliquid/durable-extension.js +39 -0
  271. package/dist/hyperliquid/errors.js +8 -0
  272. package/dist/hyperliquid/exchange.js +118 -0
  273. package/dist/hyperliquid/journal-decimal.js +48 -0
  274. package/dist/hyperliquid/journal-model.js +167 -0
  275. package/dist/hyperliquid/journal-service.js +648 -0
  276. package/dist/hyperliquid/live-dashboard.js +245 -0
  277. package/dist/hyperliquid/logo-cache.js +27 -0
  278. package/dist/hyperliquid/market-charts.js +242 -0
  279. package/dist/hyperliquid/market-snapshot.js +45 -0
  280. package/dist/hyperliquid/orders/order-contracts.js +42 -0
  281. package/dist/hyperliquid/orders/order-math.js +89 -0
  282. package/dist/hyperliquid/orders/order-outcomes.js +74 -0
  283. package/dist/hyperliquid/orders/order-prepare.js +109 -0
  284. package/dist/hyperliquid/orders/order-service.js +432 -0
  285. package/dist/hyperliquid/orders/order-store.js +134 -0
  286. package/dist/hyperliquid/orders/order-tools.js +40 -0
  287. package/dist/hyperliquid/orders/protection.js +35 -0
  288. package/dist/hyperliquid/orders/risk-settings.js +44 -0
  289. package/dist/hyperliquid/orders/trade-admission.js +117 -0
  290. package/dist/hyperliquid/orders/trade-planning.js +60 -0
  291. package/dist/hyperliquid/service.js +529 -0
  292. package/dist/hyperliquid/shared-feeds.js +40 -0
  293. package/dist/hyperliquid/signing.js +89 -0
  294. package/dist/indicators/builtin-catalog.js +59 -0
  295. package/dist/indicators/builtins/adx.pine +22 -0
  296. package/dist/indicators/builtins/atr.pine +11 -0
  297. package/dist/indicators/builtins/bollinger.pine +10 -0
  298. package/dist/indicators/builtins/ema.pine +5 -0
  299. package/dist/indicators/builtins/macd.pine +18 -0
  300. package/dist/indicators/builtins/rsi.pine +18 -0
  301. package/dist/indicators/builtins/sma.pine +5 -0
  302. package/dist/indicators/candle-window.js +36 -0
  303. package/dist/indicators/chart-contracts.js +64 -0
  304. package/dist/indicators/contracts.js +14 -0
  305. package/dist/indicators/durable-extension.js +32 -0
  306. package/dist/indicators/indicator-service.js +300 -0
  307. package/dist/indicators/indicator-store.js +137 -0
  308. package/dist/indicators/normalize.js +149 -0
  309. package/dist/indicators/result-schema.js +79 -0
  310. package/dist/indicators/runtime-wrapper.js +52 -0
  311. package/dist/indicators/runtime.js +201 -0
  312. package/dist/indicators/validation.js +124 -0
  313. package/dist/indicators/worker-queue.js +48 -0
  314. package/dist/initialize-config.js +160 -0
  315. package/dist/main.js +59 -0
  316. package/dist/memory/decision-audit.js +47 -0
  317. package/dist/memory/durable-extension.js +40 -0
  318. package/dist/memory/execution-audit.js +83 -0
  319. package/dist/memory/insight-contracts.js +33 -0
  320. package/dist/memory/insight-service.js +117 -0
  321. package/dist/memory/memory-contracts.js +113 -0
  322. package/dist/memory/memory-service.js +179 -0
  323. package/dist/memory/operations.js +46 -0
  324. package/dist/memory/tool-schemas.js +44 -0
  325. package/dist/memory/validation.js +42 -0
  326. package/dist/notifications/contracts.js +1 -0
  327. package/dist/notifications/delivery.js +11 -0
  328. package/dist/notifications/durable-extension.js +20 -0
  329. package/dist/notifications/notification-service.js +324 -0
  330. package/dist/providers/credential-store.js +86 -0
  331. package/dist/providers/ollama-cloud.js +148 -0
  332. package/dist/providers/provider-logo-url.js +18 -0
  333. package/dist/providers/provider-logo.js +31 -0
  334. package/dist/providers/provider-service.js +295 -0
  335. package/dist/research/artifacts.js +151 -0
  336. package/dist/research/call-queue.js +43 -0
  337. package/dist/research/contracts.js +37 -0
  338. package/dist/research/evidence.js +43 -0
  339. package/dist/research/journal.js +30 -0
  340. package/dist/research/research-service.js +191 -0
  341. package/dist/research/tool-activity.js +49 -0
  342. package/dist/research/tool-response.js +25 -0
  343. package/dist/runtime.js +145 -0
  344. package/dist/web/agent-events.js +418 -0
  345. package/dist/web/app.js +206 -0
  346. package/dist/web/artifact-downloads.js +23 -0
  347. package/dist/web/middleware.js +25 -0
  348. package/dist/web/random-uuid.js +14 -0
  349. package/dist/web/start.js +56 -0
  350. package/package.json +93 -3
  351. package/scripts/check-node.mjs +24 -0
  352. package/static/android-chrome-192x192.png +0 -0
  353. package/static/android-chrome-512x512.png +0 -0
  354. package/static/apple-touch-icon.png +0 -0
  355. package/static/favicon-16x16.png +0 -0
  356. package/static/favicon-32x32.png +0 -0
  357. package/static/favicon.ico +0 -0
  358. package/static/github.svg +3 -0
  359. package/static/hypervibes.png +0 -0
  360. package/static/logo.png +0 -0
  361. package/static/site.webmanifest +1 -0
  362. package/static/telegram.svg +1 -0
@@ -0,0 +1,2644 @@
1
+ import { r as lineEnds, t as createIndicatorRenderer } from "./indicator-renderer-B2muPS-8.js";
2
+ import { t as agentChatQueryOptions } from "./agent-queries-Cmx8RiUx.js";
3
+ import { r as modelChoicesQueryOptions } from "./provider-queries-BaZWLw8J.js";
4
+ import { n as defaultChartOverlays } from "./trading-contracts-zWq1Hxnb.js";
5
+ import { c as chartIntervals, d as mergeChartCandles, f as randomUuid, i as newDrawing, l as intervalMillis, n as drawing, r as drawingKinds, s as visibleDrawing, u as isChartInterval } from "./contracts-CG-nZuYJ.js";
6
+ import { a as changeChartLayout, i as chartLayoutsQueryOptions, n as chartHistoryQueryOptions, o as createChartLayout, r as chartInstrumentsQueryOptions, s as deleteChartLayout, t as chartFillsQueryOptions } from "./chart-queries-Bc3AfsEb.js";
7
+ import { t as Route } from "./_authenticated.agents._agentId.chart-D4aj8Qy9.js";
8
+ import { i as indicatorQueryOptions, r as indicatorListQueryOptions, t as chartIndicatorQueryOptions } from "./indicator-queries-4dY1-LXx.js";
9
+ import { i as formatMoneyText, n as compareDecimals } from "./decimal-BthtZ77w.js";
10
+ import { n as configButton, r as configInput, t as ConfigDialog } from "./config-dialog-9gSAjwwT.js";
11
+ import { i as useAgentEventsStatus, n as useAgentChartEvents, r as useAgentEvent } from "./agent-events-DK9Yz5__.js";
12
+ import { t as AgentChatPage } from "./agent-chat-page-DChiz8KQ.js";
13
+ import { n as SingleInstrumentPicker } from "./instrument-picker-CRroLwVq.js";
14
+ import { t as IndicatorInputFields } from "./indicator-input-fields-bzuhIh6t.js";
15
+ import { useEffect, useEffectEvent, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
16
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
17
+ import { CandlestickSeries, ColorType, HistogramSeries, LineStyle, createChart, createSeriesMarkers } from "lightweight-charts";
18
+ import { useInfiniteQuery, useMutation, useQueries, useQuery, useQueryClient, useSuspenseQuery } from "@tanstack/react-query";
19
+ import { createPortal } from "react-dom";
20
+ //#region src/components/chart-drawings.tsx
21
+ function ChartDrawings({ document, revision, projection, tool, selected, disabled, svgRef, onSelect, onSave, onDrawn, onEdit }) {
22
+ const [preparationError, setPreparationError] = useState("");
23
+ const [anchor, setAnchor] = useState(null);
24
+ const [preview, setPreview] = useState(null);
25
+ const [draft, setDraft] = useState(null);
26
+ const drag = useRef(null);
27
+ const committedRevision = draft?.revision;
28
+ const blocked = disabled || committedRevision != null && revision < committedRevision;
29
+ useEffect(() => {
30
+ if (committedRevision != null && revision >= committedRevision) setDraft((current) => current?.revision === committedRevision ? null : current);
31
+ }, [revision, committedRevision]);
32
+ const screen = (point) => {
33
+ const x = projection.x(point.time), y = projection.y(point.price);
34
+ return x === null || y === null ? null : {
35
+ x,
36
+ y
37
+ };
38
+ };
39
+ function location(event) {
40
+ const bounds = svgRef.current.getBoundingClientRect();
41
+ return projection.point(Math.max(0, Math.min(projection.width, event.clientX - bounds.left)), Math.max(0, Math.min(projection.height, event.clientY - bounds.top)), document.magnet !== (event.ctrlKey || event.metaKey));
42
+ }
43
+ function start(event, drawing, handle) {
44
+ if (tool !== "select" || event.button !== 0) return;
45
+ event.preventDefault();
46
+ event.stopPropagation();
47
+ if (blocked || drag.current) return;
48
+ onSelect(drawing.id);
49
+ if (drawing.locked) return;
50
+ const point = location(event);
51
+ if (!point) return;
52
+ svgRef.current.setPointerCapture(event.pointerId);
53
+ drag.current = {
54
+ drawing,
55
+ handle,
56
+ start: point,
57
+ draft: null,
58
+ pointerId: event.pointerId
59
+ };
60
+ }
61
+ function move(event) {
62
+ if (drag.current && drag.current.pointerId !== event.pointerId) return;
63
+ const point = location(event);
64
+ if (!point) return;
65
+ if (anchor) setPreview(point);
66
+ const moving = drag.current;
67
+ if (!moving) return;
68
+ const points = moving.drawing.points.map((old, index) => moving.handle === index ? point : moving.handle !== null ? old : {
69
+ time: Math.max(0, old.time + point.time - moving.start.time),
70
+ price: Math.max(1e-12, old.price + point.price - moving.start.price)
71
+ });
72
+ moving.draft = {
73
+ ...moving.drawing,
74
+ points
75
+ };
76
+ setDraft({
77
+ drawing: moving.draft,
78
+ revision: null
79
+ });
80
+ }
81
+ async function persist(drawing) {
82
+ let savedRevision;
83
+ try {
84
+ savedRevision = await onSave(drawing);
85
+ } catch {}
86
+ setDraft((current) => current?.drawing !== drawing ? current : savedRevision === void 0 ? null : {
87
+ drawing,
88
+ revision: savedRevision
89
+ });
90
+ }
91
+ function finish(event) {
92
+ const moving = drag.current;
93
+ if (!moving || moving.pointerId !== event.pointerId) return;
94
+ drag.current = null;
95
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
96
+ if (moving.draft) persist(moving.draft);
97
+ }
98
+ function cancel(event) {
99
+ if (!drag.current || drag.current.pointerId !== event.pointerId) return;
100
+ drag.current = null;
101
+ setDraft(null);
102
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
103
+ }
104
+ function place(event) {
105
+ if (blocked || event.button !== 0) return;
106
+ const point = location(event);
107
+ if (!point) return;
108
+ if (tool === "select") {
109
+ onSelect("");
110
+ return;
111
+ }
112
+ const single = [
113
+ "horizontal",
114
+ "horizontal-ray",
115
+ "vertical",
116
+ "text"
117
+ ].includes(tool);
118
+ if (!single && !anchor) {
119
+ setAnchor(point);
120
+ setPreview(point);
121
+ return;
122
+ }
123
+ try {
124
+ const drawing = newDrawing(tool, document.coin, single ? [point] : [anchor, point]);
125
+ setPreparationError("");
126
+ if (["text", "callout"].includes(tool)) drawing.text = "Note";
127
+ onSave(drawing);
128
+ onSelect(drawing.id);
129
+ setAnchor(null);
130
+ setPreview(null);
131
+ onDrawn();
132
+ } catch (cause) {
133
+ setPreparationError(cause instanceof Error ? cause.message : "Unable to prepare the drawing");
134
+ }
135
+ }
136
+ const previewDrawing = draft && (draft.revision === null || revision < draft.revision) ? draft.drawing : null;
137
+ const drawings = document.drawings.filter((d) => visibleDrawing(d, document)).map((d) => previewDrawing?.id === d.id ? previewDrawing : d);
138
+ if (anchor && preview && tool !== "select") drawings.push({
139
+ ...newDrawing(tool, document.coin, [anchor, preview], "preview"),
140
+ color: "#71717a"
141
+ });
142
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("svg", {
143
+ ref: svgRef,
144
+ xmlns: "http://www.w3.org/2000/svg",
145
+ width: projection.width,
146
+ height: projection.height,
147
+ viewBox: `0 0 ${projection.width} ${projection.height}`,
148
+ "aria-label": "Chart drawings",
149
+ className: "absolute left-0 top-0 z-10 overflow-hidden",
150
+ style: {
151
+ pointerEvents: tool === "select" || blocked ? "none" : "auto",
152
+ cursor: tool === "select" ? "default" : "crosshair",
153
+ touchAction: "none"
154
+ },
155
+ onPointerDown: place,
156
+ onPointerMove: move,
157
+ onPointerUp: finish,
158
+ onPointerCancel: cancel,
159
+ onLostPointerCapture: cancel,
160
+ children: [tool !== "select" && !blocked && /* @__PURE__ */ jsx("rect", {
161
+ "data-drawing-surface": "true",
162
+ width: projection.width,
163
+ height: projection.height,
164
+ fill: "transparent",
165
+ style: { pointerEvents: "all" }
166
+ }), drawings.map((drawing) => {
167
+ const a = screen(drawing.points[0]), b = screen(drawing.points[1] ?? drawing.points[0]);
168
+ if (!a || !b) return null;
169
+ const [from, to] = lineEnds(drawing.kind, a, b, projection.width);
170
+ const stroke = {
171
+ stroke: drawing.color,
172
+ strokeWidth: drawing.width,
173
+ strokeDasharray: drawing.dashed ? "6 4" : void 0,
174
+ fill: "none"
175
+ };
176
+ const path = drawing.kind === "vertical" ? `M ${a.x} 0 L ${a.x} ${projection.height}` : `M ${from.x} ${from.y} L ${to.x} ${to.y}`;
177
+ const rectangle = drawing.kind === "rectangle";
178
+ const text = drawing.kind === "text";
179
+ const labelPoint = drawing.kind === "callout" ? b : a;
180
+ const active = selected === drawing.id;
181
+ return /* @__PURE__ */ jsxs("g", {
182
+ "aria-label": drawing.name,
183
+ onDoubleClick: (event) => {
184
+ if (tool === "select" && !drawing.locked && !blocked) {
185
+ event.stopPropagation();
186
+ onSelect(drawing.id);
187
+ onEdit();
188
+ }
189
+ },
190
+ children: [
191
+ /* @__PURE__ */ jsxs("title", { children: [
192
+ drawing.name,
193
+ drawing.locked ? " (locked)" : "",
194
+ " · ",
195
+ drawing.author === "agent" ? "Agent" : "You"
196
+ ] }),
197
+ rectangle ? /* @__PURE__ */ jsx("rect", {
198
+ x: Math.min(a.x, b.x),
199
+ y: Math.min(a.y, b.y),
200
+ width: Math.abs(b.x - a.x),
201
+ height: Math.abs(b.y - a.y),
202
+ ...stroke,
203
+ fill: drawing.color,
204
+ fillOpacity: drawing.fillOpacity
205
+ }) : !text ? /* @__PURE__ */ jsx("path", {
206
+ d: path,
207
+ ...stroke
208
+ }) : null,
209
+ drawing.kind === "arrow" && /* @__PURE__ */ jsx("path", {
210
+ d: `M ${b.x - 12 * Math.cos(Math.atan2(b.y - a.y, b.x - a.x) - .45)} ${b.y - 12 * Math.sin(Math.atan2(b.y - a.y, b.x - a.x) - .45)} L ${b.x} ${b.y} L ${b.x - 12 * Math.cos(Math.atan2(b.y - a.y, b.x - a.x) + .45)} ${b.y - 12 * Math.sin(Math.atan2(b.y - a.y, b.x - a.x) + .45)}`,
211
+ ...stroke
212
+ }),
213
+ (drawing.kind === "horizontal" || drawing.kind === "horizontal-ray") && /* @__PURE__ */ jsx("text", {
214
+ x: projection.width - 6,
215
+ y: a.y - 5,
216
+ textAnchor: "end",
217
+ fill: drawing.color,
218
+ fontSize: "11",
219
+ fontFamily: "sans-serif",
220
+ stroke: "#0d0d10",
221
+ strokeWidth: "3",
222
+ paintOrder: "stroke",
223
+ children: drawing.points[0].price.toLocaleString("en", { maximumSignificantDigits: 10 })
224
+ }),
225
+ drawing.text && /* @__PURE__ */ jsx("text", {
226
+ x: labelPoint.x + 6,
227
+ y: labelPoint.y - 8,
228
+ fill: drawing.color,
229
+ fontSize: "13",
230
+ fontFamily: "sans-serif",
231
+ stroke: "#0d0d10",
232
+ strokeWidth: "3",
233
+ paintOrder: "stroke",
234
+ children: drawing.text.split("\n").map((line, i) => /* @__PURE__ */ jsx("tspan", {
235
+ x: labelPoint.x + 6,
236
+ dy: i ? "1.2em" : 0,
237
+ children: line
238
+ }, i))
239
+ }),
240
+ tool === "select" && (rectangle ? /* @__PURE__ */ jsx("rect", {
241
+ x: Math.min(a.x, b.x),
242
+ y: Math.min(a.y, b.y),
243
+ width: Math.abs(b.x - a.x),
244
+ height: Math.abs(b.y - a.y),
245
+ fill: "transparent",
246
+ stroke: "transparent",
247
+ strokeWidth: "12",
248
+ style: {
249
+ pointerEvents: "all",
250
+ cursor: blocked ? "progress" : drawing.locked ? "pointer" : "move"
251
+ },
252
+ onPointerDown: (event) => start(event, drawing, null)
253
+ }) : text ? /* @__PURE__ */ jsx("rect", {
254
+ x: a.x,
255
+ y: a.y - 24,
256
+ width: Math.max(60, drawing.text.length * 8),
257
+ height: "30",
258
+ fill: "transparent",
259
+ style: {
260
+ pointerEvents: "all",
261
+ cursor: blocked ? "progress" : drawing.locked ? "pointer" : "move"
262
+ },
263
+ onPointerDown: (event) => start(event, drawing, null)
264
+ }) : /* @__PURE__ */ jsx("path", {
265
+ d: path,
266
+ fill: "none",
267
+ stroke: "transparent",
268
+ strokeWidth: "14",
269
+ style: {
270
+ pointerEvents: "stroke",
271
+ cursor: blocked ? "progress" : drawing.locked ? "pointer" : "move"
272
+ },
273
+ onPointerDown: (event) => start(event, drawing, null)
274
+ })),
275
+ active && drawing.points.map((p, index) => {
276
+ const position = screen(p);
277
+ return position ? /* @__PURE__ */ jsx("circle", {
278
+ "data-handle": "true",
279
+ cx: position.x,
280
+ cy: position.y,
281
+ r: "5",
282
+ fill: "#0d0d10",
283
+ stroke: drawing.color,
284
+ strokeWidth: "2",
285
+ style: {
286
+ pointerEvents: tool === "select" ? "all" : "none",
287
+ cursor: blocked ? "progress" : drawing.locked ? "not-allowed" : "grab"
288
+ },
289
+ onPointerDown: (event) => start(event, drawing, index)
290
+ }, index) : null;
291
+ })
292
+ ]
293
+ }, drawing.id);
294
+ })]
295
+ }), preparationError && /* @__PURE__ */ jsx("p", {
296
+ role: "alert",
297
+ className: "absolute left-2 top-2 z-20 text-sm text-red-300",
298
+ children: preparationError
299
+ })] });
300
+ }
301
+ //#endregion
302
+ //#region src/charts/trading-overlays.ts
303
+ var buyColor = "#34d399";
304
+ var sellColor = "#f87171";
305
+ var displayPrice = (value) => {
306
+ const price = Number(value);
307
+ return Number.isFinite(price) && price > 0 ? price : null;
308
+ };
309
+ function accountSectionFresh(snapshot, section, interrupted) {
310
+ return !interrupted && snapshot?.live?.status === "connected" && snapshot.live.sections[section].fresh;
311
+ }
312
+ /** Numbers are used only for chart coordinates; labels retain exchange decimals. */
313
+ function tradeMarkers(fills, candles, step) {
314
+ const bars = new Set(candles.map((candle) => candle.time));
315
+ return fills.flatMap((fill) => {
316
+ const time = Math.floor(fill.time / 1e3 / step) * step, price = displayPrice(fill.price);
317
+ if (!bars.has(time) || price === null) return [];
318
+ const buy = fill.side === "Buy";
319
+ return [{
320
+ id: fill.id,
321
+ time,
322
+ price,
323
+ position: buy ? "atPriceBottom" : "atPriceTop",
324
+ shape: buy ? "arrowUp" : "arrowDown",
325
+ color: buy ? buyColor : sellColor
326
+ }];
327
+ }).sort((a, b) => a.time - b.time || String(a.id).localeCompare(String(b.id)));
328
+ }
329
+ function accountPriceLines(coin, snapshot, visibility, interrupted) {
330
+ const result = /* @__PURE__ */ new Map();
331
+ const position = snapshot?.positions?.find((p) => p.coin === coin);
332
+ const size = position?.size.replace(/^-/, "");
333
+ const sign = position ? compareDecimals(position.size, "0") : null;
334
+ if (visibility.position && position && sign !== null && sign !== 0) {
335
+ const price = displayPrice(position.entryPrice);
336
+ const stale = !accountSectionFresh(snapshot, "perps", interrupted);
337
+ if (price !== null) result.set("position", {
338
+ price,
339
+ color: stale ? "#a1a1aa" : sign > 0 ? buyColor : sellColor,
340
+ lineWidth: 2,
341
+ lineStyle: LineStyle.Solid,
342
+ axisLabelVisible: true,
343
+ title: `${stale ? "STALE · " : ""}${sign > 0 ? "Long" : "Short"} · ${size} ${coin} · P&L ${compareDecimals(position.unrealizedPnl, "0") === 1 ? "+" : ""}${formatMoneyText(position.unrealizedPnl)} USDC`
344
+ });
345
+ }
346
+ const orders = new Map((snapshot?.orders ?? []).map((order) => [order.id, {
347
+ order,
348
+ pending: false
349
+ }]));
350
+ const children = (order) => {
351
+ for (const child of order.children ?? []) {
352
+ if (!orders.has(child.id)) orders.set(child.id, {
353
+ order: child,
354
+ pending: true
355
+ });
356
+ children(child);
357
+ }
358
+ };
359
+ if (visibility.orders) for (const order of snapshot?.orders ?? []) children(order);
360
+ if (visibility.orders) for (const { order, pending } of orders.values()) {
361
+ if (order.coin !== coin) continue;
362
+ const price = displayPrice(order.triggerPrice ?? order.price);
363
+ if (price === null) continue;
364
+ const stale = !accountSectionFresh(snapshot, "orders", interrupted);
365
+ const quantity = order.positionTpsl ? "Entire position" : `${order.size} ${coin}`;
366
+ result.set(`order:${order.id}`, {
367
+ price,
368
+ color: stale ? "#a1a1aa" : order.side === "Buy" ? buyColor : sellColor,
369
+ lineWidth: 1,
370
+ lineStyle: order.triggerPrice === void 0 ? LineStyle.Dashed : LineStyle.Dotted,
371
+ axisLabelVisible: true,
372
+ title: `${stale ? "STALE · " : ""}${pending ? "Pending · " : ""}${order.side} ${order.type}${order.triggerPrice === void 0 ? "" : " trigger"} · ${quantity}`
373
+ });
374
+ }
375
+ return result;
376
+ }
377
+ /** Owned by the chart lifecycle: detach primitives before removing the chart. */
378
+ function createTradingOverlays(chart, price, hover) {
379
+ const plugin = createSeriesMarkers(price, [], { autoScale: false });
380
+ const lines = /* @__PURE__ */ new Map();
381
+ let fillsById = /* @__PURE__ */ new Map();
382
+ const crosshair = (event) => {
383
+ const id = event.hoveredInfo?.objectId;
384
+ const fill = typeof id === "string" ? fillsById.get(id) : void 0;
385
+ hover(fill && event.point ? {
386
+ fill,
387
+ x: event.point.x,
388
+ y: event.point.y
389
+ } : null);
390
+ };
391
+ chart.subscribeCrosshairMove(crosshair);
392
+ return {
393
+ trades(fills, candles, step) {
394
+ fillsById = new Map(fills.map((fill) => [fill.id, fill]));
395
+ plugin.setMarkers(tradeMarkers(fills, candles, step));
396
+ hover(null);
397
+ },
398
+ account(coin, snapshot, visibility, interrupted) {
399
+ const next = accountPriceLines(coin, snapshot, visibility, interrupted);
400
+ for (const [id, line] of lines) if (!next.has(id)) {
401
+ price.removePriceLine(line);
402
+ lines.delete(id);
403
+ }
404
+ for (const [id, options] of next) {
405
+ const line = lines.get(id);
406
+ if (line) line.applyOptions(options);
407
+ else lines.set(id, price.createPriceLine(options));
408
+ }
409
+ },
410
+ close() {
411
+ chart.unsubscribeCrosshairMove(crosshair);
412
+ plugin.detach();
413
+ for (const line of lines.values()) price.removePriceLine(line);
414
+ lines.clear();
415
+ }
416
+ };
417
+ }
418
+ //#endregion
419
+ //#region src/components/use-chart-account.ts
420
+ var noFills = [];
421
+ function useChartAccount(id, coin, from, to, visibility) {
422
+ const [account, setAccount] = useState({
423
+ id,
424
+ snapshot: null,
425
+ interrupted: false
426
+ });
427
+ const [retry, setRetry] = useState(0);
428
+ const enabled = visibility.position || visibility.orders;
429
+ const { interrupted, streamErrors, reconnect } = useAgentEventsStatus();
430
+ useAgentEvent("dashboard", (event) => {
431
+ setAccount({
432
+ id,
433
+ snapshot: JSON.parse(event.data),
434
+ interrupted: false
435
+ });
436
+ }, enabled);
437
+ useEffect(() => {
438
+ setAccount({
439
+ id,
440
+ snapshot: null,
441
+ interrupted: false
442
+ });
443
+ }, [
444
+ id,
445
+ enabled,
446
+ retry
447
+ ]);
448
+ useEffect(() => {
449
+ if (!enabled) return;
450
+ if (streamErrors["dashboard"] || interrupted) setAccount((current) => current.interrupted ? current : {
451
+ ...current,
452
+ interrupted: true
453
+ });
454
+ }, [
455
+ enabled,
456
+ interrupted,
457
+ streamErrors
458
+ ]);
459
+ const trades = useInfiniteQuery({
460
+ ...chartFillsQueryOptions(id, coin, from, to),
461
+ enabled: visibility.trades && to > from
462
+ });
463
+ const { hasNextPage, isFetching, isError, fetchNextPage } = trades;
464
+ useEffect(() => {
465
+ if (visibility.trades && hasNextPage && !isFetching && !isError) fetchNextPage();
466
+ }, [
467
+ visibility.trades,
468
+ hasNextPage,
469
+ isFetching,
470
+ isError,
471
+ fetchNextPage
472
+ ]);
473
+ const fills = useMemo(() => {
474
+ if (!trades.data) return noFills;
475
+ const byId = /* @__PURE__ */ new Map();
476
+ for (const page of trades.data.pages) for (const fill of page.fills) byId.set(fill.id, fill);
477
+ return [...byId.values()];
478
+ }, [trades.data]);
479
+ const snapshot = account.id === id ? account.snapshot : null;
480
+ const messages = [];
481
+ if (enabled) if (!snapshot) messages.push(account.interrupted ? "Account unavailable · Retry to reconnect" : "Connecting account…");
482
+ else {
483
+ if (visibility.position && !accountSectionFresh(snapshot, "perps", account.interrupted)) messages.push("Position data stale");
484
+ if (visibility.orders && !accountSectionFresh(snapshot, "orders", account.interrupted)) messages.push("Order data stale");
485
+ }
486
+ if (visibility.trades && to > from) {
487
+ if (isError) messages.push("Trade history unavailable · displayed markers may be stale");
488
+ else if (trades.isPending || hasNextPage) messages.push("Loading trade history…");
489
+ const page = trades.data?.pages[0];
490
+ if (page && (!page.sync?.updatedAt || page.sync.error)) messages.push("Trade journal syncing or unavailable · history may be incomplete");
491
+ if (page?.historyLimited) messages.push("Exchange history is limited · showing available journal fills");
492
+ }
493
+ return {
494
+ snapshot,
495
+ interrupted: account.interrupted,
496
+ fills,
497
+ messages,
498
+ canRetry: enabled && account.interrupted || visibility.trades && isError,
499
+ retry: () => {
500
+ setRetry((n) => n + 1);
501
+ reconnect();
502
+ if (visibility.trades) trades.refetch();
503
+ }
504
+ };
505
+ }
506
+ //#endregion
507
+ //#region src/components/use-chart-indicators.ts
508
+ function useChartIndicators(agentId, layout) {
509
+ const width = intervalMillis[layout.document.interval];
510
+ const [clock, setClock] = useState(() => Date.now());
511
+ useEffect(() => {
512
+ let timer;
513
+ const next = () => {
514
+ setClock(Date.now());
515
+ timer = setTimeout(next, width - Date.now() % width + 1e3);
516
+ };
517
+ next();
518
+ return () => clearTimeout(timer);
519
+ }, [width]);
520
+ const end = Math.floor(clock / width) * width;
521
+ const query = useQuery({
522
+ ...chartIndicatorQueryOptions(agentId, layout.id, layout.revision, layout.document.coin, layout.document.interval, end),
523
+ enabled: layout.document.indicators.some((i) => !i.hidden)
524
+ });
525
+ const identity = JSON.stringify([
526
+ layout.id,
527
+ layout.document.coin,
528
+ layout.document.interval,
529
+ layout.document.indicators
530
+ ]);
531
+ const previous = useRef(void 0);
532
+ useEffect(() => {
533
+ if (query.data) previous.current = {
534
+ identity,
535
+ data: query.data
536
+ };
537
+ }, [identity, query.data]);
538
+ const data = query.data ?? (previous.current?.identity === identity ? previous.current.data : void 0);
539
+ return {
540
+ data,
541
+ status: query.isError ? "error" : query.isFetching || data?.end !== end ? data ? "stale" : "loading" : "current",
542
+ error: query.error?.message,
543
+ retry: () => {
544
+ query.refetch();
545
+ }
546
+ };
547
+ }
548
+ //#endregion
549
+ //#region src/components/market-chart.tsx
550
+ function MarketChart({ id, layout, tool, selected, disabled, statusHost, overlayVisibility = defaultChartOverlays, onSelect, onSave, onDrawn, onEdit, ref }) {
551
+ const client = useQueryClient();
552
+ const indicatorData = useChartIndicators(id, layout);
553
+ const [generated, setGenerated] = useState(null);
554
+ const host = useRef(null), svg = useRef(null), active = useRef(null);
555
+ const [status, setStatus] = useState("connecting");
556
+ const [error, setError] = useState("");
557
+ const [historyStatus, setHistoryStatus] = useState("Loading candles…");
558
+ const [retry, setRetry] = useState(0), [frame, setFrame] = useState(0);
559
+ const [hover, setHover] = useState(null);
560
+ const { coin, interval } = layout.document;
561
+ const { interrupted: socketInterrupted, streamErrors, reconnect } = useAgentEventsStatus();
562
+ const chartStreamError = streamErrors["chart"];
563
+ const onSnapshot = useRef(() => {});
564
+ useAgentChartEvents(coin, interval, (event) => {
565
+ onSnapshot.current(JSON.parse(event.data));
566
+ });
567
+ useEffect(() => {
568
+ if (chartStreamError) {
569
+ const current = active.current;
570
+ if (current) current.status = "reconnecting";
571
+ setStatus("reconnecting");
572
+ } else if (socketInterrupted) {
573
+ const current = active.current;
574
+ if (current) current.status = "reconnecting";
575
+ setStatus("reconnecting");
576
+ }
577
+ }, [chartStreamError, socketInterrupted]);
578
+ useEffect(() => {
579
+ const element = host.current;
580
+ if (!element) return;
581
+ let disposed = false, loading = false, initialized = false, hasMore = true, historyFailed = false, raf = 0;
582
+ const chart = createChart(element, {
583
+ autoSize: true,
584
+ layout: {
585
+ background: {
586
+ type: ColorType.Solid,
587
+ color: "#0d0d10"
588
+ },
589
+ textColor: "#a1a1aa",
590
+ attributionLogo: true
591
+ },
592
+ grid: {
593
+ vertLines: { color: "#1c1c22" },
594
+ horzLines: { color: "#1c1c22" }
595
+ },
596
+ rightPriceScale: { borderColor: "#27272a" },
597
+ timeScale: {
598
+ timeVisible: true,
599
+ secondsVisible: false,
600
+ borderColor: "#27272a"
601
+ }
602
+ });
603
+ const price = chart.addSeries(CandlestickSeries, {
604
+ upColor: "#34d399",
605
+ downColor: "#f87171",
606
+ borderVisible: false,
607
+ wickUpColor: "#34d399",
608
+ wickDownColor: "#f87171"
609
+ });
610
+ price.priceScale().applyOptions({ scaleMargins: {
611
+ top: .06,
612
+ bottom: .25
613
+ } });
614
+ const volume = chart.addSeries(HistogramSeries, {
615
+ priceFormat: { type: "volume" },
616
+ priceScaleId: "volume"
617
+ });
618
+ volume.priceScale().applyOptions({
619
+ scaleMargins: {
620
+ top: .8,
621
+ bottom: 0
622
+ },
623
+ visible: false
624
+ });
625
+ const overlays = createTradingOverlays(chart, price, setHover);
626
+ const indicators = createIndicatorRenderer(chart, price);
627
+ const state = {
628
+ chart,
629
+ price,
630
+ candles: [],
631
+ status: "connecting",
632
+ overlays,
633
+ indicators,
634
+ evidence: []
635
+ };
636
+ const generatedHover = (event) => {
637
+ const id = event.hoveredInfo?.objectId;
638
+ const text = typeof id === "string" ? indicators.describe(id) : void 0;
639
+ setGenerated(typeof id === "string" && text !== void 0 ? {
640
+ id,
641
+ text
642
+ } : null);
643
+ };
644
+ chart.subscribeCrosshairMove(generatedHover);
645
+ active.current = state;
646
+ const redraw = () => {
647
+ if (raf || disposed) return;
648
+ raf = requestAnimationFrame(() => {
649
+ raf = 0;
650
+ if (!disposed) setFrame((n) => n + 1);
651
+ });
652
+ };
653
+ const draw = () => {
654
+ const close = state.candles.at(-1)?.close;
655
+ if (close && close > 0) {
656
+ const precision = Math.min(12, Math.max(2, 5 - Math.floor(Math.log10(close))));
657
+ price.applyOptions({ priceFormat: {
658
+ type: "price",
659
+ precision,
660
+ minMove: 10 ** -precision
661
+ } });
662
+ }
663
+ price.setData(state.candles.map((c) => ({
664
+ ...c,
665
+ time: c.time
666
+ })));
667
+ volume.setData(state.candles.map((c) => ({
668
+ time: c.time,
669
+ value: c.volume,
670
+ color: c.close >= c.open ? "#34d39960" : "#f8717160"
671
+ })));
672
+ redraw();
673
+ };
674
+ const load = async (before) => {
675
+ if (disposed || loading || !hasMore) return;
676
+ loading = true;
677
+ setHistoryStatus("Loading candles…");
678
+ try {
679
+ const result = await client.fetchQuery(chartHistoryQueryOptions(id, coin, interval, before));
680
+ if (disposed) return;
681
+ const range = chart.timeScale().getVisibleLogicalRange(), oldest = state.candles[0]?.time;
682
+ state.candles = mergeChartCandles(result.candles, state.candles);
683
+ const added = oldest === void 0 ? 0 : state.candles.filter((c) => c.time < oldest).length;
684
+ hasMore = result.hasMore && (before === void 0 || added > 0);
685
+ draw();
686
+ if (!initialized && state.candles.length) chart.timeScale().setVisibleLogicalRange({
687
+ from: Math.max(0, state.candles.length - 120),
688
+ to: state.candles.length + 3
689
+ });
690
+ else if (range && added) chart.timeScale().setVisibleLogicalRange({
691
+ from: range.from + added,
692
+ to: range.to + added
693
+ });
694
+ initialized = true;
695
+ historyFailed = false;
696
+ setHistoryStatus(hasMore ? "" : "No older candles available from Hyperliquid.");
697
+ } catch (failure) {
698
+ if (!disposed) {
699
+ historyFailed = true;
700
+ setError(failure instanceof Error ? failure.message : "Candle history is unavailable.");
701
+ setHistoryStatus("History unavailable. Retry to reload.");
702
+ }
703
+ } finally {
704
+ loading = false;
705
+ }
706
+ };
707
+ const onRange = (range) => {
708
+ redraw();
709
+ if (range && range.from < 50 && initialized && !historyFailed && state.candles.length) load(state.candles[0].time * 1e3);
710
+ };
711
+ chart.timeScale().subscribeVisibleLogicalRangeChange(onRange);
712
+ chart.timeScale().subscribeSizeChange(redraw);
713
+ chart.subscribeCrosshairMove(redraw);
714
+ element.addEventListener("pointermove", redraw, { passive: true });
715
+ element.addEventListener("wheel", redraw, { passive: true });
716
+ onSnapshot.current = (snapshot) => {
717
+ if (disposed) return;
718
+ state.status = snapshot.status;
719
+ setStatus(snapshot.status);
720
+ const previousLength = state.candles.length;
721
+ state.candles = mergeChartCandles(state.candles, snapshot.candles);
722
+ if (!initialized || state.candles.length !== previousLength) draw();
723
+ else {
724
+ for (const c of snapshot.candles) {
725
+ price.update({
726
+ ...c,
727
+ time: c.time
728
+ }, true);
729
+ volume.update({
730
+ time: c.time,
731
+ value: c.volume,
732
+ color: c.close >= c.open ? "#34d39960" : "#f8717160"
733
+ }, true);
734
+ }
735
+ redraw();
736
+ }
737
+ };
738
+ setError("");
739
+ load();
740
+ return () => {
741
+ disposed = true;
742
+ active.current = null;
743
+ onSnapshot.current = () => {};
744
+ cancelAnimationFrame(raf);
745
+ element.removeEventListener("pointermove", redraw);
746
+ element.removeEventListener("wheel", redraw);
747
+ chart.timeScale().unsubscribeVisibleLogicalRangeChange(onRange);
748
+ chart.timeScale().unsubscribeSizeChange(redraw);
749
+ chart.unsubscribeCrosshairMove(redraw);
750
+ chart.unsubscribeCrosshairMove(generatedHover);
751
+ indicators.close();
752
+ overlays.close();
753
+ chart.remove();
754
+ };
755
+ }, [
756
+ client,
757
+ id,
758
+ coin,
759
+ interval,
760
+ retry
761
+ ]);
762
+ const state = active.current;
763
+ const candles = state?.candles;
764
+ const step = intervalMillis[interval] / 1e3;
765
+ const account = useChartAccount(id, coin, (candles?.[0]?.time ?? 0) * 1e3, candles?.length ? (candles.at(-1).time + step) * 1e3 : 0, overlayVisibility);
766
+ useEffect(() => {
767
+ if (state && state === active.current && candles) state.overlays.trades(overlayVisibility.trades ? account.fills : [], candles, step);
768
+ }, [
769
+ state,
770
+ candles,
771
+ step,
772
+ account.fills,
773
+ overlayVisibility.trades
774
+ ]);
775
+ useEffect(() => {
776
+ if (state && state === active.current) state.overlays.account(coin, account.snapshot, overlayVisibility, account.interrupted);
777
+ }, [
778
+ state,
779
+ coin,
780
+ account.snapshot,
781
+ account.interrupted,
782
+ overlayVisibility
783
+ ]);
784
+ useEffect(() => {
785
+ if (!state || state !== active.current || !candles?.length) return;
786
+ const configurations = layout.document.indicators;
787
+ for (const evidence of state.evidence) if (!configurations.some((i) => i.id === evidence.configuration.id && !i.hidden)) state.indicators.remove(evidence.configuration.id);
788
+ state.evidence = configurations.filter((i) => !i.hidden).map((configuration) => {
789
+ const item = indicatorData.data?.instances.find((item) => item.instanceId === configuration.id);
790
+ if (item?.result) try {
791
+ state.indicators.apply(configuration, item.result, candles);
792
+ } catch (error) {
793
+ state.indicators.remove(configuration.id);
794
+ return {
795
+ configuration,
796
+ status: "error",
797
+ error: error instanceof Error ? error.message : "Unable to render indicator"
798
+ };
799
+ }
800
+ else state.indicators.remove(configuration.id);
801
+ return {
802
+ configuration,
803
+ result: item?.result,
804
+ status: item?.error ? "error" : indicatorData.status,
805
+ error: item?.error ?? indicatorData.error
806
+ };
807
+ });
808
+ }, [
809
+ state,
810
+ candles,
811
+ layout.document.indicators,
812
+ indicatorData.data,
813
+ indicatorData.status,
814
+ indicatorData.error
815
+ ]);
816
+ const projection = state ? projectChart(state, intervalMillis[interval] / 1e3) : null;
817
+ const handle = { capture: async (frozenLayout, selectedDrawingId, includeImage) => {
818
+ const current = active.current;
819
+ if (!current?.candles.length) throw new Error("Wait for chart candles before sending chart context.");
820
+ const range = current.chart.timeScale().getVisibleLogicalRange();
821
+ if (!range) throw new Error("Chart viewport is not ready.");
822
+ const step = intervalMillis[frozenLayout.document.interval] / 1e3;
823
+ const from = timeAt(current.candles, range.from, step), to = timeAt(current.candles, range.to, step);
824
+ const visible = current.candles.filter((c) => c.time >= from && c.time <= to);
825
+ if (visible.length > 1500) throw new Error("Zoom in to at most 1,500 visible candles before sending chart context.");
826
+ const capture = {
827
+ layoutId: frozenLayout.id,
828
+ revision: frozenLayout.revision,
829
+ capturedAt: Date.now(),
830
+ status: current.status,
831
+ range: {
832
+ from: Math.max(0, from),
833
+ to
834
+ },
835
+ candles: visible,
836
+ indicators: current.evidence.map(({ configuration, result, status, error }) => ({
837
+ instanceId: configuration.id,
838
+ indicatorId: configuration.indicatorId,
839
+ configuration,
840
+ status,
841
+ ...error ? { error: error.slice(0, 1e3) } : {},
842
+ ...result ? { result: {
843
+ version: result.version,
844
+ sourceHash: result.sourceHash,
845
+ engine: result.engine,
846
+ wrapperVersion: result.wrapperVersion,
847
+ resultHash: result.resultHash,
848
+ resolvedInputs: result.resolvedInputs,
849
+ inputSchemaHash: result.inputSchemaHash,
850
+ computedAt: result.computedAt,
851
+ end: result.end,
852
+ dataset: result.dataset,
853
+ coverage: result.coverage,
854
+ sampleScope: "partial-visible",
855
+ sample: result.values.filter((p) => p.time >= from && p.time <= to).slice(-20),
856
+ objects: [...result.markers.filter((m) => m.displayTime >= from && m.displayTime <= to).map((m) => ({
857
+ id: m.id,
858
+ kind: m.kind,
859
+ time: m.time,
860
+ text: m.text ?? m.character
861
+ })), ...result.drawings.map((d) => ({
862
+ id: d.id,
863
+ kind: d.kind,
864
+ time: d.kind === "label" ? d.at.time : d.from.time,
865
+ text: d.kind === "line" ? void 0 : d.text
866
+ })).filter((d) => d.time >= from && d.time <= to)].slice(-20)
867
+ } } : {}
868
+ })),
869
+ ...generated ? { selectedGeneratedObjectId: generated.id } : {},
870
+ ...selectedDrawingId ? { selectedDrawingId } : {}
871
+ };
872
+ if (includeImage) capture.image = await screenshot(current.chart.takeScreenshot(), svg.current);
873
+ if (new TextEncoder().encode(JSON.stringify(capture)).byteLength > 9e5) throw new Error("Chart context is too large. Zoom in or disable Include chart image.");
874
+ return capture;
875
+ } };
876
+ useEffect(() => {
877
+ if (typeof ref === "function") {
878
+ ref(handle);
879
+ return () => {
880
+ ref(null);
881
+ };
882
+ }
883
+ if (ref) {
884
+ ref.current = handle;
885
+ return () => {
886
+ ref.current = null;
887
+ };
888
+ }
889
+ });
890
+ const indicatorMessages = layout.document.indicators.some((i) => !i.hidden) ? [
891
+ indicatorData.status !== "current" ? `Indicators: ${indicatorData.status === "stale" ? "waiting for closed candle" : indicatorData.status}` : "",
892
+ indicatorData.error,
893
+ ...indicatorData.data?.instances.flatMap((i) => i.error ? [i.error] : []) ?? []
894
+ ].filter((message) => Boolean(message)) : [];
895
+ const statusMessages = [
896
+ status === "reconnecting" ? "Reconnecting… Data may be stale." : "",
897
+ historyStatus,
898
+ error,
899
+ chartStreamError ?? "",
900
+ ...indicatorMessages,
901
+ ...account.messages
902
+ ].filter(Boolean);
903
+ const indicatorFailed = indicatorMessages.length > 0 && Boolean(indicatorData.error || indicatorData.data?.instances.some((i) => i.error));
904
+ const needsAttention = Boolean(error || indicatorFailed || account.canRetry);
905
+ const loading = status !== "live" || needsAttention || indicatorMessages.length > 0 || account.messages.length > 0 || historyStatus === "Loading candles…";
906
+ const disconnected = status === "reconnecting";
907
+ const connectionLabel = disconnected ? "Disconnected" : loading ? "Loading" : "Connected";
908
+ return /* @__PURE__ */ jsxs("div", {
909
+ className: "flex min-h-0 flex-1 flex-col gap-2",
910
+ "data-frame": frame,
911
+ children: [statusHost && createPortal(/* @__PURE__ */ jsxs("details", {
912
+ className: "relative",
913
+ children: [/* @__PURE__ */ jsx("summary", {
914
+ className: "flex cursor-pointer list-none items-center gap-2 whitespace-nowrap text-xs text-zinc-400 [&::-webkit-details-marker]:hidden",
915
+ title: statusMessages.join("\n") || "All chart data is up to date. Click for details.",
916
+ children: /* @__PURE__ */ jsxs("span", {
917
+ role: "status",
918
+ "aria-label": "Chart connection",
919
+ title: statusMessages.join("\n") || void 0,
920
+ className: "inline-flex items-center gap-2",
921
+ children: [
922
+ /* @__PURE__ */ jsx("span", {
923
+ "aria-hidden": "true",
924
+ className: `inline-block h-2 w-2 rounded-full ${disconnected ? "bg-red-400" : loading ? "bg-amber-400" : "bg-emerald-400"}`
925
+ }),
926
+ connectionLabel,
927
+ statusMessages.length > 0 && /* @__PURE__ */ jsxs("span", {
928
+ className: "sr-only",
929
+ children: [" · ", statusMessages.join(" · ")]
930
+ })
931
+ ]
932
+ })
933
+ }), /* @__PURE__ */ jsxs("div", {
934
+ "aria-label": "Chart data status",
935
+ className: "absolute right-0 top-full z-50 mt-2 w-80 max-w-[calc(100vw-2rem)] space-y-3 rounded-xl border border-zinc-700 bg-zinc-950 p-4 text-sm text-zinc-300 shadow-xl",
936
+ children: [
937
+ /* @__PURE__ */ jsx("h3", {
938
+ className: "font-medium text-white",
939
+ children: "Chart status"
940
+ }),
941
+ /* @__PURE__ */ jsxs("p", { children: ["Market stream: ", status === "live" ? "Connected" : status === "connecting" ? "Connecting…" : "Disconnected"] }),
942
+ statusMessages.length ? /* @__PURE__ */ jsx("ul", {
943
+ className: "space-y-2",
944
+ children: statusMessages.map((message, index) => /* @__PURE__ */ jsx("li", { children: message }, index))
945
+ }) : /* @__PURE__ */ jsx("p", { children: "All chart data is up to date." }),
946
+ /* @__PURE__ */ jsxs("div", {
947
+ className: "flex flex-wrap gap-3",
948
+ children: [
949
+ (error || status === "reconnecting") && /* @__PURE__ */ jsx("button", {
950
+ type: "button",
951
+ className: "cursor-pointer underline",
952
+ onClick: () => {
953
+ setStatus("connecting");
954
+ setRetry((n) => n + 1);
955
+ reconnect();
956
+ },
957
+ children: "Retry chart"
958
+ }),
959
+ indicatorMessages.length > 0 && /* @__PURE__ */ jsx("button", {
960
+ type: "button",
961
+ className: "cursor-pointer underline",
962
+ onClick: indicatorData.retry,
963
+ children: "Retry indicators"
964
+ }),
965
+ account.canRetry && /* @__PURE__ */ jsx("button", {
966
+ type: "button",
967
+ className: "cursor-pointer underline",
968
+ onClick: account.retry,
969
+ children: "Retry account data"
970
+ })
971
+ ]
972
+ })
973
+ ]
974
+ })]
975
+ }), statusHost), /* @__PURE__ */ jsxs("div", {
976
+ className: "relative min-h-64 flex-1 overflow-hidden rounded-xl border border-zinc-800",
977
+ "aria-label": `${coin} ${interval} candlestick and volume chart`,
978
+ children: [
979
+ /* @__PURE__ */ jsx("div", {
980
+ ref: host,
981
+ className: "absolute inset-0 z-0"
982
+ }),
983
+ generated && /* @__PURE__ */ jsxs("div", {
984
+ role: "tooltip",
985
+ className: "pointer-events-none absolute left-2 top-2 z-20 max-w-[calc(100%-5rem)] rounded bg-zinc-950/85 px-2 py-1 text-xs text-zinc-300",
986
+ children: ["Read-only Pine object: ", generated.text]
987
+ }),
988
+ hover && overlayVisibility.trades && /* @__PURE__ */ jsxs("div", {
989
+ role: "tooltip",
990
+ "aria-label": "Trade fill",
991
+ className: "pointer-events-none absolute z-20 max-w-72 rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-2 text-xs text-zinc-200 shadow-xl",
992
+ style: {
993
+ left: Math.max(0, Math.min(hover.x + 12, (projection?.width ?? 300) - 280)),
994
+ top: Math.max(0, Math.min(hover.y + 12, (projection?.height ?? 100) - 70))
995
+ },
996
+ children: [/* @__PURE__ */ jsxs("p", {
997
+ className: hover.fill.side === "Buy" ? "text-emerald-400" : "text-red-400",
998
+ children: [
999
+ hover.fill.side,
1000
+ " · ",
1001
+ hover.fill.size,
1002
+ " ",
1003
+ coin,
1004
+ " @ ",
1005
+ hover.fill.price
1006
+ ]
1007
+ }), /* @__PURE__ */ jsx("time", {
1008
+ dateTime: new Date(hover.fill.time).toISOString(),
1009
+ children: new Date(hover.fill.time).toLocaleString()
1010
+ })]
1011
+ }),
1012
+ projection && /* @__PURE__ */ jsx(ChartDrawings, {
1013
+ document: layout.document,
1014
+ revision: layout.revision,
1015
+ projection,
1016
+ tool,
1017
+ selected,
1018
+ disabled,
1019
+ svgRef: svg,
1020
+ onSelect,
1021
+ onSave,
1022
+ onDrawn,
1023
+ onEdit
1024
+ }, `${layout.id}/${coin}/${interval}/${tool}`)
1025
+ ]
1026
+ })]
1027
+ });
1028
+ }
1029
+ function timeAt(candles, logical, step) {
1030
+ if (!candles.length) return 0;
1031
+ const floor = Math.floor(logical), a = candles[Math.max(0, Math.min(candles.length - 1, floor))];
1032
+ if (logical < 0) return candles[0].time + logical * step;
1033
+ if (floor >= candles.length - 1) return a.time + (logical - candles.length + 1) * step;
1034
+ return a.time + (candles[floor + 1].time - a.time) * (logical - floor);
1035
+ }
1036
+ function logicalAt(candles, time, step) {
1037
+ if (!candles.length) return 0;
1038
+ if (time < candles[0].time) return (time - candles[0].time) / step;
1039
+ let lo = 0, hi = candles.length - 1;
1040
+ while (lo < hi) {
1041
+ const mid = Math.ceil((lo + hi) / 2);
1042
+ if (candles[mid].time <= time) lo = mid;
1043
+ else hi = mid - 1;
1044
+ }
1045
+ return lo + (time - candles[lo].time) / (candles[lo + 1] ? candles[lo + 1].time - candles[lo].time : step);
1046
+ }
1047
+ function projectChart(state, step) {
1048
+ const { chart, price, candles } = state, scale = chart.timeScale();
1049
+ const coordinateAt = (logical) => {
1050
+ const index = Math.floor(logical), fraction = logical - index;
1051
+ const left = scale.logicalToCoordinate(index);
1052
+ if (left === null || fraction === 0) return left;
1053
+ const right = scale.logicalToCoordinate(index + 1);
1054
+ return right === null ? null : left + (right - left) * fraction;
1055
+ };
1056
+ return {
1057
+ width: Math.max(0, chart.chartElement().clientWidth - chart.priceScale("right").width()),
1058
+ height: Math.max(0, chart.panes()[0]?.getHeight() ?? chart.chartElement().clientHeight - scale.height()),
1059
+ x: (time) => coordinateAt(logicalAt(candles, time, step)),
1060
+ y: (value) => price.priceToCoordinate(value),
1061
+ point: (x, y, snap) => {
1062
+ const nearest = scale.coordinateToLogical(x), value = price.coordinateToPrice(y);
1063
+ if (nearest === null || value === null || value <= 0) return null;
1064
+ const index = Math.floor(nearest), left = coordinateAt(index), right = coordinateAt(index + 1);
1065
+ if (left === null || right === null || left === right) return null;
1066
+ const logical = index + (x - left) / (right - left);
1067
+ const candle = candles[Math.round(logical)];
1068
+ if (snap && candle) {
1069
+ const nearest = [
1070
+ candle.open,
1071
+ candle.high,
1072
+ candle.low,
1073
+ candle.close
1074
+ ].reduce((a, b) => Math.abs(a - value) < Math.abs(b - value) ? a : b);
1075
+ return {
1076
+ time: candle.time,
1077
+ price: nearest
1078
+ };
1079
+ }
1080
+ return {
1081
+ time: Math.max(0, timeAt(candles, logical, step)),
1082
+ price: value
1083
+ };
1084
+ }
1085
+ };
1086
+ }
1087
+ async function screenshot(canvas, svg) {
1088
+ const context = canvas.getContext("2d");
1089
+ if (!context) throw new Error("Chart screenshot is unavailable in this browser.");
1090
+ if (svg) {
1091
+ const clone = svg.cloneNode(true);
1092
+ clone.querySelectorAll("[data-handle]").forEach((node) => node.remove());
1093
+ const url = URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(clone)], { type: "image/svg+xml" }));
1094
+ try {
1095
+ const image = new Image();
1096
+ image.src = url;
1097
+ await image.decode();
1098
+ const ratio = canvas.width / Math.max(1, canvas.clientWidth || canvas.width / window.devicePixelRatio);
1099
+ context.drawImage(image, 0, 0, Number(svg.getAttribute("width")) * ratio, Number(svg.getAttribute("height")) * ratio);
1100
+ } finally {
1101
+ URL.revokeObjectURL(url);
1102
+ }
1103
+ }
1104
+ const output = document.createElement("canvas");
1105
+ for (const max of [
1106
+ 1600,
1107
+ 1e3,
1108
+ 700
1109
+ ]) {
1110
+ const ratio = Math.min(1, max / canvas.width, 1e3 / canvas.height);
1111
+ output.width = Math.round(canvas.width * ratio);
1112
+ output.height = Math.round(canvas.height * ratio);
1113
+ output.getContext("2d").drawImage(canvas, 0, 0, output.width, output.height);
1114
+ const data = output.toDataURL("image/png").split(",")[1];
1115
+ if (data.length <= 7e5) return data;
1116
+ }
1117
+ throw new Error("Chart image is too large. Zoom in or disable Include chart image.");
1118
+ }
1119
+ //#endregion
1120
+ //#region src/components/use-chart-layouts.ts
1121
+ /** SSE snapshots are authoritative, including reconnects, agent edits and other tabs. */
1122
+ function useChartLayouts(id, instruments) {
1123
+ const client = useQueryClient();
1124
+ const { data: layouts } = useSuspenseQuery(chartLayoutsQueryOptions(id));
1125
+ const [selected, setSelected] = useState(layouts[0]?.id ?? "");
1126
+ const mutation = useMutation({ mutationFn: (operation) => operation() });
1127
+ const pending = mutation.isPending;
1128
+ const error = mutation.error?.message;
1129
+ const [connectionError, setConnectionError] = useState("");
1130
+ const [ready, setReady] = useState(false);
1131
+ const busy = useRef(false), alive = useRef(true), streamVersion = useRef(0);
1132
+ const key = `hypervibes:chart-layout:${id}`;
1133
+ const restorePreference = useEffectEvent(() => {
1134
+ try {
1135
+ const saved = localStorage.getItem(key);
1136
+ if (saved && layouts.some((l) => l.id === saved)) setSelected(saved);
1137
+ } catch {}
1138
+ });
1139
+ const { interrupted, controlMessage, streamErrors } = useAgentEventsStatus();
1140
+ useAgentEvent("chart-layouts", (event) => {
1141
+ streamVersion.current++;
1142
+ const snapshot = JSON.parse(event.data);
1143
+ client.setQueryData(chartLayoutsQueryOptions(id).queryKey, (current = []) => {
1144
+ const known = new Map(current.map((layout) => [layout.id, layout]));
1145
+ return snapshot.map((layout) => {
1146
+ const previous = known.get(layout.id);
1147
+ return previous && previous.revision > layout.revision ? previous : layout;
1148
+ });
1149
+ });
1150
+ setReady(true);
1151
+ setConnectionError("");
1152
+ });
1153
+ useEffect(() => {
1154
+ const topicError = streamErrors["chart-layouts"];
1155
+ if (topicError) setConnectionError(topicError);
1156
+ else if (interrupted) setConnectionError(controlMessage || "Chart layout connection interrupted. Reconnecting…");
1157
+ }, [
1158
+ interrupted,
1159
+ controlMessage,
1160
+ streamErrors
1161
+ ]);
1162
+ useEffect(() => {
1163
+ alive.current = true;
1164
+ restorePreference();
1165
+ return () => {
1166
+ alive.current = false;
1167
+ streamVersion.current++;
1168
+ };
1169
+ }, [client, id]);
1170
+ async function run(operation) {
1171
+ if (busy.current) return void 0;
1172
+ busy.current = true;
1173
+ const before = streamVersion.current;
1174
+ try {
1175
+ return await mutation.mutateAsync(() => operation(before));
1176
+ } catch {
1177
+ return;
1178
+ } finally {
1179
+ busy.current = false;
1180
+ }
1181
+ }
1182
+ function choose(layoutId) {
1183
+ setSelected(layoutId);
1184
+ try {
1185
+ localStorage.setItem(key, layoutId);
1186
+ } catch {}
1187
+ }
1188
+ function accept(layout, requireExisting = false) {
1189
+ if (!alive.current) return;
1190
+ client.setQueryData(chartLayoutsQueryOptions(id).queryKey, (current = []) => {
1191
+ const previous = current.find((item) => item.id === layout.id);
1192
+ if (requireExisting && !previous || previous && previous.revision >= layout.revision) return current;
1193
+ return [layout, ...current.filter((item) => item.id !== layout.id)];
1194
+ });
1195
+ }
1196
+ const layout = layouts.find((item) => item.id === selected) ?? layouts[0];
1197
+ async function create(name, duplicate = false, defaultId) {
1198
+ let layoutId = defaultId;
1199
+ return run(async (before) => {
1200
+ layoutId ??= randomUuid();
1201
+ const result = await createChartLayout({ data: {
1202
+ id,
1203
+ layoutId,
1204
+ settings: {
1205
+ name,
1206
+ ...duplicate ? {} : {
1207
+ coin: instruments.includes("BTC") ? "BTC" : instruments[0] ?? "BTC",
1208
+ interval: "1h"
1209
+ }
1210
+ },
1211
+ ...duplicate && layout ? { sourceId: layout.id } : {}
1212
+ } });
1213
+ if (streamVersion.current === before) accept(result);
1214
+ if (alive.current) choose(result.id);
1215
+ return result;
1216
+ });
1217
+ }
1218
+ const initialize = useRef(false);
1219
+ const initializeLayout = useEffectEvent(() => {
1220
+ create("My chart");
1221
+ });
1222
+ useEffect(() => {
1223
+ if (!ready || initialize.current || !instruments.length) return;
1224
+ initialize.current = true;
1225
+ if (!layouts.length) initializeLayout();
1226
+ }, [
1227
+ ready,
1228
+ layouts.length,
1229
+ instruments.length
1230
+ ]);
1231
+ async function change(action, edit) {
1232
+ if (!layout) return void 0;
1233
+ let requestId;
1234
+ return run(async (before) => {
1235
+ requestId ??= randomUuid();
1236
+ const result = await changeChartLayout({ data: {
1237
+ id,
1238
+ layoutId: layout.id,
1239
+ revision: layout.revision,
1240
+ requestId,
1241
+ action,
1242
+ edit
1243
+ } });
1244
+ accept(result, streamVersion.current !== before);
1245
+ return result;
1246
+ });
1247
+ }
1248
+ async function remove() {
1249
+ if (!layout) return;
1250
+ await run(async (before) => {
1251
+ await deleteChartLayout({ data: {
1252
+ id,
1253
+ layoutId: layout.id,
1254
+ revision: layout.revision
1255
+ } });
1256
+ if (alive.current) {
1257
+ if (streamVersion.current === before) client.setQueryData(chartLayoutsQueryOptions(id).queryKey, (current) => current?.filter((item) => item.id !== layout.id));
1258
+ setSelected("");
1259
+ }
1260
+ });
1261
+ }
1262
+ return {
1263
+ layouts,
1264
+ layout,
1265
+ pending,
1266
+ error: error || connectionError,
1267
+ ready,
1268
+ choose,
1269
+ create,
1270
+ remove,
1271
+ change
1272
+ };
1273
+ }
1274
+ //#endregion
1275
+ //#region src/components/chart-drawing-editor.tsx
1276
+ function ChartDrawingEditor({ value, pending, saveError, onCancel, onApply }) {
1277
+ const [draft, setDraft] = useState(value), [error, setError] = useState("");
1278
+ async function apply() {
1279
+ try {
1280
+ if (await onApply(drawing(draft))) onCancel();
1281
+ } catch (cause) {
1282
+ setError(cause instanceof Error ? cause.message : "Invalid drawing settings");
1283
+ }
1284
+ }
1285
+ return /* @__PURE__ */ jsxs(ConfigDialog, {
1286
+ title: "Drawing settings",
1287
+ onCancel,
1288
+ onApply: () => {
1289
+ apply();
1290
+ },
1291
+ applyDisabled: pending || value.locked,
1292
+ children: [/* @__PURE__ */ jsxs("fieldset", {
1293
+ disabled: pending || value.locked,
1294
+ className: "space-y-4 disabled:opacity-50",
1295
+ children: [
1296
+ /* @__PURE__ */ jsxs("label", {
1297
+ className: "block space-y-1 text-sm",
1298
+ children: ["Name", /* @__PURE__ */ jsx("input", {
1299
+ autoFocus: true,
1300
+ className: configInput,
1301
+ value: draft.name,
1302
+ maxLength: 100,
1303
+ onChange: (e) => setDraft({
1304
+ ...draft,
1305
+ name: e.target.value
1306
+ })
1307
+ })]
1308
+ }),
1309
+ /* @__PURE__ */ jsxs("label", {
1310
+ className: "block space-y-1 text-sm",
1311
+ children: ["Text", /* @__PURE__ */ jsx("textarea", {
1312
+ className: configInput,
1313
+ value: draft.text,
1314
+ rows: 3,
1315
+ maxLength: 1e3,
1316
+ onChange: (e) => setDraft({
1317
+ ...draft,
1318
+ text: e.target.value
1319
+ })
1320
+ })]
1321
+ }),
1322
+ /* @__PURE__ */ jsxs("label", {
1323
+ className: "block space-y-1 text-sm",
1324
+ children: ["Group", /* @__PURE__ */ jsx("input", {
1325
+ className: configInput,
1326
+ value: draft.group,
1327
+ maxLength: 80,
1328
+ onChange: (e) => setDraft({
1329
+ ...draft,
1330
+ group: e.target.value
1331
+ })
1332
+ })]
1333
+ }),
1334
+ /* @__PURE__ */ jsxs("div", {
1335
+ className: "flex flex-wrap items-center gap-4",
1336
+ children: [
1337
+ /* @__PURE__ */ jsxs("label", {
1338
+ className: "flex items-center gap-2 text-sm",
1339
+ children: ["Color", /* @__PURE__ */ jsx("input", {
1340
+ type: "color",
1341
+ className: "cursor-pointer",
1342
+ value: draft.color,
1343
+ onChange: (e) => setDraft({
1344
+ ...draft,
1345
+ color: e.target.value
1346
+ })
1347
+ })]
1348
+ }),
1349
+ /* @__PURE__ */ jsxs("label", {
1350
+ className: "flex items-center gap-2 text-sm",
1351
+ children: ["Width", /* @__PURE__ */ jsx("select", {
1352
+ className: "cursor-pointer rounded bg-zinc-900 p-2",
1353
+ value: draft.width,
1354
+ onChange: (e) => setDraft({
1355
+ ...draft,
1356
+ width: Number(e.target.value)
1357
+ }),
1358
+ children: [
1359
+ 1,
1360
+ 2,
1361
+ 3,
1362
+ 4,
1363
+ 5
1364
+ ].map((n) => /* @__PURE__ */ jsx("option", { children: n }, n))
1365
+ })]
1366
+ }),
1367
+ /* @__PURE__ */ jsxs("label", {
1368
+ className: "cursor-pointer text-sm",
1369
+ children: [/* @__PURE__ */ jsx("input", {
1370
+ type: "checkbox",
1371
+ className: "mr-2 cursor-pointer",
1372
+ checked: draft.dashed,
1373
+ onChange: (e) => setDraft({
1374
+ ...draft,
1375
+ dashed: e.target.checked
1376
+ })
1377
+ }), "Dashed"]
1378
+ })
1379
+ ]
1380
+ }),
1381
+ draft.kind === "rectangle" && /* @__PURE__ */ jsxs("label", {
1382
+ className: "block text-sm",
1383
+ children: ["Fill opacity", /* @__PURE__ */ jsx("input", {
1384
+ type: "range",
1385
+ className: "ml-3 cursor-pointer",
1386
+ min: "0",
1387
+ max: "0.6",
1388
+ step: "0.05",
1389
+ value: draft.fillOpacity,
1390
+ onChange: (e) => setDraft({
1391
+ ...draft,
1392
+ fillOpacity: Number(e.target.value)
1393
+ })
1394
+ })]
1395
+ }),
1396
+ /* @__PURE__ */ jsxs("fieldset", {
1397
+ className: "space-y-2",
1398
+ children: [/* @__PURE__ */ jsx("legend", {
1399
+ className: "mb-2 text-sm",
1400
+ children: "Coordinates · UTC seconds / price"
1401
+ }), draft.points.map((point, i) => /* @__PURE__ */ jsxs("div", {
1402
+ className: "grid grid-cols-2 gap-3",
1403
+ children: [/* @__PURE__ */ jsxs("label", {
1404
+ className: "text-xs text-zinc-400",
1405
+ children: [
1406
+ "Anchor ",
1407
+ i + 1,
1408
+ " time",
1409
+ /* @__PURE__ */ jsx("input", {
1410
+ type: "number",
1411
+ step: "any",
1412
+ className: configInput,
1413
+ value: point.time,
1414
+ onChange: (e) => setDraft({
1415
+ ...draft,
1416
+ points: draft.points.map((p, n) => n === i ? {
1417
+ ...p,
1418
+ time: Number(e.target.value)
1419
+ } : p)
1420
+ })
1421
+ })
1422
+ ]
1423
+ }), /* @__PURE__ */ jsxs("label", {
1424
+ className: "text-xs text-zinc-400",
1425
+ children: [
1426
+ "Anchor ",
1427
+ i + 1,
1428
+ " price",
1429
+ /* @__PURE__ */ jsx("input", {
1430
+ type: "number",
1431
+ step: "any",
1432
+ className: configInput,
1433
+ value: point.price,
1434
+ onChange: (e) => setDraft({
1435
+ ...draft,
1436
+ points: draft.points.map((p, n) => n === i ? {
1437
+ ...p,
1438
+ price: Number(e.target.value)
1439
+ } : p)
1440
+ })
1441
+ })
1442
+ ]
1443
+ })]
1444
+ }, i))]
1445
+ }),
1446
+ /* @__PURE__ */ jsxs("fieldset", { children: [/* @__PURE__ */ jsx("legend", {
1447
+ className: "mb-2 text-sm",
1448
+ children: "Visible timeframes · none selected means all"
1449
+ }), /* @__PURE__ */ jsx("div", {
1450
+ className: "flex flex-wrap gap-3",
1451
+ children: chartIntervals.map((interval) => /* @__PURE__ */ jsxs("label", {
1452
+ className: "cursor-pointer text-xs",
1453
+ children: [/* @__PURE__ */ jsx("input", {
1454
+ type: "checkbox",
1455
+ className: "mr-1 cursor-pointer",
1456
+ checked: draft.intervals.includes(interval),
1457
+ onChange: (e) => setDraft({
1458
+ ...draft,
1459
+ intervals: e.target.checked ? [...draft.intervals, interval] : draft.intervals.filter((i) => i !== interval)
1460
+ })
1461
+ }), interval]
1462
+ }, interval))
1463
+ })] })
1464
+ ]
1465
+ }), (error || saveError) && /* @__PURE__ */ jsx("p", {
1466
+ role: "alert",
1467
+ className: "mt-3 text-sm text-red-300",
1468
+ children: error || saveError
1469
+ })]
1470
+ });
1471
+ }
1472
+ //#endregion
1473
+ //#region src/components/chart-layout-settings.tsx
1474
+ var actions = [
1475
+ {
1476
+ action: "new",
1477
+ label: "New"
1478
+ },
1479
+ {
1480
+ action: "duplicate",
1481
+ label: "Duplicate"
1482
+ },
1483
+ {
1484
+ action: "rename",
1485
+ label: "Rename"
1486
+ },
1487
+ {
1488
+ action: "delete",
1489
+ label: "Delete"
1490
+ },
1491
+ {
1492
+ action: "notes",
1493
+ label: "Layout notes"
1494
+ }
1495
+ ];
1496
+ /** A non-modal popup, portaled so the scrolling single-row toolbar cannot clip it. */
1497
+ function ChartLayoutSettings({ pending, hasLayout, onAction }) {
1498
+ const [open, setOpen] = useState(false);
1499
+ const [position, setPosition] = useState({
1500
+ left: 0,
1501
+ top: 0
1502
+ });
1503
+ const button = useRef(null), popup = useRef(null);
1504
+ const popupId = useId();
1505
+ useLayoutEffect(() => {
1506
+ if (!open) return;
1507
+ const place = () => {
1508
+ const anchor = button.current.getBoundingClientRect(), panel = popup.current.getBoundingClientRect();
1509
+ setPosition({
1510
+ left: Math.max(8, Math.min(anchor.left, window.innerWidth - panel.width - 8)),
1511
+ top: Math.max(8, Math.min(anchor.bottom + 6, window.innerHeight - panel.height - 8))
1512
+ });
1513
+ };
1514
+ place();
1515
+ popup.current.querySelector("button:not(:disabled)")?.focus();
1516
+ window.addEventListener("resize", place);
1517
+ window.addEventListener("scroll", place, true);
1518
+ return () => {
1519
+ window.removeEventListener("resize", place);
1520
+ window.removeEventListener("scroll", place, true);
1521
+ };
1522
+ }, [open]);
1523
+ useEffect(() => {
1524
+ if (!open) return;
1525
+ const outside = (event) => {
1526
+ if (event.target instanceof Node && !popup.current?.contains(event.target) && !button.current?.contains(event.target)) setOpen(false);
1527
+ };
1528
+ const escape = (event) => {
1529
+ if (event.key !== "Escape") return;
1530
+ event.preventDefault();
1531
+ event.stopPropagation();
1532
+ setOpen(false);
1533
+ button.current?.focus();
1534
+ };
1535
+ document.addEventListener("pointerdown", outside);
1536
+ document.addEventListener("focusin", outside);
1537
+ document.addEventListener("keydown", escape, true);
1538
+ return () => {
1539
+ document.removeEventListener("pointerdown", outside);
1540
+ document.removeEventListener("focusin", outside);
1541
+ document.removeEventListener("keydown", escape, true);
1542
+ };
1543
+ }, [open]);
1544
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
1545
+ ref: button,
1546
+ type: "button",
1547
+ "aria-label": "Layout settings",
1548
+ title: "Layout settings",
1549
+ "aria-haspopup": "dialog",
1550
+ "aria-expanded": open,
1551
+ "aria-controls": open ? popupId : void 0,
1552
+ onClick: () => setOpen(!open),
1553
+ className: "flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-zinc-700 text-zinc-300 hover:bg-zinc-800 aria-expanded:bg-zinc-800",
1554
+ children: /* @__PURE__ */ jsxs("svg", {
1555
+ "aria-hidden": "true",
1556
+ viewBox: "0 0 24 24",
1557
+ fill: "none",
1558
+ stroke: "currentColor",
1559
+ strokeWidth: 1.5,
1560
+ strokeLinecap: "round",
1561
+ strokeLinejoin: "round",
1562
+ className: "size-4",
1563
+ children: [/* @__PURE__ */ jsx("path", { d: "m9 3-.7 2.4-2 .9-2.3-.6-2 3.5 1.6 1.8v2l-1.6 1.8 2 3.5 2.3-.6 2 .9L9 21h6l.7-2.4 2-.9 2.3.6 2-3.5-1.6-1.8v-2L22 9.2l-2-3.5-2.3.6-2-.9L15 3Z" }), /* @__PURE__ */ jsx("circle", {
1564
+ cx: "12",
1565
+ cy: "12",
1566
+ r: "3"
1567
+ })]
1568
+ })
1569
+ }), open && createPortal(/* @__PURE__ */ jsx("div", {
1570
+ ref: popup,
1571
+ id: popupId,
1572
+ role: "dialog",
1573
+ "aria-label": "Layout settings",
1574
+ className: "fixed z-40 max-h-[calc(100dvh-1rem)] w-52 max-w-[calc(100vw-1rem)] overflow-y-auto rounded-xl border border-zinc-700 bg-zinc-950 p-1.5 shadow-2xl",
1575
+ style: position,
1576
+ children: actions.map(({ action, label }) => /* @__PURE__ */ jsx("button", {
1577
+ type: "button",
1578
+ disabled: pending || !hasLayout && action !== "new",
1579
+ "aria-label": action === "delete" ? "Delete layout" : void 0,
1580
+ className: `block w-full cursor-pointer rounded-lg px-3 py-2 text-left text-sm hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-40 ${action === "delete" ? "text-red-300" : "text-zinc-200"} ${action === "notes" ? "mt-1 border-t border-zinc-800" : ""}`,
1581
+ onClick: () => {
1582
+ setOpen(false);
1583
+ button.current?.focus();
1584
+ onAction(action);
1585
+ },
1586
+ children: label
1587
+ }, action))
1588
+ }), document.body)] });
1589
+ }
1590
+ //#endregion
1591
+ //#region src/components/chart-indicator-controls.tsx
1592
+ var iconButton$1 = "inline-flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg text-zinc-400 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40";
1593
+ function IndicatorActionIcon({ name }) {
1594
+ return /* @__PURE__ */ jsx("svg", {
1595
+ "aria-hidden": "true",
1596
+ viewBox: "0 0 24 24",
1597
+ fill: "none",
1598
+ stroke: "currentColor",
1599
+ strokeWidth: 1.5,
1600
+ strokeLinecap: "round",
1601
+ strokeLinejoin: "round",
1602
+ className: "h-4 w-4",
1603
+ children: /* @__PURE__ */ jsx("path", { d: {
1604
+ eye: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12Z M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0",
1605
+ "eye-off": "m3 3 18 18 M10.6 5.1A12 12 0 0 1 12 5c6.5 0 10 7 10 7a17 17 0 0 1-3 4 M6.5 6.5A22 22 0 0 0 2 12s3.5 7 10 7a12 12 0 0 0 5.5-1.5 M10 10a3 3 0 0 0 4 4",
1606
+ gear: "m9 3-.5 3-2 1-3-.5-2 3 2 2v2l-2 2 2 3 3-.5 2 1 .5 3h4l.5-3 2-1 3 .5 2-3-2-2v-2l2-2-2-3-3 .5-2-1L13 3Z M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0",
1607
+ trash: "M3 6h18 M9 6V3h6v3 M5 6l1 15h12l1-15 M10 10v7 M14 10v7",
1608
+ up: "m6 12 6-6 6 6 M12 6v14"
1609
+ }[name] })
1610
+ });
1611
+ }
1612
+ function ChartIndicatorControls({ agentId, instances, disabled, onChange }) {
1613
+ const [open, setOpen] = useState(false), [editing, setEditing] = useState(null);
1614
+ const [adding, setAdding] = useState(false), [preparationError, setPreparationError] = useState("");
1615
+ const [query, setQuery] = useState(""), [offset, setOffset] = useState(0);
1616
+ const catalog = useQuery({
1617
+ ...indicatorListQueryOptions(agentId, {
1618
+ query,
1619
+ offset,
1620
+ limit: 20
1621
+ }),
1622
+ enabled: open
1623
+ });
1624
+ const indicatorIds = [...new Set(instances.map((instance) => instance.indicatorId))];
1625
+ const details = useQueries({ queries: indicatorIds.map((indicatorId) => ({
1626
+ ...indicatorQueryOptions(agentId, indicatorId),
1627
+ enabled: open
1628
+ })) });
1629
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1630
+ /* @__PURE__ */ jsxs("button", {
1631
+ type: "button",
1632
+ className: configButton,
1633
+ "aria-label": "Indicators",
1634
+ disabled,
1635
+ onClick: () => setOpen(true),
1636
+ children: [
1637
+ "Indicators (",
1638
+ instances.length,
1639
+ ")"
1640
+ ]
1641
+ }),
1642
+ open && !editing && /* @__PURE__ */ jsxs(ConfigDialog, {
1643
+ title: "Chart indicators",
1644
+ onCancel: () => setOpen(false),
1645
+ onApply: () => setOpen(false),
1646
+ applyLabel: "Done",
1647
+ children: [
1648
+ instances.length > 0 && /* @__PURE__ */ jsx("div", {
1649
+ className: "overflow-x-auto rounded-xl border border-zinc-800",
1650
+ children: /* @__PURE__ */ jsxs("table", {
1651
+ "aria-label": "Indicators on chart",
1652
+ className: "w-full text-left text-sm",
1653
+ children: [/* @__PURE__ */ jsx("thead", {
1654
+ className: "border-b border-zinc-800 text-zinc-400",
1655
+ children: /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
1656
+ scope: "col",
1657
+ className: "px-4 py-3 font-medium",
1658
+ children: "Indicator"
1659
+ }), /* @__PURE__ */ jsx("th", {
1660
+ scope: "col",
1661
+ className: "px-4 py-3 text-right font-medium",
1662
+ children: "Actions"
1663
+ })] })
1664
+ }), /* @__PURE__ */ jsx("tbody", {
1665
+ className: "divide-y divide-zinc-800",
1666
+ children: instances.map((instance, index) => {
1667
+ const detail = details[indicatorIds.indexOf(instance.indicatorId)];
1668
+ const name = detail?.data?.descriptor.name ?? (detail?.isError ? "Unavailable indicator" : "Loading indicator…");
1669
+ const visibility = `${instance.hidden ? "Show" : "Hide"} ${name}`;
1670
+ return /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("td", {
1671
+ className: `px-4 py-3 ${instance.hidden ? "text-zinc-500" : "text-zinc-200"}`,
1672
+ children: name
1673
+ }), /* @__PURE__ */ jsx("td", {
1674
+ className: "px-4 py-2",
1675
+ children: /* @__PURE__ */ jsxs("div", {
1676
+ className: "flex justify-end gap-1",
1677
+ children: [
1678
+ /* @__PURE__ */ jsx("button", {
1679
+ type: "button",
1680
+ className: iconButton$1,
1681
+ "aria-label": visibility,
1682
+ title: visibility,
1683
+ disabled,
1684
+ onClick: () => {
1685
+ onChange(instances.map((v) => v.id === instance.id ? {
1686
+ ...v,
1687
+ hidden: !v.hidden
1688
+ } : v));
1689
+ },
1690
+ children: /* @__PURE__ */ jsx(IndicatorActionIcon, { name: instance.hidden ? "eye-off" : "eye" })
1691
+ }),
1692
+ /* @__PURE__ */ jsx("button", {
1693
+ type: "button",
1694
+ className: iconButton$1,
1695
+ "aria-label": `Configure ${name}`,
1696
+ title: `Configure ${name}`,
1697
+ disabled,
1698
+ onClick: () => {
1699
+ setAdding(false);
1700
+ setEditing(instance);
1701
+ },
1702
+ children: /* @__PURE__ */ jsx(IndicatorActionIcon, { name: "gear" })
1703
+ }),
1704
+ /* @__PURE__ */ jsx("button", {
1705
+ type: "button",
1706
+ className: iconButton$1,
1707
+ "aria-label": `Move ${name} up`,
1708
+ title: `Move ${name} up`,
1709
+ disabled: disabled || index === 0,
1710
+ onClick: () => {
1711
+ const next = [...instances];
1712
+ [next[index - 1], next[index]] = [next[index], next[index - 1]];
1713
+ onChange(next);
1714
+ },
1715
+ children: /* @__PURE__ */ jsx(IndicatorActionIcon, { name: "up" })
1716
+ }),
1717
+ /* @__PURE__ */ jsx("button", {
1718
+ type: "button",
1719
+ className: `${iconButton$1} hover:text-red-300`,
1720
+ "aria-label": `Remove ${name}`,
1721
+ title: `Remove ${name}`,
1722
+ disabled,
1723
+ onClick: () => {
1724
+ onChange(instances.filter((v) => v.id !== instance.id));
1725
+ },
1726
+ children: /* @__PURE__ */ jsx(IndicatorActionIcon, { name: "trash" })
1727
+ })
1728
+ ]
1729
+ })
1730
+ })] }, instance.id);
1731
+ })
1732
+ })]
1733
+ })
1734
+ }),
1735
+ /* @__PURE__ */ jsxs("label", {
1736
+ className: "my-3 block text-sm",
1737
+ children: ["Search", /* @__PURE__ */ jsx("input", {
1738
+ className: "block w-full rounded-xl border border-zinc-700 bg-zinc-950 p-3 text-sm text-white",
1739
+ value: query,
1740
+ onChange: (e) => {
1741
+ setQuery(e.target.value);
1742
+ setOffset(0);
1743
+ }
1744
+ })]
1745
+ }),
1746
+ (preparationError || catalog.error) && /* @__PURE__ */ jsx("p", {
1747
+ role: "alert",
1748
+ children: preparationError || catalog.error?.message
1749
+ }),
1750
+ /* @__PURE__ */ jsx("ul", {
1751
+ className: "space-y-2",
1752
+ children: catalog.data?.indicators.map((entry) => /* @__PURE__ */ jsxs("li", {
1753
+ className: "flex items-center justify-between gap-2 text-sm",
1754
+ children: [/* @__PURE__ */ jsxs("span", { children: [
1755
+ entry.name,
1756
+ " · ",
1757
+ entry.builtIn ? "Built-in" : `Custom · v${entry.version}`
1758
+ ] }), /* @__PURE__ */ jsx("button", {
1759
+ type: "button",
1760
+ className: "cursor-pointer rounded-full border border-zinc-700 px-4 py-2 text-sm text-zinc-200 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-50",
1761
+ disabled: disabled || instances.length >= 10,
1762
+ onClick: () => {
1763
+ try {
1764
+ const id = randomUuid();
1765
+ setPreparationError("");
1766
+ setAdding(true);
1767
+ setEditing({
1768
+ id,
1769
+ indicatorId: entry.id,
1770
+ configuredVersion: entry.version,
1771
+ inputs: {},
1772
+ pane: "auto",
1773
+ hidden: false,
1774
+ styles: {}
1775
+ });
1776
+ } catch (cause) {
1777
+ setPreparationError(cause instanceof Error ? cause.message : "Unable to prepare the indicator");
1778
+ }
1779
+ },
1780
+ children: "Add"
1781
+ })]
1782
+ }, entry.id))
1783
+ }),
1784
+ /* @__PURE__ */ jsxs("div", {
1785
+ className: "mt-3 flex gap-2",
1786
+ children: [/* @__PURE__ */ jsx("button", {
1787
+ type: "button",
1788
+ className: "cursor-pointer rounded-full border border-zinc-700 px-4 py-2 text-sm text-zinc-200 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-50",
1789
+ disabled: !offset,
1790
+ onClick: () => setOffset(Math.max(0, offset - 20)),
1791
+ children: "Previous"
1792
+ }), /* @__PURE__ */ jsx("button", {
1793
+ type: "button",
1794
+ className: "cursor-pointer rounded-full border border-zinc-700 px-4 py-2 text-sm text-zinc-200 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-50",
1795
+ disabled: catalog.data?.nextOffset == null,
1796
+ onClick: () => setOffset(catalog.data.nextOffset),
1797
+ children: "Next"
1798
+ })]
1799
+ })
1800
+ ]
1801
+ }),
1802
+ editing && /* @__PURE__ */ jsx(IndicatorInstanceDialog, {
1803
+ agentId,
1804
+ initial: editing,
1805
+ adding,
1806
+ disabled: disabled || adding && instances.length >= 10,
1807
+ onCancel: () => setEditing(null),
1808
+ onApply: async (value) => {
1809
+ if (await onChange(adding ? [...instances, value] : instances.map((v) => v.id === value.id ? value : v))) {
1810
+ setEditing(null);
1811
+ if (adding) setOpen(false);
1812
+ }
1813
+ }
1814
+ }, editing.id)
1815
+ ] });
1816
+ }
1817
+ function IndicatorInstanceDialog({ agentId, initial, adding, disabled, onCancel, onApply }) {
1818
+ const [draft, setDraft] = useState(initial), [styles, setStyles] = useState(JSON.stringify(initial.styles)), [error, setError] = useState("");
1819
+ const detail = useQuery(indicatorQueryOptions(agentId, initial.indicatorId));
1820
+ return /* @__PURE__ */ jsxs(ConfigDialog, {
1821
+ title: adding ? "Add indicator" : "Configure indicator",
1822
+ applyLabel: adding ? "Add to chart" : "Apply",
1823
+ onCancel,
1824
+ applyDisabled: disabled || !detail.data,
1825
+ onApply: () => {
1826
+ try {
1827
+ const parsed = JSON.parse(styles);
1828
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Styles must be an object keyed by output ID");
1829
+ onApply({
1830
+ ...draft,
1831
+ configuredVersion: detail.data.descriptor.version,
1832
+ styles: parsed
1833
+ }).catch((e) => setError(e instanceof Error ? e.message : "Unable to save settings"));
1834
+ } catch (e) {
1835
+ setError(e instanceof Error ? e.message : "Invalid styles");
1836
+ }
1837
+ },
1838
+ children: [
1839
+ detail.data && /* @__PURE__ */ jsx(IndicatorInputFields, {
1840
+ schema: detail.data.descriptor.inputs,
1841
+ value: draft.inputs,
1842
+ onChange: (inputs) => setDraft({
1843
+ ...draft,
1844
+ inputs
1845
+ }),
1846
+ disabled
1847
+ }),
1848
+ /* @__PURE__ */ jsxs("details", {
1849
+ className: "rounded-xl border border-zinc-800 p-3",
1850
+ children: [
1851
+ /* @__PURE__ */ jsx("summary", {
1852
+ className: "cursor-pointer text-sm text-zinc-400 hover:text-white",
1853
+ children: "Advanced options"
1854
+ }),
1855
+ /* @__PURE__ */ jsxs("label", {
1856
+ className: "my-3 block text-sm",
1857
+ children: ["Pane", /* @__PURE__ */ jsxs("select", {
1858
+ className: `${configInput} cursor-pointer`,
1859
+ value: draft.pane,
1860
+ onChange: (e) => setDraft({
1861
+ ...draft,
1862
+ pane: e.target.value
1863
+ }),
1864
+ children: [
1865
+ /* @__PURE__ */ jsx("option", {
1866
+ value: "auto",
1867
+ children: "Default"
1868
+ }),
1869
+ /* @__PURE__ */ jsx("option", {
1870
+ value: "price",
1871
+ children: "Price pane"
1872
+ }),
1873
+ /* @__PURE__ */ jsx("option", {
1874
+ value: "separate",
1875
+ children: "Separate pane"
1876
+ })
1877
+ ]
1878
+ })]
1879
+ }),
1880
+ /* @__PURE__ */ jsxs("label", {
1881
+ className: "block text-sm",
1882
+ children: ["Output styles (JSON keyed by exact output ID)", /* @__PURE__ */ jsx("textarea", {
1883
+ rows: 4,
1884
+ className: `${configInput} font-mono`,
1885
+ value: styles,
1886
+ onChange: (e) => setStyles(e.target.value)
1887
+ })]
1888
+ }),
1889
+ /* @__PURE__ */ jsxs("p", {
1890
+ className: "text-xs text-zinc-500",
1891
+ children: [
1892
+ "Example: ",
1893
+ `{"EMA":{"color":"#ff9900","width":2,"hidden":false}}`,
1894
+ ". display.none cannot be overridden."
1895
+ ]
1896
+ })
1897
+ ]
1898
+ }),
1899
+ (error || detail.error) && /* @__PURE__ */ jsx("p", {
1900
+ role: "alert",
1901
+ className: "text-red-300",
1902
+ children: error || detail.error?.message
1903
+ })
1904
+ ]
1905
+ });
1906
+ }
1907
+ //#endregion
1908
+ //#region src/components/agent-chart-page.tsx
1909
+ var control = "cursor-pointer rounded-lg border border-zinc-700 text-xs text-zinc-300 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-40";
1910
+ var button = `${control} px-2.5 py-1.5`;
1911
+ var iconButton = `${control} flex size-8 shrink-0 items-center justify-center aria-pressed:border-violet-700 aria-pressed:bg-violet-950/50 aria-pressed:text-violet-200`;
1912
+ var toolLabels = {
1913
+ select: "Select",
1914
+ horizontal: "Horizontal line",
1915
+ "horizontal-ray": "Horizontal ray",
1916
+ vertical: "Vertical line",
1917
+ trend: "Trend line",
1918
+ ray: "Ray",
1919
+ extended: "Extended line",
1920
+ rectangle: "Rectangle zone",
1921
+ text: "Text",
1922
+ callout: "Callout",
1923
+ arrow: "Arrow"
1924
+ };
1925
+ var toolIcons = {
1926
+ select: "↖",
1927
+ horizontal: "━",
1928
+ "horizontal-ray": "╶",
1929
+ vertical: "│",
1930
+ trend: "╱",
1931
+ ray: "↗",
1932
+ extended: "⤢",
1933
+ rectangle: "▭",
1934
+ text: "T",
1935
+ callout: "☷",
1936
+ arrow: "➜"
1937
+ };
1938
+ var iconPaths = {
1939
+ magnet: "M5 3v10a7 7 0 0 0 14 0V3h-4v10a3 3 0 0 1-6 0V3ZM5 7h4m6 0h4",
1940
+ eye: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7S2 12 2 12Zm7 0a3 3 0 1 0 6 0 3 3 0 0 0-6 0",
1941
+ hidden: "m3 3 18 18M10 5.2a12 12 0 0 1 2-.2c7 0 10 7 10 7a19 19 0 0 1-3 4M6 6.5A19 19 0 0 0 2 12s3 7 10 7a13 13 0 0 0 5-1",
1942
+ tools: "m4 20 4-1L20 7l-4-4L4 15Zm10-15 4 4M4 15l4 4M14 16l5 5 3-3-5-5",
1943
+ lock: "M6 10h12v11H6ZM8 10V6a4 4 0 0 1 8 0v4m-4 5v2",
1944
+ unlock: "M6 10h12v11H6ZM8 10V6a4 4 0 0 1 8 0m-4 9v2",
1945
+ account: "M3 6h18M3 12h18M3 18h18M7 3v6m10 0v6m-7 0v6",
1946
+ chat: "M3 4h18v16H3ZM15 4v16m3-10 2 2-2 2",
1947
+ chatHidden: "M3 4h18v16H3ZM15 4v16m-4-10-2 2 2 2"
1948
+ };
1949
+ function ChartIcon({ name }) {
1950
+ return /* @__PURE__ */ jsx("svg", {
1951
+ "aria-hidden": "true",
1952
+ viewBox: "0 0 24 24",
1953
+ fill: "none",
1954
+ stroke: "currentColor",
1955
+ strokeWidth: 1.5,
1956
+ strokeLinecap: "round",
1957
+ strokeLinejoin: "round",
1958
+ className: "size-4",
1959
+ children: /* @__PURE__ */ jsx("path", { d: iconPaths[name] })
1960
+ });
1961
+ }
1962
+ var minChatWidth = 240;
1963
+ var maxChatWidth = 560;
1964
+ function AgentChartPage({ id, instruments, initialChat, choices }) {
1965
+ const { layouts, layout, pending, error, ready, choose, create, remove, change } = useChartLayouts(id, instruments);
1966
+ const [tool, setTool] = useState("select"), [selected, setSelected] = useState("");
1967
+ const [chatOpen, setChatOpen] = useState(true), [chatWidth, setChatWidth] = useState(360);
1968
+ const [editing, setEditing] = useState(false), [keepDrawing, setKeepDrawing] = useState(false), [toolsOpen, setToolsOpen] = useState(false);
1969
+ const [nameDialog, setNameDialog] = useState(null), [notesOpen, setNotesOpen] = useState(false);
1970
+ const [overlayDialog, setOverlayDialog] = useState(false), [overlays, setOverlays] = useState(defaultChartOverlays);
1971
+ const overlayPreferences = `hypervibes:chart-overlays:v1:${id}`;
1972
+ useEffect(() => {
1973
+ let value = defaultChartOverlays;
1974
+ try {
1975
+ const saved = JSON.parse(localStorage.getItem(overlayPreferences) ?? "null");
1976
+ if (saved && typeof saved === "object" && "trades" in saved && typeof saved.trades === "boolean" && "position" in saved && typeof saved.position === "boolean" && "orders" in saved && typeof saved.orders === "boolean") value = {
1977
+ trades: saved.trades,
1978
+ position: saved.position,
1979
+ orders: saved.orders
1980
+ };
1981
+ } catch {}
1982
+ setOverlays(value);
1983
+ }, [overlayPreferences]);
1984
+ const chart = useRef(null), workspace = useRef(null), canvas = useRef(null);
1985
+ const [chatLimit, setChatLimit] = useState(maxChatWidth);
1986
+ const [chartStatusHost, setChartStatusHost] = useState(null);
1987
+ const splitWidth = Math.min(chatWidth, chatLimit);
1988
+ const clampWidth = (width) => Math.max(minChatWidth, Math.min(chatLimit, width));
1989
+ useEffect(() => {
1990
+ if (!workspace.current || typeof ResizeObserver === "undefined") return;
1991
+ const observer = new ResizeObserver(([entry]) => {
1992
+ if (entry) setChatLimit(Math.max(minChatWidth, Math.min(maxChatWidth, Math.floor(entry.contentRect.width - 292))));
1993
+ });
1994
+ observer.observe(workspace.current);
1995
+ return () => observer.disconnect();
1996
+ }, []);
1997
+ const resize = useRef(null);
1998
+ const preferences = `hypervibes:chart-panel:${id}`;
1999
+ const [restoredPreferences, setRestoredPreferences] = useState(null);
2000
+ useEffect(() => {
2001
+ try {
2002
+ const saved = JSON.parse(localStorage.getItem(preferences) ?? "null");
2003
+ if (saved && typeof saved === "object") {
2004
+ if ("open" in saved && typeof saved.open === "boolean") setChatOpen(saved.open);
2005
+ if ("width" in saved && typeof saved.width === "number" && Number.isFinite(saved.width)) setChatWidth(Math.max(minChatWidth, Math.min(maxChatWidth, saved.width)));
2006
+ }
2007
+ } catch {}
2008
+ setRestoredPreferences(preferences);
2009
+ }, [preferences]);
2010
+ function panelPreference(open, width) {
2011
+ setChatOpen(open);
2012
+ setChatWidth(width);
2013
+ try {
2014
+ localStorage.setItem(preferences, JSON.stringify({
2015
+ open,
2016
+ width
2017
+ }));
2018
+ } catch {}
2019
+ }
2020
+ const selectedObject = layout?.document.drawings.find((d) => d.id === selected && d.coin === layout.document.coin);
2021
+ const objects = layout?.document.drawings.filter((d) => d.coin === layout.document.coin) ?? [];
2022
+ const allLocked = objects.length > 0 && objects.every((d) => d.locked);
2023
+ const timeframeHidden = layout ? objects.filter((d) => !d.hidden && d.intervals.length > 0 && !d.intervals.includes(layout.document.interval)) : [];
2024
+ async function save(drawing) {
2025
+ return (await change("edit", {
2026
+ label: `Edit ${drawing.name}`,
2027
+ upsert: [drawing]
2028
+ }))?.revision;
2029
+ }
2030
+ function selectLayout(value) {
2031
+ choose(value);
2032
+ setSelected("");
2033
+ setTool("select");
2034
+ setEditing(false);
2035
+ }
2036
+ function layoutAction(action) {
2037
+ if (action === "notes") setNotesOpen(true);
2038
+ else if (action === "delete") {
2039
+ if (layout && window.confirm(`Delete saved layout “${layout.document.name}”? Its chat messages remain available.`)) remove();
2040
+ } else setNameDialog(action);
2041
+ }
2042
+ function onKey(event) {
2043
+ if (event.target instanceof HTMLElement && (event.target.matches("input,textarea,select") || event.target.isContentEditable) || document.querySelector("dialog[open]")) return;
2044
+ if (event.key === "Escape" && tool !== "select") {
2045
+ event.preventDefault();
2046
+ event.stopPropagation();
2047
+ setTool("select");
2048
+ return;
2049
+ }
2050
+ const key = event.key.toLowerCase();
2051
+ if ((event.ctrlKey || event.metaKey) && (key === "z" || key === "y")) {
2052
+ event.preventDefault();
2053
+ const redo = key === "y" || event.shiftKey;
2054
+ if (!pending && (redo ? layout?.canRedo : layout?.canUndo)) change(redo ? "redo" : "undo");
2055
+ }
2056
+ if ((event.key === "Delete" || event.key === "Backspace") && selectedObject && !selectedObject.locked && !pending) {
2057
+ event.preventDefault();
2058
+ change("edit", {
2059
+ label: `Delete ${selectedObject.name}`,
2060
+ remove: [selectedObject.id]
2061
+ });
2062
+ }
2063
+ }
2064
+ if (!instruments.length) return /* @__PURE__ */ jsx("p", {
2065
+ role: "alert",
2066
+ className: "text-amber-200",
2067
+ children: "No perpetual instruments are available."
2068
+ });
2069
+ if (restoredPreferences !== preferences || !layout && !ready) return /* @__PURE__ */ jsx("section", {
2070
+ ref: workspace,
2071
+ "aria-label": "Trading chart",
2072
+ "aria-busy": "true",
2073
+ className: "flex h-[calc(100dvh-6rem)] min-h-[480px] w-full items-center justify-center rounded-xl border border-zinc-800 bg-[#0d0d10]",
2074
+ children: /* @__PURE__ */ jsx("p", {
2075
+ role: "status",
2076
+ className: "text-sm text-zinc-500",
2077
+ children: "Loading chart workspace…"
2078
+ })
2079
+ });
2080
+ return /* @__PURE__ */ jsxs("section", {
2081
+ ref: workspace,
2082
+ "aria-label": "Trading chart",
2083
+ onKeyDown: onKey,
2084
+ className: "flex h-[calc(100dvh-6rem)] min-h-[480px] w-full min-w-0 flex-col gap-2",
2085
+ children: [
2086
+ /* @__PURE__ */ jsx("h2", {
2087
+ className: "sr-only",
2088
+ children: "Chart workspace"
2089
+ }),
2090
+ /* @__PURE__ */ jsxs("div", {
2091
+ role: "toolbar",
2092
+ "aria-label": "Chart controls",
2093
+ className: "flex shrink-0 flex-nowrap items-center gap-1.5 overflow-x-auto pb-1",
2094
+ children: [
2095
+ /* @__PURE__ */ jsxs("select", {
2096
+ "aria-label": "Layout",
2097
+ value: layout?.id ?? "",
2098
+ disabled: pending,
2099
+ onChange: (e) => selectLayout(e.target.value),
2100
+ className: `${button} h-8 w-40 shrink-0 truncate bg-zinc-950`,
2101
+ children: [!layouts.length && /* @__PURE__ */ jsx("option", {
2102
+ value: "",
2103
+ children: "No layouts"
2104
+ }), layouts.map((l) => /* @__PURE__ */ jsx("option", {
2105
+ value: l.id,
2106
+ children: l.document.name
2107
+ }, l.id))]
2108
+ }),
2109
+ /* @__PURE__ */ jsx(ChartLayoutSettings, {
2110
+ pending,
2111
+ hasLayout: Boolean(layout),
2112
+ onAction: layoutAction
2113
+ }),
2114
+ /* @__PURE__ */ jsx(SingleInstrumentPicker, {
2115
+ instruments,
2116
+ value: layout?.document.coin ?? null,
2117
+ disabled: pending || !layout,
2118
+ saveError: error,
2119
+ className: `${control} h-8 shrink-0 px-2`,
2120
+ onChange: async (coin) => {
2121
+ if (!layout) return false;
2122
+ if (coin === layout.document.coin) return true;
2123
+ if (!await change("edit", {
2124
+ label: "Change instrument",
2125
+ settings: { coin }
2126
+ })) return false;
2127
+ setSelected("");
2128
+ setTool("select");
2129
+ setEditing(false);
2130
+ return true;
2131
+ }
2132
+ }, layout?.id),
2133
+ /* @__PURE__ */ jsxs("select", {
2134
+ "aria-label": "Timeframe",
2135
+ value: layout?.document.interval ?? "",
2136
+ disabled: pending || !layout,
2137
+ onChange: (e) => {
2138
+ if (isChartInterval(e.target.value)) {
2139
+ setTool("select");
2140
+ change("edit", {
2141
+ label: "Change timeframe",
2142
+ settings: { interval: e.target.value }
2143
+ });
2144
+ }
2145
+ },
2146
+ className: `${button} h-8 w-20 shrink-0 bg-zinc-950`,
2147
+ children: [!layout && /* @__PURE__ */ jsx("option", {
2148
+ value: "",
2149
+ children: "Timeframe"
2150
+ }), chartIntervals.map((interval) => /* @__PURE__ */ jsx("option", { children: interval }, interval))]
2151
+ }),
2152
+ layout && /* @__PURE__ */ jsx(ChartIndicatorControls, {
2153
+ agentId: id,
2154
+ instances: layout.document.indicators,
2155
+ disabled: pending,
2156
+ onChange: async (indicators) => Boolean(await change("edit", {
2157
+ label: "Configure indicators",
2158
+ settings: { indicators }
2159
+ }))
2160
+ }),
2161
+ /* @__PURE__ */ jsx("button", {
2162
+ type: "button",
2163
+ className: iconButton,
2164
+ "aria-label": "Account overlays",
2165
+ title: "Account overlays · Trades, position and orders (read-only)",
2166
+ disabled: !layout,
2167
+ onClick: () => setOverlayDialog(true),
2168
+ children: /* @__PURE__ */ jsx(ChartIcon, { name: "account" })
2169
+ }),
2170
+ /* @__PURE__ */ jsx("button", {
2171
+ type: "button",
2172
+ className: iconButton,
2173
+ "aria-label": "Magnet",
2174
+ "aria-pressed": layout?.document.magnet ?? false,
2175
+ disabled: pending || !layout,
2176
+ title: `${layout?.document.magnet ? "Magnet on" : "Magnet off"} · Snap to candle OHLC · Ctrl/Cmd temporarily inverts snapping`,
2177
+ onClick: () => {
2178
+ if (layout) change("edit", {
2179
+ label: "Toggle magnet",
2180
+ settings: { magnet: !layout.document.magnet }
2181
+ });
2182
+ },
2183
+ children: /* @__PURE__ */ jsx(ChartIcon, { name: "magnet" })
2184
+ }),
2185
+ /* @__PURE__ */ jsx("button", {
2186
+ type: "button",
2187
+ className: iconButton,
2188
+ "aria-label": layout?.document.hideDrawings ? "Show drawings" : "Hide drawings",
2189
+ "aria-pressed": Boolean(layout && !layout.document.hideDrawings),
2190
+ disabled: pending || !layout,
2191
+ title: layout?.document.hideDrawings ? "Show drawings" : "Hide drawings",
2192
+ onClick: () => {
2193
+ if (layout) change("edit", {
2194
+ label: "Toggle drawings",
2195
+ settings: { hideDrawings: !layout.document.hideDrawings }
2196
+ });
2197
+ },
2198
+ children: /* @__PURE__ */ jsx(ChartIcon, { name: layout?.document.hideDrawings ? "hidden" : "eye" })
2199
+ }),
2200
+ /* @__PURE__ */ jsx("button", {
2201
+ type: "button",
2202
+ className: iconButton,
2203
+ "aria-label": toolsOpen ? "Hide annotation tools" : "Show annotation tools",
2204
+ "aria-pressed": toolsOpen,
2205
+ "aria-expanded": toolsOpen,
2206
+ disabled: !layout,
2207
+ title: toolsOpen ? "Hide annotation tools" : "Show annotation tools",
2208
+ onClick: () => {
2209
+ setToolsOpen(!toolsOpen);
2210
+ setTool("select");
2211
+ },
2212
+ children: /* @__PURE__ */ jsx(ChartIcon, { name: "tools" })
2213
+ }),
2214
+ /* @__PURE__ */ jsx("button", {
2215
+ type: "button",
2216
+ className: iconButton,
2217
+ "aria-label": allLocked ? "Unlock objects" : "Lock objects",
2218
+ "aria-pressed": allLocked,
2219
+ disabled: pending || !objects.length,
2220
+ title: `${allLocked ? "Unlock" : "Lock"} all annotations for ${layout?.document.coin ?? "this instrument"}`,
2221
+ onClick: () => {
2222
+ change("edit", {
2223
+ label: allLocked ? "Unlock drawings" : "Lock drawings",
2224
+ upsert: objects.filter((d) => d.locked === allLocked).map((d) => ({
2225
+ ...d,
2226
+ locked: !allLocked
2227
+ }))
2228
+ });
2229
+ },
2230
+ children: /* @__PURE__ */ jsx(ChartIcon, { name: allLocked ? "lock" : "unlock" })
2231
+ }),
2232
+ /* @__PURE__ */ jsx("button", {
2233
+ type: "button",
2234
+ className: iconButton,
2235
+ "aria-label": chatOpen ? "Hide chat" : "Show chat",
2236
+ "aria-pressed": chatOpen,
2237
+ "aria-expanded": chatOpen,
2238
+ title: chatOpen ? "Hide chat" : "Show chat",
2239
+ onClick: () => panelPreference(!chatOpen, splitWidth),
2240
+ children: /* @__PURE__ */ jsx(ChartIcon, { name: chatOpen ? "chat" : "chatHidden" })
2241
+ }),
2242
+ /* @__PURE__ */ jsx("div", {
2243
+ ref: setChartStatusHost,
2244
+ "aria-label": "Chart status",
2245
+ className: "ml-auto flex h-8 shrink-0 items-center pl-2"
2246
+ })
2247
+ ]
2248
+ }),
2249
+ /* @__PURE__ */ jsx("span", {
2250
+ role: "status",
2251
+ className: "sr-only",
2252
+ children: pending ? "Saving…" : error ? "Not saved · see error" : layout ? "Saved" : ready ? "Create a layout to begin" : "Loading…"
2253
+ }),
2254
+ error && /* @__PURE__ */ jsx("p", {
2255
+ role: "alert",
2256
+ className: "text-sm text-amber-200",
2257
+ children: error
2258
+ }),
2259
+ layout && !layout.document.hideDrawings && timeframeHidden.length > 0 && /* @__PURE__ */ jsxs("p", {
2260
+ role: "status",
2261
+ className: "flex flex-wrap items-center gap-2 text-xs text-amber-200",
2262
+ children: [/* @__PURE__ */ jsxs("span", { children: [
2263
+ timeframeHidden.length,
2264
+ " ",
2265
+ timeframeHidden.length === 1 ? "drawing" : "drawings",
2266
+ " hidden on ",
2267
+ layout.document.interval,
2268
+ " by timeframe settings."
2269
+ ] }), /* @__PURE__ */ jsx("button", {
2270
+ type: "button",
2271
+ className: button,
2272
+ disabled: pending || timeframeHidden.every((d) => d.locked),
2273
+ title: "Remove timeframe restrictions from these unlocked drawings. Locked drawings are unchanged.",
2274
+ onClick: () => {
2275
+ change("edit", {
2276
+ label: "Show drawings on all timeframes",
2277
+ upsert: timeframeHidden.filter((d) => !d.locked).map((d) => ({
2278
+ ...d,
2279
+ intervals: []
2280
+ }))
2281
+ });
2282
+ },
2283
+ children: "Show on all timeframes"
2284
+ })]
2285
+ }),
2286
+ /* @__PURE__ */ jsxs("div", {
2287
+ "aria-label": "Chart and chat workspace",
2288
+ className: "flex min-h-0 flex-1 overflow-x-auto",
2289
+ children: [/* @__PURE__ */ jsxs("div", {
2290
+ className: "flex min-w-[280px] flex-1 gap-2",
2291
+ children: [layout && toolsOpen && /* @__PURE__ */ jsxs("div", {
2292
+ role: "toolbar",
2293
+ "aria-label": "Drawing tools",
2294
+ className: "flex w-11 shrink-0 flex-col gap-1 overflow-y-auto",
2295
+ children: [["select", ...drawingKinds].map((kind) => /* @__PURE__ */ jsx("button", {
2296
+ type: "button",
2297
+ "aria-label": toolLabels[kind],
2298
+ title: `${toolLabels[kind]}${kind === "select" ? "" : " · click to place anchors"}`,
2299
+ "aria-pressed": tool === kind,
2300
+ disabled: pending,
2301
+ onClick: () => {
2302
+ setTool(kind);
2303
+ setSelected("");
2304
+ },
2305
+ className: `${control} py-1.5 text-lg ${tool === kind ? "border-violet-500 bg-violet-950/50 text-violet-200" : ""}`,
2306
+ children: toolIcons[kind]
2307
+ }, kind)), /* @__PURE__ */ jsx("button", {
2308
+ type: "button",
2309
+ className: `${control} py-1.5`,
2310
+ "aria-label": "Keep drawing",
2311
+ "aria-pressed": keepDrawing,
2312
+ title: "Keep drawing after placing an object",
2313
+ onClick: () => setKeepDrawing(!keepDrawing),
2314
+ children: "∞"
2315
+ })]
2316
+ }), layout ? /* @__PURE__ */ jsxs("div", {
2317
+ ref: canvas,
2318
+ tabIndex: 0,
2319
+ "aria-label": "Chart canvas",
2320
+ onPointerDown: (e) => {
2321
+ if (!(e.target instanceof Element) || !e.target.closest("button,input,textarea,select")) e.currentTarget.focus();
2322
+ },
2323
+ className: "flex min-w-0 flex-1 flex-col outline-none focus-visible:ring-1 focus-visible:ring-violet-500",
2324
+ children: [/* @__PURE__ */ jsx(MarketChart, {
2325
+ ref: chart,
2326
+ id,
2327
+ layout,
2328
+ tool,
2329
+ selected,
2330
+ disabled: pending,
2331
+ statusHost: chartStatusHost,
2332
+ overlayVisibility: overlays,
2333
+ onSelect: (drawingId) => {
2334
+ setSelected(drawingId);
2335
+ canvas.current?.focus({ preventScroll: true });
2336
+ },
2337
+ onSave: save,
2338
+ onDrawn: () => {
2339
+ if (!keepDrawing) setTool("select");
2340
+ },
2341
+ onEdit: () => setEditing(true)
2342
+ }, `${layout.id}/${layout.document.coin}/${layout.document.interval}`), (toolsOpen || selectedObject) && /* @__PURE__ */ jsxs("div", {
2343
+ className: "flex min-h-8 flex-wrap items-center gap-2 py-1 text-xs text-zinc-500",
2344
+ children: [toolsOpen && /* @__PURE__ */ jsxs("span", { children: [toolLabels[tool], tool !== "select" ? " · Click anchors · Esc cancels" : " · Select an object or draw from the toolbar"] }), selectedObject && /* @__PURE__ */ jsxs(Fragment$1, { children: [
2345
+ /* @__PURE__ */ jsx("span", {
2346
+ className: "text-violet-300",
2347
+ children: selectedObject.name
2348
+ }),
2349
+ /* @__PURE__ */ jsx("button", {
2350
+ className: button,
2351
+ disabled: pending || selectedObject.locked,
2352
+ onClick: () => setEditing(true),
2353
+ children: "Settings"
2354
+ }),
2355
+ /* @__PURE__ */ jsx("button", {
2356
+ className: button,
2357
+ disabled: pending,
2358
+ onClick: () => {
2359
+ save({
2360
+ ...selectedObject,
2361
+ locked: !selectedObject.locked
2362
+ });
2363
+ },
2364
+ children: selectedObject.locked ? "Unlock" : "Lock"
2365
+ }),
2366
+ /* @__PURE__ */ jsx("button", {
2367
+ className: button,
2368
+ disabled: pending || selectedObject.locked,
2369
+ onClick: () => {
2370
+ change("edit", {
2371
+ label: `Delete ${selectedObject.name}`,
2372
+ remove: [selectedObject.id]
2373
+ });
2374
+ },
2375
+ children: "Delete"
2376
+ })
2377
+ ] })]
2378
+ })]
2379
+ }) : /* @__PURE__ */ jsx("p", {
2380
+ className: "p-4 text-sm text-zinc-500",
2381
+ children: "Create a layout from the layout settings to begin."
2382
+ })]
2383
+ }), /* @__PURE__ */ jsx("aside", {
2384
+ hidden: !chatOpen,
2385
+ "aria-label": "Chart chat panel",
2386
+ className: "min-h-0 shrink-0",
2387
+ style: { width: splitWidth + 12 },
2388
+ children: /* @__PURE__ */ jsxs("div", {
2389
+ className: "flex h-full min-h-0",
2390
+ children: [/* @__PURE__ */ jsx("div", {
2391
+ role: "separator",
2392
+ "aria-label": "Resize chart chat",
2393
+ "aria-orientation": "vertical",
2394
+ "aria-valuemin": minChatWidth,
2395
+ "aria-valuemax": chatLimit,
2396
+ "aria-valuenow": splitWidth,
2397
+ "aria-valuetext": `${splitWidth}px chat width`,
2398
+ tabIndex: 0,
2399
+ className: "flex w-3 shrink-0 cursor-col-resize touch-none items-center justify-center rounded hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-violet-500",
2400
+ onKeyDown: (e) => {
2401
+ const delta = e.key === "ArrowLeft" ? 20 : e.key === "ArrowRight" ? -20 : 0;
2402
+ if (delta || e.key === "Home" || e.key === "End") {
2403
+ e.preventDefault();
2404
+ panelPreference(true, e.key === "Home" ? minChatWidth : e.key === "End" ? chatLimit : clampWidth(splitWidth + delta));
2405
+ }
2406
+ },
2407
+ onPointerDown: (e) => {
2408
+ if (e.button !== 0) return;
2409
+ e.preventDefault();
2410
+ resize.current = {
2411
+ x: e.clientX,
2412
+ width: splitWidth,
2413
+ pointerId: e.pointerId
2414
+ };
2415
+ e.currentTarget.setPointerCapture(e.pointerId);
2416
+ },
2417
+ onPointerMove: (e) => {
2418
+ const moving = resize.current;
2419
+ if (moving && moving.pointerId === e.pointerId) setChatWidth(clampWidth(moving.width + moving.x - e.clientX));
2420
+ },
2421
+ onPointerUp: (e) => {
2422
+ const moving = resize.current;
2423
+ if (!moving || moving.pointerId !== e.pointerId) return;
2424
+ resize.current = null;
2425
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId);
2426
+ panelPreference(true, clampWidth(moving.width + moving.x - e.clientX));
2427
+ },
2428
+ onPointerCancel: (e) => {
2429
+ const moving = resize.current;
2430
+ if (moving && moving.pointerId === e.pointerId) {
2431
+ setChatWidth(moving.width);
2432
+ resize.current = null;
2433
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId);
2434
+ }
2435
+ },
2436
+ onLostPointerCapture: (e) => {
2437
+ const moving = resize.current;
2438
+ if (moving && moving.pointerId === e.pointerId) {
2439
+ setChatWidth(moving.width);
2440
+ resize.current = null;
2441
+ }
2442
+ },
2443
+ children: /* @__PURE__ */ jsx("span", { className: "h-12 w-1 rounded bg-zinc-700" })
2444
+ }), /* @__PURE__ */ jsx("div", {
2445
+ className: "min-h-0 min-w-0 flex-1",
2446
+ children: /* @__PURE__ */ jsx(AgentChatPage, {
2447
+ initial: initialChat,
2448
+ choices,
2449
+ embedded: true,
2450
+ visible: chatOpen,
2451
+ chartContext: layout ? {
2452
+ label: `${layout.document.name} · ${layout.document.coin} · ${layout.document.interval}${tool === "select" ? "" : " · Finish drawing or choose Select before sending"}`,
2453
+ disabled: pending || tool !== "select",
2454
+ capture: async (image) => {
2455
+ if (!chart.current) throw new Error("Chart is not ready");
2456
+ return chart.current.capture(layout, selectedObject?.id ?? "", image);
2457
+ }
2458
+ } : void 0
2459
+ })
2460
+ })]
2461
+ })
2462
+ })]
2463
+ }),
2464
+ editing && selectedObject && layout && /* @__PURE__ */ jsx(ChartDrawingEditor, {
2465
+ value: selectedObject,
2466
+ pending,
2467
+ saveError: error,
2468
+ onCancel: () => setEditing(false),
2469
+ onApply: async (drawing) => await save(drawing) !== void 0
2470
+ }, `${layout.id}/${layout.revision}/${selected}`),
2471
+ overlayDialog && /* @__PURE__ */ jsx(ChartOverlayDialog, {
2472
+ value: overlays,
2473
+ onCancel: () => setOverlayDialog(false),
2474
+ onApply: (value) => {
2475
+ setOverlays(value);
2476
+ setOverlayDialog(false);
2477
+ try {
2478
+ localStorage.setItem(overlayPreferences, JSON.stringify(value));
2479
+ } catch {}
2480
+ }
2481
+ }),
2482
+ notesOpen && layout && /* @__PURE__ */ jsx(LayoutNotesDialog, {
2483
+ notes: layout.document.notes,
2484
+ pending,
2485
+ error,
2486
+ onCancel: () => setNotesOpen(false),
2487
+ onApply: async (notes) => {
2488
+ if (await change("edit", {
2489
+ label: "Update layout notes",
2490
+ settings: { notes }
2491
+ })) setNotesOpen(false);
2492
+ }
2493
+ }, layout.id),
2494
+ nameDialog && /* @__PURE__ */ jsx(LayoutNameDialog, {
2495
+ title: nameDialog === "new" ? "New layout" : nameDialog === "duplicate" ? "Duplicate layout" : "Rename layout",
2496
+ initial: nameDialog === "new" ? "My chart" : `${layout?.document.name ?? "Chart"}${nameDialog === "duplicate" ? " copy" : ""}`,
2497
+ pending,
2498
+ error,
2499
+ onCancel: () => setNameDialog(null),
2500
+ onApply: async (name, operationId) => {
2501
+ if (nameDialog === "rename" ? await change("edit", {
2502
+ label: "Rename layout",
2503
+ settings: { name }
2504
+ }) : await create(name, nameDialog === "duplicate", operationId)) {
2505
+ setNameDialog(null);
2506
+ setSelected("");
2507
+ }
2508
+ }
2509
+ }, nameDialog)
2510
+ ]
2511
+ });
2512
+ }
2513
+ function ChartOverlayDialog({ value, onApply, onCancel }) {
2514
+ const [draft, setDraft] = useState(value);
2515
+ return /* @__PURE__ */ jsxs(ConfigDialog, {
2516
+ title: "Account overlays",
2517
+ onCancel,
2518
+ onApply: () => onApply(draft),
2519
+ children: [/* @__PURE__ */ jsx("div", {
2520
+ className: "space-y-3 text-sm",
2521
+ children: [
2522
+ ["trades", "Historical buys & sells"],
2523
+ ["position", "Current position"],
2524
+ ["orders", "Open orders & TP/SL triggers"]
2525
+ ].map(([key, label]) => /* @__PURE__ */ jsxs("label", {
2526
+ className: "flex cursor-pointer items-center gap-3",
2527
+ children: [/* @__PURE__ */ jsx("input", {
2528
+ type: "checkbox",
2529
+ checked: draft[key],
2530
+ onChange: (event) => setDraft({
2531
+ ...draft,
2532
+ [key]: event.target.checked
2533
+ }),
2534
+ className: "cursor-pointer accent-violet-500"
2535
+ }), label]
2536
+ }, key))
2537
+ }), /* @__PURE__ */ jsx("p", {
2538
+ className: "mt-4 text-xs text-zinc-500",
2539
+ children: "Read-only data from this Agent’s linked Hyperliquid account, including manual trades. History follows the journal. Visibility is saved on this device, independently of layouts and annotations."
2540
+ })]
2541
+ });
2542
+ }
2543
+ function LayoutNotesDialog({ notes, pending, error, onApply, onCancel }) {
2544
+ const [value, setValue] = useState(notes);
2545
+ return /* @__PURE__ */ jsxs(ConfigDialog, {
2546
+ title: "Layout notes",
2547
+ applyLabel: "Save notes",
2548
+ applyDisabled: pending,
2549
+ onCancel,
2550
+ onApply: () => {
2551
+ onApply(value);
2552
+ },
2553
+ children: [
2554
+ /* @__PURE__ */ jsxs("label", {
2555
+ className: "block space-y-2 text-sm",
2556
+ children: ["Notes", /* @__PURE__ */ jsx("textarea", {
2557
+ autoFocus: true,
2558
+ rows: 6,
2559
+ maxLength: 8e3,
2560
+ className: configInput,
2561
+ value,
2562
+ disabled: pending,
2563
+ onChange: (e) => setValue(e.target.value)
2564
+ })]
2565
+ }),
2566
+ /* @__PURE__ */ jsx("p", {
2567
+ className: "text-xs text-zinc-500",
2568
+ children: "Local to this layout. Not published research or Agent-wide insights."
2569
+ }),
2570
+ error && /* @__PURE__ */ jsx("p", {
2571
+ role: "alert",
2572
+ className: "text-sm text-red-300",
2573
+ children: error
2574
+ })
2575
+ ]
2576
+ });
2577
+ }
2578
+ function LayoutNameDialog({ title, initial, pending, error, onApply, onCancel }) {
2579
+ const [name, setName] = useState(initial);
2580
+ const request = useRef(null);
2581
+ const [preparationError, setPreparationError] = useState("");
2582
+ async function apply() {
2583
+ try {
2584
+ setPreparationError("");
2585
+ if (request.current?.name !== name.trim()) request.current = {
2586
+ name: name.trim(),
2587
+ id: randomUuid()
2588
+ };
2589
+ await onApply(name.trim(), request.current.id);
2590
+ } catch (cause) {
2591
+ setPreparationError(cause instanceof Error ? cause.message : "Unable to prepare the layout");
2592
+ }
2593
+ }
2594
+ return /* @__PURE__ */ jsxs(ConfigDialog, {
2595
+ title,
2596
+ onCancel,
2597
+ onApply: () => {
2598
+ apply();
2599
+ },
2600
+ applyDisabled: pending || !name.trim(),
2601
+ children: [
2602
+ /* @__PURE__ */ jsxs("label", {
2603
+ className: "block space-y-2 text-sm",
2604
+ children: ["Name", /* @__PURE__ */ jsx("input", {
2605
+ autoFocus: true,
2606
+ maxLength: 80,
2607
+ value: name,
2608
+ disabled: pending,
2609
+ onChange: (e) => setName(e.target.value),
2610
+ className: configInput
2611
+ })]
2612
+ }),
2613
+ /* @__PURE__ */ jsx("p", {
2614
+ className: "mt-3 text-xs text-zinc-500",
2615
+ children: "Layouts autosave instrument, timeframe, objects, styles and notes. Reopening follows the latest candles."
2616
+ }),
2617
+ (preparationError || error) && /* @__PURE__ */ jsx("p", {
2618
+ role: "alert",
2619
+ className: "text-sm text-red-300",
2620
+ children: preparationError || error
2621
+ })
2622
+ ]
2623
+ });
2624
+ }
2625
+ //#endregion
2626
+ //#region src/routes/_authenticated.agents.$agentId.chart.tsx?tsr-split=component
2627
+ function ChartRoute() {
2628
+ const { agentId } = Route.useParams();
2629
+ const { data: instruments, error } = useSuspenseQuery(chartInstrumentsQueryOptions(agentId));
2630
+ const { data: choices } = useSuspenseQuery(modelChoicesQueryOptions());
2631
+ const { data: chat } = useSuspenseQuery(agentChatQueryOptions(agentId));
2632
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [error && /* @__PURE__ */ jsx("p", {
2633
+ role: "alert",
2634
+ className: "text-sm text-amber-200",
2635
+ children: error.message
2636
+ }), /* @__PURE__ */ jsx(AgentChartPage, {
2637
+ id: agentId,
2638
+ instruments,
2639
+ initialChat: chat,
2640
+ choices
2641
+ }, agentId)] });
2642
+ }
2643
+ //#endregion
2644
+ export { ChartRoute as component };