railwatch 0.6.1 → 0.7.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 (345) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/app/frontend/components/app-content.tsx +26 -0
  4. data/app/frontend/components/app-logo-icon.tsx +23 -0
  5. data/app/frontend/components/app-logo.tsx +14 -0
  6. data/app/frontend/components/app-shell.tsx +38 -0
  7. data/app/frontend/components/app-sidebar-header.tsx +54 -0
  8. data/app/frontend/components/app-sidebar.tsx +94 -0
  9. data/app/frontend/components/app-wordmark.tsx +29 -0
  10. data/app/frontend/components/breadcrumbs.tsx +46 -0
  11. data/app/frontend/components/icon.tsx +16 -0
  12. data/app/frontend/components/input-error.tsx +18 -0
  13. data/app/frontend/components/nav-footer.tsx +46 -0
  14. data/app/frontend/components/nav-main.tsx +41 -0
  15. data/app/frontend/components/nav-user.tsx +50 -0
  16. data/app/frontend/components/railwatch/chart-hover.tsx +34 -0
  17. data/app/frontend/components/railwatch/chart-panel.tsx +170 -0
  18. data/app/frontend/components/railwatch/code.tsx +28 -0
  19. data/app/frontend/components/railwatch/command-palette.tsx +132 -0
  20. data/app/frontend/components/railwatch/copy-block.tsx +52 -0
  21. data/app/frontend/components/railwatch/copy-id.tsx +49 -0
  22. data/app/frontend/components/railwatch/cursor-load-more.tsx +39 -0
  23. data/app/frontend/components/railwatch/data-table.tsx +195 -0
  24. data/app/frontend/components/railwatch/digit-roll.tsx +312 -0
  25. data/app/frontend/components/railwatch/empty-state.tsx +159 -0
  26. data/app/frontend/components/railwatch/env-nav.tsx +130 -0
  27. data/app/frontend/components/railwatch/env-switcher.tsx +145 -0
  28. data/app/frontend/components/railwatch/filter-bar.tsx +153 -0
  29. data/app/frontend/components/railwatch/flamegraph.tsx +324 -0
  30. data/app/frontend/components/railwatch/frames.tsx +295 -0
  31. data/app/frontend/components/railwatch/json-viewer.tsx +159 -0
  32. data/app/frontend/components/railwatch/live-dot.tsx +78 -0
  33. data/app/frontend/components/railwatch/live-toggle.tsx +76 -0
  34. data/app/frontend/components/railwatch/metric-chart.tsx +112 -0
  35. data/app/frontend/components/railwatch/mobile-page-switcher.tsx +89 -0
  36. data/app/frontend/components/railwatch/onboarding.tsx +91 -0
  37. data/app/frontend/components/railwatch/origin-identity.tsx +84 -0
  38. data/app/frontend/components/railwatch/page-header.tsx +42 -0
  39. data/app/frontend/components/railwatch/percentile-picker.tsx +18 -0
  40. data/app/frontend/components/railwatch/relative-time.tsx +18 -0
  41. data/app/frontend/components/railwatch/saved-views.tsx +232 -0
  42. data/app/frontend/components/railwatch/segmented.tsx +64 -0
  43. data/app/frontend/components/railwatch/series-chart.tsx +400 -0
  44. data/app/frontend/components/railwatch/shortcuts-dialog.tsx +89 -0
  45. data/app/frontend/components/railwatch/signal-toast.tsx +121 -0
  46. data/app/frontend/components/railwatch/sort-header.tsx +37 -0
  47. data/app/frontend/components/railwatch/source-link.tsx +57 -0
  48. data/app/frontend/components/railwatch/sparkline-cell.tsx +14 -0
  49. data/app/frontend/components/railwatch/sql-block.tsx +116 -0
  50. data/app/frontend/components/railwatch/stat.tsx +173 -0
  51. data/app/frontend/components/railwatch/status-badge.tsx +79 -0
  52. data/app/frontend/components/railwatch/step-picker.tsx +57 -0
  53. data/app/frontend/components/railwatch/timeline.tsx +405 -0
  54. data/app/frontend/components/railwatch/utilisation-bar.tsx +44 -0
  55. data/app/frontend/components/railwatch/window-picker.tsx +147 -0
  56. data/app/frontend/components/ui/avatar.tsx +51 -0
  57. data/app/frontend/components/ui/badge.tsx +46 -0
  58. data/app/frontend/components/ui/breadcrumb.tsx +109 -0
  59. data/app/frontend/components/ui/button.tsx +60 -0
  60. data/app/frontend/components/ui/card.tsx +92 -0
  61. data/app/frontend/components/ui/chart.tsx +374 -0
  62. data/app/frontend/components/ui/checkbox.tsx +32 -0
  63. data/app/frontend/components/ui/collapsible.tsx +31 -0
  64. data/app/frontend/components/ui/command.tsx +182 -0
  65. data/app/frontend/components/ui/dialog.tsx +158 -0
  66. data/app/frontend/components/ui/dropdown-menu.tsx +255 -0
  67. data/app/frontend/components/ui/hover-card.tsx +42 -0
  68. data/app/frontend/components/ui/input.tsx +21 -0
  69. data/app/frontend/components/ui/label.tsx +24 -0
  70. data/app/frontend/components/ui/popover.tsx +87 -0
  71. data/app/frontend/components/ui/select.tsx +185 -0
  72. data/app/frontend/components/ui/separator.tsx +28 -0
  73. data/app/frontend/components/ui/sheet.tsx +137 -0
  74. data/app/frontend/components/ui/sidebar.tsx +726 -0
  75. data/app/frontend/components/ui/skeleton.tsx +13 -0
  76. data/app/frontend/components/ui/sonner.tsx +41 -0
  77. data/app/frontend/components/ui/table.tsx +114 -0
  78. data/app/frontend/components/ui/tabs.tsx +91 -0
  79. data/app/frontend/components/ui/textarea.tsx +18 -0
  80. data/app/frontend/components/ui/tooltip.tsx +61 -0
  81. data/app/frontend/components/user-info.tsx +32 -0
  82. data/app/frontend/components/user-menu-content.tsx +94 -0
  83. data/app/frontend/entrypoints/application.css +244 -0
  84. data/app/frontend/entrypoints/inertia.tsx +95 -0
  85. data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
  86. data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
  87. data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
  88. data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
  89. data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
  90. data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
  91. data/app/frontend/hooks/use-appearance.tsx +67 -0
  92. data/app/frontend/hooks/use-clipboard.ts +32 -0
  93. data/app/frontend/hooks/use-flash.tsx +23 -0
  94. data/app/frontend/hooks/use-hotkeys.ts +63 -0
  95. data/app/frontend/hooks/use-initials.tsx +15 -0
  96. data/app/frontend/hooks/use-live.ts +179 -0
  97. data/app/frontend/hooks/use-mobile-navigation.ts +8 -0
  98. data/app/frontend/hooks/use-mobile.ts +21 -0
  99. data/app/frontend/hooks/use-percentile.ts +24 -0
  100. data/app/frontend/hooks/use-reduced-motion.ts +25 -0
  101. data/app/frontend/hooks/use-row-nav.ts +106 -0
  102. data/app/frontend/hooks/use-step.ts +27 -0
  103. data/app/frontend/hooks/use-ticker.ts +12 -0
  104. data/app/frontend/hooks/use-window.ts +58 -0
  105. data/app/frontend/layouts/app/app-sidebar-layout.tsx +107 -0
  106. data/app/frontend/layouts/app-layout.tsx +21 -0
  107. data/app/frontend/layouts/env-layout.tsx +53 -0
  108. data/app/frontend/layouts/persistent-layout.tsx +18 -0
  109. data/app/frontend/lib/cable.ts +10 -0
  110. data/app/frontend/lib/env-pages.ts +236 -0
  111. data/app/frontend/lib/execution-path.ts +55 -0
  112. data/app/frontend/lib/explain.ts +50 -0
  113. data/app/frontend/lib/filter.ts +25 -0
  114. data/app/frontend/lib/flamegraph.ts +155 -0
  115. data/app/frontend/lib/format.ts +71 -0
  116. data/app/frontend/lib/keyboard.ts +18 -0
  117. data/app/frontend/lib/source-link.ts +217 -0
  118. data/app/frontend/lib/utils.ts +6 -0
  119. data/app/frontend/pages/alerts/index.tsx +171 -0
  120. data/app/frontend/pages/broadcasts/index.tsx +171 -0
  121. data/app/frontend/pages/cache_events/index.tsx +127 -0
  122. data/app/frontend/pages/commands/index.tsx +143 -0
  123. data/app/frontend/pages/deploys/index.tsx +94 -0
  124. data/app/frontend/pages/deploys/show.tsx +309 -0
  125. data/app/frontend/pages/deprecations/index.tsx +176 -0
  126. data/app/frontend/pages/exceptions/index.tsx +271 -0
  127. data/app/frontend/pages/executions/show.tsx +821 -0
  128. data/app/frontend/pages/issues/index.tsx +399 -0
  129. data/app/frontend/pages/issues/show.tsx +1367 -0
  130. data/app/frontend/pages/jobs/index.tsx +381 -0
  131. data/app/frontend/pages/jobs/klass.tsx +215 -0
  132. data/app/frontend/pages/llm_calls/index.tsx +664 -0
  133. data/app/frontend/pages/logs/index.tsx +303 -0
  134. data/app/frontend/pages/mails/index.tsx +218 -0
  135. data/app/frontend/pages/notifications/index.tsx +161 -0
  136. data/app/frontend/pages/outgoing_requests/index.tsx +309 -0
  137. data/app/frontend/pages/overview/show.tsx +333 -0
  138. data/app/frontend/pages/people/index.tsx +102 -0
  139. data/app/frontend/pages/people/show.tsx +149 -0
  140. data/app/frontend/pages/processes/index.tsx +335 -0
  141. data/app/frontend/pages/profiles/index.tsx +110 -0
  142. data/app/frontend/pages/profiles/show.tsx +105 -0
  143. data/app/frontend/pages/queries/index.tsx +492 -0
  144. data/app/frontend/pages/queries/show.tsx +276 -0
  145. data/app/frontend/pages/releases/index.tsx +190 -0
  146. data/app/frontend/pages/releases/release-health.tsx +68 -0
  147. data/app/frontend/pages/releases/release-path.ts +25 -0
  148. data/app/frontend/pages/releases/show.tsx +262 -0
  149. data/app/frontend/pages/requests/index.tsx +235 -0
  150. data/app/frontend/pages/requests/route.tsx +159 -0
  151. data/app/frontend/pages/scheduled_tasks/index.tsx +220 -0
  152. data/app/frontend/pages/spans/index.tsx +229 -0
  153. data/app/frontend/pages/spans/show.tsx +220 -0
  154. data/app/frontend/pages/storage_ops/index.tsx +150 -0
  155. data/app/frontend/pages/tenants/index.tsx +236 -0
  156. data/app/frontend/pages/tenants/show.tsx +477 -0
  157. data/app/frontend/pages/tenants/tenant-path.ts +26 -0
  158. data/app/frontend/pages/thresholds/index.tsx +414 -0
  159. data/app/frontend/pages/traces/show.tsx +208 -0
  160. data/app/frontend/pages/transactions/index.tsx +220 -0
  161. data/app/frontend/pages/view_renders/index.tsx +241 -0
  162. data/app/frontend/pages/visits/index.tsx +321 -0
  163. data/app/frontend/public/icon.png +0 -0
  164. data/app/frontend/public/icon.svg +5 -0
  165. data/app/frontend/routes/index.d.ts +2136 -0
  166. data/app/frontend/routes/index.js +2112 -0
  167. data/app/frontend/types/globals.d.ts +9 -0
  168. data/app/frontend/types/index.ts +288 -0
  169. data/app/frontend/types/vite-env.d.ts +9 -0
  170. data/lib/railwatch/version.rb +1 -1
  171. data/lib/railwatch.rb +8 -3
  172. data/public/railwatch/assets/app-layout-LvxN9xSS.js +41 -0
  173. data/public/railwatch/assets/application-Cc5v_Vk9.css +1 -0
  174. data/public/railwatch/assets/{arrow-up-CLQ-7heQ.js → arrow-up-DG01k9AL.js} +1 -1
  175. data/public/railwatch/assets/{braces-rSYydpLY.js → braces-C3w7pfxS.js} +1 -1
  176. data/public/railwatch/assets/{card-DPjFKfen.js → card-Dpwlr0uY.js} +1 -1
  177. data/public/railwatch/assets/{chart-DWh7l8yM.js → chart-DJHv3R7W.js} +1 -1
  178. data/public/railwatch/assets/{chart-hover-CfoZUY4J.js → chart-hover-CEH-DtXO.js} +1 -1
  179. data/public/railwatch/assets/{chart-panel-CC49WTQL.js → chart-panel-CUdIzJvk.js} +1 -1
  180. data/public/railwatch/assets/code-BRZprwun.js +1 -0
  181. data/public/railwatch/assets/copy-id-CwhGGiwF.js +1 -0
  182. data/public/railwatch/assets/cursor-load-more-uF1_955Q.js +1 -0
  183. data/public/railwatch/assets/data-table-DYi-N0hm.js +1 -0
  184. data/public/railwatch/assets/env-layout-HdcOaPPX.js +1 -0
  185. data/public/railwatch/assets/{execution-path-FAuIzOXB.js → execution-path-CElXXFO_.js} +1 -1
  186. data/public/railwatch/assets/filter-bar-DEv0G_34.js +1 -0
  187. data/public/railwatch/assets/{flamegraph-EaGkP2NT.js → flamegraph-SKD9yxW9.js} +2 -2
  188. data/public/railwatch/assets/frames-CUpyJQDy.js +1 -0
  189. data/public/railwatch/assets/index-2W-ppwap.js +1 -0
  190. data/public/railwatch/assets/index-74ocuU5d.js +1 -0
  191. data/public/railwatch/assets/{index-C8Cggnbw.js → index-B8NA62Lz.js} +2 -2
  192. data/public/railwatch/assets/index-BBCEkLqp.js +1 -0
  193. data/public/railwatch/assets/index-BDcglJzX.js +1 -0
  194. data/public/railwatch/assets/index-B_KFb9LS.js +1 -0
  195. data/public/railwatch/assets/index-BqK_VKom.js +1 -0
  196. data/public/railwatch/assets/index-BxCXBHP2.js +1 -0
  197. data/public/railwatch/assets/index-C1jc1OoH.js +1 -0
  198. data/public/railwatch/assets/index-C7zREldp.js +1 -0
  199. data/public/railwatch/assets/index-CEetm4oC.js +1 -0
  200. data/public/railwatch/assets/index-CIaZkokF.js +1 -0
  201. data/public/railwatch/assets/index-CNH85TZp.js +1 -0
  202. data/public/railwatch/assets/index-Ce4SB5AB.js +1 -0
  203. data/public/railwatch/assets/index-CiRMSYMn.js +1 -0
  204. data/public/railwatch/assets/index-CjxXgOs-.js +1 -0
  205. data/public/railwatch/assets/index-ClL-PYEV.js +1 -0
  206. data/public/railwatch/assets/index-CteZ6fop.js +1 -0
  207. data/public/railwatch/assets/index-CvqfEExO.js +1 -0
  208. data/public/railwatch/assets/index-CxTLT-Qh.js +1 -0
  209. data/public/railwatch/assets/index-DJd3OUSd.js +1 -0
  210. data/public/railwatch/assets/index-DPD-rLp6.js +1 -0
  211. data/public/railwatch/assets/index-DYK_dps4.js +1 -0
  212. data/public/railwatch/assets/index-DrayARrI.js +1 -0
  213. data/public/railwatch/assets/index-HRXGir-0.js +1 -0
  214. data/public/railwatch/assets/index-UJOUALr_.js +1 -0
  215. data/public/railwatch/assets/index-d-3J7gQ5.js +1 -0
  216. data/public/railwatch/assets/index-oQrvBpOm.js +1 -0
  217. data/public/railwatch/assets/inertia-FoqYZAk8.js +97 -0
  218. data/public/railwatch/assets/json-viewer-CxqMojEm.js +1 -0
  219. data/public/railwatch/assets/klass-DEF-Vat5.js +1 -0
  220. data/public/railwatch/assets/origin-identity-Y6gvfQVR.js +1 -0
  221. data/public/railwatch/assets/{percentile-picker-CeQgllxD.js → percentile-picker-E2aUdLxx.js} +1 -1
  222. data/public/railwatch/assets/relative-time-DcJbkKlN.js +1 -0
  223. data/public/railwatch/assets/{release-health-uP-GF3_V.js → release-health-C8WSCDKD.js} +1 -1
  224. data/public/railwatch/assets/route-qO5vX1p-.js +1 -0
  225. data/public/railwatch/assets/{segmented-BeIe4uqk.js → segmented-BWee8uc9.js} +1 -1
  226. data/public/railwatch/assets/select-G7rNBzv0.js +1 -0
  227. data/public/railwatch/assets/series-chart-C5nXIUc5.js +1 -0
  228. data/public/railwatch/assets/show-BEWisLQw.js +1 -0
  229. data/public/railwatch/assets/show-BEa9PGu1.js +2 -0
  230. data/public/railwatch/assets/{show-YsNpqbcI.js → show-BiT_Aloc.js} +3 -3
  231. data/public/railwatch/assets/show-BpCO49d3.js +1 -0
  232. data/public/railwatch/assets/show-CH7yxt7R.js +1 -0
  233. data/public/railwatch/assets/show-CTO8Lvd2.js +1 -0
  234. data/public/railwatch/assets/show-DnYsDMUt.js +2 -0
  235. data/public/railwatch/assets/show-DyzHM527.js +1 -0
  236. data/public/railwatch/assets/show-Nfpxs8su.js +1 -0
  237. data/public/railwatch/assets/show-ddjUMo9G.js +1 -0
  238. data/public/railwatch/assets/show-ksjbSzDH.js +1 -0
  239. data/public/railwatch/assets/{sort-header-mEJUM3Av.js → sort-header-uOq5VlrB.js} +1 -1
  240. data/public/railwatch/assets/{sparkline-cell-Dwg7awwh.js → sparkline-cell-DgGij0cu.js} +1 -1
  241. data/public/railwatch/assets/stat-BTzNKTm2.js +1 -0
  242. data/public/railwatch/assets/status-badge-BDkOXa-C.js +1 -0
  243. data/public/railwatch/assets/{tenant-path-CQoP-BeF.js → tenant-path-DU0ezKSU.js} +1 -1
  244. data/public/railwatch/assets/{use-clipboard-DNefo-ky.js → use-clipboard-DoENmlWY.js} +1 -1
  245. data/public/railwatch/assets/{use-live-DMTuhKfB.js → use-live-CE_fN5_6.js} +1 -1
  246. data/public/railwatch/manifest.json +612 -1591
  247. metadata +242 -117
  248. data/public/railwatch/assets/app-layout-Zc0v-hYh.js +0 -1
  249. data/public/railwatch/assets/app-wordmark-BA_60AVb.js +0 -1
  250. data/public/railwatch/assets/appearance-CcfP9tZ7.js +0 -1
  251. data/public/railwatch/assets/application-BrN3Sz94.css +0 -1
  252. data/public/railwatch/assets/auth-layout-C05gEBIQ.js +0 -1
  253. data/public/railwatch/assets/badge-DRae8XwK.js +0 -1
  254. data/public/railwatch/assets/checkbox-DPkLUiwM.js +0 -1
  255. data/public/railwatch/assets/code-CLmYS6FU.js +0 -1
  256. data/public/railwatch/assets/copy-DaQMJWoT.js +0 -1
  257. data/public/railwatch/assets/copy-block-CGcXxp8J.js +0 -1
  258. data/public/railwatch/assets/copy-id-vBYHQwxg.js +0 -1
  259. data/public/railwatch/assets/cursor-load-more-Ddwxe5dZ.js +0 -1
  260. data/public/railwatch/assets/data-table-CzKTEE-O.js +0 -1
  261. data/public/railwatch/assets/edit-B1kmWkzd.js +0 -8
  262. data/public/railwatch/assets/edit-D9cx4pbG.js +0 -1
  263. data/public/railwatch/assets/edit-yv6j9p-V.js +0 -1
  264. data/public/railwatch/assets/empty-state-CW4wclK_.js +0 -1
  265. data/public/railwatch/assets/env-layout-Kz7wks1x.js +0 -1
  266. data/public/railwatch/assets/filter-bar-CJDjWFib.js +0 -1
  267. data/public/railwatch/assets/frames-zZuIaNH9.js +0 -1
  268. data/public/railwatch/assets/google-sign-in-button-2_zgbgVy.js +0 -1
  269. data/public/railwatch/assets/index-B49SWz7K.js +0 -1
  270. data/public/railwatch/assets/index-BHlY4wKe.js +0 -1
  271. data/public/railwatch/assets/index-BZpPtyFY.js +0 -1
  272. data/public/railwatch/assets/index-BeVK6jCL.js +0 -1
  273. data/public/railwatch/assets/index-BfbSo01U.js +0 -1
  274. data/public/railwatch/assets/index-BfgncAv6.js +0 -1
  275. data/public/railwatch/assets/index-BhNszK1k.js +0 -1
  276. data/public/railwatch/assets/index-Bu01uWvw.js +0 -1
  277. data/public/railwatch/assets/index-C1s_hK3p.js +0 -1
  278. data/public/railwatch/assets/index-CBip6V4z.js +0 -1
  279. data/public/railwatch/assets/index-CMJGss5R.js +0 -1
  280. data/public/railwatch/assets/index-CRo3yK20.js +0 -1
  281. data/public/railwatch/assets/index-CWB_p2J8.js +0 -1
  282. data/public/railwatch/assets/index-CWHpndGc.js +0 -1
  283. data/public/railwatch/assets/index-Ca_S4Sc3.js +0 -1
  284. data/public/railwatch/assets/index-CanPDDOa.js +0 -1
  285. data/public/railwatch/assets/index-Cie90Yat.js +0 -1
  286. data/public/railwatch/assets/index-CiepQ_pR.js +0 -1
  287. data/public/railwatch/assets/index-Cm1uCIGN.js +0 -1
  288. data/public/railwatch/assets/index-CzitnnSC.js +0 -1
  289. data/public/railwatch/assets/index-DEUfClv3.js +0 -1
  290. data/public/railwatch/assets/index-DJKwo-mI.js +0 -1
  291. data/public/railwatch/assets/index-DK6y0YHp.js +0 -1
  292. data/public/railwatch/assets/index-DMNPpLH9.js +0 -1
  293. data/public/railwatch/assets/index-DZO1mSfX.js +0 -1
  294. data/public/railwatch/assets/index-Db5wj3M5.js +0 -1
  295. data/public/railwatch/assets/index-Dcy5WktB.js +0 -1
  296. data/public/railwatch/assets/index-DvG0-7Lx.js +0 -1
  297. data/public/railwatch/assets/index-Dvj1wuka.js +0 -1
  298. data/public/railwatch/assets/index-KyZX46qX.js +0 -1
  299. data/public/railwatch/assets/index-Ze-KP-sl.js +0 -1
  300. data/public/railwatch/assets/index-mhRbWLBM.js +0 -1
  301. data/public/railwatch/assets/index-x099JL5f.js +0 -1
  302. data/public/railwatch/assets/index-x28zb_nf.js +0 -1
  303. data/public/railwatch/assets/inertia-Cuyz2ZHO.js +0 -97
  304. data/public/railwatch/assets/input-error-hog6gGxg.js +0 -1
  305. data/public/railwatch/assets/json-viewer-DH2W9HXf.js +0 -1
  306. data/public/railwatch/assets/klass-DHbDelLk.js +0 -1
  307. data/public/railwatch/assets/label-DdCBgiUn.js +0 -1
  308. data/public/railwatch/assets/layout-Cueyl7c5.js +0 -1
  309. data/public/railwatch/assets/live-dot-BZgYTYdt.js +0 -41
  310. data/public/railwatch/assets/nav-BSSGObDZ.js +0 -1
  311. data/public/railwatch/assets/new-B8FSb8Bl.js +0 -1
  312. data/public/railwatch/assets/new-C39_v2Ll.js +0 -1
  313. data/public/railwatch/assets/new-DVOPwaC5.js +0 -1
  314. data/public/railwatch/assets/new-Dd40nvJR.js +0 -1
  315. data/public/railwatch/assets/new-SxnYe1SE.js +0 -1
  316. data/public/railwatch/assets/new-eP5vKD3Y.js +0 -1
  317. data/public/railwatch/assets/onboarding-CUpZl5KB.js +0 -1
  318. data/public/railwatch/assets/origin-identity-BYt2uiuo.js +0 -1
  319. data/public/railwatch/assets/relative-time-D5UbF4oO.js +0 -1
  320. data/public/railwatch/assets/route-DQAY8JEr.js +0 -1
  321. data/public/railwatch/assets/select-2R16457Z.js +0 -1
  322. data/public/railwatch/assets/separator-D5I0UCB5.js +0 -1
  323. data/public/railwatch/assets/series-chart-gnrzhmM6.js +0 -1
  324. data/public/railwatch/assets/show-2BkeNRUC.js +0 -2
  325. data/public/railwatch/assets/show-B0X1hRQH.js +0 -1
  326. data/public/railwatch/assets/show-BKUV5l5q.js +0 -1
  327. data/public/railwatch/assets/show-BQJD_CsF.js +0 -6
  328. data/public/railwatch/assets/show-BhmD6Sxp.js +0 -1
  329. data/public/railwatch/assets/show-C3KcnVo2.js +0 -1
  330. data/public/railwatch/assets/show-C95cHd06.js +0 -2
  331. data/public/railwatch/assets/show-CdB4uVQS.js +0 -1
  332. data/public/railwatch/assets/show-CoCqcVIp.js +0 -1
  333. data/public/railwatch/assets/show-D2LqjEsU.js +0 -1
  334. data/public/railwatch/assets/show-DNpnKyTh.js +0 -1
  335. data/public/railwatch/assets/show-DOlTxbig.js +0 -1
  336. data/public/railwatch/assets/show-DQCkttL8.js +0 -1
  337. data/public/railwatch/assets/show-DcxesipC.js +0 -1
  338. data/public/railwatch/assets/show-IGJoc_X0.js +0 -1
  339. data/public/railwatch/assets/show-qNV6H8SH.js +0 -2
  340. data/public/railwatch/assets/stat-ZtxGU8lE.js +0 -1
  341. data/public/railwatch/assets/status-badge-CH5P-Xkj.js +0 -1
  342. data/public/railwatch/assets/text-link-DHJ8BXx5.js +0 -1
  343. data/public/railwatch/assets/textarea-BeQtQyl5.js +0 -1
  344. data/public/railwatch/assets/timeline-CaKQVu48.js +0 -1
  345. data/public/railwatch/assets/transition-ksDpqhKJ.js +0 -5
