railwatch 0.9.4 → 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 (452) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +37 -33
  3. data/CHANGELOG.md +50 -0
  4. data/README.md +51 -60
  5. data/config/routes.rb +0 -59
  6. data/docs/ai-and-mcp.md +9 -8
  7. data/docs/configuration.md +35 -66
  8. data/docs/faq.md +17 -25
  9. data/docs/getting-started.md +105 -117
  10. data/docs/replacing-nightwatch.md +8 -7
  11. data/docs/replacing-sentry.md +12 -16
  12. data/docs/security.md +14 -20
  13. data/docs/self-hosting.md +24 -22
  14. data/docs/testing.md +6 -14
  15. data/docs/troubleshooting.md +12 -46
  16. data/lib/generators/railwatch/install/install_generator.rb +12 -240
  17. data/lib/generators/railwatch/install/templates/initializer.rb.tt +10 -40
  18. data/lib/generators/railwatch/install/templates/post-deploy +0 -7
  19. data/lib/railwatch/authentication.rb +6 -5
  20. data/lib/railwatch/configuration.rb +21 -151
  21. data/lib/railwatch/engine.rb +9 -118
  22. data/lib/railwatch/patches.rb +0 -11
  23. data/lib/railwatch/reporter.rb +3 -19
  24. data/lib/railwatch/subscribers/process_info.rb +1 -2
  25. data/lib/railwatch/version.rb +1 -1
  26. data/lib/railwatch.rb +4 -62
  27. data/lib/tasks/railwatch_tasks.rake +3 -325
  28. data/llms.txt +23 -25
  29. metadata +3 -453
  30. data/app/channels/railwatch/environment_channel.rb +0 -62
  31. data/app/controllers/concerns/railwatch/telemetry_identity.rb +0 -25
  32. data/app/controllers/railwatch/alerts_controller.rb +0 -48
  33. data/app/controllers/railwatch/anomaly_rules_controller.rb +0 -31
  34. data/app/controllers/railwatch/attachments_controller.rb +0 -22
  35. data/app/controllers/railwatch/broadcasts_controller.rb +0 -15
  36. data/app/controllers/railwatch/cache_events_controller.rb +0 -30
  37. data/app/controllers/railwatch/commands_controller.rb +0 -16
  38. data/app/controllers/railwatch/comments_controller.rb +0 -11
  39. data/app/controllers/railwatch/dashboard_controller.rb +0 -70
  40. data/app/controllers/railwatch/deploys_controller.rb +0 -34
  41. data/app/controllers/railwatch/deprecations_controller.rb +0 -54
  42. data/app/controllers/railwatch/environment_scoped.rb +0 -81
  43. data/app/controllers/railwatch/exceptions_controller.rb +0 -52
  44. data/app/controllers/railwatch/executions_controller.rb +0 -13
  45. data/app/controllers/railwatch/issues_controller.rb +0 -258
  46. data/app/controllers/railwatch/jobs_controller.rb +0 -63
  47. data/app/controllers/railwatch/llm_calls_controller.rb +0 -124
  48. data/app/controllers/railwatch/logs_controller.rb +0 -40
  49. data/app/controllers/railwatch/mails_controller.rb +0 -19
  50. data/app/controllers/railwatch/notifications_controller.rb +0 -11
  51. data/app/controllers/railwatch/outgoing_requests_controller.rb +0 -19
  52. data/app/controllers/railwatch/overview_controller.rb +0 -38
  53. data/app/controllers/railwatch/people_controller.rb +0 -25
  54. data/app/controllers/railwatch/processes_controller.rb +0 -33
  55. data/app/controllers/railwatch/profiles_controller.rb +0 -65
  56. data/app/controllers/railwatch/queries_controller.rb +0 -59
  57. data/app/controllers/railwatch/releases_controller.rb +0 -75
  58. data/app/controllers/railwatch/requests_controller.rb +0 -37
  59. data/app/controllers/railwatch/saved_views_controller.rb +0 -53
  60. data/app/controllers/railwatch/scheduled_tasks_controller.rb +0 -45
  61. data/app/controllers/railwatch/spans_controller.rb +0 -47
  62. data/app/controllers/railwatch/storage_ops_controller.rb +0 -15
  63. data/app/controllers/railwatch/tenants_controller.rb +0 -44
  64. data/app/controllers/railwatch/thresholds_controller.rb +0 -47
  65. data/app/controllers/railwatch/traces_controller.rb +0 -72
  66. data/app/controllers/railwatch/transactions_controller.rb +0 -21
  67. data/app/controllers/railwatch/view_renders_controller.rb +0 -28
  68. data/app/controllers/railwatch/visits_controller.rb +0 -53
  69. data/app/frontend/components/app-content.tsx +0 -26
  70. data/app/frontend/components/app-logo-icon.tsx +0 -23
  71. data/app/frontend/components/app-logo.tsx +0 -14
  72. data/app/frontend/components/app-shell.tsx +0 -38
  73. data/app/frontend/components/app-sidebar-header.tsx +0 -54
  74. data/app/frontend/components/app-sidebar.tsx +0 -94
  75. data/app/frontend/components/app-wordmark.tsx +0 -29
  76. data/app/frontend/components/breadcrumbs.tsx +0 -46
  77. data/app/frontend/components/icon.tsx +0 -16
  78. data/app/frontend/components/input-error.tsx +0 -18
  79. data/app/frontend/components/nav-footer.tsx +0 -46
  80. data/app/frontend/components/nav-main.tsx +0 -41
  81. data/app/frontend/components/nav-user.tsx +0 -50
  82. data/app/frontend/components/railwatch/chart-hover.tsx +0 -34
  83. data/app/frontend/components/railwatch/chart-panel.tsx +0 -170
  84. data/app/frontend/components/railwatch/code.tsx +0 -28
  85. data/app/frontend/components/railwatch/command-palette.tsx +0 -132
  86. data/app/frontend/components/railwatch/copy-block.tsx +0 -52
  87. data/app/frontend/components/railwatch/copy-id.tsx +0 -49
  88. data/app/frontend/components/railwatch/cursor-load-more.tsx +0 -39
  89. data/app/frontend/components/railwatch/data-table.tsx +0 -195
  90. data/app/frontend/components/railwatch/digit-roll.tsx +0 -312
  91. data/app/frontend/components/railwatch/empty-state.tsx +0 -159
  92. data/app/frontend/components/railwatch/env-nav.tsx +0 -130
  93. data/app/frontend/components/railwatch/env-switcher.tsx +0 -145
  94. data/app/frontend/components/railwatch/filter-bar.tsx +0 -153
  95. data/app/frontend/components/railwatch/flamegraph.tsx +0 -324
  96. data/app/frontend/components/railwatch/frames.tsx +0 -295
  97. data/app/frontend/components/railwatch/json-viewer.tsx +0 -159
  98. data/app/frontend/components/railwatch/live-dot.tsx +0 -78
  99. data/app/frontend/components/railwatch/live-toggle.tsx +0 -76
  100. data/app/frontend/components/railwatch/metric-chart.tsx +0 -112
  101. data/app/frontend/components/railwatch/mobile-page-switcher.tsx +0 -89
  102. data/app/frontend/components/railwatch/onboarding.tsx +0 -91
  103. data/app/frontend/components/railwatch/origin-identity.tsx +0 -84
  104. data/app/frontend/components/railwatch/page-header.tsx +0 -42
  105. data/app/frontend/components/railwatch/percentile-picker.tsx +0 -18
  106. data/app/frontend/components/railwatch/relative-time.tsx +0 -18
  107. data/app/frontend/components/railwatch/saved-views.tsx +0 -232
  108. data/app/frontend/components/railwatch/segmented.tsx +0 -64
  109. data/app/frontend/components/railwatch/series-chart.tsx +0 -400
  110. data/app/frontend/components/railwatch/shortcuts-dialog.tsx +0 -89
  111. data/app/frontend/components/railwatch/signal-toast.tsx +0 -121
  112. data/app/frontend/components/railwatch/sort-header.tsx +0 -37
  113. data/app/frontend/components/railwatch/source-link.tsx +0 -57
  114. data/app/frontend/components/railwatch/sparkline-cell.tsx +0 -14
  115. data/app/frontend/components/railwatch/sql-block.tsx +0 -116
  116. data/app/frontend/components/railwatch/stat.tsx +0 -185
  117. data/app/frontend/components/railwatch/status-badge.tsx +0 -79
  118. data/app/frontend/components/railwatch/step-picker.tsx +0 -57
  119. data/app/frontend/components/railwatch/timeline.tsx +0 -405
  120. data/app/frontend/components/railwatch/utilisation-bar.tsx +0 -44
  121. data/app/frontend/components/railwatch/window-picker.tsx +0 -147
  122. data/app/frontend/components/ui/avatar.tsx +0 -51
  123. data/app/frontend/components/ui/badge.tsx +0 -46
  124. data/app/frontend/components/ui/breadcrumb.tsx +0 -109
  125. data/app/frontend/components/ui/button.tsx +0 -60
  126. data/app/frontend/components/ui/card.tsx +0 -92
  127. data/app/frontend/components/ui/chart.tsx +0 -374
  128. data/app/frontend/components/ui/checkbox.tsx +0 -32
  129. data/app/frontend/components/ui/collapsible.tsx +0 -31
  130. data/app/frontend/components/ui/command.tsx +0 -182
  131. data/app/frontend/components/ui/dialog.tsx +0 -158
  132. data/app/frontend/components/ui/dropdown-menu.tsx +0 -255
  133. data/app/frontend/components/ui/hover-card.tsx +0 -42
  134. data/app/frontend/components/ui/input.tsx +0 -21
  135. data/app/frontend/components/ui/label.tsx +0 -24
  136. data/app/frontend/components/ui/popover.tsx +0 -87
  137. data/app/frontend/components/ui/select.tsx +0 -185
  138. data/app/frontend/components/ui/separator.tsx +0 -28
  139. data/app/frontend/components/ui/sheet.tsx +0 -137
  140. data/app/frontend/components/ui/sidebar.tsx +0 -726
  141. data/app/frontend/components/ui/skeleton.tsx +0 -13
  142. data/app/frontend/components/ui/sonner.tsx +0 -41
  143. data/app/frontend/components/ui/table.tsx +0 -114
  144. data/app/frontend/components/ui/tabs.tsx +0 -91
  145. data/app/frontend/components/ui/textarea.tsx +0 -18
  146. data/app/frontend/components/ui/tooltip.tsx +0 -61
  147. data/app/frontend/components/user-info.tsx +0 -32
  148. data/app/frontend/components/user-menu-content.tsx +0 -94
  149. data/app/frontend/entrypoints/application.css +0 -244
  150. data/app/frontend/entrypoints/inertia.tsx +0 -92
  151. data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
  152. data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
  153. data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
  154. data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
  155. data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
  156. data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
  157. data/app/frontend/hooks/use-appearance.tsx +0 -67
  158. data/app/frontend/hooks/use-clipboard.ts +0 -32
  159. data/app/frontend/hooks/use-flash.tsx +0 -23
  160. data/app/frontend/hooks/use-hotkeys.ts +0 -63
  161. data/app/frontend/hooks/use-initials.tsx +0 -15
  162. data/app/frontend/hooks/use-live.ts +0 -205
  163. data/app/frontend/hooks/use-mobile-navigation.ts +0 -8
  164. data/app/frontend/hooks/use-mobile.ts +0 -21
  165. data/app/frontend/hooks/use-percentile.ts +0 -24
  166. data/app/frontend/hooks/use-reduced-motion.ts +0 -25
  167. data/app/frontend/hooks/use-row-nav.ts +0 -106
  168. data/app/frontend/hooks/use-step.ts +0 -27
  169. data/app/frontend/hooks/use-ticker.ts +0 -12
  170. data/app/frontend/hooks/use-window.ts +0 -58
  171. data/app/frontend/layouts/app/app-sidebar-layout.tsx +0 -107
  172. data/app/frontend/layouts/app-layout.tsx +0 -21
  173. data/app/frontend/layouts/env-layout.tsx +0 -53
  174. data/app/frontend/layouts/persistent-layout.tsx +0 -18
  175. data/app/frontend/lib/cable.ts +0 -10
  176. data/app/frontend/lib/env-pages.ts +0 -236
  177. data/app/frontend/lib/execution-path.ts +0 -55
  178. data/app/frontend/lib/explain.ts +0 -50
  179. data/app/frontend/lib/filter.ts +0 -25
  180. data/app/frontend/lib/flamegraph.ts +0 -155
  181. data/app/frontend/lib/format.ts +0 -71
  182. data/app/frontend/lib/keyboard.ts +0 -18
  183. data/app/frontend/lib/mount.ts +0 -23
  184. data/app/frontend/lib/source-link.ts +0 -217
  185. data/app/frontend/lib/utils.ts +0 -6
  186. data/app/frontend/pages/alerts/index.tsx +0 -171
  187. data/app/frontend/pages/broadcasts/index.tsx +0 -171
  188. data/app/frontend/pages/cache_events/index.tsx +0 -127
  189. data/app/frontend/pages/commands/index.tsx +0 -143
  190. data/app/frontend/pages/deploys/index.tsx +0 -94
  191. data/app/frontend/pages/deploys/show.tsx +0 -309
  192. data/app/frontend/pages/deprecations/index.tsx +0 -176
  193. data/app/frontend/pages/exceptions/index.tsx +0 -271
  194. data/app/frontend/pages/executions/show.tsx +0 -821
  195. data/app/frontend/pages/issues/index.tsx +0 -399
  196. data/app/frontend/pages/issues/show.tsx +0 -1367
  197. data/app/frontend/pages/jobs/index.tsx +0 -381
  198. data/app/frontend/pages/jobs/klass.tsx +0 -215
  199. data/app/frontend/pages/llm_calls/index.tsx +0 -664
  200. data/app/frontend/pages/logs/index.tsx +0 -303
  201. data/app/frontend/pages/mails/index.tsx +0 -218
  202. data/app/frontend/pages/notifications/index.tsx +0 -161
  203. data/app/frontend/pages/outgoing_requests/index.tsx +0 -309
  204. data/app/frontend/pages/overview/show.tsx +0 -333
  205. data/app/frontend/pages/people/index.tsx +0 -102
  206. data/app/frontend/pages/people/show.tsx +0 -149
  207. data/app/frontend/pages/processes/index.tsx +0 -335
  208. data/app/frontend/pages/profiles/index.tsx +0 -110
  209. data/app/frontend/pages/profiles/show.tsx +0 -105
  210. data/app/frontend/pages/queries/index.tsx +0 -492
  211. data/app/frontend/pages/queries/show.tsx +0 -276
  212. data/app/frontend/pages/releases/index.tsx +0 -190
  213. data/app/frontend/pages/releases/release-health.tsx +0 -68
  214. data/app/frontend/pages/releases/release-path.ts +0 -25
  215. data/app/frontend/pages/releases/show.tsx +0 -262
  216. data/app/frontend/pages/requests/index.tsx +0 -235
  217. data/app/frontend/pages/requests/route.tsx +0 -159
  218. data/app/frontend/pages/scheduled_tasks/index.tsx +0 -220
  219. data/app/frontend/pages/spans/index.tsx +0 -229
  220. data/app/frontend/pages/spans/show.tsx +0 -220
  221. data/app/frontend/pages/storage_ops/index.tsx +0 -150
  222. data/app/frontend/pages/tenants/index.tsx +0 -236
  223. data/app/frontend/pages/tenants/show.tsx +0 -477
  224. data/app/frontend/pages/tenants/tenant-path.ts +0 -26
  225. data/app/frontend/pages/thresholds/index.tsx +0 -414
  226. data/app/frontend/pages/traces/show.tsx +0 -208
  227. data/app/frontend/pages/transactions/index.tsx +0 -220
  228. data/app/frontend/pages/view_renders/index.tsx +0 -241
  229. data/app/frontend/pages/visits/index.tsx +0 -321
  230. data/app/frontend/public/icon.png +0 -0
  231. data/app/frontend/public/icon.svg +0 -5
  232. data/app/frontend/routes/index.d.ts +0 -2136
  233. data/app/frontend/routes/index.js +0 -2112
  234. data/app/frontend/types/globals.d.ts +0 -9
  235. data/app/frontend/types/index.ts +0 -288
  236. data/app/frontend/types/vite-env.d.ts +0 -9
  237. data/app/helpers/railwatch/assets_helper.rb +0 -52
  238. data/app/jobs/railwatch/anomaly_scan_job.rb +0 -11
  239. data/app/jobs/railwatch/application_job.rb +0 -7
  240. data/app/jobs/railwatch/auto_resolve_issues_job.rb +0 -19
  241. data/app/jobs/railwatch/check_scheduled_tasks_job.rb +0 -113
  242. data/app/jobs/railwatch/detect_anomalies_job.rb +0 -171
  243. data/app/jobs/railwatch/detect_performance_issues_job.rb +0 -95
  244. data/app/jobs/railwatch/group_exceptions_job.rb +0 -91
  245. data/app/jobs/railwatch/optimize_telemetry_job.rb +0 -23
  246. data/app/jobs/railwatch/performance_scan_job.rb +0 -11
  247. data/app/jobs/railwatch/prune_telemetry_job.rb +0 -129
  248. data/app/jobs/railwatch/release_health_rollup_job.rb +0 -64
  249. data/app/jobs/railwatch/rollup_catchup_job.rb +0 -21
  250. data/app/jobs/railwatch/rollup_job.rb +0 -132
  251. data/app/jobs/railwatch/scheduled_task_scan_job.rb +0 -11
  252. data/app/models/concerns/railwatch/detection_snapshotting.rb +0 -20
  253. data/app/models/railwatch/alert.rb +0 -229
  254. data/app/models/railwatch/alert_rule.rb +0 -121
  255. data/app/models/railwatch/anomaly_rule.rb +0 -51
  256. data/app/models/railwatch/application.rb +0 -44
  257. data/app/models/railwatch/application_record.rb +0 -33
  258. data/app/models/railwatch/comment.rb +0 -36
  259. data/app/models/railwatch/deploy.rb +0 -67
  260. data/app/models/railwatch/environment.rb +0 -68
  261. data/app/models/railwatch/execution_presenter.rb +0 -185
  262. data/app/models/railwatch/filter_query.rb +0 -184
  263. data/app/models/railwatch/followup_receipt.rb +0 -27
  264. data/app/models/railwatch/ingest/batch.rb +0 -355
  265. data/app/models/railwatch/ingest/mapper.rb +0 -585
  266. data/app/models/railwatch/ingest/payload.rb +0 -96
  267. data/app/models/railwatch/ingest/rollup_absorber.rb +0 -170
  268. data/app/models/railwatch/ingest/writer.rb +0 -137
  269. data/app/models/railwatch/issue.rb +0 -277
  270. data/app/models/railwatch/issue_activity.rb +0 -23
  271. data/app/models/railwatch/issue_detection_presenter.rb +0 -309
  272. data/app/models/railwatch/issue_detection_snapshot.rb +0 -77
  273. data/app/models/railwatch/maintenance_task.rb +0 -53
  274. data/app/models/railwatch/saved_view.rb +0 -63
  275. data/app/models/railwatch/telemetry/aggregations.rb +0 -251
  276. data/app/models/railwatch/telemetry/attachment.rb +0 -31
  277. data/app/models/railwatch/telemetry/bounded_gzip.rb +0 -101
  278. data/app/models/railwatch/telemetry/broadcast.rb +0 -13
  279. data/app/models/railwatch/telemetry/cache_event.rb +0 -16
  280. data/app/models/railwatch/telemetry/child.rb +0 -53
  281. data/app/models/railwatch/telemetry/cursor_page.rb +0 -99
  282. data/app/models/railwatch/telemetry/deprecation.rb +0 -13
  283. data/app/models/railwatch/telemetry/enqueued_job.rb +0 -13
  284. data/app/models/railwatch/telemetry/exception.rb +0 -28
  285. data/app/models/railwatch/telemetry/execution.rb +0 -152
  286. data/app/models/railwatch/telemetry/export_delivery.rb +0 -32
  287. data/app/models/railwatch/telemetry/export_destination.rb +0 -66
  288. data/app/models/railwatch/telemetry/health_sample.rb +0 -72
  289. data/app/models/railwatch/telemetry/ingest_batch.rb +0 -31
  290. data/app/models/railwatch/telemetry/llm_call.rb +0 -37
  291. data/app/models/railwatch/telemetry/log.rb +0 -91
  292. data/app/models/railwatch/telemetry/mail.rb +0 -13
  293. data/app/models/railwatch/telemetry/n_plus_one.rb +0 -92
  294. data/app/models/railwatch/telemetry/notification.rb +0 -13
  295. data/app/models/railwatch/telemetry/outgoing_request.rb +0 -13
  296. data/app/models/railwatch/telemetry/person.rb +0 -50
  297. data/app/models/railwatch/telemetry/process.rb +0 -10
  298. data/app/models/railwatch/telemetry/profile.rb +0 -37
  299. data/app/models/railwatch/telemetry/query.rb +0 -57
  300. data/app/models/railwatch/telemetry/query_shape.rb +0 -45
  301. data/app/models/railwatch/telemetry/release_health.rb +0 -69
  302. data/app/models/railwatch/telemetry/rollup.rb +0 -157
  303. data/app/models/railwatch/telemetry/session.rb +0 -24
  304. data/app/models/railwatch/telemetry/span.rb +0 -19
  305. data/app/models/railwatch/telemetry/storage_op.rb +0 -13
  306. data/app/models/railwatch/telemetry/tenant.rb +0 -240
  307. data/app/models/railwatch/telemetry/transaction.rb +0 -13
  308. data/app/models/railwatch/telemetry/view_render.rb +0 -13
  309. data/app/models/railwatch/telemetry/visit.rb +0 -24
  310. data/app/models/railwatch/telemetry_record.rb +0 -107
  311. data/app/models/railwatch/threshold.rb +0 -66
  312. data/app/models/railwatch/user.rb +0 -47
  313. data/app/models/railwatch/viewer.rb +0 -13
  314. data/app/models/railwatch/window.rb +0 -66
  315. data/app/views/layouts/railwatch/dashboard.html.erb +0 -25
  316. data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +0 -151
  317. data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +0 -18
  318. data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +0 -20
  319. data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +0 -71
  320. data/db/railwatch_telemetry_migrate/20260903000000_enable_incremental_vacuum.rb +0 -62
  321. data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +0 -481
  322. data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +0 -14
  323. data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +0 -9
  324. data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +0 -10
  325. data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +0 -9
  326. data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +0 -82
  327. data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +0 -10
  328. data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +0 -60
  329. data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +0 -9
  330. data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +0 -51
  331. data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +0 -11
  332. data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +0 -10
  333. data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +0 -37
  334. data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +0 -11
  335. data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +0 -15
  336. data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +0 -9
  337. data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +0 -10
  338. data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +0 -9
  339. data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +0 -18
  340. data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +0 -25
  341. data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +0 -55
  342. data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +0 -21
  343. data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +0 -16
  344. data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +0 -18
  345. data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +0 -16
  346. data/db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb +0 -146
  347. data/db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb +0 -34
  348. data/db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb +0 -15
  349. data/db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb +0 -17
  350. data/db/railwatch_telemetry_migrate/20261008000000_add_in_transaction_to_outgoing_requests.rb +0 -10
  351. data/docs/embedded.md +0 -503
  352. data/lib/puma/plugin/railwatch.rb +0 -215
  353. data/lib/railwatch/dashboard_assets.rb +0 -45
  354. data/lib/railwatch/embedded.rb +0 -50
  355. data/lib/railwatch/export/client.rb +0 -124
  356. data/lib/railwatch/export/lease.rb +0 -56
  357. data/lib/railwatch/export/outbox.rb +0 -361
  358. data/lib/railwatch/export/policy.rb +0 -79
  359. data/lib/railwatch/export/sender.rb +0 -130
  360. data/lib/railwatch/ingest_request_body_limit.rb +0 -10
  361. data/lib/railwatch/maintenance.rb +0 -194
  362. data/lib/railwatch/patches/migration_busy_timeout.rb +0 -73
  363. data/lib/railwatch/transport/local.rb +0 -78
  364. data/lib/railwatch/transport/socket.rb +0 -183
  365. data/lib/railwatch/writer.rb +0 -370
  366. data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
  367. data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
  368. data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
  369. data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
  370. data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
  371. data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
  372. data/public/railwatch/assets/app-layout-by7G3sa1.js +0 -41
  373. data/public/railwatch/assets/application-Cc5v_Vk9.css +0 -1
  374. data/public/railwatch/assets/arrow-up-DCZ7emux.js +0 -1
  375. data/public/railwatch/assets/braces-CykT5_5u.js +0 -1
  376. data/public/railwatch/assets/card-C-PPSA0g.js +0 -1
  377. data/public/railwatch/assets/chart-Cov79YC5.js +0 -39
  378. data/public/railwatch/assets/chart-hover-DdK4E-2h.js +0 -1
  379. data/public/railwatch/assets/chart-panel-BPaYRXJB.js +0 -1
  380. data/public/railwatch/assets/code-BYjC4YZ_.js +0 -1
  381. data/public/railwatch/assets/copy-id-LPfoj9cC.js +0 -1
  382. data/public/railwatch/assets/cursor-load-more-DSdhgpGl.js +0 -1
  383. data/public/railwatch/assets/data-table-CQ_YRWQs.js +0 -1
  384. data/public/railwatch/assets/env-layout-CdMWbUkm.js +0 -1
  385. data/public/railwatch/assets/execution-path-7MYt-Qbn.js +0 -1
  386. data/public/railwatch/assets/filter-bar-CrzpQ_vD.js +0 -1
  387. data/public/railwatch/assets/flamegraph-BIFEdTAg.js +0 -2
  388. data/public/railwatch/assets/format-B9SDkrWj.js +0 -1
  389. data/public/railwatch/assets/frames-C5t-x4f0.js +0 -1
  390. data/public/railwatch/assets/index--NQ7us-L.js +0 -1
  391. data/public/railwatch/assets/index-2kgYe_99.js +0 -1
  392. data/public/railwatch/assets/index-4YEPHxtO.js +0 -1
  393. data/public/railwatch/assets/index-AOCC1GDJ.js +0 -1
  394. data/public/railwatch/assets/index-BEo-u0eO.js +0 -1
  395. data/public/railwatch/assets/index-BJ1V3w2N.js +0 -1
  396. data/public/railwatch/assets/index-BPoHsBKb.js +0 -1
  397. data/public/railwatch/assets/index-BZzyGUM3.js +0 -1
  398. data/public/railwatch/assets/index-BgVq7yKL.js +0 -1
  399. data/public/railwatch/assets/index-BqFGEZvW.js +0 -1
  400. data/public/railwatch/assets/index-ByuHJ1CH.js +0 -1
  401. data/public/railwatch/assets/index-C2j50AL2.js +0 -1
  402. data/public/railwatch/assets/index-CLEdTkvW.js +0 -1
  403. data/public/railwatch/assets/index-CWgq8qBu.js +0 -1
  404. data/public/railwatch/assets/index-CXQ7_oVj.js +0 -2
  405. data/public/railwatch/assets/index-CmyYrjS2.js +0 -1
  406. data/public/railwatch/assets/index-Cn9_HjDo.js +0 -1
  407. data/public/railwatch/assets/index-CsMAm9UK.js +0 -1
  408. data/public/railwatch/assets/index-D0Kk_oGY.js +0 -1
  409. data/public/railwatch/assets/index-DC9ZptJh.js +0 -1
  410. data/public/railwatch/assets/index-DNzg7jmV.js +0 -1
  411. data/public/railwatch/assets/index-DQ8uJO5f.js +0 -1
  412. data/public/railwatch/assets/index-Deq14BxB.js +0 -1
  413. data/public/railwatch/assets/index-IaV3Blzp.js +0 -1
  414. data/public/railwatch/assets/index-TYNWcJQs.js +0 -1
  415. data/public/railwatch/assets/index-WonH2fcZ.js +0 -1
  416. data/public/railwatch/assets/index-WxWUaH_d.js +0 -1
  417. data/public/railwatch/assets/index-mUNrl4ky.js +0 -1
  418. data/public/railwatch/assets/inertia-DfNRARuA.js +0 -97
  419. data/public/railwatch/assets/json-viewer-DMsF5glp.js +0 -1
  420. data/public/railwatch/assets/klass-CmHYk5rz.js +0 -1
  421. data/public/railwatch/assets/origin-identity-1R3bqa3R.js +0 -1
  422. data/public/railwatch/assets/percentile-picker-73z5PAps.js +0 -1
  423. data/public/railwatch/assets/relative-time-Dsc_cCdX.js +0 -1
  424. data/public/railwatch/assets/release-health-BV5OaOWd.js +0 -1
  425. data/public/railwatch/assets/route-CWh58C7K.js +0 -1
  426. data/public/railwatch/assets/segmented-DB4zh-Am.js +0 -1
  427. data/public/railwatch/assets/select-DBKjFlOX.js +0 -1
  428. data/public/railwatch/assets/series-chart-BaiDG795.js +0 -1
  429. data/public/railwatch/assets/show-B4ICCFGh.js +0 -1
  430. data/public/railwatch/assets/show-B5lOU9Gv.js +0 -1
  431. data/public/railwatch/assets/show-BYPB74t4.js +0 -2
  432. data/public/railwatch/assets/show-C7A0m2M8.js +0 -1
  433. data/public/railwatch/assets/show-DT5X1zqE.js +0 -1
  434. data/public/railwatch/assets/show-D_NBdXsn.js +0 -2
  435. data/public/railwatch/assets/show-DgPCnRg8.js +0 -1
  436. data/public/railwatch/assets/show-Yl69HF0x.js +0 -1
  437. data/public/railwatch/assets/show-_QxuUQCm.js +0 -3
  438. data/public/railwatch/assets/show-g__jnN36.js +0 -1
  439. data/public/railwatch/assets/show-l4ztfjl9.js +0 -1
  440. data/public/railwatch/assets/sort-header-BcG8ulEH.js +0 -1
  441. data/public/railwatch/assets/source-link-B2183i2-.js +0 -1
  442. data/public/railwatch/assets/sparkline-cell-B47ipscB.js +0 -1
  443. data/public/railwatch/assets/stat-DT2hhsqS.js +0 -1
  444. data/public/railwatch/assets/status-badge-CmbRKlY7.js +0 -1
  445. data/public/railwatch/assets/tenant-path-C4rNGauE.js +0 -1
  446. data/public/railwatch/assets/use-clipboard-oTNeP4ax.js +0 -1
  447. data/public/railwatch/assets/use-live-CLZZj245.js +0 -1
  448. data/public/railwatch/icon.png +0 -0
  449. data/public/railwatch/icon.svg +0 -5
  450. data/public/railwatch/manifest.json +0 -1245
  451. data/public/railwatch/rails-vite.json +0 -1
  452. /data/{app/frontend/lib → lib/generators/railwatch/install/templates}/railwatch.ts +0 -0
