rails_pulse 0.3.3 → 0.4.0.pre.1

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 (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +272 -0
  3. data/README.md +44 -3
  4. data/app/assets/stylesheets/rails_pulse/application.css +21 -32
  5. data/app/assets/stylesheets/rails_pulse/components/backtrace.css +238 -0
  6. data/app/assets/stylesheets/rails_pulse/components/badge.css +32 -0
  7. data/app/assets/stylesheets/rails_pulse/components/button.css +2 -36
  8. data/app/assets/stylesheets/rails_pulse/components/card.css +8 -11
  9. data/app/assets/stylesheets/rails_pulse/components/chart.css +16 -3
  10. data/app/assets/stylesheets/rails_pulse/components/csp_safe_positioning.css +5 -39
  11. data/app/assets/stylesheets/rails_pulse/components/dashboard.css +0 -52
  12. data/app/assets/stylesheets/rails_pulse/components/deployments.css +39 -0
  13. data/app/assets/stylesheets/rails_pulse/components/dialog.css +0 -5
  14. data/app/assets/stylesheets/rails_pulse/components/input.css +0 -14
  15. data/app/assets/stylesheets/rails_pulse/components/layouts.css +0 -20
  16. data/app/assets/stylesheets/rails_pulse/components/menu.css +0 -16
  17. data/app/assets/stylesheets/rails_pulse/components/sidebar_menu.css +0 -16
  18. data/app/assets/stylesheets/rails_pulse/components/storage.css +187 -0
  19. data/app/assets/stylesheets/rails_pulse/components/tags.css +0 -14
  20. data/app/assets/stylesheets/rails_pulse/components/time_range_selector.css +5 -5
  21. data/app/assets/stylesheets/rails_pulse/components/utilities.css +0 -17
  22. data/app/controllers/concerns/chart_table_concern.rb +15 -3
  23. data/app/controllers/concerns/ransack_params_concern.rb +18 -0
  24. data/app/controllers/concerns/response_range_concern.rb +1 -1
  25. data/app/controllers/concerns/time_range_concern.rb +28 -14
  26. data/app/controllers/rails_pulse/application_controller.rb +104 -31
  27. data/app/controllers/rails_pulse/dashboard_controller.rb +9 -0
  28. data/app/controllers/rails_pulse/deployments_controller.rb +26 -5
  29. data/app/controllers/rails_pulse/exception_occurrences_controller.rb +19 -0
  30. data/app/controllers/rails_pulse/exceptions_controller.rb +119 -0
  31. data/app/controllers/rails_pulse/job_runs_controller.rb +1 -1
  32. data/app/controllers/rails_pulse/requests_controller.rb +1 -1
  33. data/app/controllers/rails_pulse/routes_controller.rb +7 -1
  34. data/app/controllers/rails_pulse/storage_controller.rb +7 -0
  35. data/app/controllers/rails_pulse/tags_controller.rb +2 -2
  36. data/app/helpers/rails_pulse/application_helper.rb +1 -0
  37. data/app/helpers/rails_pulse/backtrace_helper.rb +85 -0
  38. data/app/helpers/rails_pulse/breadcrumbs_helper.rb +4 -1
  39. data/app/helpers/rails_pulse/chart_helper.rb +0 -3
  40. data/app/helpers/rails_pulse/route_helper.rb +8 -16
  41. data/app/javascript/rails_pulse/application.js +29 -8
  42. data/app/javascript/rails_pulse/controllers/clipboard_controller.js +58 -0
  43. data/app/javascript/rails_pulse/controllers/index_controller.js +16 -10
  44. data/app/javascript/rails_pulse/controllers/pagination_controller.js +6 -2
  45. data/app/javascript/rails_pulse/controllers/popover_controller.js +4 -0
  46. data/app/models/concerns/rails_pulse/taggable.rb +11 -6
  47. data/app/models/rails_pulse/cards/base.rb +53 -0
  48. data/app/models/rails_pulse/dashboard/health_summary.rb +52 -0
  49. data/app/models/rails_pulse/dashboard/needs_attention.rb +73 -1
  50. data/app/models/rails_pulse/dashboard/storage_status.rb +490 -0
  51. data/app/models/rails_pulse/deployment.rb +47 -1
  52. data/app/models/rails_pulse/exception_group.rb +37 -0
  53. data/app/models/rails_pulse/exception_occurrence.rb +27 -0
  54. data/app/models/rails_pulse/exceptions/cards/exception_rate.rb +116 -0
  55. data/app/models/rails_pulse/exceptions/cards/open_groups.rb +49 -0
  56. data/app/models/rails_pulse/exceptions/cards/total_occurrences.rb +62 -0
  57. data/app/models/rails_pulse/exceptions/charts/occurrence_volume.rb +56 -0
  58. data/app/models/rails_pulse/exceptions/raw_data.rb +216 -0
  59. data/app/models/rails_pulse/jobs/cards/p95_duration.rb +8 -2
  60. data/app/models/rails_pulse/operation.rb +13 -4
  61. data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +8 -3
  62. data/app/models/rails_pulse/request.rb +5 -1
  63. data/app/models/rails_pulse/route.rb +71 -21
  64. data/app/models/rails_pulse/routes/cards/error_rates.rb +6 -3
  65. data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +8 -3
  66. data/app/models/rails_pulse/routes/tables/index.rb +8 -6
  67. data/app/models/rails_pulse/summary.rb +21 -10
  68. data/app/models/rails_pulse/tables/base.rb +4 -2
  69. data/app/services/rails_pulse/analysis/explain_plan_analyzer.rb +47 -12
  70. data/app/services/rails_pulse/exception_capture_service.rb +228 -0
  71. data/app/services/rails_pulse/exception_message_sanitizer.rb +152 -0
  72. data/app/services/rails_pulse/operations/change_point.rb +144 -0
  73. data/app/services/rails_pulse/operations/compare.rb +92 -0
  74. data/app/services/rails_pulse/operations/comparison.rb +126 -0
  75. data/app/services/rails_pulse/operations/metric.rb +63 -0
  76. data/app/services/rails_pulse/operations/series.rb +84 -0
  77. data/app/services/rails_pulse/operations/subject.rb +77 -0
  78. data/app/services/rails_pulse/route_controller_action_backfiller.rb +110 -0
  79. data/app/services/rails_pulse/route_merger.rb +99 -0
  80. data/app/services/rails_pulse/route_migrator.rb +139 -0
  81. data/app/services/rails_pulse/route_path_normalizer.rb +79 -0
  82. data/app/services/rails_pulse/route_recognizer.rb +47 -0
  83. data/app/services/rails_pulse/summary_service.rb +45 -0
  84. data/app/services/rails_pulse/tag_filter_service.rb +5 -3
  85. data/app/views/layouts/rails_pulse/_menu_items.html.erb +7 -0
  86. data/app/views/layouts/rails_pulse/_onboarding_banner.html.erb +8 -0
  87. data/app/views/layouts/rails_pulse/_setup_screen.html.erb +5 -5
  88. data/app/views/layouts/rails_pulse/application.html.erb +4 -4
  89. data/app/views/rails_pulse/components/_empty_state.html.erb +1 -1
  90. data/app/views/rails_pulse/components/_panel.html.erb +16 -6
  91. data/app/views/rails_pulse/components/_raw_data.html.erb +34 -0
  92. data/app/views/rails_pulse/dashboard/_chart_panel.html.erb +6 -8
  93. data/app/views/rails_pulse/dashboard/_deployments_panel.html.erb +64 -0
  94. data/app/views/rails_pulse/dashboard/_health_badge.html.erb +21 -10
  95. data/app/views/rails_pulse/dashboard/_storage_panel.html.erb +67 -0
  96. data/app/views/rails_pulse/dashboard/index.html.erb +22 -1
  97. data/app/views/rails_pulse/deployments/_status_badge.html.erb +5 -0
  98. data/app/views/rails_pulse/deployments/_table.html.erb +29 -0
  99. data/app/views/rails_pulse/deployments/index.html.erb +15 -0
  100. data/app/views/rails_pulse/deployments/show.html.erb +62 -0
  101. data/app/views/rails_pulse/exception_occurrences/show.html.erb +45 -0
  102. data/app/views/rails_pulse/exceptions/_backtrace.html.erb +33 -0
  103. data/app/views/rails_pulse/exceptions/_metric_cards.html.erb +6 -0
  104. data/app/views/rails_pulse/exceptions/_occurrences_table.html.erb +23 -0
  105. data/app/views/rails_pulse/exceptions/_status_badge.html.erb +6 -0
  106. data/app/views/rails_pulse/exceptions/_table.html.erb +33 -0
  107. data/app/views/rails_pulse/exceptions/index.html.erb +66 -0
  108. data/app/views/rails_pulse/exceptions/show.html.erb +52 -0
  109. data/app/views/rails_pulse/queries/_raw_data.html.erb +1 -23
  110. data/app/views/rails_pulse/requests/_table.html.erb +1 -1
  111. data/app/views/rails_pulse/routes/_requests_table.html.erb +5 -0
  112. data/app/views/rails_pulse/routes/_table.html.erb +22 -8
  113. data/app/views/rails_pulse/routes/index.html.erb +5 -1
  114. data/app/views/rails_pulse/routes/show.html.erb +5 -0
  115. data/app/views/rails_pulse/storage/show.html.erb +123 -0
  116. data/config/routes.rb +13 -4
  117. data/db/rails_pulse_migrate/20260506000001_create_rails_pulse_exceptions.rb +50 -0
  118. data/db/rails_pulse_migrate/20260507000001_add_actual_query_to_operations.rb +10 -5
  119. data/db/rails_pulse_migrate/20260610000001_add_controller_action_to_rails_pulse_routes.rb +8 -0
  120. data/db/rails_pulse_migrate/20260610000002_change_rails_pulse_routes_to_multi_verb_model.rb +185 -0
  121. data/db/rails_pulse_migrate/20260610000003_add_null_action_unique_index_to_routes.rb +32 -0
  122. data/db/rails_pulse_migrate/20260823000001_add_location_to_exception_groups.rb +12 -0
  123. data/db/rails_pulse_schema.rb +54 -6
  124. data/lib/generators/rails_pulse/base_methods.rb +2 -0
  125. data/lib/generators/rails_pulse/install_generator.rb +5 -0
  126. data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +54 -6
  127. data/lib/generators/rails_pulse/templates/rails_pulse.rb +152 -16
  128. data/lib/generators/rails_pulse/upgrade_generator.rb +166 -38
  129. data/lib/rails_pulse/cleanup_service.rb +110 -15
  130. data/lib/rails_pulse/configuration.rb +125 -6
  131. data/lib/rails_pulse/engine.rb +32 -17
  132. data/lib/rails_pulse/installers/config_updater.rb +202 -0
  133. data/lib/rails_pulse/job_run_collector.rb +43 -16
  134. data/lib/rails_pulse/middleware/asset_server.rb +11 -0
  135. data/lib/rails_pulse/middleware/request_collector.rb +9 -4
  136. data/lib/rails_pulse/packaged_assets.rb +147 -0
  137. data/lib/rails_pulse/route_indexes.rb +66 -0
  138. data/lib/rails_pulse/subscribers/exception_subscriber.rb +36 -0
  139. data/lib/rails_pulse/tracker.rb +12 -9
  140. data/lib/rails_pulse/version.rb +1 -1
  141. data/lib/rails_pulse.rb +1 -0
  142. data/lib/rails_pulse_server.ru +16 -47
  143. data/lib/tasks/rails_pulse.rake +83 -4
  144. data/lib/tasks/rails_pulse_assets.rake +15 -0
  145. data/lib/tasks/rails_pulse_tasks.rake +29 -0
  146. data/public/rails-pulse-assets/rails-pulse-icons.js +6 -2
  147. data/public/rails-pulse-assets/rails-pulse.css +1 -1
  148. data/public/rails-pulse-assets/rails-pulse.js +18 -117
  149. metadata +78 -37
  150. data/Rakefile +0 -554
  151. data/app/javascript/rails_pulse/controllers/form_controller.js +0 -39
  152. data/app/javascript/rails_pulse/controllers/period_selector_controller.js +0 -19
  153. data/app/javascript/rails_pulse/controllers/timezone_controller.js +0 -89
  154. data/app/javascript/rails_pulse/theme.js +0 -416
  155. data/config/brakeman.ignore +0 -190
  156. data/config/brakeman.yml +0 -68
  157. data/config/initializers/rails_pulse.rb +0 -287
  158. data/public/rails-pulse-assets/csp-test.js +0 -120
  159. data/public/rails-pulse-assets/rails-pulse-icons.js.map +0 -13
  160. data/public/rails-pulse-assets/rails-pulse.css.map +0 -1
  161. data/public/rails-pulse-assets/rails-pulse.js.map +0 -7
  162. data/vendor/assets/javascripts/rails-pulse-icons.js +0 -93
  163. data/vendor/assets/javascripts/rails-pulse-icons.js.map +0 -13
  164. data/vendor/assets/javascripts/rails-pulse.js +0 -157
  165. data/vendor/assets/javascripts/rails-pulse.js.map +0 -7
  166. data/vendor/assets/stylesheets/rails-pulse.css +0 -1
  167. data/vendor/assets/stylesheets/rails-pulse.css.map +0 -1
@@ -57,6 +57,38 @@
57
57
  --badge-color: var(--color-negative);
58
58
  }
