@hexclave/js 1.0.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 (370) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +26 -0
  3. package/dist/chunk-BE-pF4vm.js +34 -0
  4. package/dist/dev-tool/dev-tool-core.d.ts +7 -0
  5. package/dist/dev-tool/dev-tool-core.d.ts.map +1 -0
  6. package/dist/dev-tool/dev-tool-core.js +2116 -0
  7. package/dist/dev-tool/dev-tool-core.js.map +1 -0
  8. package/dist/dev-tool/dev-tool-styles.d.ts +5 -0
  9. package/dist/dev-tool/dev-tool-styles.d.ts.map +1 -0
  10. package/dist/dev-tool/dev-tool-styles.js +2757 -0
  11. package/dist/dev-tool/dev-tool-styles.js.map +1 -0
  12. package/dist/dev-tool/dev-tool-trigger-position.d.ts +36 -0
  13. package/dist/dev-tool/dev-tool-trigger-position.d.ts.map +1 -0
  14. package/dist/dev-tool/dev-tool-trigger-position.js +70 -0
  15. package/dist/dev-tool/dev-tool-trigger-position.js.map +1 -0
  16. package/dist/dev-tool/dev-tool-trigger-position.test.d.ts +1 -0
  17. package/dist/dev-tool/dev-tool-trigger-position.test.js +155 -0
  18. package/dist/dev-tool/dev-tool-trigger-position.test.js.map +1 -0
  19. package/dist/dev-tool/index.d.ts +19 -0
  20. package/dist/dev-tool/index.d.ts.map +1 -0
  21. package/dist/dev-tool/index.js +119 -0
  22. package/dist/dev-tool/index.js.map +1 -0
  23. package/dist/esm/dev-tool/dev-tool-core.d.ts +7 -0
  24. package/dist/esm/dev-tool/dev-tool-core.d.ts.map +1 -0
  25. package/dist/esm/dev-tool/dev-tool-core.js +2114 -0
  26. package/dist/esm/dev-tool/dev-tool-core.js.map +1 -0
  27. package/dist/esm/dev-tool/dev-tool-styles.d.ts +5 -0
  28. package/dist/esm/dev-tool/dev-tool-styles.d.ts.map +1 -0
  29. package/dist/esm/dev-tool/dev-tool-styles.js +2755 -0
  30. package/dist/esm/dev-tool/dev-tool-styles.js.map +1 -0
  31. package/dist/esm/dev-tool/dev-tool-trigger-position.d.ts +36 -0
  32. package/dist/esm/dev-tool/dev-tool-trigger-position.d.ts.map +1 -0
  33. package/dist/esm/dev-tool/dev-tool-trigger-position.js +65 -0
  34. package/dist/esm/dev-tool/dev-tool-trigger-position.js.map +1 -0
  35. package/dist/esm/dev-tool/dev-tool-trigger-position.test.d.ts +1 -0
  36. package/dist/esm/dev-tool/dev-tool-trigger-position.test.js +155 -0
  37. package/dist/esm/dev-tool/dev-tool-trigger-position.test.js.map +1 -0
  38. package/dist/esm/dev-tool/index.d.ts +19 -0
  39. package/dist/esm/dev-tool/index.d.ts.map +1 -0
  40. package/dist/esm/dev-tool/index.js +117 -0
  41. package/dist/esm/dev-tool/index.js.map +1 -0
  42. package/dist/esm/index.d.ts +4 -0
  43. package/dist/esm/index.js +7 -0
  44. package/dist/esm/integrations/convex/component/convex.config.d.ts +7 -0
  45. package/dist/esm/integrations/convex/component/convex.config.d.ts.map +1 -0
  46. package/dist/esm/integrations/convex/component/convex.config.js +8 -0
  47. package/dist/esm/integrations/convex/component/convex.config.js.map +1 -0
  48. package/dist/esm/integrations/convex.d.ts +13 -0
  49. package/dist/esm/integrations/convex.d.ts.map +1 -0
  50. package/dist/esm/integrations/convex.js +23 -0
  51. package/dist/esm/integrations/convex.js.map +1 -0
  52. package/dist/esm/internal/deprecation-warning.d.ts +1 -0
  53. package/dist/esm/internal/deprecation-warning.js +24 -0
  54. package/dist/esm/internal/deprecation-warning.js.map +1 -0
  55. package/dist/esm/lib/auth.d.ts +31 -0
  56. package/dist/esm/lib/auth.d.ts.map +1 -0
  57. package/dist/esm/lib/auth.js +108 -0
  58. package/dist/esm/lib/auth.js.map +1 -0
  59. package/dist/esm/lib/auth.test.d.ts +1 -0
  60. package/dist/esm/lib/auth.test.js +60 -0
  61. package/dist/esm/lib/auth.test.js.map +1 -0
  62. package/dist/esm/lib/cookie.d.ts +42 -0
  63. package/dist/esm/lib/cookie.d.ts.map +1 -0
  64. package/dist/esm/lib/cookie.js +287 -0
  65. package/dist/esm/lib/cookie.js.map +1 -0
  66. package/dist/esm/lib/env.d.ts +42 -0
  67. package/dist/esm/lib/env.d.ts.map +1 -0
  68. package/dist/esm/lib/env.js +93 -0
  69. package/dist/esm/lib/env.js.map +1 -0
  70. package/dist/esm/lib/stack-app/api-keys/index.d.ts +53 -0
  71. package/dist/esm/lib/stack-app/api-keys/index.d.ts.map +1 -0
  72. package/dist/esm/lib/stack-app/api-keys/index.js +22 -0
  73. package/dist/esm/lib/stack-app/api-keys/index.js.map +1 -0
  74. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.d.ts +364 -0
  75. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.d.ts.map +1 -0
  76. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js +924 -0
  77. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -0
  78. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.cross-domain.test.d.ts +1 -0
  79. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.cross-domain.test.js +55 -0
  80. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -0
  81. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.d.ts +542 -0
  82. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.d.ts.map +1 -0
  83. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +2504 -0
  84. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -0
  85. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.oauth-prefetch.test.d.ts +1 -0
  86. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.oauth-prefetch.test.js +32 -0
  87. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.oauth-prefetch.test.js.map +1 -0
  88. package/dist/esm/lib/stack-app/apps/implementations/common.d.ts +65 -0
  89. package/dist/esm/lib/stack-app/apps/implementations/common.d.ts.map +1 -0
  90. package/dist/esm/lib/stack-app/apps/implementations/common.js +127 -0
  91. package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -0
  92. package/dist/esm/lib/stack-app/apps/implementations/event-tracker.d.ts +42 -0
  93. package/dist/esm/lib/stack-app/apps/implementations/event-tracker.d.ts.map +1 -0
  94. package/dist/esm/lib/stack-app/apps/implementations/event-tracker.js +211 -0
  95. package/dist/esm/lib/stack-app/apps/implementations/event-tracker.js.map +1 -0
  96. package/dist/esm/lib/stack-app/apps/implementations/event-tracker.test.d.ts +1 -0
  97. package/dist/esm/lib/stack-app/apps/implementations/event-tracker.test.js +87 -0
  98. package/dist/esm/lib/stack-app/apps/implementations/event-tracker.test.js.map +1 -0
  99. package/dist/esm/lib/stack-app/apps/implementations/index.d.ts +9 -0
  100. package/dist/esm/lib/stack-app/apps/implementations/index.d.ts.map +1 -0
  101. package/dist/esm/lib/stack-app/apps/implementations/index.js +27 -0
  102. package/dist/esm/lib/stack-app/apps/implementations/index.js.map +1 -0
  103. package/dist/esm/lib/stack-app/apps/implementations/redirect-page-urls.d.ts +35 -0
  104. package/dist/esm/lib/stack-app/apps/implementations/redirect-page-urls.d.ts.map +1 -0
  105. package/dist/esm/lib/stack-app/apps/implementations/redirect-page-urls.js +197 -0
  106. package/dist/esm/lib/stack-app/apps/implementations/redirect-page-urls.js.map +1 -0
  107. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.d.ts +203 -0
  108. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.d.ts.map +1 -0
  109. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +1094 -0
  110. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -0
  111. package/dist/esm/lib/stack-app/apps/implementations/session-refresh-subscription.d.ts +28 -0
  112. package/dist/esm/lib/stack-app/apps/implementations/session-refresh-subscription.d.ts.map +1 -0
  113. package/dist/esm/lib/stack-app/apps/implementations/session-refresh-subscription.js +31 -0
  114. package/dist/esm/lib/stack-app/apps/implementations/session-refresh-subscription.js.map +1 -0
  115. package/dist/esm/lib/stack-app/apps/implementations/session-refresh-subscription.test.d.ts +1 -0
  116. package/dist/esm/lib/stack-app/apps/implementations/session-refresh-subscription.test.js +82 -0
  117. package/dist/esm/lib/stack-app/apps/implementations/session-refresh-subscription.test.js.map +1 -0
  118. package/dist/esm/lib/stack-app/apps/implementations/session-replay.d.ts +111 -0
  119. package/dist/esm/lib/stack-app/apps/implementations/session-replay.d.ts.map +1 -0
  120. package/dist/esm/lib/stack-app/apps/implementations/session-replay.js +241 -0
  121. package/dist/esm/lib/stack-app/apps/implementations/session-replay.js.map +1 -0
  122. package/dist/esm/lib/stack-app/apps/implementations/session-replay.test.d.ts +1 -0
  123. package/dist/esm/lib/stack-app/apps/implementations/session-replay.test.js +29 -0
  124. package/dist/esm/lib/stack-app/apps/implementations/session-replay.test.js.map +1 -0
  125. package/dist/esm/lib/stack-app/apps/index.d.ts +4 -0
  126. package/dist/esm/lib/stack-app/apps/index.js +5 -0
  127. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.d.ts +244 -0
  128. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.d.ts.map +1 -0
  129. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js +9 -0
  130. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js.map +1 -0
  131. package/dist/esm/lib/stack-app/apps/interfaces/client-app.d.ts +208 -0
  132. package/dist/esm/lib/stack-app/apps/interfaces/client-app.d.ts.map +1 -0
  133. package/dist/esm/lib/stack-app/apps/interfaces/client-app.js +9 -0
  134. package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -0
  135. package/dist/esm/lib/stack-app/apps/interfaces/server-app.d.ts +116 -0
  136. package/dist/esm/lib/stack-app/apps/interfaces/server-app.d.ts.map +1 -0
  137. package/dist/esm/lib/stack-app/apps/interfaces/server-app.js +9 -0
  138. package/dist/esm/lib/stack-app/apps/interfaces/server-app.js.map +1 -0
  139. package/dist/esm/lib/stack-app/common.d.ts +174 -0
  140. package/dist/esm/lib/stack-app/common.d.ts.map +1 -0
  141. package/dist/esm/lib/stack-app/common.js +7 -0
  142. package/dist/esm/lib/stack-app/common.js.map +1 -0
  143. package/dist/esm/lib/stack-app/connected-accounts/index.d.ts +40 -0
  144. package/dist/esm/lib/stack-app/connected-accounts/index.d.ts.map +1 -0
  145. package/dist/esm/lib/stack-app/connected-accounts/index.js +1 -0
  146. package/dist/esm/lib/stack-app/contact-channels/index.d.ts +43 -0
  147. package/dist/esm/lib/stack-app/contact-channels/index.d.ts.map +1 -0
  148. package/dist/esm/lib/stack-app/contact-channels/index.js +39 -0
  149. package/dist/esm/lib/stack-app/contact-channels/index.js.map +1 -0
  150. package/dist/esm/lib/stack-app/customers/index.d.ts +129 -0
  151. package/dist/esm/lib/stack-app/customers/index.d.ts.map +1 -0
  152. package/dist/esm/lib/stack-app/customers/index.js +1 -0
  153. package/dist/esm/lib/stack-app/data-vault/index.d.ts +14 -0
  154. package/dist/esm/lib/stack-app/data-vault/index.d.ts.map +1 -0
  155. package/dist/esm/lib/stack-app/data-vault/index.js +1 -0
  156. package/dist/esm/lib/stack-app/email/index.d.ts +204 -0
  157. package/dist/esm/lib/stack-app/email/index.d.ts.map +1 -0
  158. package/dist/esm/lib/stack-app/email/index.js +1 -0
  159. package/dist/esm/lib/stack-app/email-templates/index.d.ts +17 -0
  160. package/dist/esm/lib/stack-app/email-templates/index.d.ts.map +1 -0
  161. package/dist/esm/lib/stack-app/email-templates/index.js +11 -0
  162. package/dist/esm/lib/stack-app/email-templates/index.js.map +1 -0
  163. package/dist/esm/lib/stack-app/index.d.ts +14 -0
  164. package/dist/esm/lib/stack-app/index.js +5 -0
  165. package/dist/esm/lib/stack-app/internal-api-keys/index.d.ts +42 -0
  166. package/dist/esm/lib/stack-app/internal-api-keys/index.d.ts.map +1 -0
  167. package/dist/esm/lib/stack-app/internal-api-keys/index.js +14 -0
  168. package/dist/esm/lib/stack-app/internal-api-keys/index.js.map +1 -0
  169. package/dist/esm/lib/stack-app/notification-categories/index.d.ts +11 -0
  170. package/dist/esm/lib/stack-app/notification-categories/index.d.ts.map +1 -0
  171. package/dist/esm/lib/stack-app/notification-categories/index.js +1 -0
  172. package/dist/esm/lib/stack-app/permissions/index.d.ts +43 -0
  173. package/dist/esm/lib/stack-app/permissions/index.d.ts.map +1 -0
  174. package/dist/esm/lib/stack-app/permissions/index.js +31 -0
  175. package/dist/esm/lib/stack-app/permissions/index.js.map +1 -0
  176. package/dist/esm/lib/stack-app/project-configs/index.d.ts +99 -0
  177. package/dist/esm/lib/stack-app/project-configs/index.d.ts.map +1 -0
  178. package/dist/esm/lib/stack-app/project-configs/index.js +1 -0
  179. package/dist/esm/lib/stack-app/projects/index.d.ts +145 -0
  180. package/dist/esm/lib/stack-app/projects/index.d.ts.map +1 -0
  181. package/dist/esm/lib/stack-app/projects/index.js +66 -0
  182. package/dist/esm/lib/stack-app/projects/index.js.map +1 -0
  183. package/dist/esm/lib/stack-app/session-replays/index.d.ts +66 -0
  184. package/dist/esm/lib/stack-app/session-replays/index.d.ts.map +1 -0
  185. package/dist/esm/lib/stack-app/session-replays/index.js +1 -0
  186. package/dist/esm/lib/stack-app/teams/index.d.ts +160 -0
  187. package/dist/esm/lib/stack-app/teams/index.d.ts.map +1 -0
  188. package/dist/esm/lib/stack-app/teams/index.js +35 -0
  189. package/dist/esm/lib/stack-app/teams/index.js.map +1 -0
  190. package/dist/esm/lib/stack-app/url-targets.d.ts +39 -0
  191. package/dist/esm/lib/stack-app/url-targets.d.ts.map +1 -0
  192. package/dist/esm/lib/stack-app/url-targets.js +266 -0
  193. package/dist/esm/lib/stack-app/url-targets.js.map +1 -0
  194. package/dist/esm/lib/stack-app/url-targets.test.d.ts +1 -0
  195. package/dist/esm/lib/stack-app/url-targets.test.js +213 -0
  196. package/dist/esm/lib/stack-app/url-targets.test.js.map +1 -0
  197. package/dist/esm/lib/stack-app/users/index.d.ts +357 -0
  198. package/dist/esm/lib/stack-app/users/index.d.ts.map +1 -0
  199. package/dist/esm/lib/stack-app/users/index.js +71 -0
  200. package/dist/esm/lib/stack-app/users/index.js.map +1 -0
  201. package/dist/esm/utils/url.d.ts +5 -0
  202. package/dist/esm/utils/url.d.ts.map +1 -0
  203. package/dist/esm/utils/url.js +16 -0
  204. package/dist/esm/utils/url.js.map +1 -0
  205. package/dist/index.d.ts +17 -0
  206. package/dist/index.js +31 -0
  207. package/dist/integrations/convex/component/convex.config.d.ts +61 -0
  208. package/dist/integrations/convex/component/convex.config.d.ts.map +1 -0
  209. package/dist/integrations/convex/component/convex.config.js +9 -0
  210. package/dist/integrations/convex/component/convex.config.js.map +1 -0
  211. package/dist/integrations/convex.d.ts +13 -0
  212. package/dist/integrations/convex.d.ts.map +1 -0
  213. package/dist/integrations/convex.js +25 -0
  214. package/dist/integrations/convex.js.map +1 -0
  215. package/dist/internal/deprecation-warning.d.ts +1 -0
  216. package/dist/internal/deprecation-warning.js +24 -0
  217. package/dist/internal/deprecation-warning.js.map +1 -0
  218. package/dist/lib/auth.d.ts +31 -0
  219. package/dist/lib/auth.d.ts.map +1 -0
  220. package/dist/lib/auth.js +111 -0
  221. package/dist/lib/auth.js.map +1 -0
  222. package/dist/lib/auth.test.d.ts +1 -0
  223. package/dist/lib/auth.test.js +60 -0
  224. package/dist/lib/auth.test.js.map +1 -0
  225. package/dist/lib/cookie.d.ts +42 -0
  226. package/dist/lib/cookie.d.ts.map +1 -0
  227. package/dist/lib/cookie.js +305 -0
  228. package/dist/lib/cookie.js.map +1 -0
  229. package/dist/lib/env.d.ts +42 -0
  230. package/dist/lib/env.d.ts.map +1 -0
  231. package/dist/lib/env.js +95 -0
  232. package/dist/lib/env.js.map +1 -0
  233. package/dist/lib/stack-app/api-keys/index.d.ts +53 -0
  234. package/dist/lib/stack-app/api-keys/index.d.ts.map +1 -0
  235. package/dist/lib/stack-app/api-keys/index.js +25 -0
  236. package/dist/lib/stack-app/api-keys/index.js.map +1 -0
  237. package/dist/lib/stack-app/apps/implementations/admin-app-impl.d.ts +362 -0
  238. package/dist/lib/stack-app/apps/implementations/admin-app-impl.d.ts.map +1 -0
  239. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js +926 -0
  240. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -0
  241. package/dist/lib/stack-app/apps/implementations/client-app-impl.cross-domain.test.d.ts +1 -0
  242. package/dist/lib/stack-app/apps/implementations/client-app-impl.cross-domain.test.js +55 -0
  243. package/dist/lib/stack-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -0
  244. package/dist/lib/stack-app/apps/implementations/client-app-impl.d.ts +541 -0
  245. package/dist/lib/stack-app/apps/implementations/client-app-impl.d.ts.map +1 -0
  246. package/dist/lib/stack-app/apps/implementations/client-app-impl.js +2507 -0
  247. package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -0
  248. package/dist/lib/stack-app/apps/implementations/client-app-impl.oauth-prefetch.test.d.ts +1 -0
  249. package/dist/lib/stack-app/apps/implementations/client-app-impl.oauth-prefetch.test.js +32 -0
  250. package/dist/lib/stack-app/apps/implementations/client-app-impl.oauth-prefetch.test.js.map +1 -0
  251. package/dist/lib/stack-app/apps/implementations/common.d.ts +65 -0
  252. package/dist/lib/stack-app/apps/implementations/common.d.ts.map +1 -0
  253. package/dist/lib/stack-app/apps/implementations/common.js +144 -0
  254. package/dist/lib/stack-app/apps/implementations/common.js.map +1 -0
  255. package/dist/lib/stack-app/apps/implementations/event-tracker.d.ts +42 -0
  256. package/dist/lib/stack-app/apps/implementations/event-tracker.d.ts.map +1 -0
  257. package/dist/lib/stack-app/apps/implementations/event-tracker.js +213 -0
  258. package/dist/lib/stack-app/apps/implementations/event-tracker.js.map +1 -0
  259. package/dist/lib/stack-app/apps/implementations/event-tracker.test.d.ts +1 -0
  260. package/dist/lib/stack-app/apps/implementations/event-tracker.test.js +87 -0
  261. package/dist/lib/stack-app/apps/implementations/event-tracker.test.js.map +1 -0
  262. package/dist/lib/stack-app/apps/implementations/index.d.ts +9 -0
  263. package/dist/lib/stack-app/apps/implementations/index.d.ts.map +1 -0
  264. package/dist/lib/stack-app/apps/implementations/index.js +31 -0
  265. package/dist/lib/stack-app/apps/implementations/index.js.map +1 -0
  266. package/dist/lib/stack-app/apps/implementations/redirect-page-urls.d.ts +35 -0
  267. package/dist/lib/stack-app/apps/implementations/redirect-page-urls.d.ts.map +1 -0
  268. package/dist/lib/stack-app/apps/implementations/redirect-page-urls.js +201 -0
  269. package/dist/lib/stack-app/apps/implementations/redirect-page-urls.js.map +1 -0
  270. package/dist/lib/stack-app/apps/implementations/server-app-impl.d.ts +203 -0
  271. package/dist/lib/stack-app/apps/implementations/server-app-impl.d.ts.map +1 -0
  272. package/dist/lib/stack-app/apps/implementations/server-app-impl.js +1096 -0
  273. package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -0
  274. package/dist/lib/stack-app/apps/implementations/session-refresh-subscription.d.ts +28 -0
  275. package/dist/lib/stack-app/apps/implementations/session-refresh-subscription.d.ts.map +1 -0
  276. package/dist/lib/stack-app/apps/implementations/session-refresh-subscription.js +33 -0
  277. package/dist/lib/stack-app/apps/implementations/session-refresh-subscription.js.map +1 -0
  278. package/dist/lib/stack-app/apps/implementations/session-refresh-subscription.test.d.ts +1 -0
  279. package/dist/lib/stack-app/apps/implementations/session-refresh-subscription.test.js +82 -0
  280. package/dist/lib/stack-app/apps/implementations/session-refresh-subscription.test.js.map +1 -0
  281. package/dist/lib/stack-app/apps/implementations/session-replay.d.ts +111 -0
  282. package/dist/lib/stack-app/apps/implementations/session-replay.d.ts.map +1 -0
  283. package/dist/lib/stack-app/apps/implementations/session-replay.js +250 -0
  284. package/dist/lib/stack-app/apps/implementations/session-replay.js.map +1 -0
  285. package/dist/lib/stack-app/apps/implementations/session-replay.test.d.ts +1 -0
  286. package/dist/lib/stack-app/apps/implementations/session-replay.test.js +29 -0
  287. package/dist/lib/stack-app/apps/implementations/session-replay.test.js.map +1 -0
  288. package/dist/lib/stack-app/apps/index.d.ts +4 -0
  289. package/dist/lib/stack-app/apps/index.js +42 -0
  290. package/dist/lib/stack-app/apps/interfaces/admin-app.d.ts +244 -0
  291. package/dist/lib/stack-app/apps/interfaces/admin-app.d.ts.map +1 -0
  292. package/dist/lib/stack-app/apps/interfaces/admin-app.js +12 -0
  293. package/dist/lib/stack-app/apps/interfaces/admin-app.js.map +1 -0
  294. package/dist/lib/stack-app/apps/interfaces/client-app.d.ts +208 -0
  295. package/dist/lib/stack-app/apps/interfaces/client-app.d.ts.map +1 -0
  296. package/dist/lib/stack-app/apps/interfaces/client-app.js +12 -0
  297. package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -0
  298. package/dist/lib/stack-app/apps/interfaces/server-app.d.ts +116 -0
  299. package/dist/lib/stack-app/apps/interfaces/server-app.d.ts.map +1 -0
  300. package/dist/lib/stack-app/apps/interfaces/server-app.js +12 -0
  301. package/dist/lib/stack-app/apps/interfaces/server-app.js.map +1 -0
  302. package/dist/lib/stack-app/common.d.ts +174 -0
  303. package/dist/lib/stack-app/common.d.ts.map +1 -0
  304. package/dist/lib/stack-app/common.js +9 -0
  305. package/dist/lib/stack-app/common.js.map +1 -0
  306. package/dist/lib/stack-app/connected-accounts/index.d.ts +40 -0
  307. package/dist/lib/stack-app/connected-accounts/index.d.ts.map +1 -0
  308. package/dist/lib/stack-app/connected-accounts/index.js +0 -0
  309. package/dist/lib/stack-app/contact-channels/index.d.ts +43 -0
  310. package/dist/lib/stack-app/contact-channels/index.d.ts.map +1 -0
  311. package/dist/lib/stack-app/contact-channels/index.js +44 -0
  312. package/dist/lib/stack-app/contact-channels/index.js.map +1 -0
  313. package/dist/lib/stack-app/customers/index.d.ts +129 -0
  314. package/dist/lib/stack-app/customers/index.d.ts.map +1 -0
  315. package/dist/lib/stack-app/customers/index.js +0 -0
  316. package/dist/lib/stack-app/data-vault/index.d.ts +14 -0
  317. package/dist/lib/stack-app/data-vault/index.d.ts.map +1 -0
  318. package/dist/lib/stack-app/data-vault/index.js +0 -0
  319. package/dist/lib/stack-app/email/index.d.ts +204 -0
  320. package/dist/lib/stack-app/email/index.d.ts.map +1 -0
  321. package/dist/lib/stack-app/email/index.js +0 -0
  322. package/dist/lib/stack-app/email-templates/index.d.ts +17 -0
  323. package/dist/lib/stack-app/email-templates/index.d.ts.map +1 -0
  324. package/dist/lib/stack-app/email-templates/index.js +13 -0
  325. package/dist/lib/stack-app/email-templates/index.js.map +1 -0
  326. package/dist/lib/stack-app/index.d.ts +15 -0
  327. package/dist/lib/stack-app/index.js +54 -0
  328. package/dist/lib/stack-app/internal-api-keys/index.d.ts +42 -0
  329. package/dist/lib/stack-app/internal-api-keys/index.d.ts.map +1 -0
  330. package/dist/lib/stack-app/internal-api-keys/index.js +16 -0
  331. package/dist/lib/stack-app/internal-api-keys/index.js.map +1 -0
  332. package/dist/lib/stack-app/notification-categories/index.d.ts +11 -0
  333. package/dist/lib/stack-app/notification-categories/index.d.ts.map +1 -0
  334. package/dist/lib/stack-app/notification-categories/index.js +0 -0
  335. package/dist/lib/stack-app/permissions/index.d.ts +43 -0
  336. package/dist/lib/stack-app/permissions/index.d.ts.map +1 -0
  337. package/dist/lib/stack-app/permissions/index.js +36 -0
  338. package/dist/lib/stack-app/permissions/index.js.map +1 -0
  339. package/dist/lib/stack-app/project-configs/index.d.ts +99 -0
  340. package/dist/lib/stack-app/project-configs/index.d.ts.map +1 -0
  341. package/dist/lib/stack-app/project-configs/index.js +0 -0
  342. package/dist/lib/stack-app/projects/index.d.ts +145 -0
  343. package/dist/lib/stack-app/projects/index.d.ts.map +1 -0
  344. package/dist/lib/stack-app/projects/index.js +69 -0
  345. package/dist/lib/stack-app/projects/index.js.map +1 -0
  346. package/dist/lib/stack-app/session-replays/index.d.ts +66 -0
  347. package/dist/lib/stack-app/session-replays/index.d.ts.map +1 -0
  348. package/dist/lib/stack-app/session-replays/index.js +0 -0
  349. package/dist/lib/stack-app/teams/index.d.ts +160 -0
  350. package/dist/lib/stack-app/teams/index.d.ts.map +1 -0
  351. package/dist/lib/stack-app/teams/index.js +40 -0
  352. package/dist/lib/stack-app/teams/index.js.map +1 -0
  353. package/dist/lib/stack-app/url-targets.d.ts +39 -0
  354. package/dist/lib/stack-app/url-targets.d.ts.map +1 -0
  355. package/dist/lib/stack-app/url-targets.js +274 -0
  356. package/dist/lib/stack-app/url-targets.js.map +1 -0
  357. package/dist/lib/stack-app/url-targets.test.d.ts +1 -0
  358. package/dist/lib/stack-app/url-targets.test.js +213 -0
  359. package/dist/lib/stack-app/url-targets.test.js.map +1 -0
  360. package/dist/lib/stack-app/users/index.d.ts +357 -0
  361. package/dist/lib/stack-app/users/index.d.ts.map +1 -0
  362. package/dist/lib/stack-app/users/index.js +76 -0
  363. package/dist/lib/stack-app/users/index.js.map +1 -0
  364. package/dist/storage-CKsyME2q.d.ts +1912 -0
  365. package/dist/storage-CKsyME2q.d.ts.map +1 -0
  366. package/dist/utils/url.d.ts +5 -0
  367. package/dist/utils/url.d.ts.map +1 -0
  368. package/dist/utils/url.js +18 -0
  369. package/dist/utils/url.js.map +1 -0
  370. package/package.json +88 -0