@@ -0,0 +1,57 @@
1
+ import { usePage } from "@inertiajs/react"
2
+ import { ExternalLink } from "lucide-react"
3
+
4
+ import { sourceUrl } from "@/lib/source-link"
5
+ import { cn } from "@/lib/utils"
6
+ import type { SharedProps } from "@/types"
7
+
8
+ // A "file:line" location, linked to the line on the code host (pinned to the
9
+ // deploy that produced it) or opened in the viewer's editor, depending on
10
+ // their profile. Renders plain text when we can't build a URL.
11
+ export function SourceLink({
12
+ location,
13
+ deploy,
14
+ className,
15
+ }: {
16
+ location: string
17
+ // The deploy the location came from, used as the git ref so the link points
18
+ // at the code that was actually running. (Named `deploy`, not `ref`: React
19
+ // treats a `ref` prop as an element ref.)
20
+ deploy?: string | null
21
+ className?: string
22
+ }) {
23
+ const { auth, environment } = usePage<SharedProps>().props
24
+ const match = /^(.*):(\d+)$/.exec(location)
25
+ const url = match
26
+ ? sourceUrl({
27
+ file: match[1],
28
+ line: Number(match[2]),
29
+ repositoryUrl: environment?.repository_url,
30
+ ref: deploy,
31
+ defaultBranch: environment?.default_branch,
32
+ editor: auth.user.editor,
33
+ editorRoot: auth.user.editor_root,
34
+ })
35
+ : null
36
+
37
+ if (!url)
38
+ return (
39
+ <span className={cn("truncate font-mono text-xs", className)}>
40
+ {location}
41
+ </span>
42
+ )
43
+ const external = url.startsWith("http")
44
+ return (
45
+ <a
46
+ href={url}
47
+ {...(external ? { target: "_blank", rel: "noreferrer" } : {})}
48
+ className={cn(
49
+ "inline-flex min-w-0 items-center gap-1 font-mono text-xs hover:underline",
50
+ className,
51
+ )}
52
+ >
53
+ <span className="truncate">{location}</span>
54
+ <ExternalLink className="size-3 shrink-0 opacity-60" />
55
+ </a>
56
+ )
57
+ }
@@ -0,0 +1,14 @@
1
+ import { Sparkline } from "@/components/railwatch/series-chart"
2
+
3
+ // Compact trend cell for a DataTable row: a 32x10 area chart, no axes.
4
+ // `hoverKey` (typically the row's group_hash) wires it into
5
+ // ChartHoverProvider so hovering the row emphasizes its own sparkline.
6
+ export function SparklineCell({
7
+ data,
8
+ hoverKey,
9
+ }: {
10
+ data: number[]
11
+ hoverKey?: string
12
+ }) {
13
+ return <Sparkline data={data} className="h-[10px] w-8" hoverKey={hoverKey} />
14
+ }
@@ -0,0 +1,116 @@
1
+ import { cn } from "@/lib/utils"
2
+
3
+ const KEYWORDS = new Set([
4
+ "SELECT",
5
+ "FROM",
6
+ "WHERE",
7
+ "JOIN",
8
+ "LEFT",
9
+ "INNER",
10
+ "ON",
11
+ "AND",
12
+ "OR",
13
+ "ORDER",
14
+ "BY",
15
+ "GROUP",
16
+ "LIMIT",
17
+ "OFFSET",
18
+ "INSERT",
19
+ "INTO",
20
+ "VALUES",
21
+ "UPDATE",
22
+ "SET",
23
+ "DELETE",
24
+ "RETURNING",
25
+ "AS",
26
+ "IN",
27
+ "IS",
28
+ "NULL",
29
+ "NOT",
30
+ "DISTINCT",
31
+ "COUNT",
32
+ "SUM",
33
+ "AVG",
34
+ "MAX",
35
+ "MIN",
36
+ "CASE",
37
+ "WHEN",
38
+ "THEN",
39
+ "ELSE",
40
+ "END",
41
+ ])
42
+
43
+ type TokenKind =
44
+ "identifier" | "string" | "placeholder" | "number" | "keyword" | "text"
45
+
46
+ interface Token {
47
+ text: string
48
+ kind: TokenKind
49
+ }
50
+
51
+ // Quoted identifier | string literal | `?` bind placeholder | number | bare word.
52
+ // Anything not matched (whitespace, punctuation) falls through as plain text.
53
+ const TOKEN_RE =
54
+ /("(?:[^"]|"")*"|`(?:[^`]|``)*`)|('(?:[^']|'')*')|(\?)|(\b\d+(?:\.\d+)?\b)|(\b[A-Za-z_][A-Za-z_0-9]*\b)/g
55
+
56
+ function tokenize(sql: string): Token[] {
57
+ const tokens: Token[] = []
58
+ let last = 0
59
+ TOKEN_RE.lastIndex = 0
60
+ let m: RegExpExecArray | null
61
+ while ((m = TOKEN_RE.exec(sql))) {
62
+ if (m.index > last)
63
+ tokens.push({ text: sql.slice(last, m.index), kind: "text" })
64
+ const [full, ident, str, placeholder, number] = m
65
+ if (ident) tokens.push({ text: full, kind: "identifier" })
66
+ else if (str) tokens.push({ text: full, kind: "string" })
67
+ else if (placeholder) tokens.push({ text: full, kind: "placeholder" })
68
+ else if (number) tokens.push({ text: full, kind: "number" })
69
+ else if (KEYWORDS.has(full.toUpperCase()))
70
+ tokens.push({ text: full, kind: "keyword" })
71
+ else tokens.push({ text: full, kind: "text" })
72
+ last = TOKEN_RE.lastIndex
73
+ }
74
+ if (last < sql.length) tokens.push({ text: sql.slice(last), kind: "text" })
75
+ return tokens
76
+ }
77
+
78
+ const toneClass: Record<TokenKind, string> = {
79
+ keyword: "text-primary font-semibold",
80
+ string: "text-emerald-600 dark:text-emerald-400",
81
+ number: "text-amber-600 dark:text-amber-400",
82
+ placeholder: "text-primary font-semibold",
83
+ identifier: "text-muted-foreground",
84
+ text: "",
85
+ }
86
+
87
+ export function SqlBlock({
88
+ sql,
89
+ wrap = false,
90
+ inline = false,
91
+ className,
92
+ }: {
93
+ sql: string
94
+ wrap?: boolean
95
+ inline?: boolean
96
+ className?: string
97
+ }) {
98
+ const tokens = tokenize(sql)
99
+ const Tag = inline ? "span" : "code"
100
+ return (
101
+ <Tag
102
+ className={cn(
103
+ "font-mono text-xs",
104
+ wrap ? "break-words whitespace-pre-wrap" : "block max-w-full truncate",
105
+ className,
106
+ )}
107
+ title={wrap ? undefined : sql}
108
+ >
109
+ {tokens.map((t, i) => (
110
+ <span key={i} className={toneClass[t.kind] || undefined}>
111
+ {t.text}
112
+ </span>
113
+ ))}
114
+ </Tag>
115
+ )
116
+ }
@@ -0,0 +1,173 @@
1
+ import { ArrowDown, ArrowUp } from "lucide-react"
2
+ import type { ReactNode } from "react"
3
+
4
+ import {
5
+ DIGIT_ROLL_ENABLED,
6
+ DigitRoll,
7
+ } from "@/components/railwatch/digit-roll"
8
+ import { useIsLive } from "@/hooks/use-live"
9
+ import { useReducedMotion } from "@/hooks/use-reduced-motion"
10
+ import { cn } from "@/lib/utils"
11
+
12
+ function Delta({
13
+ current,
14
+ previous,
15
+ goodDirection,
16
+ caption,
17
+ }: {
18
+ current: number
19
+ previous: number
20
+ goodDirection: "up" | "down"
21
+ caption?: ReactNode
22
+ }) {
23
+ if (!previous) return null
24
+ const change = ((current - previous) / previous) * 100
25
+ if (change === 0) return null
26
+ const direction = change > 0 ? "up" : "down"
27
+ const good = direction === goodDirection
28
+ const Icon = direction === "up" ? ArrowUp : ArrowDown
29
+ return (
30
+ <>
31
+ <span
32
+ className={cn(
33
+ "inline-flex items-center gap-0.5 font-mono text-xs tabular-nums",
34
+ good ? "text-live" : "text-destructive",
35
+ )}
36
+ >
37
+ <Icon className="size-3" />
38
+ {Math.abs(change).toFixed(1)}%
39
+ </span>
40
+ {caption && (
41
+ <span className="text-muted-foreground text-[11px]">{caption}</span>
42
+ )}
43
+ </>
44
+ )
45
+ }
46
+
47
+ // A metric a page is happy to see move: the raw number, and the formatter
48
+ // that turns it into what the cell reads. Passing this instead of `value` is
49
+ // how a page opts into the digit roll, which is deliberately the wrong way
50
+ // round from a prop that opts out -- a page that says nothing gets no
51
+ // motion, so a new incident page (issues, executions, traces) cannot inherit
52
+ // it by accident.
53
+ export interface RollingValue {
54
+ value: number
55
+ format: (value: number) => string
56
+ }
57
+
58
+ // The rolling digits are decoration, so they are hidden from assistive tech
59
+ // and the number itself sits beside them in one plain text node. Not an
60
+ // aria-live region: a value that changes every few seconds read aloud all
61
+ // day is not an improvement, and fast live regions misbehave across screen
62
+ // readers anyway. A metric that genuinely needs announcing wants its own
63
+ // debounced role="status" saying what happened, not the number.
64
+ //
65
+ // Four gates stand between a rolling number and motion on the screen, and
66
+ // all of them have to be open: the global switch, the page having opted in
67
+ // at all (this component only renders for `roll`), live updates actually
68
+ // flowing, and the user not having asked for less motion. Live off, paused,
69
+ // disconnected, or reduced motion each mean an instant swap -- which is
70
+ // also WCAG 2.2.2's pause/stop mechanism: the live toggle in the page
71
+ // header stops the motion, not just the data.
72
+ function RollingStatValue({ value, format }: RollingValue) {
73
+ const live = useIsLive()
74
+ const reducedMotion = useReducedMotion()
75
+ const formatted = format(value)
76
+ const animate = DIGIT_ROLL_ENABLED && live && !reducedMotion
77
+ return (
78
+ <>
79
+ <span aria-hidden="true">
80
+ {animate ? <DigitRoll value={value} format={format} /> : formatted}
81
+ </span>
82
+ <span className="sr-only">{formatted}</span>
83
+ </>
84
+ )
85
+ }
86
+
87
+ // One metric. Nightwatch-style: a small uppercase mono label, a large
88
+ // tabular number, and an optional sub-line. Renders as a cell, not a card,
89
+ // so a row of them reads as one strip (see StatStrip); pass `card` to get
90
+ // the standalone bordered version.
91
+ export function Stat({
92
+ label,
93
+ value,
94
+ roll,
95
+ hint,
96
+ tone,
97
+ delta,
98
+ deltaCaption,
99
+ card = false,
100
+ className,
101
+ }: {
102
+ label: string
103
+ hint?: ReactNode
104
+ tone?: "default" | "destructive" | "warning" | "success"
105
+ delta?: {
106
+ current: number
107
+ previous: number
108
+ goodDirection: "up" | "down"
109
+ }
110
+ // e.g. "vs previous 24 hours" — shown next to the delta so it's clear what
111
+ // period the percentage compares against.
112
+ deltaCaption?: ReactNode
113
+ card?: boolean
114
+ className?: string
115
+ // A pre-formatted value, or a `roll` the digits of which may animate --
116
+ // one or the other, never both.
117
+ } & (
118
+ { value: ReactNode; roll?: never } | { value?: never; roll: RollingValue }
119
+ )) {
120
+ return (
121
+ <div
122
+ className={cn(
123
+ "flex min-w-0 flex-col gap-1 px-4 py-3",
124
+ card && "bg-card rounded-lg border",
125
+ className,
126
+ )}
127
+ >
128
+ <div className="label-caps truncate">{label}</div>
129
+ <div className="flex flex-wrap items-baseline gap-x-2 gap-y-0.5">
130
+ <div
131
+ className={cn(
132
+ "text-xl font-semibold tracking-tight tabular-nums sm:text-2xl",
133
+ tone === "destructive" && "text-destructive",
134
+ tone === "warning" && "text-warning",
135
+ tone === "success" && "text-live",
136
+ )}
137
+ >
138
+ {roll ? <RollingStatValue {...roll} /> : value}
139
+ </div>
140
+ {delta && <Delta {...delta} caption={deltaCaption} />}
141
+ </div>
142
+ {hint && (
143
+ <div className="text-muted-foreground truncate font-mono text-[11px]">
144
+ {hint}
145
+ </div>
146
+ )}
147
+ </div>
148
+ )
149
+ }
150
+
151
+ // A row of Stats sharing one border, divided by hairlines, that wraps to
152
+ // two columns on phones. The header strip on every Nightwatch page.
153
+ export function StatStrip({
154
+ children,
155
+ className,
156
+ }: {
157
+ children: ReactNode
158
+ className?: string
159
+ }) {
160
+ return (
161
+ <div
162
+ className={cn(
163
+ "bg-card divide-border grid grid-cols-2 divide-y overflow-hidden rounded-lg border sm:divide-x sm:divide-y-0",
164
+ "[&>*:nth-child(odd)]:border-r sm:[&>*:nth-child(odd)]:border-r-0",
165
+ "[&>*:nth-child(odd):last-child]:col-span-2 [&>*:nth-child(odd):last-child]:border-r-0 sm:[&>*:nth-child(odd):last-child]:col-span-1",
166
+ "sm:auto-cols-fr sm:grid-flow-col",
167
+ className,
168
+ )}
169
+ >
170
+ {children}
171
+ </div>
172
+ )
173
+ }
@@ -0,0 +1,79 @@
1
+ import { Badge } from "@/components/ui/badge"
2
+ import { statusTone } from "@/lib/format"
3
+ import { cn } from "@/lib/utils"
4
+
5
+ // The one status vocabulary: grey is fine, amber is a warning (4xx, elevated),
6
+ // red is broken (5xx, failed, unhandled, open). Small mono caps like
7
+ // Nightwatch's [ALERT] / HANDLED / UNHANDLED chips.
8
+ const tones: Record<string, string> = {
9
+ success: "border-live/30 bg-live/10 text-live",
10
+ warning: "border-warning/40 bg-warning/10 text-warning",
11
+ destructive: "border-danger/40 bg-danger/10 text-danger",
12
+ muted: "border-border bg-muted text-muted-foreground",
13
+ }
14
+
15
+ const base = "rounded-sm px-1.5 font-mono text-[10px] tracking-wider uppercase"
16
+
17
+ export function StatusBadge({
18
+ status,
19
+ outcome,
20
+ label,
21
+ }: {
22
+ status?: number | null
23
+ outcome?: string | null
24
+ label?: string
25
+ }) {
26
+ const tone = statusTone(status, outcome)
27
+ return (
28
+ <Badge variant="outline" className={cn(base, "tabular-nums", tones[tone])}>
29
+ {label ?? outcome ?? status ?? "–"}
30
+ </Badge>
31
+ )
32
+ }
33
+
34
+ export function LevelBadge({ level }: { level: string }) {
35
+ const tone =
36
+ level === "error" || level === "fatal"
37
+ ? "destructive"
38
+ : level === "warn"
39
+ ? "warning"
40
+ : level === "event"
41
+ ? "success"
42
+ : "muted"
43
+ return (
44
+ <Badge variant="outline" className={cn(base, tones[tone])}>
45
+ {level}
46
+ </Badge>
47
+ )
48
+ }
49
+
50
+ export function IssueStatusBadge({ status }: { status: string }) {
51
+ const tone =
52
+ status === "open"
53
+ ? "destructive"
54
+ : status === "resolved"
55
+ ? "success"
56
+ : "muted"
57
+ return (
58
+ <Badge variant="outline" className={cn(base, tones[tone])}>
59
+ {status}
60
+ </Badge>
61
+ )
62
+ }
63
+
64
+ // Generic tinted chip for kinds and categories (EXCEPTION, REQUEST, JOB…).
65
+ export function KindBadge({
66
+ children,
67
+ tone = "muted",
68
+ className,
69
+ }: {
70
+ children: React.ReactNode
71
+ tone?: keyof typeof tones
72
+ className?: string
73
+ }) {
74
+ return (
75
+ <Badge variant="outline" className={cn(base, tones[tone], className)}>
76
+ {children}
77
+ </Badge>
78
+ )
79
+ }
@@ -0,0 +1,57 @@
1
+ import { ChevronDown } from "lucide-react"
2
+
3
+ import { Button } from "@/components/ui/button"
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuContent,
7
+ DropdownMenuLabel,
8
+ DropdownMenuRadioGroup,
9
+ DropdownMenuRadioItem,
10
+ DropdownMenuSeparator,
11
+ DropdownMenuTrigger,
12
+ } from "@/components/ui/dropdown-menu"
13
+ import { STEP_LABELS, useStep } from "@/hooks/use-step"
14
+ import type { Step } from "@/types"
15
+
16
+ // Bucket width for every chart on the page, next to the window picker:
17
+ // "1m", "5m", ... as a mono chip opening a menu of the widths the window
18
+ // offers. Hidden when the window offers only one.
19
+ export function StepPicker() {
20
+ const { step, steps, set } = useStep()
21
+ if (steps.length < 2) return null
22
+ return (
23
+ <DropdownMenu>
24
+ <DropdownMenuTrigger asChild>
25
+ <Button
26
+ variant="outline"
27
+ size="sm"
28
+ className="h-8 gap-1 px-2 font-mono text-xs uppercase"
29
+ aria-label={`Chart buckets: ${STEP_LABELS[step]}`}
30
+ >
31
+ <span className="text-muted-foreground normal-case">per</span>
32
+ {step}
33
+ <ChevronDown className="text-muted-foreground size-3" />
34
+ </Button>
35
+ </DropdownMenuTrigger>
36
+ <DropdownMenuContent align="end" className="w-44">
37
+ <DropdownMenuLabel className="label-caps">
38
+ Chart buckets
39
+ </DropdownMenuLabel>
40
+ <DropdownMenuSeparator />
41
+ <DropdownMenuRadioGroup
42
+ value={step}
43
+ onValueChange={(value) => set(value as Step)}
44
+ >
45
+ {steps.map((s) => (
46
+ <DropdownMenuRadioItem key={s} value={s} className="text-xs">
47
+ <span className="font-mono uppercase">{s}</span>
48
+ <span className="text-muted-foreground ml-auto">
49
+ {STEP_LABELS[s]}
50
+ </span>
51
+ </DropdownMenuRadioItem>
52
+ ))}
53
+ </DropdownMenuRadioGroup>
54
+ </DropdownMenuContent>
55
+ </DropdownMenu>
56
+ )
57
+ }