59
59
 
60
+ .badge--get {
61
+ --badge-background: var(--blue-100);
62
+ --badge-border-color: transparent;
63
+ --badge-color: var(--blue-800);
64
+ }
65
+
66
+ .badge--post {
67
+ --badge-background: var(--green-100);
68
+ --badge-border-color: transparent;
69
+ --badge-color: var(--green-800);
70
+ }
71
+
72
+ .badge--put,
73
+ .badge--patch {
74
+ --badge-background: var(--amber-100);
75
+ --badge-border-color: transparent;
76
+ --badge-color: var(--amber-800);
77
+ }
78
+
79
+ .badge--delete {
80
+ --badge-background: var(--red-100);
81
+ --badge-border-color: transparent;
82
+ --badge-color: var(--red-800);
83
+ }
84
+
85
+ .badge--head,
86
+ .badge--options {
87
+ --badge-background: var(--color-secondary);
88
+ --badge-border-color: transparent;
89
+ --badge-color: var(--color-text);
90
+ }
91
+
60
92
  /* Trend badge icon lightening (dark mode only) */
61
93
  .badge--trend rails-pulse-icon { color: var(--badge-color, currentColor); }
62
94
  html[data-color-scheme="dark"] .badge--trend rails-pulse-icon {
@@ -10,7 +10,7 @@
10
10
  box-shadow: var(--btn-box-shadow, var(--shadow-xs));
11
11
  color: var(--btn-color, var(--color-text));
12
12
  column-gap: var(--size-2);
13
- cursor: default;
13
+ cursor: pointer;
14
14
  display: inline-flex;
15
15
  font-size: var(--btn-font-size, var(--text-sm));
16
16
  font-weight: var(--btn-font-weight, var(--font-medium));
@@ -43,11 +43,7 @@
43
43
  --btn-border-color: transparent;
44
44
  --btn-color: var(--color-text-reversed);
45
45
  --btn-icon-color: var(--color-filter-text-reversed);
46
- }
47
-
48
- .btn--secondary {
49
- --btn-background: var(--color-secondary);
50
- --btn-border-color: transparent;
46
+ --btn-hover-color: color-mix(in oklch, var(--color-primary) 85%, black);
51
47
  }