@@ -0,0 +1,2114 @@
1
+ import { runAsynchronously } from "@hexclave/shared/dist/utils/promises";
2
+ import { isLocalhost } from "@hexclave/shared/dist/utils/urls";
3
+ import { envVars } from "../lib/env.js";
4
+ import { getBaseUrl } from "../lib/stack-app/apps/implementations/common.js";
5
+ import { stackAppInternalsSymbol } from "../lib/stack-app/common.js";
6
+ import { getPagePrompt } from "../lib/stack-app/url-targets.js";
7
+ import { devToolCSS } from "./dev-tool-styles.js";
8
+ import { clampTriggerPosition, getSnappedTriggerPlacement, resolveTriggerPosition } from "./dev-tool-trigger-position.js";
9
+
10
+ //#region src/dev-tool/dev-tool-core.ts
11
+ const STORAGE_KEY = "__hexclave-dev-tool-state";
12
+ const TRIGGER_POS_KEY = "hexclave-devtool-trigger-position";
13
+ const ROOT_ID = "__hexclave-dev-tool-root";
14
+ const GLOBAL_INSTANCE_KEY = "__hexclave-dev-tool-instance";
15
+ const MAX_LOG_ENTRIES = 500;
16
+ const CONSOLE_LOG_BATCH_SIZE = 100;
17
+ const DRAG_THRESHOLD = 5;
18
+ const DOCS_URL = "https://docs.hexclave.com";
19
+ const TABS = [
20
+ {
21
+ id: "overview",
22
+ label: "Overview",
23
+ icon: "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\"/></svg>"
24
+ },
25
+ {
26
+ id: "customize",
27
+ label: "Customize",
28
+ icon: "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 20h9\"/><path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z\"/></svg>"
29
+ },
30
+ {
31
+ id: "ai",
32
+ label: "AI",
33
+ icon: "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>"
34
+ },
35
+ {
36
+ id: "console",
37
+ label: "Console",
38
+ icon: "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"4 17 10 11 4 5\"/><line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\"/></svg>"
39
+ },
40
+ {
41
+ id: "dashboard",
42
+ label: "Dashboard",
43
+ icon: "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M3 9h18\"/><path d=\"M9 21V9\"/></svg>"
44
+ },
45
+ {
46
+ id: "support",
47
+ label: "Support",
48
+ icon: "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>"
49
+ }
50
+ ];
51
+ const DEFAULT_STATE = {
52
+ isOpen: false,
53
+ activeTab: "overview",
54
+ panelWidth: 800,
55
+ panelHeight: 520
56
+ };
57
+ const STACK_LOGO_SVG = "<svg width=\"14\" height=\"17\" viewBox=\"0 0 131 156\" fill=\"currentColor\"><path d=\"M124.447 28.6459L70.1382 1.75616C67.3472 0.374284 64.0715 0.372197 61.279 1.75051L0.740967 31.6281V87.6369L65.7101 119.91L117.56 93.675V112.414L65.7101 138.44L0.740967 106.584V119.655C0.740967 122.359 2.28151 124.827 4.71097 126.015L62.282 154.161C65.0966 155.538 68.3938 155.515 71.1888 154.099L130.47 124.074V79.7105C130.47 74.8003 125.34 71.5769 120.915 73.7077L79.4531 93.675V75.9771L130.47 50.1589V38.3485C130.47 34.2325 128.137 30.4724 124.447 28.6459Z\"/></svg>";
58
+ function loadState() {
59
+ try {
60
+ const stored = localStorage.getItem(STORAGE_KEY);
61
+ if (stored) {
62
+ const parsed = JSON.parse(stored);
63
+ if (parsed.activeTab === "components") parsed.activeTab = "customize";
64
+ if (parsed.activeTab === "docs") parsed.activeTab = "overview";
65
+ return {
66
+ ...DEFAULT_STATE,
67
+ ...parsed,
68
+ isOpen: false
69
+ };
70
+ }
71
+ } catch {}
72
+ return { ...DEFAULT_STATE };
73
+ }
74
+ function saveState(state) {
75
+ try {
76
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({
77
+ ...state,
78
+ isOpen: false
79
+ }));
80
+ } catch {}
81
+ }
82
+ function createStateStore() {
83
+ let state = loadState();
84
+ const listeners = /* @__PURE__ */ new Set();
85
+ return {
86
+ get: () => state,
87
+ update(partial) {
88
+ state = {
89
+ ...state,
90
+ ...partial
91
+ };
92
+ saveState(state);
93
+ listeners.forEach((fn) => fn());
94
+ },
95
+ subscribe(fn) {
96
+ listeners.add(fn);
97
+ return () => {
98
+ listeners.delete(fn);
99
+ };
100
+ }
101
+ };
102
+ }
103
+ function isDevToolGlobalInstance(value) {
104
+ return typeof value === "object" && value !== null && typeof Reflect.get(value, "cleanup") === "function";
105
+ }
106
+ function getGlobalDevToolInstance() {
107
+ if (typeof window === "undefined") return null;
108
+ const value = Reflect.get(window, GLOBAL_INSTANCE_KEY);
109
+ return isDevToolGlobalInstance(value) ? value : null;
110
+ }
111
+ function setGlobalDevToolInstance(instance) {
112
+ if (typeof window === "undefined") return;
113
+ if (instance === null) Reflect.deleteProperty(window, GLOBAL_INSTANCE_KEY);
114
+ else Reflect.set(window, GLOBAL_INSTANCE_KEY, instance);
115
+ }
116
+ function getGlobalLogStore() {
117
+ const g = globalThis;
118
+ if (!g.__STACK_DEV_TOOL_LOG_STORE__) g.__STACK_DEV_TOOL_LOG_STORE__ = {
119
+ apiLogs: [],
120
+ eventLogs: [],
121
+ listeners: /* @__PURE__ */ new Set(),
122
+ addApiLog(entry) {
123
+ this.apiLogs = [entry, ...this.apiLogs].slice(0, MAX_LOG_ENTRIES);
124
+ this.listeners.forEach((fn) => fn());
125
+ },
126
+ addEventLog(entry) {
127
+ this.eventLogs = [entry, ...this.eventLogs].slice(0, MAX_LOG_ENTRIES);
128
+ this.listeners.forEach((fn) => fn());
129
+ },
130
+ clear() {
131
+ this.apiLogs = [];
132
+ this.eventLogs = [];
133
+ this.listeners.forEach((fn) => fn());
134
+ },
135
+ subscribe(fn) {
136
+ this.listeners.add(fn);
137
+ return () => {
138
+ this.listeners.delete(fn);
139
+ };
140
+ }
141
+ };
142
+ return g.__STACK_DEV_TOOL_LOG_STORE__;
143
+ }
144
+ let _idCounter = 0;
145
+ function nextId() {
146
+ return `sdt-${++_idCounter}-${Date.now()}`;
147
+ }
148
+ function resolveApiBaseUrl(app) {
149
+ return getBaseUrl(app[stackAppInternalsSymbol].getConstructorOptions().baseUrl);
150
+ }
151
+ function shouldShowDashboardTab(app) {
152
+ return envVars.NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR === "true" && isLocalhost(resolveApiBaseUrl(app));
153
+ }
154
+ function getTabsForApp(app) {
155
+ if (shouldShowDashboardTab(app)) return TABS;
156
+ return TABS.filter((tab) => tab.id !== "dashboard");
157
+ }
158
+ function deriveDashboardBaseUrl(apiBaseUrl) {
159
+ try {
160
+ const url = new URL(apiBaseUrl);
161
+ if (url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]") {
162
+ const port = url.port;
163
+ if (port && port.endsWith("02")) url.port = port.slice(0, -2) + "01";
164
+ return url.origin;
165
+ }
166
+ if (url.hostname.startsWith("api.")) {
167
+ url.hostname = "app." + url.hostname.slice(4);
168
+ return url.origin;
169
+ }
170
+ return url.origin;
171
+ } catch {
172
+ return "https://app.hexclave.com";
173
+ }
174
+ }
175
+ function resolveDashboardUrl(app) {
176
+ return `${deriveDashboardBaseUrl(resolveApiBaseUrl(app))}/projects/${encodeURIComponent(app.projectId)}`;
177
+ }
178
+ function formatTimestamp(ts) {
179
+ return new Date(ts).toLocaleTimeString([], {
180
+ hour: "2-digit",
181
+ minute: "2-digit",
182
+ second: "2-digit",
183
+ fractionalSecondDigits: 3
184
+ });
185
+ }
186
+ function escapeHtml(str) {
187
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
188
+ }
189
+ function generateRandomEmail() {
190
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
191
+ let id = "";
192
+ for (let i = 0; i < 8; i++) id += chars[Math.floor(Math.random() * 36)];
193
+ return `dev-${id}@test.hexclave.com`;
194
+ }
195
+ function h(tag, attrs, ...children) {
196
+ const el = document.createElement(tag);
197
+ if (attrs) for (const [k, v] of Object.entries(attrs)) {
198
+ if (v == null) continue;
199
+ if (k === "className") el.className = v;
200
+ else if (k === "style" && typeof v === "object") Object.assign(el.style, v);
201
+ else if (k.startsWith("on") && typeof v === "function") el.addEventListener(k.slice(2).toLowerCase(), v);
202
+ else el.setAttribute(k, String(v));
203
+ }
204
+ for (const child of children) {
205
+ if (child == null) continue;
206
+ el.appendChild(typeof child === "string" ? document.createTextNode(child) : child);
207
+ }
208
+ return el;
209
+ }
210
+ function setHtml(el, html) {
211
+ el.innerHTML = html;
212
+ }
213
+ function hasAppendChild(value) {
214
+ return typeof value === "object" && value !== null && typeof Reflect.get(value, "appendChild") === "function";
215
+ }
216
+ function appendInlineMarkdown(container, text) {
217
+ const tokenPattern = /(\[[^\]]+\]\([^)]+\)|`[^`\n]+`|\*\*[^*\n]+\*\*|__[^_\n]+__|\*[^*\n]+\*|_[^_\n]+_)/g;
218
+ let lastIndex = 0;
219
+ let match;
220
+ while ((match = tokenPattern.exec(text)) !== null) {
221
+ if (match.index > lastIndex) container.appendChild(document.createTextNode(text.slice(lastIndex, match.index)));
222
+ const token = match[0];
223
+ if (token.startsWith("`")) container.appendChild(h("code", { className: "sdt-ai-inline-code" }, token.slice(1, -1)));
224
+ else if (token.startsWith("**") || token.startsWith("__")) {
225
+ const bold = h("strong", { className: "sdt-ai-bold" });
226
+ appendInlineMarkdown(bold, token.slice(2, -2));
227
+ container.appendChild(bold);
228
+ } else if (token.startsWith("*") || token.startsWith("_")) {
229
+ const italic = h("em");
230
+ appendInlineMarkdown(italic, token.slice(1, -1));
231
+ container.appendChild(italic);
232
+ } else {
233
+ const linkMatch = token.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
234
+ if (linkMatch) {
235
+ const [, linkText, href] = linkMatch;
236
+ const trimmedHref = href.trim();
237
+ if (/^(https?:\/\/|mailto:)/i.test(trimmedHref)) {
238
+ const link = h("a", {
239
+ className: "sdt-ai-link",
240
+ href: trimmedHref,
241
+ target: "_blank",
242
+ rel: "noopener noreferrer"
243
+ });
244
+ appendInlineMarkdown(link, linkText);
245
+ container.appendChild(link);
246
+ } else container.appendChild(document.createTextNode(token));
247
+ } else container.appendChild(document.createTextNode(token));
248
+ }
249
+ lastIndex = tokenPattern.lastIndex;
250
+ }
251
+ if (lastIndex < text.length) container.appendChild(document.createTextNode(text.slice(lastIndex)));
252
+ }
253
+ function createTrigger(onClick) {
254
+ let triggerSize = {
255
+ width: 36,
256
+ height: 36
257
+ };
258
+ function isPosition(value) {
259
+ if (typeof value !== "object" || value === null) return false;
260
+ return typeof Reflect.get(value, "left") === "number" && typeof Reflect.get(value, "top") === "number";
261
+ }
262
+ function isPlacement(value) {
263
+ if (typeof value !== "object" || value === null) return false;
264
+ const corner = Reflect.get(value, "corner");
265
+ return [
266
+ "top-left",
267
+ "top-right",
268
+ "bottom-left",
269
+ "bottom-right"
270
+ ].includes(String(corner));
271
+ }
272
+ function loadPlacement() {
273
+ try {
274
+ const raw = localStorage.getItem(TRIGGER_POS_KEY);
275
+ if (!raw) return null;
276
+ const parsed = JSON.parse(raw);
277
+ if (isPlacement(parsed)) return parsed;
278
+ if (typeof parsed === "object" && parsed !== null && "side" in parsed && "offset" in parsed) {
279
+ const side = String(Reflect.get(parsed, "side"));
280
+ const offset = Number(Reflect.get(parsed, "offset"));
281
+ const vw = window.innerWidth;
282
+ const vh = window.innerHeight;
283
+ let corner;
284
+ if (side === "right") corner = offset < vh / 2 ? "top-right" : "bottom-right";
285
+ else if (side === "left") corner = offset < vh / 2 ? "top-left" : "bottom-left";
286
+ else if (side === "top") corner = offset < vw / 2 ? "top-left" : "top-right";
287
+ else corner = offset < vw / 2 ? "bottom-left" : "bottom-right";
288
+ return { corner };
289
+ }
290
+ if (isPosition(parsed)) return getSnappedTriggerPlacement(parsed, triggerSize, {
291
+ width: window.innerWidth,
292
+ height: window.innerHeight
293
+ });
294
+ } catch {}
295
+ return null;
296
+ }
297
+ function savePlacement(placement) {
298
+ try {
299
+ localStorage.setItem(TRIGGER_POS_KEY, JSON.stringify(placement));
300
+ } catch {}
301
+ }
302
+ let animationTimeout = null;
303
+ function setPositionAnimation(isAnimated) {
304
+ if (animationTimeout !== null) {
305
+ window.clearTimeout(animationTimeout);
306
+ animationTimeout = null;
307
+ }
308
+ btn.classList.toggle("sdt-trigger-position-animated", isAnimated);
309
+ if (isAnimated) animationTimeout = window.setTimeout(() => {
310
+ animationTimeout = null;
311
+ btn.classList.remove("sdt-trigger-position-animated");
312
+ }, 180);
313
+ }
314
+ function applyPos(nextPos, options) {
315
+ setPositionAnimation(options?.animate === true);
316
+ pos = nextPos;
317
+ btn.style.left = pos.left + "px";
318
+ btn.style.top = pos.top + "px";
319
+ }
320
+ const btn = h("button", {
321
+ className: "sdt-trigger",
322
+ "aria-label": "Toggle Hexclave Dev Tools",
323
+ "data-hexclave-devtool-trigger": "true",
324
+ title: "Hexclave Dev Tools"
325
+ });
326
+ const logoSpan = h("span", { className: "sdt-trigger-logo" });
327
+ setHtml(logoSpan, STACK_LOGO_SVG);
328
+ btn.appendChild(logoSpan);
329
+ let placement = loadPlacement() ?? { corner: "bottom-right" };
330
+ let pos = resolveTriggerPosition(placement, triggerSize, {
331
+ width: window.innerWidth,
332
+ height: window.innerHeight
333
+ });
334
+ applyPos(pos);
335
+ let dragState = null;
336
+ requestAnimationFrame(() => {
337
+ const rect = btn.getBoundingClientRect();
338
+ if (rect.width > 0 && rect.height > 0) {
339
+ triggerSize = {
340
+ width: rect.width,
341
+ height: rect.height
342
+ };
343
+ const measured = resolveTriggerPosition(placement, triggerSize, {
344
+ width: window.innerWidth,
345
+ height: window.innerHeight
346
+ });
347
+ if (measured.left !== pos.left || measured.top !== pos.top) applyPos(measured, { animate: true });
348
+ }
349
+ });
350
+ btn.addEventListener("pointerdown", (e) => {
351
+ e.preventDefault();
352
+ setPositionAnimation(false);
353
+ btn.setPointerCapture(e.pointerId);
354
+ dragState = {
355
+ startX: e.clientX,
356
+ startY: e.clientY,
357
+ startLeft: pos.left,
358
+ startTop: pos.top,
359
+ didDrag: false
360
+ };
361
+ });
362
+ btn.addEventListener("pointermove", (e) => {
363
+ if (!dragState) return;
364
+ const dx = e.clientX - dragState.startX;
365
+ const dy = e.clientY - dragState.startY;
366
+ if (!dragState.didDrag && Math.abs(dx) + Math.abs(dy) < DRAG_THRESHOLD) return;
367
+ dragState.didDrag = true;
368
+ applyPos(clampTriggerPosition({
369
+ left: dragState.startLeft + dx,
370
+ top: dragState.startTop + dy
371
+ }, triggerSize, {
372
+ width: window.innerWidth,
373
+ height: window.innerHeight
374
+ }));
375
+ });
376
+ btn.addEventListener("pointerup", (e) => {
377
+ const ds = dragState;
378
+ dragState = null;
379
+ if (!ds) return;
380
+ btn.releasePointerCapture(e.pointerId);
381
+ if (ds.didDrag) {
382
+ placement = getSnappedTriggerPlacement(pos, triggerSize, {
383
+ width: window.innerWidth,
384
+ height: window.innerHeight
385
+ });
386
+ applyPos(resolveTriggerPosition(placement, triggerSize, {
387
+ width: window.innerWidth,
388
+ height: window.innerHeight
389
+ }), { animate: true });
390
+ savePlacement(placement);
391
+ } else onClick();
392
+ });
393
+ function onResize() {
394
+ const resizedPos = resolveTriggerPosition(placement, triggerSize, {
395
+ width: window.innerWidth,
396
+ height: window.innerHeight
397
+ });
398
+ if (resizedPos.left !== pos.left || resizedPos.top !== pos.top) applyPos(resizedPos, { animate: true });
399
+ }
400
+ window.addEventListener("resize", onResize);
401
+ return {
402
+ element: btn,
403
+ cleanup: () => {
404
+ if (animationTimeout !== null) window.clearTimeout(animationTimeout);
405
+ window.removeEventListener("resize", onResize);
406
+ }
407
+ };
408
+ }
409
+ function createTabBar(tabs, activeTab, onTabChange, opts) {
410
+ const variant = opts?.variant ?? "bar";
411
+ const barClass = variant === "pills" ? "sdt-console-tabs" : "sdt-tabbar";
412
+ const tabClass = variant === "pills" ? "sdt-console-tab" : "sdt-tab";
413
+ const indicatorClass = variant === "pills" ? "sdt-console-tab-indicator" : "sdt-tab-indicator";
414
+ const bar = h("div", { className: barClass });
415
+ const indicator = h("div", { className: indicatorClass });
416
+ indicator.style.opacity = "0";
417
+ bar.appendChild(indicator);
418
+ let current = activeTab;
419
+ let isInitial = true;
420
+ const buttons = tabs.map((tab) => {
421
+ const btn = h("button", {
422
+ className: tabClass,
423
+ "data-tab-id": tab.id,
424
+ "data-active": String(tab.id === activeTab)
425
+ });
426
+ if (tab.icon) {
427
+ const iconSpan = h("span", { className: "sdt-tab-icon" });
428
+ setHtml(iconSpan, tab.icon);
429
+ btn.appendChild(iconSpan);
430
+ }
431
+ btn.appendChild(document.createTextNode(tab.label));
432
+ btn.addEventListener("click", () => onTabChange(tab.id));
433
+ bar.appendChild(btn);
434
+ return btn;
435
+ });
436
+ if (variant === "bar") bar.appendChild(h("div", { className: "sdt-tabbar-spacer" }));
437
+ if (opts?.trailing) bar.appendChild(opts.trailing);
438
+ function measure() {
439
+ const btn = bar.querySelector(`[data-tab-id="${current}"]`);
440
+ if (!btn) return;
441
+ indicator.style.transform = `translateX(${btn.offsetLeft}px)`;
442
+ indicator.style.width = btn.offsetWidth + "px";
443
+ indicator.style.height = btn.offsetHeight + "px";
444
+ indicator.style.opacity = "1";
445
+ indicator.style.transition = isInitial ? "none" : "";
446
+ isInitial = false;
447
+ }
448
+ new ResizeObserver(measure).observe(bar);
449
+ requestAnimationFrame(measure);
450
+ function setActive(id) {
451
+ current = id;
452
+ buttons.forEach((btn) => {
453
+ const tabId = btn.getAttribute("data-tab-id");
454
+ btn.setAttribute("data-active", String(tabId === id));
455
+ });
456
+ measure();
457
+ }
458
+ return {
459
+ el: bar,
460
+ setActive
461
+ };
462
+ }
463
+ function createIframeTab(src, title, loadingMsg = "Loading…", errorMsg = "Unable to load content", errorDetail, openExternallyLabel) {
464
+ const container = h("div", { className: "sdt-iframe-container" });
465
+ if (openExternallyLabel != null) container.appendChild(h("div", { className: "sdt-iframe-toolbar" }, h("a", {
466
+ href: src,
467
+ target: "_blank",
468
+ rel: "noopener noreferrer",
469
+ className: "sdt-iframe-open-link"
470
+ }, openExternallyLabel)));
471
+ const loadingEl = h("div", { className: "sdt-iframe-loading" }, loadingMsg);
472
+ container.appendChild(loadingEl);
473
+ const iframe = document.createElement("iframe");
474
+ iframe.src = src;
475
+ iframe.title = title;
476
+ iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-popups allow-forms");
477
+ iframe.style.display = "none";
478
+ iframe.addEventListener("load", () => {
479
+ loadingEl.style.display = "none";
480
+ iframe.style.display = "block";
481
+ });
482
+ iframe.addEventListener("error", () => {
483
+ loadingEl.style.display = "none";
484
+ container.innerHTML = "";
485
+ const errDiv = h("div", { className: "sdt-iframe-error" });
486
+ errDiv.appendChild(h("div", null, errorMsg));
487
+ if (errorDetail) errDiv.appendChild(h("div", { style: {
488
+ fontSize: "12px",
489
+ color: "var(--sdt-text-tertiary)"
490
+ } }, errorDetail));
491
+ const retryBtn = h("button", { className: "sdt-iframe-error-btn" }, "Retry");
492
+ retryBtn.addEventListener("click", () => {
493
+ container.replaceWith(createIframeTab(src, title, loadingMsg, errorMsg, errorDetail, openExternallyLabel));
494
+ });
495
+ errDiv.appendChild(retryBtn);
496
+ const link = h("a", {
497
+ href: src,
498
+ target: "_blank",
499
+ rel: "noopener noreferrer",
500
+ style: {
501
+ color: "var(--sdt-accent)",
502
+ fontSize: "12px",
503
+ textDecoration: "none"
504
+ }
505
+ }, "Open in new tab");
506
+ errDiv.appendChild(link);
507
+ container.appendChild(errDiv);
508
+ });
509
+ container.appendChild(iframe);
510
+ return container;
511
+ }
512
+ function createOverviewTab(app) {
513
+ const container = h("div", { className: "sdt-ov" });
514
+ const heroCard = h("div", { className: "sdt-ov-card sdt-ov-card-hero" });
515
+ heroCard.appendChild(h("div", { className: "sdt-ov-label" }, "Identity"));
516
+ const userRow = h("div", { className: "sdt-ov-user-row" });
517
+ const avatar = h("div", { className: "sdt-ov-avatar" }, "?");
518
+ const userMeta = h("div", { className: "sdt-ov-user-meta" });
519
+ const userName = h("div", { className: "sdt-ov-user-name" }, "Loading…");
520
+ const userEmail = h("div", { className: "sdt-ov-user-email" }, "");
521
+ const authIndicator = h("div", {
522
+ className: "sdt-ov-auth-indicator",
523
+ style: { display: "none" }
524
+ }, "Authenticated");
525
+ userMeta.append(userName, userEmail, authIndicator);
526
+ userRow.append(avatar, userMeta);
527
+ heroCard.appendChild(userRow);
528
+ const actions = h("div", { className: "sdt-ov-actions" });
529
+ const toast = h("div", {
530
+ className: "sdt-ov-toast",
531
+ style: { display: "none" }
532
+ });
533
+ const emailRow = h("div", { className: "sdt-ov-email-input" });
534
+ const emailInput = h("input", {
535
+ type: "email",
536
+ placeholder: "Sign in as email…"
537
+ });
538
+ const emailBtn = h("button", null);
539
+ setHtml(emailBtn, "<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/><polyline points=\"12 5 19 12 12 19\"/></svg>");
540
+ emailRow.append(emailInput, emailBtn);
541
+ function isBestEffortOverviewError(error) {
542
+ if (error instanceof DOMException && error.name === "AbortError") return true;
543
+ if (error instanceof TypeError) return true;
544
+ if (error instanceof Error) return error.message.includes("Failed to fetch") || error.message.includes("NetworkError") || error.message.includes("Load failed") || error.message.includes("network connection");
545
+ return false;
546
+ }
547
+ function showToast(msg, type) {
548
+ toast.textContent = msg;
549
+ toast.className = `sdt-ov-toast sdt-ov-toast-${type}`;
550
+ toast.style.display = "";
551
+ setTimeout(() => {
552
+ toast.style.display = "none";
553
+ }, 4e3);
554
+ }
555
+ let currentUser = null;
556
+ let loading = false;
557
+ function rebuildActions() {
558
+ actions.innerHTML = "";
559
+ if (currentUser) {
560
+ const signOutBtn = h("button", { className: "sdt-ov-btn sdt-ov-btn-danger" }, "Sign Out");
561
+ signOutBtn.disabled = loading;
562
+ signOutBtn.addEventListener("click", () => {
563
+ runAsynchronously(async () => {
564
+ loading = true;
565
+ rebuildActions();
566
+ try {
567
+ await currentUser.signOut();
568
+ showToast("Signed out", "success");
569
+ } catch (e) {
570
+ showToast(e.message || "Sign out failed", "error");
571
+ }
572
+ loading = false;
573
+ await refreshUser();
574
+ });
575
+ });
576
+ const randomBtn = h("button", { className: "sdt-ov-btn sdt-ov-btn-primary" }, "Random User");
577
+ randomBtn.disabled = loading;
578
+ randomBtn.addEventListener("click", () => {
579
+ runAsynchronously(doQuickSignIn());
580
+ });
581
+ actions.append(signOutBtn, randomBtn);
582
+ } else {
583
+ const quickBtn = h("button", { className: "sdt-ov-btn sdt-ov-btn-primary sdt-ov-btn-wide" }, loading ? "Working…" : "Quick Sign In");
584
+ quickBtn.disabled = loading;
585
+ quickBtn.addEventListener("click", () => {
586
+ runAsynchronously(doQuickSignIn());
587
+ });
588
+ actions.appendChild(quickBtn);
589
+ }
590
+ emailInput.placeholder = currentUser ? "Switch to email…" : "Sign in as email…";
591
+ actions.appendChild(emailRow);
592
+ }
593
+ async function doQuickSignIn() {
594
+ if (!isLocalhost(window.location.href)) {
595
+ showToast("Quick sign-in is only available on localhost", "error");
596
+ return;
597
+ }
598
+ loading = true;
599
+ rebuildActions();
600
+ const email = generateRandomEmail();
601
+ try {
602
+ const signUpResult = await app.signUpWithCredential({
603
+ email,
604
+ password: email,
605
+ noRedirect: true
606
+ });
607
+ if (signUpResult.status === "error") {
608
+ showToast(`Sign up failed: ${signUpResult.error.message}`, "error");
609
+ loading = false;
610
+ rebuildActions();
611
+ return;
612
+ }
613
+ const signInResult = await app.signInWithCredential({
614
+ email,
615
+ password: email,
616
+ noRedirect: true
617
+ });
618
+ if (signInResult.status === "error") showToast(`Sign in failed: ${signInResult.error.message}`, "error");
619
+ else showToast(`Signed in as ${email}`, "success");
620
+ } catch (e) {
621
+ showToast(e.message || "Unknown error", "error");
622
+ }
623
+ loading = false;
624
+ await refreshUser();
625
+ }
626
+ async function doSignInAs(targetEmail) {
627
+ if (!targetEmail.trim()) return;
628
+ if (!isLocalhost(window.location.href)) {
629
+ showToast("Quick sign-in is only available on localhost", "error");
630
+ return;
631
+ }
632
+ loading = true;
633
+ rebuildActions();
634
+ const trimmed = targetEmail.trim();
635
+ try {
636
+ if ((await app.signInWithCredential({
637
+ email: trimmed,
638
+ password: trimmed,
639
+ noRedirect: true
640
+ })).status === "ok") {
641
+ showToast(`Signed in as ${trimmed}`, "success");
642
+ emailInput.value = "";
643
+ loading = false;
644
+ await refreshUser();
645
+ return;
646
+ }
647
+ const signUpResult = await app.signUpWithCredential({
648
+ email: trimmed,
649
+ password: trimmed,
650
+ noRedirect: true
651
+ });
652
+ if (signUpResult.status === "error") {
653
+ showToast(`Failed: ${signUpResult.error.message}`, "error");
654
+ loading = false;
655
+ rebuildActions();
656
+ return;
657
+ }
658
+ const retryResult = await app.signInWithCredential({
659
+ email: trimmed,
660
+ password: trimmed,
661
+ noRedirect: true
662
+ });
663
+ if (retryResult.status === "error") showToast(`Sign in failed: ${retryResult.error.message}`, "error");
664
+ else {
665
+ showToast(`Signed in as ${trimmed}`, "success");
666
+ emailInput.value = "";
667
+ }
668
+ } catch (e) {
669
+ showToast(e.message || "Unknown error", "error");
670
+ }
671
+ loading = false;
672
+ await refreshUser();
673
+ }
674
+ emailBtn.addEventListener("click", () => {
675
+ runAsynchronously(doSignInAs(emailInput.value));
676
+ });
677
+ emailInput.addEventListener("keydown", (e) => {
678
+ if (e.key === "Enter") runAsynchronously(doSignInAs(emailInput.value));
679
+ });
680
+ heroCard.append(actions, toast);
681
+ const methodsCard = h("div", { className: "sdt-ov-card sdt-ov-card-auth" });
682
+ methodsCard.appendChild(h("div", { className: "sdt-ov-label" }, "Auth Methods"));
683
+ const authGrid = h("div", { className: "sdt-ov-auth-grid" });
684
+ for (let i = 0; i < 3; i++) authGrid.appendChild(h("div", { className: "sdt-ov-method sdt-ov-skeleton-pill" }));
685
+ methodsCard.appendChild(authGrid);
686
+ let hasActiveAuthMethod = null;
687
+ async function loadAuthMethods() {
688
+ try {
689
+ const project = await app.getProject();
690
+ authGrid.innerHTML = "";
691
+ const config = project.config;
692
+ hasActiveAuthMethod = config.credentialEnabled || config.magicLinkEnabled || config.passkeyEnabled || config.oauthProviders.length > 0;
693
+ const methods = [
694
+ {
695
+ label: "Password",
696
+ enabled: config.credentialEnabled
697
+ },
698
+ {
699
+ label: "Magic Link",
700
+ enabled: config.magicLinkEnabled
701
+ },
702
+ {
703
+ label: "Passkey",
704
+ enabled: config.passkeyEnabled
705
+ }
706
+ ];
707
+ for (const m of methods) {
708
+ const pill = h("div", { className: `sdt-ov-method ${m.enabled ? "sdt-ov-method-on" : "sdt-ov-method-off"}` });
709
+ pill.appendChild(h("span", { className: "sdt-ov-method-name" }, m.label));
710
+ authGrid.appendChild(pill);
711
+ }
712
+ for (const p of config.oauthProviders) {
713
+ const pill = h("div", { className: "sdt-ov-method sdt-ov-method-on sdt-ov-method-oauth" });
714
+ pill.appendChild(h("span", { className: "sdt-ov-method-name" }, p.id));
715
+ authGrid.appendChild(pill);
716
+ }
717
+ if (!config.signUpEnabled) {
718
+ const pill = h("div", { className: "sdt-ov-method sdt-ov-method-warn" });
719
+ pill.appendChild(h("span", { className: "sdt-ov-method-name" }, "Sign-up off"));
720
+ authGrid.appendChild(pill);
721
+ }
722
+ buildChecklist();
723
+ } catch (error) {
724
+ authGrid.innerHTML = "<div style=\"font-size:11px;color:var(--sdt-text-tertiary)\">Could not load auth methods</div>";
725
+ hasActiveAuthMethod = null;
726
+ buildChecklist();
727
+ if (!isBestEffortOverviewError(error)) throw error;
728
+ }
729
+ }
730
+ runAsynchronously(loadAuthMethods());
731
+ const checksCard = h("div", { className: "sdt-ov-card sdt-ov-card-checks" });
732
+ const projectId = app.projectId;
733
+ let checksCardMounted = false;
734
+ function buildChecklist() {
735
+ checksCard.innerHTML = "";
736
+ const checks = [
737
+ {
738
+ ok: !!projectId && projectId !== "default",
739
+ label: "Project configured",
740
+ hint: null
741
+ },
742
+ {
743
+ ok: hasActiveAuthMethod === true,
744
+ label: "Auth method active",
745
+ hint: hasActiveAuthMethod === null ? "Still checking project config" : null
746
+ },
747
+ {
748
+ ok: !!currentUser,
749
+ label: "Sign in a test user",
750
+ hint: "Use “Quick Sign In” above →"
751
+ }
752
+ ];
753
+ const passCount = checks.filter((c) => c.ok).length;
754
+ if (passCount === checks.length) {
755
+ if (checksCardMounted && checksCard.parentElement) {
756
+ container.removeChild(checksCard);
757
+ checksCardMounted = false;
758
+ }
759
+ return;
760
+ }
761
+ if (!checksCardMounted) {
762
+ container.appendChild(checksCard);
763
+ checksCardMounted = true;
764
+ }
765
+ const titleRow = h("div", { className: "sdt-ov-checks-header" });
766
+ const titleLabel = h("div", {
767
+ className: "sdt-ov-label",
768
+ style: {
769
+ marginBottom: "0",
770
+ color: "var(--sdt-warning)"
771
+ }
772
+ }, "Setup");
773
+ const badge = h("span", { className: "sdt-ov-checks-badge sdt-ov-checks-badge-warn" }, `${passCount}\u200a/\u200a${checks.length}`);
774
+ titleRow.append(titleLabel, badge);
775
+ checksCard.appendChild(titleRow);
776
+ const bar = h("div", { className: "sdt-ov-checks-bar" });
777
+ const fill = h("div", { className: "sdt-ov-checks-bar-fill" });
778
+ fill.style.width = `${passCount / checks.length * 100}%`;
779
+ bar.appendChild(fill);
780
+ checksCard.appendChild(bar);
781
+ for (const c of checks) {
782
+ const row = h("div", { className: "sdt-ov-setup-row" });
783
+ row.appendChild(h("span", { className: `sdt-ov-setup-dot ${c.ok ? "sdt-ov-setup-dot-ok" : "sdt-ov-setup-dot-warn"}` }));
784
+ row.appendChild(h("span", { className: "sdt-ov-setup-label" }, c.label));
785
+ if (!c.ok && c.hint) row.appendChild(h("span", { className: "sdt-ov-setup-hint" }, c.hint));
786
+ checksCard.appendChild(row);
787
+ }
788
+ }
789
+ async function refreshUser() {
790
+ try {
791
+ currentUser = await app.getUser();
792
+ if (currentUser) {
793
+ const initials = (currentUser.displayName || currentUser.primaryEmail || "?").split(" ").map((s) => s[0]).join("").slice(0, 2).toUpperCase();
794
+ avatar.className = "sdt-ov-avatar sdt-ov-avatar-active";
795
+ if (currentUser.profileImageUrl) avatar.innerHTML = `<img src="${escapeHtml(currentUser.profileImageUrl)}" alt="" />`;
796
+ else avatar.textContent = initials;
797
+ userName.textContent = currentUser.displayName || "Anonymous";
798
+ userEmail.textContent = currentUser.primaryEmail || "No email";
799
+ authIndicator.style.display = "";
800
+ } else {
801
+ avatar.className = "sdt-ov-avatar";
802
+ avatar.textContent = "?";
803
+ userName.textContent = "No user signed in";
804
+ userEmail.textContent = "Sign in to test auth flows";
805
+ authIndicator.style.display = "none";
806
+ }
807
+ } catch (error) {
808
+ avatar.className = "sdt-ov-avatar";
809
+ avatar.textContent = "?";
810
+ userName.textContent = "Could not load user";
811
+ userEmail.textContent = "Check your local Stack backend";
812
+ authIndicator.style.display = "none";
813
+ currentUser = null;
814
+ if (!isBestEffortOverviewError(error)) throw error;
815
+ }
816
+ rebuildActions();
817
+ buildChecklist();
818
+ }
819
+ container.append(heroCard, methodsCard);
820
+ buildChecklist();
821
+ runAsynchronously(refreshUser());
822
+ const userPoll = setInterval(() => {
823
+ runAsynchronously(refreshUser());
824
+ }, 3e3);
825
+ return {
826
+ element: container,
827
+ cleanup: () => clearInterval(userPoll)
828
+ };
829
+ }
830
+ function createConsoleTab(logStore) {
831
+ const container = h("div", { className: "sdt-console-panel" });
832
+ const EVENT_TYPE_STYLES = {
833
+ "error": "sdt-badge-error",
834
+ "info": "sdt-badge-info"
835
+ };
836
+ const title = h("div", { className: "sdt-console-title" }, "Logs");
837
+ const actions = h("div", { className: "sdt-console-actions" });
838
+ const copyBtn = h("button", {
839
+ className: "sdt-console-action-btn",
840
+ title: "Copy logs"
841
+ });
842
+ setHtml(copyBtn, "<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg>Copy");
843
+ const exportBtn = h("button", {
844
+ className: "sdt-console-action-btn",
845
+ title: "Export logs"
846
+ });
847
+ setHtml(exportBtn, "<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/></svg>Export");
848
+ const clearBtn = h("button", {
849
+ className: "sdt-console-action-btn",
850
+ title: "Clear logs"
851
+ });
852
+ setHtml(clearBtn, "<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/></svg>Clear");
853
+ actions.append(copyBtn, exportBtn, clearBtn);
854
+ container.appendChild(h("div", { className: "sdt-console-header" }, title, actions));
855
+ const contentArea = h("div", { className: "sdt-console-log-scroll sdt-tab-content-fade" });
856
+ container.appendChild(contentArea);
857
+ let visibleLogCount = CONSOLE_LOG_BATCH_SIZE;
858
+ function getMergedLogs() {
859
+ return [...logStore.apiLogs.map((entry) => ({
860
+ kind: "api",
861
+ entry
862
+ })), ...logStore.eventLogs.map((entry) => ({
863
+ kind: "event",
864
+ entry
865
+ }))].sort((a, b) => b.entry.timestamp - a.entry.timestamp);
866
+ }
867
+ function formatLogLine(item) {
868
+ if (item.kind === "api") {
869
+ const log = item.entry;
870
+ const status = log.status !== void 0 ? ` [${log.status}]` : "";
871
+ const duration = log.duration !== void 0 ? ` ${log.duration}ms` : "";
872
+ const error = log.error !== void 0 ? ` ${log.error}` : "";
873
+ return `${new Date(log.timestamp).toISOString()} ${log.method} ${log.url}${status}${duration}${error}`;
874
+ }
875
+ const log = item.entry;
876
+ return `${new Date(log.timestamp).toISOString()} ${log.type.toUpperCase()} ${log.message}`;
877
+ }
878
+ function formatLogsForExport() {
879
+ return [
880
+ "=== Hexclave Dev Tool Logs ===",
881
+ `Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
882
+ `Total logs: ${getMergedLogs().length}`,
883
+ "",
884
+ ...getMergedLogs().map(formatLogLine)
885
+ ].join("\n");
886
+ }
887
+ function renderLogItem(item) {
888
+ if (item.kind === "api") {
889
+ const log = item.entry;
890
+ const row = h("div", { className: "sdt-log-item" });
891
+ row.appendChild(h("span", { className: "sdt-log-time" }, formatTimestamp(log.timestamp)));
892
+ row.appendChild(h("span", { className: `sdt-log-method sdt-log-method-${log.method.toLowerCase()}` }, log.method));
893
+ row.appendChild(h("span", { className: "sdt-log-url" }, log.url));
894
+ if (log.status !== void 0) row.appendChild(h("span", { className: `sdt-log-status ${log.status < 400 ? "sdt-log-status-ok" : "sdt-log-status-err"}` }, String(log.status)));
895
+ if (log.duration !== void 0) row.appendChild(h("span", { className: "sdt-log-time" }, log.duration + "ms"));
896
+ return row;
897
+ }
898
+ const log = item.entry;
899
+ const row = h("div", { className: "sdt-log-item" });
900
+ row.appendChild(h("span", { className: "sdt-log-time" }, formatTimestamp(log.timestamp)));
901
+ row.appendChild(h("span", { className: `sdt-badge ${EVENT_TYPE_STYLES[log.type] || "sdt-badge-info"}` }, log.type));
902
+ row.appendChild(h("span", { className: "sdt-log-message" }, log.message));
903
+ return row;
904
+ }
905
+ function renderLogs() {
906
+ const previousScrollTop = contentArea.scrollTop;
907
+ contentArea.innerHTML = "";
908
+ const merged = getMergedLogs();
909
+ visibleLogCount = Math.min(Math.max(visibleLogCount, CONSOLE_LOG_BATCH_SIZE), Math.max(merged.length, CONSOLE_LOG_BATCH_SIZE));
910
+ if (merged.length === 0) {
911
+ contentArea.innerHTML = "<div class=\"sdt-empty-state\"><div class=\"sdt-empty-state-icon\">📋</div><div>No logs recorded yet</div><div style=\"font-size:12px;color:var(--sdt-text-tertiary)\">API calls and auth events will appear here</div></div>";
912
+ return;
913
+ }
914
+ const list = h("div", { className: "sdt-log-list" });
915
+ for (const item of merged.slice(0, visibleLogCount)) list.appendChild(renderLogItem(item));
916
+ if (visibleLogCount < merged.length) list.appendChild(h("div", { className: "sdt-log-load-hint" }, `${merged.length - visibleLogCount} older logs available`));
917
+ contentArea.appendChild(list);
918
+ contentArea.scrollTop = Math.min(previousScrollTop, contentArea.scrollHeight);
919
+ }
920
+ function maybeLoadOlderLogs() {
921
+ const mergedLength = getMergedLogs().length;
922
+ if (visibleLogCount >= mergedLength) return;
923
+ if (contentArea.scrollHeight - contentArea.scrollTop - contentArea.clientHeight <= 48) {
924
+ visibleLogCount = Math.min(visibleLogCount + CONSOLE_LOG_BATCH_SIZE, mergedLength);
925
+ renderLogs();
926
+ }
927
+ }
928
+ contentArea.addEventListener("scroll", maybeLoadOlderLogs);
929
+ renderLogs();
930
+ copyBtn.addEventListener("click", () => {
931
+ runAsynchronously(navigator.clipboard.writeText(formatLogsForExport()).then(() => {
932
+ copyBtn.textContent = "✓ Copied";
933
+ setTimeout(() => {
934
+ setHtml(copyBtn, "<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg>Copy");
935
+ }, 1500);
936
+ }));
937
+ });
938
+ exportBtn.addEventListener("click", () => {
939
+ const blob = new Blob([formatLogsForExport()], { type: "text/plain;charset=utf-8" });
940
+ const url = URL.createObjectURL(blob);
941
+ const link = h("a", {
942
+ href: url,
943
+ download: `stack-auth-dev-tool-logs-${(/* @__PURE__ */ new Date()).toISOString()}.txt`
944
+ });
945
+ document.body.appendChild(link);
946
+ link.click();
947
+ link.remove();
948
+ URL.revokeObjectURL(url);
949
+ });
950
+ clearBtn.addEventListener("click", () => {
951
+ visibleLogCount = CONSOLE_LOG_BATCH_SIZE;
952
+ logStore.clear();
953
+ });
954
+ const unsub = logStore.subscribe(() => {
955
+ renderLogs();
956
+ });
957
+ return {
958
+ element: container,
959
+ cleanup: () => {
960
+ contentArea.removeEventListener("scroll", maybeLoadOlderLogs);
961
+ unsub();
962
+ }
963
+ };
964
+ }
965
+ function createAITab(app) {
966
+ const container = h("div", { className: "sdt-ai-container" });
967
+ const apiBaseUrl = resolveApiBaseUrl(app);
968
+ const messages = [];
969
+ let aiLoading = false;
970
+ let activeAiAbortController = null;
971
+ const messagesArea = h("div", { className: "sdt-ai-messages" });
972
+ const inputArea = h("div", { className: "sdt-ai-input-area" });
973
+ const SUGGESTED_QUESTIONS = [
974
+ {
975
+ icon: "🔒",
976
+ text: "How do I protect a Next.js route?"
977
+ },
978
+ {
979
+ icon: "👥",
980
+ text: "How do teams and permissions work?"
981
+ },
982
+ {
983
+ icon: "🔗",
984
+ text: "How do I add OAuth providers?"
985
+ },
986
+ {
987
+ icon: "✉️",
988
+ text: "How do I customize auth emails?"
989
+ }
990
+ ];
991
+ function getHeaders() {
992
+ const opts = app[stackAppInternalsSymbol].getConstructorOptions();
993
+ const headers = {
994
+ "X-Hexclave-Access-Type": "client",
995
+ "X-Hexclave-Project-Id": app.projectId
996
+ };
997
+ if ("publishableClientKey" in opts && opts.publishableClientKey) headers["X-Hexclave-Publishable-Client-Key"] = opts.publishableClientKey;
998
+ return headers;
999
+ }
1000
+ function renderToolCard(toolCall) {
1001
+ const toolCard = h("div", { className: "sdt-ai-tool-card" });
1002
+ const header = h("button", {
1003
+ className: "sdt-ai-tool-header",
1004
+ type: "button"
1005
+ });
1006
+ header.appendChild(h("span", { className: "sdt-ai-tool-name" }, toolCall.toolName));
1007
+ header.appendChild(h("span", { className: `sdt-ai-tool-status sdt-ai-tool-status-${toolCall.state}` }, toolCall.state));
1008
+ header.appendChild(h("span", { className: `sdt-ai-tool-chevron${toolCall.isExpanded ? " sdt-ai-tool-chevron-open" : ""}` }, "▾"));
1009
+ header.addEventListener("click", () => {
1010
+ toolCall.isExpanded = !toolCall.isExpanded;
1011
+ renderMessages();
1012
+ });
1013
+ toolCard.appendChild(header);
1014
+ if (toolCall.isExpanded) {
1015
+ const body = h("div", { className: "sdt-ai-tool-body" });
1016
+ if (toolCall.argsText !== null) {
1017
+ body.appendChild(h("div", { className: "sdt-ai-tool-label" }, "Args"));
1018
+ const argsPre = h("pre", { className: "sdt-ai-tool-pre" });
1019
+ argsPre.appendChild(h("code", null, toolCall.argsText));
1020
+ body.appendChild(argsPre);
1021
+ }
1022
+ if (toolCall.resultText !== null) {
1023
+ body.appendChild(h("div", { className: "sdt-ai-tool-label" }, toolCall.state === "error" ? "Error" : "Result"));
1024
+ const resultPre = h("pre", { className: "sdt-ai-tool-pre" });
1025
+ resultPre.appendChild(h("code", null, toolCall.resultText));
1026
+ body.appendChild(resultPre);
1027
+ }
1028
+ if (toolCall.state === "running") body.appendChild(h("div", { className: "sdt-ai-tool-running" }, "Running..."));
1029
+ toolCard.appendChild(body);
1030
+ }
1031
+ return toolCard;
1032
+ }
1033
+ function renderMessages() {
1034
+ messagesArea.innerHTML = "";
1035
+ if (messages.length === 0) {
1036
+ const empty = h("div", { className: "sdt-ai-empty" });
1037
+ const icon = h("div", { className: "sdt-ai-empty-icon" });
1038
+ setHtml(icon, "<svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>");
1039
+ empty.appendChild(icon);
1040
+ empty.appendChild(h("div", { className: "sdt-ai-empty-title" }, "Ask AI"));
1041
+ empty.appendChild(h("div", { className: "sdt-ai-empty-desc" }, "Get help with Hexclave integration, troubleshooting, and best practices."));
1042
+ const suggestions = h("div", { className: "sdt-ai-suggestions" });
1043
+ for (const q of SUGGESTED_QUESTIONS) {
1044
+ const btn = h("button", { className: "sdt-ai-suggestion" });
1045
+ btn.appendChild(h("span", { className: "sdt-ai-suggestion-icon" }, q.icon));
1046
+ btn.appendChild(h("span", null, q.text));
1047
+ btn.addEventListener("click", () => {
1048
+ runAsynchronously(sendMessage(q.text));
1049
+ });
1050
+ suggestions.appendChild(btn);
1051
+ }
1052
+ empty.appendChild(suggestions);
1053
+ messagesArea.appendChild(empty);
1054
+ return;
1055
+ }
1056
+ const list = h("div", { className: "sdt-ai-message-list" });
1057
+ for (const msg of messages) if (msg.role === "user") {
1058
+ const msgDiv = h("div", { className: "sdt-ai-msg sdt-ai-msg-user" });
1059
+ const bubble = h("div", { className: "sdt-ai-bubble sdt-ai-bubble-user" });
1060
+ bubble.appendChild(h("p", null, msg.content));
1061
+ msgDiv.appendChild(bubble);
1062
+ const avatarDiv = h("div", { className: "sdt-ai-avatar sdt-ai-avatar-user" });
1063
+ setHtml(avatarDiv, "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/></svg>");
1064
+ msgDiv.appendChild(avatarDiv);
1065
+ list.appendChild(msgDiv);
1066
+ } else {
1067
+ const msgDiv = h("div", { className: "sdt-ai-msg sdt-ai-msg-assistant" });
1068
+ const avatarDiv = h("div", { className: "sdt-ai-avatar sdt-ai-avatar-assistant" });
1069
+ setHtml(avatarDiv, "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>");
1070
+ msgDiv.appendChild(avatarDiv);
1071
+ const bubble = h("div", { className: "sdt-ai-bubble sdt-ai-bubble-assistant" });
1072
+ if (msg.parts.length === 0) bubble.innerHTML = "<div class=\"sdt-ai-thinking\"><span class=\"sdt-ai-thinking-dot\"></span><span class=\"sdt-ai-thinking-dot\"></span><span class=\"sdt-ai-thinking-dot\"></span></div>";
1073
+ else for (const part of msg.parts) {
1074
+ if (part.type === "text") {
1075
+ const textContainer = h("div", { className: "sdt-ai-part-text" });
1076
+ renderMarkdownInto(textContainer, part.content);
1077
+ bubble.appendChild(textContainer);
1078
+ continue;
1079
+ }
1080
+ const toolCall = msg.toolCallsById.get(part.toolCallId);
1081
+ if (toolCall == null) {
1082
+ const missingTool = h("div", { className: "sdt-ai-tool-card" });
1083
+ const missingBody = h("div", { className: "sdt-ai-tool-body" });
1084
+ missingBody.appendChild(h("div", { className: "sdt-ai-tool-label" }, "Error"));
1085
+ const missingPre = h("pre", { className: "sdt-ai-tool-pre" });
1086
+ missingPre.appendChild(h("code", null, `Missing tool call state for ${part.toolCallId}`));
1087
+ missingBody.appendChild(missingPre);
1088
+ missingTool.appendChild(missingBody);
1089
+ bubble.appendChild(missingTool);
1090
+ continue;
1091
+ }
1092
+ const toolsContainer = h("div", { className: "sdt-ai-tools" });
1093
+ toolsContainer.appendChild(renderToolCard(toolCall));
1094
+ bubble.appendChild(toolsContainer);
1095
+ }
1096
+ msgDiv.appendChild(bubble);
1097
+ list.appendChild(msgDiv);
1098
+ }
1099
+ messagesArea.appendChild(list);
1100
+ messagesArea.scrollTop = messagesArea.scrollHeight;
1101
+ }
1102
+ function renderMarkdownInto(el, content) {
1103
+ function appendBlockWithInlineMarkdown(tag, className, text) {
1104
+ const block = h(tag, { className });
1105
+ appendInlineMarkdown(block, text);
1106
+ el.appendChild(block);
1107
+ }
1108
+ const lines = content.split("\n");
1109
+ let i = 0;
1110
+ while (i < lines.length) {
1111
+ const line = lines[i];
1112
+ if (line.startsWith("```")) {
1113
+ const lang = line.slice(3).trim();
1114
+ const codeLines = [];
1115
+ i++;
1116
+ while (i < lines.length && !lines[i].startsWith("```")) {
1117
+ codeLines.push(lines[i]);
1118
+ i++;
1119
+ }
1120
+ i++;
1121
+ const block = h("div", { className: "sdt-ai-code-block" });
1122
+ const header = h("div", { className: "sdt-ai-code-header" });
1123
+ header.appendChild(h("span", { className: "sdt-ai-code-lang" }, lang || "CODE"));
1124
+ const copyBtn = h("button", { className: "sdt-ai-copy-btn" }, "⎘");
1125
+ const code = codeLines.join("\n");
1126
+ copyBtn.addEventListener("click", () => {
1127
+ runAsynchronously(navigator.clipboard.writeText(code).then(() => {
1128
+ copyBtn.textContent = "✓";
1129
+ setTimeout(() => {
1130
+ copyBtn.textContent = "⎘";
1131
+ }, 1500);
1132
+ }));
1133
+ });
1134
+ header.appendChild(copyBtn);
1135
+ block.appendChild(header);
1136
+ const pre = h("pre", { className: "sdt-ai-code-pre" });
1137
+ pre.appendChild(h("code", null, code));
1138
+ block.appendChild(pre);
1139
+ el.appendChild(block);
1140
+ continue;
1141
+ }
1142
+ const headingMatch = line.match(/^(#{1,3}) (.+)/);
1143
+ if (headingMatch) {
1144
+ appendBlockWithInlineMarkdown(`h${headingMatch[1].length}`, "sdt-ai-heading", headingMatch[2]);
1145
+ i++;
1146
+ continue;
1147
+ }
1148
+ if (/^[-*] /.test(line)) {
1149
+ const ul = h("ul", { className: "sdt-ai-list" });
1150
+ while (i < lines.length && /^[-*] /.test(lines[i])) {
1151
+ const li = h("li");
1152
+ appendInlineMarkdown(li, lines[i].replace(/^[-*] /, ""));
1153
+ ul.appendChild(li);
1154
+ i++;
1155
+ }
1156
+ el.appendChild(ul);
1157
+ continue;
1158
+ }
1159
+ if (/^\d+\. /.test(line)) {
1160
+ const ol = h("ol", { className: "sdt-ai-list sdt-ai-list-ordered" });
1161
+ while (i < lines.length && /^\d+\. /.test(lines[i])) {
1162
+ const li = h("li");
1163
+ appendInlineMarkdown(li, lines[i].replace(/^\d+\. /, ""));
1164
+ ol.appendChild(li);
1165
+ i++;
1166
+ }
1167
+ el.appendChild(ol);
1168
+ continue;
1169
+ }
1170
+ if (line.trim() === "") {
1171
+ i++;
1172
+ continue;
1173
+ }
1174
+ appendBlockWithInlineMarkdown("p", "sdt-ai-paragraph", line);
1175
+ i++;
1176
+ }
1177
+ }
1178
+ function stringifyForDebug(value) {
1179
+ if (value === void 0 || typeof value === "function" || typeof value === "symbol") return String(value);
1180
+ return JSON.stringify(value, null, 2);
1181
+ }
1182
+ function getLastItem(items) {
1183
+ return items.length > 0 ? items[items.length - 1] : void 0;
1184
+ }
1185
+ function isRecord(value) {
1186
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1187
+ }
1188
+ function expectObject(value, payload) {
1189
+ if (!isRecord(value)) throw new Error(`SSE payload must be an object: ${payload}`);
1190
+ return value;
1191
+ }
1192
+ function getRequiredStringField(event, field, payload) {
1193
+ const value = event[field];
1194
+ if (typeof value !== "string") throw new Error(`SSE event '${String(event.type)}' missing string '${field}': ${payload}`);
1195
+ return value;
1196
+ }
1197
+ function getCurrentAssistantMessage() {
1198
+ const lastMessage = getLastItem(messages);
1199
+ if (lastMessage?.role !== "assistant") throw new Error("Expected current message to be an assistant message");
1200
+ return lastMessage;
1201
+ }
1202
+ function appendTextDelta(delta) {
1203
+ const assistantMessage = getCurrentAssistantMessage();
1204
+ const lastPart = getLastItem(assistantMessage.parts);
1205
+ if (lastPart?.type === "text") {
1206
+ lastPart.content += delta;
1207
+ return;
1208
+ }
1209
+ assistantMessage.parts.push({
1210
+ type: "text",
1211
+ content: delta
1212
+ });
1213
+ }
1214
+ function ensureToolPart(assistantMessage, toolCallId) {
1215
+ if (!assistantMessage.parts.some((part) => part.type === "tool" && part.toolCallId === toolCallId)) assistantMessage.parts.push({
1216
+ type: "tool",
1217
+ toolCallId
1218
+ });
1219
+ }
1220
+ function findOrCreateToolCall(toolCallId, fallbackToolName) {
1221
+ const assistantMessage = getCurrentAssistantMessage();
1222
+ const existing = assistantMessage.toolCallsById.get(toolCallId);
1223
+ if (existing != null) {
1224
+ if (existing.toolName === "tool" && fallbackToolName !== "tool") existing.toolName = fallbackToolName;
1225
+ ensureToolPart(assistantMessage, toolCallId);
1226
+ return existing;
1227
+ }
1228
+ const created = {
1229
+ id: toolCallId,
1230
+ toolName: fallbackToolName,
1231
+ argsText: null,
1232
+ resultText: null,
1233
+ state: "running",
1234
+ errorText: null,
1235
+ isExpanded: false
1236
+ };
1237
+ assistantMessage.toolCallsById.set(toolCallId, created);
1238
+ ensureToolPart(assistantMessage, toolCallId);
1239
+ return created;
1240
+ }
1241
+ async function sendMessage(text) {
1242
+ if (!text.trim() || aiLoading) return;
1243
+ messages.push({
1244
+ role: "user",
1245
+ content: text.trim()
1246
+ });
1247
+ messages.push({
1248
+ role: "assistant",
1249
+ parts: [],
1250
+ toolCallsById: /* @__PURE__ */ new Map()
1251
+ });
1252
+ aiLoading = true;
1253
+ renderMessages();
1254
+ renderInput();
1255
+ try {
1256
+ const abortController = new AbortController();
1257
+ activeAiAbortController = abortController;
1258
+ const res = await fetch(`${apiBaseUrl}/api/latest/ai/query/stream`, {
1259
+ method: "POST",
1260
+ headers: {
1261
+ "Content-Type": "application/json",
1262
+ ...getHeaders()
1263
+ },
1264
+ signal: abortController.signal,
1265
+ body: JSON.stringify({
1266
+ systemPrompt: "command-center-ask-ai",
1267
+ tools: ["docs"],
1268
+ quality: "smart",
1269
+ speed: "slow",
1270
+ messages: messages.slice(0, -1).map((m) => ({
1271
+ role: m.role,
1272
+ content: [{
1273
+ type: "text",
1274
+ text: m.role === "user" ? m.content : m.parts.filter((part) => part.type === "text").map((part) => part.content).join("")
1275
+ }]
1276
+ }))
1277
+ })
1278
+ });
1279
+ if (!res.ok) throw new Error(`AI request failed with status ${res.status}`);
1280
+ if (!res.body) throw new Error("AI request returned no response body");
1281
+ const reader = res.body.getReader();
1282
+ const decoder = new TextDecoder();
1283
+ let buffer = "";
1284
+ while (true) {
1285
+ const { done, value } = await reader.read();
1286
+ if (done) break;
1287
+ buffer += decoder.decode(value, { stream: true });
1288
+ const streamLines = buffer.split("\n");
1289
+ buffer = streamLines.pop() || "";
1290
+ for (const streamLine of streamLines) {
1291
+ const line = streamLine.trim();
1292
+ if (line === "" || line.startsWith(":")) continue;
1293
+ if (!line.startsWith("data: ")) throw new Error(`Unexpected SSE line: ${line}`);
1294
+ const payload = line.slice(6);
1295
+ if (payload === "[DONE]") continue;
1296
+ const event = expectObject(JSON.parse(payload), payload);
1297
+ const eventType = getRequiredStringField(event, "type", payload);
1298
+ switch (eventType) {
1299
+ case "start":
1300
+ case "start-step":
1301
+ case "finish-step":
1302
+ case "finish":
1303
+ case "message-metadata":
1304
+ case "text-start":
1305
+ case "text-end":
1306
+ case "reasoning-start":
1307
+ case "reasoning-delta":
1308
+ case "reasoning-end":
1309
+ case "source-url":
1310
+ case "source-document":
1311
+ case "file": break;
1312
+ case "text-delta":
1313
+ appendTextDelta(getRequiredStringField(event, "delta", payload));
1314
+ break;
1315
+ case "tool-input-start": {
1316
+ const toolCall = findOrCreateToolCall(getRequiredStringField(event, "toolCallId", payload), getRequiredStringField(event, "toolName", payload));
1317
+ toolCall.state = "running";
1318
+ toolCall.resultText = null;
1319
+ toolCall.errorText = null;
1320
+ toolCall.argsText = "";
1321
+ break;
1322
+ }
1323
+ case "tool-input-delta": {
1324
+ const toolCallId = getRequiredStringField(event, "toolCallId", payload);
1325
+ const inputTextDelta = getRequiredStringField(event, "inputTextDelta", payload);
1326
+ const toolCall = findOrCreateToolCall(toolCallId, "tool");
1327
+ toolCall.argsText = (toolCall.argsText ?? "") + inputTextDelta;
1328
+ break;
1329
+ }
1330
+ case "tool-input-available": {
1331
+ const toolCall = findOrCreateToolCall(getRequiredStringField(event, "toolCallId", payload), getRequiredStringField(event, "toolName", payload));
1332
+ toolCall.argsText = stringifyForDebug(event.input);
1333
+ break;
1334
+ }
1335
+ case "tool-input-error": {
1336
+ const toolCallId = getRequiredStringField(event, "toolCallId", payload);
1337
+ const toolName = getRequiredStringField(event, "toolName", payload);
1338
+ const errorText = getRequiredStringField(event, "errorText", payload);
1339
+ const toolCall = findOrCreateToolCall(toolCallId, toolName);
1340
+ toolCall.state = "error";
1341
+ toolCall.errorText = errorText;
1342
+ toolCall.resultText = errorText;
1343
+ break;
1344
+ }
1345
+ case "tool-output-available": {
1346
+ const toolCall = findOrCreateToolCall(getRequiredStringField(event, "toolCallId", payload), "tool");
1347
+ const preliminary = event.preliminary === true;
1348
+ toolCall.resultText = stringifyForDebug(event.output);
1349
+ if (!preliminary) toolCall.state = "success";
1350
+ break;
1351
+ }
1352
+ case "tool-output-error": {
1353
+ const toolCallId = getRequiredStringField(event, "toolCallId", payload);
1354
+ const errorText = getRequiredStringField(event, "errorText", payload);
1355
+ const toolCall = findOrCreateToolCall(toolCallId, "tool");
1356
+ toolCall.state = "error";
1357
+ toolCall.errorText = errorText;
1358
+ toolCall.resultText = errorText;
1359
+ break;
1360
+ }
1361
+ case "tool-output-denied": {
1362
+ const toolCall = findOrCreateToolCall(getRequiredStringField(event, "toolCallId", payload), "tool");
1363
+ toolCall.state = "error";
1364
+ toolCall.errorText = "Tool output denied";
1365
+ toolCall.resultText = "Tool output denied";
1366
+ break;
1367
+ }
1368
+ case "tool-approval-request": {
1369
+ const toolCallId = getRequiredStringField(event, "toolCallId", payload);
1370
+ const approvalId = getRequiredStringField(event, "approvalId", payload);
1371
+ const toolCall = findOrCreateToolCall(toolCallId, "tool");
1372
+ toolCall.state = "running";
1373
+ toolCall.resultText = `Approval requested (${approvalId})`;
1374
+ break;
1375
+ }
1376
+ case "abort": {
1377
+ const reason = typeof event.reason === "string" ? event.reason : "unknown reason";
1378
+ throw new Error(`AI stream aborted: ${reason}`);
1379
+ }
1380
+ case "error": throw new Error(typeof event.errorText === "string" ? `AI stream error: ${event.errorText}` : `AI stream error event: ${payload}`);
1381
+ default:
1382
+ if (eventType.startsWith("data-")) break;
1383
+ throw new Error(`Unexpected AI stream event type: ${eventType}`);
1384
+ }
1385
+ }
1386
+ renderMessages();
1387
+ }
1388
+ } catch (error) {
1389
+ if (error instanceof DOMException && error.name === "AbortError") {
1390
+ const assistantMessage = getCurrentAssistantMessage();
1391
+ if (assistantMessage.parts.length === 0) assistantMessage.parts.push({
1392
+ type: "text",
1393
+ content: "Stopped."
1394
+ });
1395
+ renderMessages();
1396
+ return;
1397
+ }
1398
+ const message = error instanceof Error ? error.message : "Unknown AI stream error";
1399
+ const lastMessage = getLastItem(messages);
1400
+ if (lastMessage?.role === "assistant") {
1401
+ lastMessage.parts = [{
1402
+ type: "text",
1403
+ content: message
1404
+ }];
1405
+ lastMessage.toolCallsById.clear();
1406
+ }
1407
+ renderMessages();
1408
+ alert(`AI stream failed: ${message}`);
1409
+ } finally {
1410
+ aiLoading = false;
1411
+ activeAiAbortController = null;
1412
+ renderMessages();
1413
+ renderInput();
1414
+ }
1415
+ }
1416
+ const inputWrapper = h("div", { className: "sdt-ai-input-wrapper" });
1417
+ const input = h("input", {
1418
+ type: "text",
1419
+ className: "sdt-ai-input",
1420
+ placeholder: "Ask anything about Hexclave...",
1421
+ autocomplete: "off",
1422
+ autocorrect: "off",
1423
+ spellcheck: "false"
1424
+ });
1425
+ const sendBtn = h("button", {
1426
+ className: "sdt-ai-send-btn",
1427
+ title: "Send"
1428
+ });
1429
+ setHtml(sendBtn, "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>");
1430
+ function renderInput() {
1431
+ input.disabled = false;
1432
+ input.placeholder = messages.length === 0 ? "Ask anything about Hexclave..." : "Ask a follow-up...";
1433
+ if (aiLoading) {
1434
+ sendBtn.classList.add("sdt-ai-send-btn-active");
1435
+ sendBtn.classList.add("sdt-ai-stop-btn");
1436
+ sendBtn.setAttribute("title", "Stop");
1437
+ setHtml(sendBtn, "<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\"/></svg>");
1438
+ } else if (input.value.trim()) {
1439
+ sendBtn.classList.add("sdt-ai-send-btn-active");
1440
+ sendBtn.classList.remove("sdt-ai-stop-btn");
1441
+ sendBtn.setAttribute("title", "Send");
1442
+ setHtml(sendBtn, "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>");
1443
+ } else {
1444
+ sendBtn.classList.remove("sdt-ai-send-btn-active");
1445
+ sendBtn.classList.remove("sdt-ai-stop-btn");
1446
+ sendBtn.setAttribute("title", "Send");
1447
+ setHtml(sendBtn, "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>");
1448
+ }
1449
+ }
1450
+ input.addEventListener("input", renderInput);
1451
+ input.addEventListener("keydown", (e) => {
1452
+ if (e.key === "Enter" && !e.shiftKey) {
1453
+ e.preventDefault();
1454
+ if (aiLoading) activeAiAbortController?.abort();
1455
+ else {
1456
+ runAsynchronously(sendMessage(input.value));
1457
+ input.value = "";
1458
+ }
1459
+ renderInput();
1460
+ }
1461
+ });
1462
+ sendBtn.addEventListener("click", () => {
1463
+ if (aiLoading) activeAiAbortController?.abort();
1464
+ else {
1465
+ runAsynchronously(sendMessage(input.value));
1466
+ input.value = "";
1467
+ }
1468
+ renderInput();
1469
+ });
1470
+ const newChatBtn = h("button", {
1471
+ className: "sdt-ai-new-chat",
1472
+ title: "New conversation",
1473
+ style: { display: "none" }
1474
+ });
1475
+ setHtml(newChatBtn, "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/></svg>");
1476
+ newChatBtn.addEventListener("click", () => {
1477
+ if (aiLoading) activeAiAbortController?.abort();
1478
+ messages.length = 0;
1479
+ input.value = "";
1480
+ renderMessages();
1481
+ renderInput();
1482
+ newChatBtn.style.display = "none";
1483
+ });
1484
+ inputWrapper.append(input, sendBtn);
1485
+ inputArea.append(newChatBtn, inputWrapper);
1486
+ container.append(messagesArea, inputArea);
1487
+ renderMessages();
1488
+ renderInput();
1489
+ return container;
1490
+ }
1491
+ function createDashboardTab(app) {
1492
+ return createIframeTab(resolveDashboardUrl(app), "Hexclave Dashboard", "Loading dashboard…", "Unable to load dashboard", "The dashboard may require authentication or block framing", "Open in New Tab");
1493
+ }
1494
+ function createSupportTab(app) {
1495
+ const container = h("div", { className: "sdt-support-tab" });
1496
+ const apiBaseUrl = resolveApiBaseUrl(app);
1497
+ function createFeedbackForm() {
1498
+ const pane = h("div", { className: "sdt-support-feedback-pane" });
1499
+ const form = h("form", { className: "sdt-support-form" });
1500
+ let feedbackType = "feedback";
1501
+ let status = "idle";
1502
+ let errorMessage = "";
1503
+ const nameInput = h("input", {
1504
+ className: "sdt-support-input",
1505
+ type: "text",
1506
+ placeholder: "Your name"
1507
+ });
1508
+ const emailInput = h("input", {
1509
+ className: "sdt-support-input",
1510
+ type: "email",
1511
+ placeholder: "you@example.com",
1512
+ required: "true"
1513
+ });
1514
+ const messageInput = h("textarea", {
1515
+ className: "sdt-support-textarea",
1516
+ placeholder: "What's on your mind?",
1517
+ required: "true",
1518
+ rows: "5"
1519
+ });
1520
+ function render() {
1521
+ form.innerHTML = "";
1522
+ if (status === "success") {
1523
+ const successDiv = h("div", { className: "sdt-support-status sdt-support-status-success" });
1524
+ const icon = h("div", { className: "sdt-support-status-icon" });
1525
+ setHtml(icon, "<svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\"><path d=\"M6 10l3 3 5-6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>");
1526
+ successDiv.append(icon, h("div", { className: "sdt-support-status-title" }, "Feedback sent"), h("div", { className: "sdt-support-status-msg" }, "Thank you! We'll get back to you soon."));
1527
+ const resetBtn = h("button", {
1528
+ className: "sdt-support-submit",
1529
+ style: {
1530
+ marginTop: "12px",
1531
+ width: "auto"
1532
+ }
1533
+ }, "Send another");
1534
+ resetBtn.addEventListener("click", () => {
1535
+ status = "idle";
1536
+ render();
1537
+ });
1538
+ successDiv.appendChild(resetBtn);
1539
+ form.appendChild(successDiv);
1540
+ return;
1541
+ }
1542
+ if (status === "error") {
1543
+ const errDiv = h("div", { className: "sdt-support-status sdt-support-status-error" });
1544
+ const icon = h("div", { className: "sdt-support-status-icon" });
1545
+ setHtml(icon, "<svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\"><path d=\"M10 6v5m0 3h.01\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>");
1546
+ errDiv.append(icon, h("div", { className: "sdt-support-status-title" }, "Failed to send"), h("div", { className: "sdt-support-status-msg" }, errorMessage || "Please try again."));
1547
+ const retryBtn = h("button", {
1548
+ className: "sdt-support-submit",
1549
+ style: {
1550
+ marginTop: "12px",
1551
+ width: "auto"
1552
+ }
1553
+ }, "Try again");
1554
+ retryBtn.addEventListener("click", () => {
1555
+ status = "idle";
1556
+ errorMessage = "";
1557
+ render();
1558
+ });
1559
+ errDiv.appendChild(retryBtn);
1560
+ form.appendChild(errDiv);
1561
+ return;
1562
+ }
1563
+ const nameField = h("div", { className: "sdt-support-field" });
1564
+ const nameLabel = h("label", { className: "sdt-support-label" }, "Name ");
1565
+ nameLabel.appendChild(h("span", { className: "sdt-support-optional" }, "optional"));
1566
+ nameField.append(nameLabel, nameInput);
1567
+ form.appendChild(nameField);
1568
+ const emailField = h("div", { className: "sdt-support-field" });
1569
+ emailField.append(h("label", { className: "sdt-support-label" }, "Email"), emailInput);
1570
+ form.appendChild(emailField);
1571
+ const msgField = h("div", { className: "sdt-support-field" });
1572
+ msgField.append(h("label", { className: "sdt-support-label" }, feedbackType === "bug" ? "Description" : "Message"), messageInput);
1573
+ messageInput.placeholder = feedbackType === "bug" ? "Steps to reproduce, expected vs. actual behavior…" : "What's on your mind?";
1574
+ form.appendChild(msgField);
1575
+ const typeCards = h("div", { className: "sdt-support-type-cards" });
1576
+ const feedbackBtn = h("button", {
1577
+ type: "button",
1578
+ className: `sdt-support-type-card ${feedbackType === "feedback" ? "sdt-support-type-card-active" : ""}`
1579
+ });
1580
+ setHtml(feedbackBtn, "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg><span>Feedback</span>");
1581
+ feedbackBtn.addEventListener("click", () => {
1582
+ feedbackType = "feedback";
1583
+ render();
1584
+ });
1585
+ const bugBtn = h("button", {
1586
+ type: "button",
1587
+ className: `sdt-support-type-card ${feedbackType === "bug" ? "sdt-support-type-card-active" : ""}`
1588
+ });
1589
+ setHtml(bugBtn, "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 2l1.88 1.88M14.12 3.88L16 2M9 7.13v-1a3.003 3.003 0 1 1 6 0v1\"/><path d=\"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6\"/><path d=\"M12 20v-9M6.53 9C4.6 8.8 3 7.1 3 5M6 13H2M6 17H3M21 5c0 2.1-1.6 3.8-3.53 4M18 13h4M21 17h-3\"/></svg><span>Bug Report</span>");
1590
+ bugBtn.addEventListener("click", () => {
1591
+ feedbackType = "bug";
1592
+ render();
1593
+ });
1594
+ typeCards.append(feedbackBtn, bugBtn);
1595
+ form.appendChild(typeCards);
1596
+ const submitBtn = h("button", {
1597
+ type: "submit",
1598
+ className: "sdt-support-submit"
1599
+ });
1600
+ setHtml(submitBtn, "Submit <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/><polyline points=\"12 5 19 12 12 19\"/></svg>");
1601
+ submitBtn.disabled = status === "submitting";
1602
+ form.appendChild(submitBtn);
1603
+ const channels = h("div", { className: "sdt-support-channels" });
1604
+ channels.innerHTML = `
1605
+ <a href="https://discord.hexclave.com" target="_blank" rel="noopener noreferrer" class="sdt-support-channel">
1606
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.095 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.095 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
1607
+ <span>Discord</span>
1608
+ </a>
1609
+ <a href="mailto:team@hexclave.com" class="sdt-support-channel">
1610
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
1611
+ <span>Email</span>
1612
+ </a>
1613
+ <a href="https://github.com/hexclave/hexclave" target="_blank" rel="noopener noreferrer" class="sdt-support-channel">
1614
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0 1 12 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/></svg>
1615
+ <span>GitHub</span>
1616
+ </a>`;
1617
+ form.appendChild(channels);
1618
+ form.insertBefore(channels, form.firstChild);
1619
+ }
1620
+ form.addEventListener("submit", (e) => {
1621
+ e.preventDefault();
1622
+ if (!emailInput.value.trim() || !messageInput.value.trim()) return;
1623
+ runAsynchronously(async () => {
1624
+ status = "submitting";
1625
+ render();
1626
+ try {
1627
+ const response = await fetch(`${apiBaseUrl}/api/latest/internal/feedback`, {
1628
+ method: "POST",
1629
+ headers: {
1630
+ "Content-Type": "application/json",
1631
+ "Accept": "application/json"
1632
+ },
1633
+ body: JSON.stringify({
1634
+ name: nameInput.value.trim() || void 0,
1635
+ email: emailInput.value.trim(),
1636
+ message: messageInput.value.trim(),
1637
+ feedback_type: feedbackType
1638
+ })
1639
+ });
1640
+ if (!response.ok) throw new Error(`Failed to send: ${response.status} ${response.statusText}`);
1641
+ const result = await response.json();
1642
+ if (!result.success) throw new Error(result.message || "Failed to send feedback");
1643
+ status = "success";
1644
+ messageInput.value = "";
1645
+ } catch (err) {
1646
+ status = "error";
1647
+ errorMessage = err.message || "An unexpected error occurred";
1648
+ }
1649
+ render();
1650
+ });
1651
+ });
1652
+ render();
1653
+ pane.appendChild(form);
1654
+ return pane;
1655
+ }
1656
+ container.appendChild(createFeedbackForm());
1657
+ return container;
1658
+ }
1659
+ function createComponentsTab(app) {
1660
+ const container = h("div", { className: "sdt-pg-layout" });
1661
+ const apiBaseUrl = resolveApiBaseUrl(app);
1662
+ const urls = app.urls;
1663
+ const urlOptions = app[stackAppInternalsSymbol].getConstructorOptions().urls ?? {};
1664
+ const PAGE_ENTRIES = [
1665
+ {
1666
+ key: "signIn",
1667
+ label: "Sign-in"
1668
+ },
1669
+ {
1670
+ key: "signUp",
1671
+ label: "Sign-up"
1672
+ },
1673
+ {
1674
+ key: "forgotPassword",
1675
+ label: "Forgot password"
1676
+ },
1677
+ {
1678
+ key: "passwordReset",
1679
+ label: "Password reset"
1680
+ },
1681
+ {
1682
+ key: "emailVerification",
1683
+ label: "Email verification"
1684
+ },
1685
+ {
1686
+ key: "accountSettings",
1687
+ label: "Account settings"
1688
+ },
1689
+ {
1690
+ key: "teamInvitation",
1691
+ label: "Team invitation"
1692
+ },
1693
+ {
1694
+ key: "cliAuthConfirm",
1695
+ label: "CLI auth confirmation"
1696
+ },
1697
+ {
1698
+ key: "mfa",
1699
+ label: "MFA"
1700
+ },
1701
+ {
1702
+ key: "onboarding",
1703
+ label: "Onboarding"
1704
+ },
1705
+ {
1706
+ key: "error",
1707
+ label: "Error"
1708
+ }
1709
+ ];
1710
+ function classifyPage(key) {
1711
+ const target = urlOptions[key] ?? urlOptions.default ?? { type: "handler-component" };
1712
+ if (typeof target === "string") return {
1713
+ classification: "custom",
1714
+ version: null
1715
+ };
1716
+ if ("type" in target) {
1717
+ if (target.type === "custom") return {
1718
+ classification: "custom",
1719
+ version: target.version ?? null
1720
+ };
1721
+ return {
1722
+ classification: target.type,
1723
+ version: null
1724
+ };
1725
+ }
1726
+ return {
1727
+ classification: "handler-component",
1728
+ version: null
1729
+ };
1730
+ }
1731
+ let latestVersions = null;
1732
+ let selectedKey = null;
1733
+ runAsynchronously(fetch(`${apiBaseUrl}/api/latest/internal/component-versions`).then((r) => r.json()).then((data) => {
1734
+ latestVersions = new Map(Object.entries(data.versions ?? {}));
1735
+ renderSidebar();
1736
+ }).catch(() => {}));
1737
+ function buildPages() {
1738
+ return PAGE_ENTRIES.map((entry) => {
1739
+ const { classification, version } = classifyPage(entry.key);
1740
+ let versionStatus = "current";
1741
+ let versionChangelogs = [];
1742
+ if (classification === "custom" && version != null && latestVersions) {
1743
+ const info = latestVersions.get(entry.key);
1744
+ if (info && version < info.version) {
1745
+ versionStatus = "outdated";
1746
+ versionChangelogs = Object.entries(info.changelogs).map(([v, cl]) => ({
1747
+ version: Number(v),
1748
+ changelog: cl
1749
+ })).filter((e) => e.version > version).sort((a, b) => a.version - b.version);
1750
+ }
1751
+ }
1752
+ return {
1753
+ key: entry.key,
1754
+ label: entry.label,
1755
+ url: urls[entry.key] || "",
1756
+ classification,
1757
+ version,
1758
+ versionStatus,
1759
+ versionChangelogs
1760
+ };
1761
+ });
1762
+ }
1763
+ function getCompactUrl(url) {
1764
+ const resolved = new URL(url, window.location.origin);
1765
+ return `${resolved.pathname}${resolved.search}${resolved.hash}`;
1766
+ }
1767
+ const sidebar = h("div", { className: "sdt-pg-sidebar" });
1768
+ const mainArea = h("div", { className: "sdt-pg-main" });
1769
+ function renderSidebar() {
1770
+ sidebar.innerHTML = "";
1771
+ const pages = buildPages();
1772
+ const outdatedCount = pages.filter((p) => p.versionStatus === "outdated").length;
1773
+ const head = h("div", { className: "sdt-pg-sidebar-head" });
1774
+ head.appendChild(h("span", { className: "sdt-pg-sidebar-title" }, "Pages"));
1775
+ head.appendChild(h("span", { className: "sdt-pg-sidebar-count" }, String(pages.length)));
1776
+ if (outdatedCount > 0) head.appendChild(h("span", { className: "sdt-pg-sidebar-warn" }, `${outdatedCount} outdated`));
1777
+ sidebar.appendChild(head);
1778
+ const list = h("div", { className: "sdt-pg-list" });
1779
+ for (const page of pages) {
1780
+ const isOutdated = page.versionStatus === "outdated";
1781
+ const item = h("div", {
1782
+ className: `sdt-pg-item ${isOutdated ? "sdt-pg-item-warn" : ""}`,
1783
+ "data-selected": String(selectedKey === page.key)
1784
+ });
1785
+ const dotClass = isOutdated ? "sdt-pg-item-dot-warn" : page.classification === "custom" ? "sdt-pg-item-dot-custom" : "sdt-pg-item-dot-handler";
1786
+ item.appendChild(h("span", { className: `sdt-pg-item-dot ${dotClass}` }));
1787
+ item.appendChild(h("span", { className: "sdt-pg-item-label" }, page.label));
1788
+ if (isOutdated) item.appendChild(h("span", { className: "sdt-pg-badge sdt-pg-badge-outdated" }, "Outdated"));
1789
+ item.addEventListener("click", () => {
1790
+ selectedKey = page.key;
1791
+ renderSidebar();
1792
+ renderDetail(page);
1793
+ });
1794
+ list.appendChild(item);
1795
+ }
1796
+ sidebar.appendChild(list);
1797
+ }
1798
+ function renderDetail(page) {
1799
+ mainArea.innerHTML = "";
1800
+ const detail = h("div", { className: "sdt-pg-detail" });
1801
+ const header = h("div", { className: "sdt-pg-header" });
1802
+ const headerTop = h("div", { className: "sdt-pg-header-top" });
1803
+ headerTop.appendChild(h("h3", { className: "sdt-pg-title" }, `${page.label} Page`));
1804
+ headerTop.appendChild(h("a", {
1805
+ href: page.url,
1806
+ target: "_blank",
1807
+ rel: "noopener noreferrer",
1808
+ className: "sdt-pg-title-url"
1809
+ }, getCompactUrl(page.url)));
1810
+ if (page.versionStatus === "outdated") headerTop.appendChild(h("span", { className: "sdt-pg-badge sdt-pg-badge-outdated" }, "Outdated"));
1811
+ header.appendChild(headerTop);
1812
+ const redirectMethod = `stackApp.redirectTo${page.key.charAt(0).toUpperCase()}${page.key.slice(1)}()`;
1813
+ const codeRow = h("div", { className: "sdt-pg-code-inline" });
1814
+ codeRow.appendChild(h("code", { className: "sdt-pg-code" }, redirectMethod));
1815
+ const openBtn = h("button", { className: "sdt-pg-copy-btn sdt-pg-open-btn" });
1816
+ setHtml(openBtn, "Open <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M7 17L17 7\"/><path d=\"M7 7h10v10\"/></svg>");
1817
+ openBtn.addEventListener("click", () => {
1818
+ const resolved = new URL(page.url, window.location.origin);
1819
+ window.open(resolved.toString(), "_blank", "noopener,noreferrer");
1820
+ });
1821
+ codeRow.appendChild(openBtn);
1822
+ header.appendChild(codeRow);
1823
+ detail.appendChild(header);
1824
+ const prompt = getPagePrompt(page.key, page.version ?? void 0);
1825
+ if (prompt) {
1826
+ const isOutdated = page.versionStatus === "outdated";
1827
+ if (page.classification === "handler-component" || page.classification === "hosted" || isOutdated) {
1828
+ let promptText;
1829
+ if (isOutdated && prompt.upgradePrompt) promptText = prompt.upgradePrompt;
1830
+ else if (prompt.fullPrompt) promptText = prompt.fullPrompt;
1831
+ else promptText = "";
1832
+ if (promptText) {
1833
+ const section = h("div", { className: "sdt-pg-section" });
1834
+ section.appendChild(h("div", { className: "sdt-pg-section-label" }, isOutdated ? "Use this prompt to upgrade your component:" : "Want to customize this page? Paste this prompt into your coding agent."));
1835
+ section.appendChild(h("pre", { className: "sdt-pg-pre" }, promptText));
1836
+ const footer = h("div", { className: "sdt-pg-section-footer" });
1837
+ const copyBtn = h("button", { className: "sdt-pg-copy-btn" }, "Copy prompt");
1838
+ copyBtn.addEventListener("click", () => {
1839
+ runAsynchronously(navigator.clipboard.writeText(promptText).then(() => {
1840
+ copyBtn.textContent = "✓ Copied";
1841
+ setTimeout(() => {
1842
+ copyBtn.textContent = "Copy prompt";
1843
+ }, 1500);
1844
+ }));
1845
+ });
1846
+ footer.appendChild(copyBtn);
1847
+ section.appendChild(footer);
1848
+ detail.appendChild(section);
1849
+ }
1850
+ }
1851
+ }
1852
+ mainArea.appendChild(detail);
1853
+ }
1854
+ function renderEmptyMain() {
1855
+ mainArea.innerHTML = "";
1856
+ const empty = h("div", { className: "sdt-pg-empty" });
1857
+ const icon = h("div", { className: "sdt-pg-empty-icon" });
1858
+ setHtml(icon, "<svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/></svg>");
1859
+ empty.appendChild(icon);
1860
+ empty.appendChild(h("div", { className: "sdt-pg-empty-text" }, "Select a page to inspect"));
1861
+ empty.appendChild(h("div", { className: "sdt-pg-empty-sub" }, "View configuration, preview, and upgrade prompts"));
1862
+ mainArea.appendChild(empty);
1863
+ }
1864
+ renderSidebar();
1865
+ renderEmptyMain();
1866
+ container.append(sidebar, mainArea);
1867
+ return container;
1868
+ }
1869
+ function createPanel(app, state, logStore, onClose) {
1870
+ const panel = h("div", { className: "sdt-panel" });
1871
+ let panelAnimationTimeout = null;
1872
+ function animateNextPanelGeometryChange() {
1873
+ panel.classList.add("sdt-panel-geometry-animated");
1874
+ if (panelAnimationTimeout !== null) clearTimeout(panelAnimationTimeout);
1875
+ panelAnimationTimeout = setTimeout(() => {
1876
+ panel.classList.remove("sdt-panel-geometry-animated");
1877
+ panelAnimationTimeout = null;
1878
+ }, 220);
1879
+ }
1880
+ function applyPanelMode(tabId, opts) {
1881
+ if (opts?.animate === true) animateNextPanelGeometryChange();
1882
+ if (tabId === "dashboard") {
1883
+ panel.classList.add("sdt-panel-fullscreen");
1884
+ panel.style.width = "";
1885
+ panel.style.height = "";
1886
+ return;
1887
+ }
1888
+ panel.classList.remove("sdt-panel-fullscreen");
1889
+ panel.style.width = state.get().panelWidth + "px";
1890
+ panel.style.height = state.get().panelHeight + "px";
1891
+ }
1892
+ const tabs = getTabsForApp(app);
1893
+ const storedActiveTab = state.get().activeTab;
1894
+ const activeTab = tabs.some((tab) => tab.id === storedActiveTab) ? storedActiveTab : DEFAULT_STATE.activeTab;
1895
+ applyPanelMode(activeTab);
1896
+ const inner = h("div", { className: "sdt-panel-inner" });
1897
+ const closeBtn = h("button", {
1898
+ className: "sdt-close-btn",
1899
+ "aria-label": "Close"
1900
+ });
1901
+ setHtml(closeBtn, "<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><line x1=\"3\" y1=\"3\" x2=\"11\" y2=\"11\"/><line x1=\"11\" y1=\"3\" x2=\"3\" y2=\"11\"/></svg>");
1902
+ closeBtn.addEventListener("click", onClose);
1903
+ const docsLink = h("a", {
1904
+ href: DOCS_URL,
1905
+ target: "_blank",
1906
+ rel: "noopener noreferrer",
1907
+ className: "sdt-docs-link"
1908
+ });
1909
+ docsLink.appendChild(document.createTextNode("Docs"));
1910
+ const docsIcon = h("span", {
1911
+ className: "sdt-docs-link-icon",
1912
+ "aria-hidden": "true"
1913
+ });
1914
+ setHtml(docsIcon, "<svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 17L17 7\"/><path d=\"M7 7h10v10\"/></svg>");
1915
+ docsLink.appendChild(docsIcon);
1916
+ const tabBar = createTabBar(tabs, activeTab, (id) => {
1917
+ state.update({ activeTab: id });
1918
+ applyPanelMode(id, { animate: true });
1919
+ showTab(id);
1920
+ }, { trailing: h("div", { className: "sdt-tabbar-actions" }, docsLink, closeBtn) });
1921
+ inner.appendChild(tabBar.el);
1922
+ const content = h("div", { className: "sdt-content" });
1923
+ const layers = h("div", { className: "sdt-tab-layers" });
1924
+ content.appendChild(layers);
1925
+ inner.appendChild(content);
1926
+ const mountedPanes = /* @__PURE__ */ new Map();
1927
+ const cleanups = [];
1928
+ function mountTab(pane, result) {
1929
+ if ("element" in result) {
1930
+ pane.appendChild(result.element);
1931
+ if (result.cleanup) cleanups.push(result.cleanup);
1932
+ } else pane.appendChild(result);
1933
+ }
1934
+ function getOrCreatePane(tabId) {
1935
+ if (mountedPanes.has(tabId)) return mountedPanes.get(tabId);
1936
+ const pane = h("div", { className: "sdt-tab-pane" });
1937
+ if (tabId === "dashboard") pane.classList.add("sdt-tab-pane-iframe");
1938
+ switch (tabId) {
1939
+ case "overview":
1940
+ mountTab(pane, createOverviewTab(app));
1941
+ break;
1942
+ case "customize":
1943
+ mountTab(pane, createComponentsTab(app));
1944
+ break;
1945
+ case "ai":
1946
+ mountTab(pane, createAITab(app));
1947
+ break;
1948
+ case "console":
1949
+ mountTab(pane, createConsoleTab(logStore));
1950
+ break;
1951
+ case "dashboard":
1952
+ mountTab(pane, createDashboardTab(app));
1953
+ break;
1954
+ case "support":
1955
+ mountTab(pane, createSupportTab(app));
1956
+ break;
1957
+ }
1958
+ mountedPanes.set(tabId, pane);
1959
+ layers.appendChild(pane);
1960
+ return pane;
1961
+ }
1962
+ function showTab(tabId) {
1963
+ const pane = getOrCreatePane(tabId);
1964
+ tabBar.setActive(tabId);
1965
+ for (const [, p] of mountedPanes) p.classList.remove("sdt-tab-pane-active");
1966
+ pane.classList.add("sdt-tab-pane-active");
1967
+ }
1968
+ showTab(activeTab);
1969
+ function addResizeHandle(edge) {
1970
+ const handle = h("div", { className: `sdt-resize-handle sdt-resize-${edge}` });
1971
+ let startX = 0;
1972
+ let startY = 0;
1973
+ let startW = 0;
1974
+ let startH = 0;
1975
+ handle.addEventListener("pointerdown", (e) => {
1976
+ e.preventDefault();
1977
+ if (panelAnimationTimeout !== null) {
1978
+ clearTimeout(panelAnimationTimeout);
1979
+ panelAnimationTimeout = null;
1980
+ }
1981
+ panel.classList.remove("sdt-panel-geometry-animated");
1982
+ handle.setPointerCapture(e.pointerId);
1983
+ startX = e.clientX;
1984
+ startY = e.clientY;
1985
+ startW = panel.offsetWidth;
1986
+ startH = panel.offsetHeight;
1987
+ });
1988
+ handle.addEventListener("pointermove", (e) => {
1989
+ if (!handle.hasPointerCapture(e.pointerId)) return;
1990
+ const dx = startX - e.clientX;
1991
+ const dy = startY - e.clientY;
1992
+ if (edge === "left" || edge === "top-left") {
1993
+ const newW = Math.max(400, Math.min(startW + dx, window.innerWidth - 32));
1994
+ panel.style.width = newW + "px";
1995
+ }
1996
+ if (edge === "top" || edge === "top-left") {
1997
+ const newH = Math.max(300, Math.min(startH + dy, window.innerHeight - 80));
1998
+ panel.style.height = newH + "px";
1999
+ }
2000
+ });
2001
+ handle.addEventListener("pointerup", (e) => {
2002
+ handle.releasePointerCapture(e.pointerId);
2003
+ state.update({
2004
+ panelWidth: panel.offsetWidth,
2005
+ panelHeight: panel.offsetHeight
2006
+ });
2007
+ });
2008
+ panel.appendChild(handle);
2009
+ }
2010
+ addResizeHandle("top");
2011
+ addResizeHandle("left");
2012
+ addResizeHandle("top-left");
2013
+ panel.appendChild(inner);
2014
+ return {
2015
+ element: panel,
2016
+ cleanup: () => {
2017
+ if (panelAnimationTimeout !== null) clearTimeout(panelAnimationTimeout);
2018
+ for (const fn of cleanups) fn();
2019
+ }
2020
+ };
2021
+ }
2022
+ function createDevTool(app) {
2023
+ if (typeof document === "undefined" || typeof document.createElement !== "function") return () => {};
2024
+ const body = Reflect.get(document, "body");
2025
+ if (!hasAppendChild(body)) return () => {};
2026
+ getGlobalDevToolInstance()?.cleanup();
2027
+ let existingRoot = document.getElementById(ROOT_ID);
2028
+ while (existingRoot !== null) {
2029
+ existingRoot.remove();
2030
+ existingRoot = document.getElementById(ROOT_ID);
2031
+ }
2032
+ const root = document.createElement("div");
2033
+ root.id = ROOT_ID;
2034
+ body.appendChild(root);
2035
+ const wrapper = h("div", { className: "stack-devtool" });
2036
+ root.appendChild(wrapper);
2037
+ const style = document.createElement("style");
2038
+ style.textContent = devToolCSS;
2039
+ wrapper.appendChild(style);
2040
+ const state = createStateStore();
2041
+ const logStore = getGlobalLogStore();
2042
+ let panel = null;
2043
+ function closePanelAndPersistClosed() {
2044
+ closePanel();
2045
+ }
2046
+ function openPanel() {
2047
+ if (panel) return;
2048
+ panel = createPanel(app, state, logStore, closePanelAndPersistClosed);
2049
+ wrapper.appendChild(panel.element);
2050
+ }
2051
+ function closePanel() {
2052
+ if (!panel) return;
2053
+ state.update({ isOpen: false });
2054
+ const closing = panel;
2055
+ panel = null;
2056
+ closing.cleanup();
2057
+ closing.element.classList.add("sdt-panel-exiting");
2058
+ setTimeout(() => {
2059
+ if (wrapper.contains(closing.element)) wrapper.removeChild(closing.element);
2060
+ }, 150);
2061
+ }
2062
+ function togglePanel() {
2063
+ if (state.get().isOpen) closePanel();
2064
+ else {
2065
+ state.update({ isOpen: true });
2066
+ openPanel();
2067
+ }
2068
+ }
2069
+ const trigger = createTrigger(togglePanel);
2070
+ wrapper.appendChild(trigger.element);
2071
+ if (state.get().isOpen) openPanel();
2072
+ const removeRequestListener = app[stackAppInternalsSymbol].addRequestListener((entry) => {
2073
+ const timestamp = Date.now();
2074
+ logStore.addApiLog({
2075
+ id: nextId(),
2076
+ timestamp,
2077
+ method: entry.method,
2078
+ url: entry.path,
2079
+ status: entry.status,
2080
+ duration: entry.duration,
2081
+ error: entry.error
2082
+ });
2083
+ if (entry.error) logStore.addEventLog({
2084
+ id: nextId(),
2085
+ timestamp,
2086
+ type: "error",
2087
+ message: `Network error on ${entry.method} ${entry.path}: ${entry.error}`
2088
+ });
2089
+ else if (entry.status && entry.status >= 400) logStore.addEventLog({
2090
+ id: nextId(),
2091
+ timestamp,
2092
+ type: "error",
2093
+ message: `API error ${entry.status} on ${entry.method} ${entry.path}`
2094
+ });
2095
+ });
2096
+ let didCleanup = false;
2097
+ const instance = { cleanup: () => {
2098
+ if (didCleanup) return;
2099
+ didCleanup = true;
2100
+ if (getGlobalDevToolInstance() === instance) setGlobalDevToolInstance(null);
2101
+ trigger.cleanup();
2102
+ removeRequestListener();
2103
+ panel?.cleanup();
2104
+ if (root.parentNode) root.parentNode.removeChild(root);
2105
+ } };
2106
+ setGlobalDevToolInstance(instance);
2107
+ return () => {
2108
+ instance.cleanup();
2109
+ };
2110
+ }
2111
+
2112
+ //#endregion
2113
+ export { createDevTool };
2114
+ //# sourceMappingURL=dev-tool-core.js.map