@@ -1,244 +0,0 @@
1
- @import "tailwindcss";
2
-
3
- /* Commit Mono for numbers, identifiers, and labels (SIL OFL). Roboto for UI
4
- text, matching the rebulk.com brand system (Apache 2.0); four weights,
5
- subset to the Latin range (about 18 KB each) and preloaded from the layout
6
- head so they arrive with the HTML rather than after first layout. */
7
- @font-face {
8
- font-family: "CommitMono";
9
- font-style: normal;
10
- font-weight: 400;
11
- font-display: swap;
12
- src: url("../fonts/CommitMono-Regular.woff2") format("woff2");
13
- }
14
- @font-face {
15
- font-family: "CommitMono";
16
- font-style: normal;
17
- font-weight: 700;
18
- font-display: swap;
19
- src: url("../fonts/CommitMono-Bold.woff2") format("woff2");
20
- }
21
- @font-face {
22
- font-family: "Roboto";
23
- font-style: normal;
24
- font-weight: 400;
25
- font-display: swap;
26
- src: url("../fonts/Roboto-Regular.woff2") format("woff2");
27
- }
28
- @font-face {
29
- font-family: "Roboto";
30
- font-style: normal;
31
- font-weight: 500;
32
- font-display: swap;
33
- src: url("../fonts/Roboto-Medium.woff2") format("woff2");
34
- }
35
- @font-face {
36
- font-family: "Roboto";
37
- font-style: normal;
38
- font-weight: 700;
39
- font-display: swap;
40
- src: url("../fonts/Roboto-Bold.woff2") format("woff2");
41
- }
42
- @font-face {
43
- font-family: "Roboto";
44
- font-style: normal;
45
- font-weight: 900;
46
- font-display: swap;
47
- src: url("../fonts/Roboto-Black.woff2") format("woff2");
48
- }
49
- /* While Roboto is still in flight the page renders in this face instead: a
50
- local sans with its metrics overridden to Roboto's (size-adjust from the
51
- average glyph width, ascent and descent from Roboto's hhea table), so line
52
- breaks and box heights are the same before and after the swap and nothing
53
- moves when the real font lands. */
54
- @font-face {
55
- font-family: "Roboto Fallback";
56
- src:
57
- local("Liberation Sans"), local("Arial"), local("Helvetica Neue"),
58
- local("Helvetica");
59
- size-adjust: 96.1%;
60
- ascent-override: 96.5%;
61
- descent-override: 25.4%;
62
- line-gap-override: 0%;
63
- }
64
-
65
- @import "tw-animate-css";
66
- @plugin "@tailwindcss/typography";
67
- @plugin "@tailwindcss/forms";
68
-
69
- @custom-variant dark (&:is(.dark *));
70
-
71
- /* Railwatch palette: the rebulk.com brand system. Navy ink, gold as the sole
72
- accent, Roboto for UI text. Light: soft blue-grey page, white panels, navy
73
- text. Dark: the navy stack from the marketing site (#020618 page, #0F1729
74
- panel, #1E2A45 edge) with gold as primary. Status vocabulary is shared by
75
- badges, charts and signals: grey = fine, amber = warning/4xx, rose =
76
- error/5xx, green = live. */
77
- :root {
78
- --background: #f5f7fb;
79
- --foreground: #0f1729;
80
- --card: #ffffff;
81
- --card-foreground: #0f1729;
82
- --popover: #ffffff;
83
- --popover-foreground: #0f1729;
84
- --primary: #0f1729;
85
- --primary-foreground: #ffffff;
86
- --secondary: #e9edf5;
87
- --secondary-foreground: #0f1729;
88
- --muted: #eef1f7;
89
- --muted-foreground: #5e708f;
90
- --accent: #eef1f7;
91
- --accent-foreground: #0f1729;
92
- --destructive: oklch(0.645 0.246 16.439);
93
- --destructive-foreground: oklch(0.645 0.246 16.439);
94
- --border: #dbe1ec;
95
- --input: #dbe1ec;
96
- --ring: rgb(253 193 26 / 0.6);
97
- --ok: #c9d2e3;
98
- --warning: oklch(0.769 0.188 70.08);
99
- --danger: oklch(0.645 0.246 16.439);
100
- --live: oklch(0.696 0.17 162.48);
101
- --chart-1: var(--ok);
102
- --chart-2: #0f1729;
103
- --chart-3: var(--warning);
104
- --chart-4: var(--warning);
105
- --chart-5: var(--danger);
106
- --radius: 0.5rem;
107
- --sidebar: #ffffff;
108
- --sidebar-foreground: #5e708f;
109
- --sidebar-primary: #0f1729;
110
- --sidebar-primary-foreground: #ffffff;
111
- --sidebar-accent: #eef1f7;
112
- --sidebar-accent-foreground: #0f1729;
113
- --sidebar-border: #dbe1ec;
114
- --sidebar-ring: rgb(253 193 26 / 0.6);
115
- }
116
-
117
- .dark {
118
- --background: #020618;
119
- --foreground: #e6ebf4;
120
- --card: #0f1729;
121
- --card-foreground: #e6ebf4;
122
- --popover: #0f1729;
123
- --popover-foreground: #e6ebf4;
124
- --primary: #fdc11a;
125
- --primary-foreground: #020618;
126
- --secondary: #1e2a45;
127
- --secondary-foreground: #e6ebf4;
128
- --muted: #0a1122;
129
- --muted-foreground: #93a6c4;
130
- --accent: #1e2a45;
131
- --accent-foreground: #ffffff;
132
- --destructive-foreground: oklch(0.72 0.2 16.439);
133
- --border: #1e2a45;
134
- --input: #26355a;
135
- --ring: rgb(253 193 26 / 0.5);
136
- --ok: #26355a;
137
- --warning: oklch(0.666 0.179 58.318);
138
- --chart-2: #fdc11a;
139
- --sidebar: #0a1122;
140
- --sidebar-foreground: #93a6c4;
141
- --sidebar-primary: #fdc11a;
142
- --sidebar-primary-foreground: #020618;
143
- --sidebar-accent: #1e2a45;
144
- --sidebar-accent-foreground: #ffffff;
145
- --sidebar-border: #1e2a45;
146
- --sidebar-ring: rgb(253 193 26 / 0.5);
147
- }
148
-
149
- @theme inline {
150
- --color-background: var(--background);
151
- --color-foreground: var(--foreground);
152
- --color-card: var(--card);
153
- --color-card-foreground: var(--card-foreground);
154
- --color-popover: var(--popover);
155
- --color-popover-foreground: var(--popover-foreground);
156
- --color-primary: var(--primary);
157
- --color-primary-foreground: var(--primary-foreground);
158
- --color-secondary: var(--secondary);
159
- --color-secondary-foreground: var(--secondary-foreground);
160
- --color-muted: var(--muted);
161
- --color-muted-foreground: var(--muted-foreground);
162
- --color-accent: var(--accent);
163
- --color-accent-foreground: var(--accent-foreground);
164
- --color-destructive: var(--destructive);
165
- --color-destructive-foreground: var(--destructive-foreground);
166
- --color-border: var(--border);
167
- --color-input: var(--input);
168
- --color-ring: var(--ring);
169
- --color-chart-1: var(--chart-1);
170
- --color-chart-2: var(--chart-2);
171
- --color-chart-3: var(--chart-3);
172
- --color-chart-4: var(--chart-4);
173
- --color-chart-5: var(--chart-5);
174
- --color-ok: var(--ok);
175
- --color-warning: var(--warning);
176
- --color-danger: var(--danger);
177
- --color-live: var(--live);
178
- --font-sans:
179
- "Roboto", "Roboto Fallback", ui-sans-serif, system-ui, sans-serif;
180
- --font-mono: "CommitMono", ui-monospace, SFMono-Regular, Menlo, monospace;
181
- --radius-sm: calc(var(--radius) - 4px);
182
- --radius-md: calc(var(--radius) - 2px);
183
- --radius-lg: var(--radius);
184
- --radius-xl: calc(var(--radius) + 4px);
185
- --color-sidebar: var(--sidebar);
186
- --color-sidebar-foreground: var(--sidebar-foreground);
187
- --color-sidebar-primary: var(--sidebar-primary);
188
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
189
- --color-sidebar-accent: var(--sidebar-accent);
190
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
191
- --color-sidebar-border: var(--sidebar-border);
192
- --color-sidebar-ring: var(--sidebar-ring);
193
- --animate-accordion-down: accordion-down 0.2s ease-out;
194
- --animate-accordion-up: accordion-up 0.2s ease-out;
195
-
196
- @keyframes accordion-down {
197
- from {
198
- height: 0;
199
- }
200
- to {
201
- height: var(--radix-accordion-content-height);
202
- }
203
- }
204
-
205
- @keyframes accordion-up {
206
- from {
207
- height: var(--radix-accordion-content-height);
208
- }
209
- to {
210
- height: 0;
211
- }
212
- }
213
- }
214
-
215
- @layer base {
216
- * {
217
- @apply border-border outline-ring/50;
218
- }
219
- html {
220
- color-scheme: light dark;
221
- }
222
- body {
223
- @apply bg-background text-foreground antialiased;
224
- font-feature-settings: "cv11", "ss01", "tnum";
225
- letter-spacing: -0.02em;
226
- }
227
- /* Small uppercase mono label used on stat tiles, table heads, and badges. */
228
- .label-caps {
229
- @apply text-muted-foreground font-mono text-[11px] tracking-wide uppercase;
230
- }
231
- /* Full-text search hit inside a log message snippet. */
232
- .fts-mark {
233
- background-color: color-mix(in oklch, var(--warning) 30%, transparent);
234
- @apply rounded-[2px] px-0.5 text-inherit;
235
- }
236
- /* Faint dot grid backdrop behind the app canvas. */
237
- .dotted-canvas {
238
- background-image: radial-gradient(
239
- color-mix(in oklch, var(--foreground) 7%, transparent) 1px,
240
- transparent 1px
241
- );
242
- background-size: 22px 22px;
243
- }
244
- }
@@ -1,92 +0,0 @@
1
- import type { ResolvedComponent } from "@inertiajs/react"
2
- import { createInertiaApp } from "@inertiajs/react"
3
- import { StrictMode } from "react"
4
- import { createRoot } from "react-dom/client"
5
-
6
- import { initializeTheme } from "@/hooks/use-appearance"
7
- import PersistentLayout from "@/layouts/persistent-layout"
8
- import { applyMount } from "@/lib/mount"
9
- import { railwatchRootOptions, startRailwatch } from "@/lib/railwatch"
10
-
11
- // When served under a mount point (the embedded engine, or a Railwatch server
12
- // mounted inside another app), every route helper must carry it. The layout
13
- // emits it; the hosted platform does not, so this is a no-op there.
14
- applyMount()
15
-
16
- const appName = import.meta.env.VITE_APP_NAME ?? "Railwatch"
17
-
18
- void createInertiaApp({
19
- // Set default page title
20
- // see https://inertia-rails.dev/guide/title-and-meta
21
- //
22
- title: (title) => (title ? `${title} - ${appName}` : appName),
23
-
24
- resolve: async (name) => {
25
- const pages = import.meta.glob<{ default: ResolvedComponent }>([
26
- "../pages/**/*.tsx",
27
- "!../pages/**/*.test.tsx",
28
- ])
29
- const loadPage = pages[`../pages/${name}.tsx`]
30
- if (!loadPage) {
31
- throw new Error(`Missing Inertia page component: '${name}.tsx'`)
32
- }
33
- const page = await loadPage()
34
-
35
- // To use a default layout, import the Layout component
36
- // and use the following line.
37
- // see https://inertia-rails.dev/guide/pages#default-layouts
38
- //
39
- page.default.layout ??= [PersistentLayout]
40
-
41
- return page
42
- },
43
-
44
- setup({ el, App, props }) {
45
- // Uncomment the following to enable SSR hydration:
46
- // if (el.hasChildNodes()) {
47
- // hydrateRoot(el, <App {...props} />)
48
- // return
49
- // }
50
- createRoot(el, railwatchRootOptions()).render(
51
- <StrictMode>
52
- <App {...props} />
53
- </StrictMode>,
54
- )
55
- },
56
-
57
- defaults: {
58
- form: {
59
- forceIndicesArrayFormatInFormData: false,
60
- withAllErrors: true,
61
- },
62
- future: {
63
- useScriptElementForInitialPage: true,
64
- useDataInertiaHeadAttribute: true,
65
- useDialogForErrorModal: true,
66
- preserveEqualProps: true,
67
- },
68
- },
69
-
70
- progress: {
71
- color: "#4B5563",
72
- },
73
- }).catch((error) => {
74
- // This ensures this entrypoint is only loaded on Inertia pages
75
- // by checking for the presence of the root element (#app by default).
76
- // Feel free to remove this `catch` if you don't need it.
77
- if (document.getElementById("app")) {
78
- throw error
79
- } else {
80
- console.error(
81
- "Missing root element.\n\n" +
82
- "If you see this error, it probably means you loaded Inertia.js on non-Inertia pages.\n" +
83
- 'Consider moving <%= vite_typescript_tag "inertia" %> to the Inertia-specific layout instead.',
84
- )
85
- }
86
- })
87
-
88
- // This will set light / dark mode on load...
89
- initializeTheme()
90
-
91
- // Report Inertia visit timings to Railwatch (the platform monitors itself).
92
- startRailwatch()
Binary file
Binary file
Binary file
@@ -1,67 +0,0 @@
1
- import { useCallback, useEffect, useState } from "react"
2
-
3
- export type Appearance = "light" | "dark" | "system"
4
-
5
- const prefersDark = () => {
6
- if (typeof window === "undefined") {
7
- return false
8
- }
9
- return window.matchMedia("(prefers-color-scheme: dark)").matches
10
- }
11
-
12
- const applyTheme = (appearance: Appearance) => {
13
- const isDark =
14
- appearance === "dark" || (appearance === "system" && prefersDark())
15
-
16
- document.documentElement.classList.toggle("dark", isDark)
17
- document.documentElement.style.colorScheme = isDark ? "dark" : "light"
18
- }
19
-
20
- const mediaQuery = () => {
21
- if (typeof window === "undefined") {
22
- return null
23
- }
24
-
25
- return window.matchMedia("(prefers-color-scheme: dark)")
26
- }
27
-
28
- const handleSystemThemeChange = () => {
29
- const currentAppearance = localStorage.getItem("appearance") as Appearance
30
- applyTheme(currentAppearance ?? "system")
31
- }
32
-
33
- export function initializeTheme() {
34
- const savedAppearance =
35
- (localStorage.getItem("appearance") as Appearance) || "system"
36
-
37
- applyTheme(savedAppearance)
38
-
39
- mediaQuery()?.addEventListener("change", handleSystemThemeChange)
40
- }
41
-
42
- export function useAppearance() {
43
- const [appearance, setAppearance] = useState<Appearance>(() => {
44
- if (typeof window === "undefined") return "system"
45
- const saved = localStorage.getItem("appearance") as Appearance | null
46
- return saved ?? "system"
47
- })
48
-
49
- const updateAppearance = useCallback((mode: Appearance) => {
50
- setAppearance(mode)
51
- if (mode === "system") {
52
- localStorage.removeItem("appearance")
53
- } else {
54
- localStorage.setItem("appearance", mode)
55
- }
56
- applyTheme(mode)
57
- }, [])
58
-
59
- useEffect(() => {
60
- applyTheme(appearance)
61
-
62
- return () =>
63
- mediaQuery()?.removeEventListener("change", handleSystemThemeChange)
64
- }, [appearance])
65
-
66
- return { appearance, updateAppearance } as const
67
- }
@@ -1,32 +0,0 @@
1
- // Credit: https://usehooks-ts.com/
2
- import { useCallback, useState } from "react"
3
-
4
- type CopiedValue = string | null
5
-
6
- type CopyFn = (text: string) => Promise<boolean>
7
-
8
- export function useClipboard(): [CopiedValue, CopyFn] {
9
- const [copiedText, setCopiedText] = useState<CopiedValue>(null)
10
-
11
- const copy: CopyFn = useCallback(async (text) => {
12
- if (!navigator?.clipboard) {
13
- console.warn("Clipboard not supported")
14
-
15
- return false
16
- }
17
-
18
- try {
19
- await navigator.clipboard.writeText(text)
20
- setCopiedText(text)
21
-
22
- return true
23
- } catch (error) {
24
- console.warn("Copy failed", error)
25
- setCopiedText(null)
26
-
27
- return false
28
- }
29
- }, [])
30
-
31
- return [copiedText, copy]
32
- }
@@ -1,23 +0,0 @@
1
- import { usePage } from "@inertiajs/react"
2
- import { useEffect } from "react"
3
-
4
- import { signalToast } from "@/components/railwatch/signal-toast"
5
- import type { FlashData } from "@/types"
6
-
7
- // Rails flash to signal aspects: alert is stop, warning is caution, notice
8
- // is clear.
9
- function showFlash(flash: FlashData) {
10
- if (flash.alert) signalToast("stop", flash.alert)
11
- if (flash.warning) signalToast("caution", flash.warning)
12
- if (flash.notice) signalToast("clear", flash.notice)
13
- }
14
-
15
- export function useFlash() {
16
- const { flash } = usePage()
17
-
18
- useEffect(() => {
19
- // setTimeout + cleanup prevents double-firing in React StrictMode
20
- const timeout = setTimeout(() => showFlash(flash), 0)
21
- return () => clearTimeout(timeout)
22
- }, [flash])
23
- }
@@ -1,63 +0,0 @@
1
- import { useEffect, useRef } from "react"
2
-
3
- type HotkeyMap = Record<string, () => void>
4
-
5
- const CHORD_TIMEOUT_MS = 800
6
-
7
- // Tiny keyboard shortcut hook supporting single keys ("?") and two-key
8
- // chords ("g d") without pulling in a dependency. Ignores keystrokes while
9
- // focus is in a form field, and while a modifier key is held.
10
- export function useHotkeys(map: HotkeyMap) {
11
- const mapRef = useRef(map)
12
- useEffect(() => {
13
- mapRef.current = map
14
- })
15
-
16
- const pending = useRef<string | null>(null)
17
- const timer = useRef<number | undefined>(undefined)
18
-
19
- useEffect(() => {
20
- const onKeyDown = (event: KeyboardEvent) => {
21
- const target = event.target as HTMLElement
22
- if (
23
- target.tagName === "INPUT" ||
24
- target.tagName === "TEXTAREA" ||
25
- target.tagName === "SELECT" ||
26
- target.isContentEditable
27
- ) {
28
- return
29
- }
30
- if (event.metaKey || event.ctrlKey || event.altKey) return
31
-
32
- if (pending.current) {
33
- const combo = `${pending.current} ${event.key}`
34
- window.clearTimeout(timer.current)
35
- pending.current = null
36
- if (mapRef.current[combo]) {
37
- event.preventDefault()
38
- mapRef.current[combo]()
39
- return
40
- }
41
- }
42
-
43
- if (mapRef.current[event.key]) {
44
- event.preventDefault()
45
- mapRef.current[event.key]()
46
- return
47
- }
48
-
49
- if (event.key === "g") {
50
- pending.current = "g"
51
- timer.current = window.setTimeout(() => {
52
- pending.current = null
53
- }, CHORD_TIMEOUT_MS)
54
- }
55
- }
56
-
57
- document.addEventListener("keydown", onKeyDown)
58
- return () => {
59
- document.removeEventListener("keydown", onKeyDown)
60
- window.clearTimeout(timer.current)
61
- }
62
- }, [])
63
- }
@@ -1,15 +0,0 @@
1
- import { useCallback } from "react"
2
-
3
- export function useInitials() {
4
- return useCallback((fullName: string): string => {
5
- const names = fullName.trim().split(" ")
6
-
7
- if (names.length === 0) return ""
8
- if (names.length === 1) return names[0].charAt(0).toUpperCase()
9
-
10
- const firstInitial = names[0].charAt(0)
11
- const lastInitial = names[names.length - 1].charAt(0)
12
-
13
- return `${firstInitial}${lastInitial}`.toUpperCase()
14
- }, [])
15
- }