52
48
 
53
49
  .btn--borderless {
@@ -55,20 +51,6 @@
55
51
  --btn-box-shadow: none;
56
52
  }
57
53
 
58
- .btn--positive {
59
- --btn-background: var(--color-positive);
60
- --btn-border-color: transparent;
61
- --btn-color: white;
62
- --btn-icon-color: invert(1);
63
- }
64
-
65
- .btn--negative {
66
- --btn-background: var(--color-negative);
67
- --btn-border-color: transparent;
68
- --btn-color: white;
69
- --btn-icon-color: invert(1);
70
- }
71
-
72
54
  .btn--plain {
73
55
  --btn-background: transparent;
74
56
  --btn-border-color: transparent;
@@ -81,19 +63,3 @@
81
63
  --btn-padding: var(--size-2);
82
64
  }
83
65
 
84
- [aria-busy] .btn--loading:disabled {
85
- > * {
86
- visibility: hidden;
87
- }
88
-
89
- &::after {
90
- animation: spin 1s linear infinite;
91
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='12' x2='12' y1='2' y2='6'/%3e%3cline x1='12' x2='12' y1='18' y2='22'/%3e%3cline x1='4.93' x2='7.76' y1='4.93' y2='7.76'/%3e%3cline x1='16.24' x2='19.07' y1='16.24' y2='19.07'/%3e%3cline x1='2' x2='6' y1='12' y2='12'/%3e%3cline x1='18' x2='22' y1='12' y2='12'/%3e%3cline x1='4.93' x2='7.76' y1='19.07' y2='16.24'/%3e%3cline x1='16.24' x2='19.07' y1='7.76' y2='4.93'/%3e%3c/svg%3e");
92
- background-size: cover;
93
- block-size: var(--size-5);
94
- content: "";
95
- filter: var(--btn-icon-color, var(--color-filter-text));
96
- inline-size: var(--size-5);
97
- position: absolute;
98
- }
99
- }
@@ -4,6 +4,7 @@
4
4
  border-width: var(--border);
5
5
  padding: var(--size-6);
6
6
  box-shadow: var(--shadow-sm);
7
+ position: relative;
7
8
  }
8
9
 
9
10
  /* Metric strip — single card with vertical dividers between metrics */
@@ -12,6 +13,11 @@
12
13
  display: flex;
13
14
  flex-direction: row;
14
15
  overflow: hidden; /* keeps border-radius intact at edges */
16
+ /* The page container is a flex column inside a fixed-height <main>, so a long
17
+ page shrinks its children. The strip must keep its natural height or the
18
+ trend row — whose overflow:hidden zeroes its automatic minimum size —
19
+ collapses to nothing. */
20
+ flex-shrink: 0;
15
21
  }
16
22
 
17
23
  .metric-strip__section {
@@ -57,6 +63,7 @@
57
63
  align-items: center;
58
64
  overflow: hidden;
59
65
  min-width: 0;
66
+ flex-shrink: 0;
60
67
  }
61
68
 
62
69
  .metric-strip__trend-badge {
@@ -69,6 +76,7 @@
69
76
  text-overflow: ellipsis;
70
77
  white-space: nowrap;
71
78
  min-width: 0;
79
+ flex-shrink: 0;
72
80
  }
73
81
 
74
82
  /* 2-column grid on tablet */
@@ -114,14 +122,3 @@
114
122
  }
115
123
  }
116
124
 
117
- .card-selectable {
118
- background-color: var(--color-surface, var(--color-bg));
119
- border-radius: var(--rounded-xl);
120
- border-width: var(--border);
121
- padding: var(--size-3);
122
-
123
- &:has(:checked) {
124
- background-color: var(--color-secondary);
125
- border-color: var(--color-primary);
126
- }
127
- }
@@ -1,3 +1,12 @@
1
+ .panel-icon-bar {
2
+ position: absolute;
3
+ top: var(--size-6);
4
+ right: var(--size-6);
5
+ display: flex;
6
+ gap: var(--size-2);
7
+ align-items: center;
8
+ }
9
+
1
10
  .panel-icon-bar > a,
2
11
  .panel-icon-bar > div,
3
12
  .panel-icon-bar > form {
@@ -10,7 +19,7 @@
10
19
  align-items: center;
11
20
  }
12
21
 
13
- .panel-icon-bar > form > button {
22
+ .panel-icon-bar > form > button:not(.btn) {
14
23
  background: none;
15
24
  border: none;
16
25
  box-shadow: none;
@@ -68,8 +77,12 @@
68
77
  border-left: 1px solid var(--color-border);
69
78
  }
70
79
 
71
- .text-deploy {
72
- color: var(--color-deploy);
80
+ /* The toggles group normally follows a row of chart tabs, so it carries a
81
+ divider. Standing alone — a chart with no tabs — there is nothing to divide. */
82
+ .panel-tabs__list > .panel-tabs__toggles:first-child {
83
+ padding-left: 0;
84
+ margin-left: 0;
85
+ border-left: none;
73
86
  }
74
87
 
75
88
  .panel-tab {
@@ -14,15 +14,13 @@
14
14
  --context-menu-y: 0px;
15
15
  }
16
16
 
17
- /* Popover positioning (used by popover_controller.js) */
17
+ /* Positioning for popover_controller.js and context_menu_controller.js.
18
+ These were previously two separate [popover].positioned blocks of equal
19
+ specificity, so the second silently overrode the first and correct
20
+ behaviour depended on CSS file load order. Merged into one rule: the
21
+ context-menu coordinates win when set, otherwise the popover ones do. */
18
22
  [popover].positioned {
19
23
  position: fixed;
20
- inset-inline-start: var(--popover-x, 0px) !important;
21
- inset-block-start: var(--popover-y, 0px) !important;
22
- }
23
-
24
- /* Context menu positioning (used by context_menu_controller.js) */
25
- [popover].positioned {
26
24
  inset-inline-start: var(--context-menu-x, var(--popover-x, 0px)) !important;
27
25
  inset-block-start: var(--context-menu-y, var(--popover-y, 0px)) !important;
28
26
  }
@@ -33,19 +31,6 @@
33
31
  line-height: 0;
34
32
  }
35
33
 
36
- [data-controller*="rails-pulse--icon"].loading {
37
- opacity: 0.6;
38
- }
39
-
40
- [data-controller*="rails-pulse--icon"].error {
41
- opacity: 0.4;
42
- filter: grayscale(1);
43
- }
44
-
45
- [data-controller*="rails-pulse--icon"].loaded {
46
- opacity: 1;
47
- }
48
-
49
34
  /* CSP-safe icon rendering */
50
35
  [data-controller*="rails-pulse--icon"] svg {
51
36
  display: block;
@@ -65,22 +50,3 @@
65
50
  border-radius: 2px;
66
51
  }
67
52
 
68
- /* CSP Test Page Utilities */
69
- .csp-test-grid-single {
70
- --columns: 1;
71
- }
72
-
73
- .csp-test-context-area {
74
- padding: 2rem;
75
- border: 2px dashed var(--color-border);
76
- text-align: center;
77
- }
78
-
79
- .csp-test-nav-gap {
80
- --column-gap: 1rem;
81
- }
82
-
83
- /* Sheet sizing for dialog */
84
- .csp-test-sheet {
85
- --sheet-size: 288px;
86
- }
@@ -23,48 +23,6 @@
23
23
  min-width: 0;
24
24
  }
25
25
 
26
- .dashboard-health-bar__controls {
27
- margin-left: auto;
28
- display: flex;
29
- align-items: center;
30
- gap: var(--size-4);
31
- flex-shrink: 0;
32
- }
33
-
34
- /* Period Selector Button Group */
35
- .dashboard-health-bar__period-selector {
36
- display: flex;
37
- align-items: center;
38
- gap: 0;
39
- border: 1px solid var(--color-border);
40
- border-radius: var(--rounded-md);
41
- overflow: hidden;
42
- }
43
-
44
- .dashboard-health-bar__period-button {
45
- padding: var(--size-0_5) var(--size-2_5);
46
- font-size: var(--text-xs);
47
- font-weight: var(--font-semibold);
48
- text-decoration: none;
49
- display: inline-block;
50
- border: none;
51
- cursor: pointer;
52
- }
53
-
54
- .dashboard-health-bar__period-button:not(:first-child) {
55
- border-left: 1px solid var(--color-border);
56
- }
57
-
58
- .dashboard-health-bar__period-button[data-active="true"] {
59
- background: var(--color-primary);
60
- color: var(--color-text-reversed);
61
- }
62
-
63
- .dashboard-health-bar__period-button[data-active="false"] {
64
- background: transparent;
65
- color: var(--color-text-subtle);
66
- }
67
-
68
26
  /* Attention Section */
69
27
  .attention-section__divider {
70
28
  flex: 1;
@@ -167,20 +125,10 @@
167
125
  .dashboard-health-bar__content {
168
126
  gap: var(--size-3);
169
127
  }
170
- .dashboard-health-bar__label {
171
- width: 100%;
172
- margin-bottom: var(--size-1);
173
- }
174
128
  .dashboard-health-bar__badges {
175
129
  flex: 1;
176
130
  min-width: 0;
177
131
  }
178
- .dashboard-health-bar__controls {
179
- margin-left: 0;
180
- width: 100%;
181
- justify-content: space-between;
182
- margin-top: var(--size-2);
183
- }
184
132
  }
185
133
 
186
134
  @media (max-width: 640px) {
@@ -0,0 +1,39 @@
1
+ /* Deployments dashboard panel */
2
+
3
+ .deployments-panel-stats {
4
+ display: grid;
5
+ grid-template-columns: repeat(2, minmax(0, 1fr));
6
+ gap: var(--size-4);
7
+ margin-bottom: var(--size-4);
8
+ padding-bottom: var(--size-4);
9
+ border-bottom: 1px solid var(--color-border);
10
+ }
11
+
12
+ .deployments-panel-rows {
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: var(--size-3);
16
+ }
17
+
18
+ .deployments-panel-row {
19
+ display: grid;
20
+ grid-template-columns: 1fr auto 10.5rem;
21
+ gap: var(--size-3);
22
+ align-items: center;
23
+ }
24
+
25
+ .deployments-panel-row__time {
26
+ white-space: nowrap;
27
+ text-align: right;
28
+ font-variant-numeric: tabular-nums;
29
+ }
30
+
31
+ @media (max-width: 768px) {
32
+ .deployments-panel-row {
33
+ grid-template-columns: 1fr auto;
34
+ }
35
+
36
+ .deployments-panel-row__time {
37
+ grid-column: 1 / -1;
38
+ }
39
+ }
@@ -49,8 +49,3 @@
49
49
  padding: var(--size-6);
50
50
  }
51
51
 
52
- .dialog__close {
53
- inset-block-start: var(--size-3);
54
- inset-inline-end: var(--size-3);
55
- position: absolute;
56
- }
@@ -43,20 +43,6 @@ html[data-color-scheme="dark"] .input:is(select):not([multiple], [size]) {
43
43
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m6 9 6 6 6-6'/%3e%3c/svg%3e");
44
44
  }
45
45
 
46
- .input--actor {
47
- input {
48
- border: 0; inline-size: 100%; outline: 0;
49
- }
50
-
51
- img:not([class]) {
52
- filter: var(--input-icon-color, var(--color-filter-text));
53
- }
54
-
55
- &:focus-within {
56
- outline: var(--input-outline-size, 2px) solid var(--color-selected-dark);
57
- }
58
- }
59
-
60
46
  .invalid-feedback {
61
47
  display: none;
62
48
  }
@@ -1,17 +1,3 @@
1
- .sidebar-layout {
2
- display: grid;
3
- grid-template-areas: "header header" "sidebar main";
4
- grid-template-columns: var(--sidebar-width, 0) 1fr;
5
- grid-template-rows: auto 1fr;
6
- block-size: 100dvh;
7
-
8
- @media (min-width: 48rem) {
9
- --sidebar-border-width: var(--border);
10
- --sidebar-padding: var(--size-2);
11
- --sidebar-width: var(--max-i-3xs);
12
- }
13
- }
14
-
15
1
  .header-layout {
16
2
  display: grid;
17
3
  grid-template-areas: "header" "main";
@@ -19,12 +5,6 @@
19
5
  block-size: 100dvh;
20
6
  }
21
7
 
22
- .centered-layout {
23
- display: grid;
24
- place-items: center;
25
- block-size: 100dvh;
26
- }
27
-
28
8
  .container {
29
9
  inline-size: 100%;
30
10
  margin-inline: auto;
@@ -5,22 +5,12 @@
5
5
  row-gap: var(--size-1);
6
6
  }
7
7
 
8
- .menu__header {
9
- font-size: var(--text-sm);
10
- font-weight: var(--font-semibold);
11
- padding: var(--size-1_5) var(--size-2);
12
- }
13
-
14
8
  .menu__group {
15
9
  display: flex;
16
10
  flex-direction: column;
17
11
  row-gap: 1px;
18
12
  }
19
13
 
20
- .menu__separator {
21
- margin-inline: -0.25rem;
22
- }
23
-
24
14
  .menu__item {
25
15
  --btn-border-color: transparent;
26
16
  --btn-box-shadow: none;
@@ -41,12 +31,6 @@
41
31
  }
42
32
  }
43
33
 
44
- .menu__item-key {
45
- color: var(--color-text-subtle);
46
- font-size: var(--text-xs);
47
- margin-inline-start: auto;
48
- }
49
-
50
34
  .menu__item--active {
51
35
  --btn-background: var(--color-primary);
52
36
  --btn-color: var(--color-text-reversed);
@@ -56,28 +56,12 @@
56
56
  flex-direction: column;
57
57
  }
58
58
 
59
- .sidebar-menu__group-label {
60
- color: var(--color-text-subtle);
61
- font-size: var(--text-xs);
62
- font-weight: var(--font-medium);
63
- padding: var(--size-1_5) var(--size-2);
64
- }
65
-
66
59
  .sidebar-menu__items {
67
60
  display: flex;
68
61
  flex-direction: column;
69
62
  row-gap: var(--size-1);
70
63
  }
71
64
 
72
- .sidebar-menu__sub {
73
- border-inline-start-width: var(--border);
74
- display: flex;
75
- flex-direction: column;
76
- margin-inline-start: var(--size-4);
77
- padding: var(--size-0_5) var(--size-2);
78
- row-gap: var(--size-1);
79
- }
80
-
81
65
  /* Sheet component styles for mobile menu */
82
66
  .sheet {
83
67
  border: 0;
@@ -0,0 +1,187 @@
1
+ /* Storage status page */
2
+
3
+ .health-badge-link {
4
+ text-decoration: none;
5
+ color: inherit;
6
+ }
7
+
8
+ .health-badge-link:hover {
9
+ text-decoration: none;
10
+ }
11
+
12
+ .health-badge-link:hover .badge {
13
+ background-color: var(--color-secondary);
14
+ }
15
+
16
+ .storage-overview-card {
17
+ min-height: 6.5rem;
18
+ }
19
+
20
+ .storage-panel-stats {
21
+ display: grid;
22
+ grid-template-columns: repeat(4, minmax(0, 1fr));
23
+ gap: var(--size-4);
24
+ margin-bottom: var(--size-4);
25
+ padding-bottom: var(--size-4);
26
+ border-bottom: 1px solid var(--color-border);
27
+ }
28
+
29
+ .storage-panel-tables {
30
+ display: flex;
31
+ flex-direction: column;
32
+ gap: var(--size-3);
33
+ }
34
+
35
+ .storage-panel-row {
36
+ display: grid;
37
+ grid-template-columns: 7.5rem 1fr auto;
38
+ gap: var(--size-3);
39
+ align-items: center;
40
+ }
41
+
42
+ .storage-panel-row__label {
43
+ white-space: nowrap;
44
+ overflow: hidden;
45
+ text-overflow: ellipsis;
46
+ }
47
+
48
+ .storage-panel-row__count {
49
+ white-space: nowrap;
50
+ font-variant-numeric: tabular-nums;
51
+ }
52
+
53
+ .storage-fill--compact {
54
+ margin-block: 0;
55
+ height: 0.375rem;
56
+ }
57
+
58
+ @media (max-width: 768px) {
59
+ .storage-panel-stats {
60
+ grid-template-columns: repeat(2, minmax(0, 1fr));
61
+ }
62
+
63
+ .storage-panel-row {
64
+ grid-template-columns: 1fr auto;
65
+ }
66
+
67
+ .storage-panel-row .storage-fill {
68
+ grid-column: 1 / -1;
69
+ order: 3;
70
+ }
71
+ }
72
+
73
+ .storage-health--healthy {
74
+ color: var(--color-positive);
75
+ }
76
+
77
+ .storage-health--warning {
78
+ color: var(--color-warning-text);
79
+ }
80
+
81
+ .storage-health--critical,
82
+ .storage-health--disabled,
83
+ .storage-health--error {
84
+ color: var(--color-negative);
85
+ }
86
+
87
+ .storage-tables {
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: var(--size-5);
91
+ }
92
+
93
+ .storage-table {
94
+ padding-bottom: var(--size-5);
95
+ border-bottom: 1px solid var(--color-border);
96
+ }
97
+
98
+ .storage-table:last-child {
99
+ padding-bottom: 0;
100
+ border-bottom: none;
101
+ }
102
+
103
+ .storage-table__header {
104
+ display: flex;
105
+ align-items: flex-start;
106
+ justify-content: space-between;
107
+ gap: var(--size-4);
108
+ }
109
+
110
+ .storage-table__counts {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: var(--size-2);
114
+ flex-shrink: 0;
115
+ }
116
+
117
+ .storage-table__percent {
118
+ font-variant-numeric: tabular-nums;
119
+ }
120
+
121
+ .storage-table__percent--healthy {
122
+ color: var(--color-positive);
123
+ border-color: color-mix(in srgb, var(--color-positive) 30%, transparent);
124
+ }
125
+
126
+ .storage-table__percent--warning {
127
+ color: var(--color-warning-text);
128
+ border-color: color-mix(in srgb, #eab308 40%, transparent);
129
+ }
130
+
131
+ .storage-table__percent--critical,
132
+ .storage-table__percent--error {
133
+ color: var(--color-negative);
134
+ border-color: color-mix(in srgb, var(--color-negative) 30%, transparent);
135
+ }
136
+
137
+ .storage-table__percent--uncapped {
138
+ color: var(--color-text-subtle);
139
+ }
140
+
141
+ .storage-fill {
142
+ height: 0.5rem;
143
+ margin-block: var(--size-2);
144
+ border-radius: var(--rounded-full, 999px);
145
+ background: var(--color-secondary);
146
+ overflow: hidden;
147
+ }
148
+
149
+ .storage-fill__bar {
150
+ height: 100%;
151
+ border-radius: inherit;
152
+ min-width: 0;
153
+ }
154
+
155
+ .storage-fill__bar--healthy {
156
+ background: var(--color-positive);
157
+ }
158
+
159
+ .storage-fill__bar--warning {
160
+ background: #eab308;
161
+ }
162
+
163
+ .storage-fill__bar--critical,
164
+ .storage-fill__bar--error {
165
+ background: var(--color-negative);
166
+ }
167
+
168
+ .storage-fill__bar--uncapped {
169
+ background: transparent;
170
+ }
171
+
172
+ .storage-table__meta {
173
+ display: flex;
174
+ flex-wrap: wrap;
175
+ gap: var(--size-1) var(--size-4);
176
+ }
177
+
178
+ .storage-table__meta span + span::before {
179
+ content: none;
180
+ }
181
+
182
+ @media (max-width: 768px) {
183
+ .storage-table__header {
184
+ flex-direction: column;
185
+ gap: var(--size-2);
186
+ }
187
+ }
@@ -31,20 +31,6 @@
31
31
  padding-right: 3px !important;
32
32
  }
33
33
 
34
- /* Individual Tag */
35
- .tag {
36
- display: inline-flex;
37
- align-items: center;
38
- gap: 0.25rem;
39
- padding: 0.25rem 0.5rem;
40
- background-color: var(--color-background-secondary);
41
- border: 1px solid var(--color-border);
42
- border-radius: 9999px;
43
- font-size: 0.875rem;
44
- line-height: 1.25;
45
- white-space: nowrap;
46
- }
47
-
48
34
  /* Tag chips — pill shape with subtle translucent fill */
49
35
  .tag-list .badge {
50
36
  border-radius: 9999px;