rails_pulse 0.1.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +638 -0
- data/Rakefile +207 -0
- data/app/assets/images/rails_pulse/dashboard.png +0 -0
- data/app/assets/images/rails_pulse/menu.svg +1 -0
- data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
- data/app/assets/images/rails_pulse/request.png +0 -0
- data/app/assets/images/rails_pulse/routes.png +0 -0
- data/app/assets/stylesheets/rails_pulse/application.css +102 -0
- data/app/assets/stylesheets/rails_pulse/components/alert.css +24 -0
- data/app/assets/stylesheets/rails_pulse/components/badge.css +58 -0
- data/app/assets/stylesheets/rails_pulse/components/base.css +79 -0
- data/app/assets/stylesheets/rails_pulse/components/breadcrumb.css +31 -0
- data/app/assets/stylesheets/rails_pulse/components/button.css +99 -0
- data/app/assets/stylesheets/rails_pulse/components/card.css +19 -0
- data/app/assets/stylesheets/rails_pulse/components/chart.css +18 -0
- data/app/assets/stylesheets/rails_pulse/components/csp_safe_positioning.css +86 -0
- data/app/assets/stylesheets/rails_pulse/components/descriptive_list.css +9 -0
- data/app/assets/stylesheets/rails_pulse/components/dialog.css +56 -0
- data/app/assets/stylesheets/rails_pulse/components/flash.css +47 -0
- data/app/assets/stylesheets/rails_pulse/components/input.css +80 -0
- data/app/assets/stylesheets/rails_pulse/components/layouts.css +63 -0
- data/app/assets/stylesheets/rails_pulse/components/menu.css +43 -0
- data/app/assets/stylesheets/rails_pulse/components/popover.css +36 -0
- data/app/assets/stylesheets/rails_pulse/components/prose.css +144 -0
- data/app/assets/stylesheets/rails_pulse/components/row.css +24 -0
- data/app/assets/stylesheets/rails_pulse/components/sidebar_menu.css +79 -0
- data/app/assets/stylesheets/rails_pulse/components/skeleton.css +5 -0
- data/app/assets/stylesheets/rails_pulse/components/table.css +37 -0
- data/app/assets/stylesheets/rails_pulse/components/utilities.css +36 -0
- data/app/controllers/concerns/chart_table_concern.rb +82 -0
- data/app/controllers/concerns/response_range_concern.rb +24 -0
- data/app/controllers/concerns/time_range_concern.rb +67 -0
- data/app/controllers/concerns/zoom_range_concern.rb +40 -0
- data/app/controllers/rails_pulse/application_controller.rb +67 -0
- data/app/controllers/rails_pulse/assets_controller.rb +33 -0
- data/app/controllers/rails_pulse/caches_controller.rb +115 -0
- data/app/controllers/rails_pulse/csp_test_controller.rb +57 -0
- data/app/controllers/rails_pulse/dashboard_controller.rb +6 -0
- data/app/controllers/rails_pulse/operations_controller.rb +219 -0
- data/app/controllers/rails_pulse/queries_controller.rb +121 -0
- data/app/controllers/rails_pulse/requests_controller.rb +69 -0
- data/app/controllers/rails_pulse/routes_controller.rb +99 -0
- data/app/helpers/rails_pulse/application_helper.rb +111 -0
- data/app/helpers/rails_pulse/breadcrumbs_helper.rb +62 -0
- data/app/helpers/rails_pulse/cached_component_helper.rb +73 -0
- data/app/helpers/rails_pulse/chart_formatters.rb +43 -0
- data/app/helpers/rails_pulse/chart_helper.rb +140 -0
- data/app/helpers/rails_pulse/formatting_helper.rb +29 -0
- data/app/helpers/rails_pulse/status_helper.rb +279 -0
- data/app/helpers/rails_pulse/table_helper.rb +54 -0
- data/app/javascript/rails_pulse/application.js +119 -0
- data/app/javascript/rails_pulse/controllers/color_scheme_controller.js +20 -0
- data/app/javascript/rails_pulse/controllers/context_menu_controller.js +16 -0
- data/app/javascript/rails_pulse/controllers/dialog_controller.js +21 -0
- data/app/javascript/rails_pulse/controllers/expandable_row_controller.js +67 -0
- data/app/javascript/rails_pulse/controllers/form_controller.js +39 -0
- data/app/javascript/rails_pulse/controllers/icon_controller.js +170 -0
- data/app/javascript/rails_pulse/controllers/index_controller.js +230 -0
- data/app/javascript/rails_pulse/controllers/menu_controller.js +60 -0
- data/app/javascript/rails_pulse/controllers/pagination_controller.js +69 -0
- data/app/javascript/rails_pulse/controllers/popover_controller.js +91 -0
- data/app/javascript/rails_pulse/controllers/timezone_controller.js +106 -0
- data/app/javascript/rails_pulse/theme.js +416 -0
- data/app/jobs/rails_pulse/application_job.rb +4 -0
- data/app/jobs/rails_pulse/cleanup_job.rb +21 -0
- data/app/mailers/rails_pulse/application_mailer.rb +6 -0
- data/app/models/rails_pulse/application_record.rb +7 -0
- data/app/models/rails_pulse/component_cache_key.rb +33 -0
- data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +27 -0
- data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +37 -0
- data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +59 -0
- data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +45 -0
- data/app/models/rails_pulse/operation.rb +87 -0
- data/app/models/rails_pulse/queries/cards/average_query_times.rb +52 -0
- data/app/models/rails_pulse/queries/cards/execution_rate.rb +57 -0
- data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +71 -0
- data/app/models/rails_pulse/queries/charts/average_query_times.rb +112 -0
- data/app/models/rails_pulse/query.rb +58 -0
- data/app/models/rails_pulse/request.rb +64 -0
- data/app/models/rails_pulse/requests/charts/average_response_times.rb +99 -0
- data/app/models/rails_pulse/requests/charts/operations_chart.rb +35 -0
- data/app/models/rails_pulse/route.rb +77 -0
- data/app/models/rails_pulse/routes/cards/average_response_times.rb +54 -0
- data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +73 -0
- data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +73 -0
- data/app/models/rails_pulse/routes/cards/request_count_totals.rb +59 -0
- data/app/models/rails_pulse/routes/charts/average_response_times.rb +115 -0
- data/app/models/rails_pulse/routes/tables/index.rb +63 -0
- data/app/services/rails_pulse/sql_query_normalizer.rb +124 -0
- data/app/views/layouts/rails_pulse/_menu_items.html.erb +19 -0
- data/app/views/layouts/rails_pulse/_sidebar_menu.html.erb +44 -0
- data/app/views/layouts/rails_pulse/application.html.erb +72 -0
- data/app/views/rails_pulse/caches/show.html.erb +9 -0
- data/app/views/rails_pulse/components/_breadcrumbs.html.erb +12 -0
- data/app/views/rails_pulse/components/_code_panel.html.erb +12 -0
- data/app/views/rails_pulse/components/_metric_card.html.erb +55 -0
- data/app/views/rails_pulse/components/_metric_row.html.erb +9 -0
- data/app/views/rails_pulse/components/_operation_details_popover.html.erb +241 -0
- data/app/views/rails_pulse/components/_panel.html.erb +56 -0
- data/app/views/rails_pulse/components/_sparkline_stats.html.erb +15 -0
- data/app/views/rails_pulse/components/_table.html.erb +50 -0
- data/app/views/rails_pulse/components/_table_head.html.erb +20 -0
- data/app/views/rails_pulse/components/_table_pagination.html.erb +45 -0
- data/app/views/rails_pulse/components/_time_period.html.erb +16 -0
- data/app/views/rails_pulse/csp_test/show.html.erb +207 -0
- data/app/views/rails_pulse/dashboard/charts/_bar_chart.html.erb +1 -0
- data/app/views/rails_pulse/dashboard/index.html.erb +64 -0
- data/app/views/rails_pulse/dashboard/tables/_routes_table.html.erb +32 -0
- data/app/views/rails_pulse/dashboard/tables/_standard_table.html.erb +1 -0
- data/app/views/rails_pulse/operations/_operation_analysis_application.html.erb +43 -0
- data/app/views/rails_pulse/operations/_operation_analysis_database.html.erb +12 -0
- data/app/views/rails_pulse/operations/_operation_analysis_generic.html.erb +15 -0
- data/app/views/rails_pulse/operations/_operation_analysis_other.html.erb +69 -0
- data/app/views/rails_pulse/operations/_operation_analysis_view.html.erb +39 -0
- data/app/views/rails_pulse/operations/show.html.erb +79 -0
- data/app/views/rails_pulse/queries/_show_table.html.erb +19 -0
- data/app/views/rails_pulse/queries/_table.html.erb +31 -0
- data/app/views/rails_pulse/queries/index.html.erb +64 -0
- data/app/views/rails_pulse/queries/show.html.erb +86 -0
- data/app/views/rails_pulse/requests/_operations.html.erb +85 -0
- data/app/views/rails_pulse/requests/_table.html.erb +31 -0
- data/app/views/rails_pulse/requests/index.html.erb +64 -0
- data/app/views/rails_pulse/requests/show.html.erb +44 -0
- data/app/views/rails_pulse/routes/_table.html.erb +29 -0
- data/app/views/rails_pulse/routes/index.html.erb +65 -0
- data/app/views/rails_pulse/routes/show.html.erb +67 -0
- data/app/views/rails_pulse/skeletons/_chart.html.erb +3 -0
- data/app/views/rails_pulse/skeletons/_metric_card.html.erb +20 -0
- data/app/views/rails_pulse/skeletons/_panel.html.erb +19 -0
- data/app/views/rails_pulse/skeletons/_table.html.erb +8 -0
- data/config/importmap.rb +12 -0
- data/config/initializers/rails_charts_csp_patch.rb +83 -0
- data/config/initializers/rails_pulse.rb +198 -0
- data/config/routes.rb +16 -0
- data/db/migrate/20250227235904_create_routes.rb +12 -0
- data/db/migrate/20250227235915_create_requests.rb +19 -0
- data/db/migrate/20250228000000_create_queries.rb +14 -0
- data/db/migrate/20250228000056_create_operations.rb +24 -0
- data/lib/generators/rails_pulse/install_generator.rb +17 -0
- data/lib/generators/rails_pulse/templates/rails_pulse.rb +198 -0
- data/lib/rails_pulse/cleanup_service.rb +212 -0
- data/lib/rails_pulse/configuration.rb +176 -0
- data/lib/rails_pulse/engine.rb +88 -0
- data/lib/rails_pulse/middleware/asset_server.rb +84 -0
- data/lib/rails_pulse/middleware/request_collector.rb +120 -0
- data/lib/rails_pulse/migration.rb +29 -0
- data/lib/rails_pulse/subscribers/operation_subscriber.rb +280 -0
- data/lib/rails_pulse/version.rb +3 -0
- data/lib/rails_pulse.rb +38 -0
- data/lib/tasks/rails_pulse_tasks.rake +138 -0
- data/public/rails-pulse-assets/csp-test.js +110 -0
- data/public/rails-pulse-assets/rails-pulse-icons.js +89 -0
- data/public/rails-pulse-assets/rails-pulse-icons.js.map +13 -0
- data/public/rails-pulse-assets/rails-pulse.css +1 -0
- data/public/rails-pulse-assets/rails-pulse.css.map +1 -0
- data/public/rails-pulse-assets/rails-pulse.js +183 -0
- data/public/rails-pulse-assets/rails-pulse.js.map +7 -0
- metadata +339 -0
data/Rakefile
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
5
|
+
|
6
|
+
load "rails/tasks/statistics.rake"
|
7
|
+
|
8
|
+
require "bundler/gem_tasks"
|
9
|
+
|
10
|
+
# Test tasks
|
11
|
+
namespace :test do
|
12
|
+
desc "Run unit tests (fastest - uses in-memory database)"
|
13
|
+
task :unit do
|
14
|
+
ENV["TEST_TYPE"] = "unit"
|
15
|
+
ENV["MEMORY_DATABASE"] = "true"
|
16
|
+
Rake::Task["test:run_unit"].invoke
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Run functional tests"
|
20
|
+
task :functional do
|
21
|
+
ENV["TEST_TYPE"] = "functional"
|
22
|
+
ENV["MEMORY_DATABASE"] = "true"
|
23
|
+
Rake::Task["test:run_functional"].invoke
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Run integration tests"
|
27
|
+
task :integration do
|
28
|
+
ENV["TEST_TYPE"] = "integration"
|
29
|
+
ENV["MEMORY_DATABASE"] = "false"
|
30
|
+
Rake::Task["test:run_integration"].invoke
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Run all tests (unit, functional, integration)"
|
34
|
+
task :all do
|
35
|
+
%w[unit functional integration].each do |test_type|
|
36
|
+
puts "\n=== Running #{test_type} tests ==="
|
37
|
+
Rake::Task["test:#{test_type}"].invoke
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Run unit tests (alias for test:unit)"
|
42
|
+
task units: :unit
|
43
|
+
|
44
|
+
desc "Run functional tests (alias for test:functional)"
|
45
|
+
task functionals: :functional
|
46
|
+
|
47
|
+
desc "Run integration tests (alias for test:integration)"
|
48
|
+
task integrations: :integration
|
49
|
+
|
50
|
+
desc "Run tests with speed optimizations"
|
51
|
+
task fast: :unit
|
52
|
+
|
53
|
+
|
54
|
+
# Internal tasks
|
55
|
+
task :run_unit do
|
56
|
+
sh "rails test test/models test/middleware test/lib test/support"
|
57
|
+
end
|
58
|
+
|
59
|
+
task :run_functional do
|
60
|
+
sh "rails test test/controllers test/helpers"
|
61
|
+
end
|
62
|
+
|
63
|
+
task :run_integration do
|
64
|
+
sh "rails test test/integration"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Speed-optimized test task (unit tests only)
|
69
|
+
desc "Run fast unit tests only"
|
70
|
+
task test_fast: "test:unit"
|
71
|
+
|
72
|
+
# Override default test task to run all tests
|
73
|
+
desc "Run all tests"
|
74
|
+
task test: "test:all"
|
75
|
+
|
76
|
+
# Simplified database testing tasks
|
77
|
+
namespace :test do
|
78
|
+
desc "Run tests with SQLite (default)"
|
79
|
+
task :sqlite do
|
80
|
+
puts "🗂️ Testing with SQLite..."
|
81
|
+
Rake::Task["test:all"].invoke
|
82
|
+
end
|
83
|
+
|
84
|
+
desc "Run tests with PostgreSQL"
|
85
|
+
task :postgresql do
|
86
|
+
puts "🐘 Testing with PostgreSQL..."
|
87
|
+
env = ENV.to_h.merge(
|
88
|
+
"DATABASE_ADAPTER" => "postgresql",
|
89
|
+
"FORCE_DB_CONFIG" => "true"
|
90
|
+
)
|
91
|
+
system(env, "rails test:all") || raise("PostgreSQL tests failed")
|
92
|
+
end
|
93
|
+
|
94
|
+
desc "Run tests with MySQL"
|
95
|
+
task :mysql do
|
96
|
+
puts "🐬 Testing with MySQL..."
|
97
|
+
env = ENV.to_h.merge(
|
98
|
+
"DATABASE_ADAPTER" => "mysql2",
|
99
|
+
"FORCE_DB_CONFIG" => "true",
|
100
|
+
"PARALLEL_WORKERS" => "1"
|
101
|
+
)
|
102
|
+
system(env, "rails test:all") || raise("MySQL tests failed")
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "Run test matrix (SQLite + PostgreSQL + MySQL)"
|
106
|
+
task :matrix do
|
107
|
+
puts "\n🧪 Running test matrix...\n"
|
108
|
+
|
109
|
+
databases = [
|
110
|
+
{ name: "SQLite", env: {}, emoji: "🗂️" },
|
111
|
+
{ name: "PostgreSQL", env: { "DATABASE_ADAPTER" => "postgresql", "FORCE_DB_CONFIG" => "true" }, emoji: "🐘" },
|
112
|
+
{ name: "MySQL", env: { "DATABASE_ADAPTER" => "mysql2", "FORCE_DB_CONFIG" => "true", "PARALLEL_WORKERS" => "1" }, emoji: "🐬" }
|
113
|
+
]
|
114
|
+
|
115
|
+
results = {}
|
116
|
+
|
117
|
+
databases.each do |db|
|
118
|
+
puts "\n" + "="*60
|
119
|
+
puts "#{db[:emoji]} Testing with #{db[:name]}..."
|
120
|
+
puts "="*60
|
121
|
+
|
122
|
+
begin
|
123
|
+
env = ENV.to_h.merge(db[:env])
|
124
|
+
success = system(env, "rails test:all")
|
125
|
+
|
126
|
+
results[db[:name]] = success ? "✅ PASSED" : "❌ FAILED"
|
127
|
+
rescue => e
|
128
|
+
results[db[:name]] = "❌ FAILED"
|
129
|
+
puts "Error: #{e.message}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Print summary
|
134
|
+
puts "\n" + "="*60
|
135
|
+
puts "🏁 TEST MATRIX SUMMARY"
|
136
|
+
puts "="*60
|
137
|
+
results.each do |db, status|
|
138
|
+
puts "#{status} #{db}"
|
139
|
+
end
|
140
|
+
puts "="*60
|
141
|
+
|
142
|
+
# Fail if any tests failed
|
143
|
+
failed_count = results.values.count { |status| status.include?("FAILED") }
|
144
|
+
if failed_count > 0
|
145
|
+
puts "\n❌ #{failed_count} database(s) failed tests"
|
146
|
+
exit 1
|
147
|
+
else
|
148
|
+
puts "\n🎉 All databases passed!"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
desc "Run full test matrix (SQLite + PostgreSQL + MySQL)"
|
153
|
+
task :matrix_full do
|
154
|
+
puts "\n🧪 Running full test matrix...\n"
|
155
|
+
|
156
|
+
databases = [
|
157
|
+
{ name: "SQLite", env: {}, emoji: "🗂️" },
|
158
|
+
{ name: "PostgreSQL", env: { "DATABASE_ADAPTER" => "postgresql", "FORCE_DB_CONFIG" => "true" }, emoji: "🐘" },
|
159
|
+
{ name: "MySQL", env: { "DATABASE_ADAPTER" => "mysql2", "FORCE_DB_CONFIG" => "true", "PARALLEL_WORKERS" => "1" }, emoji: "🐬" }
|
160
|
+
]
|
161
|
+
|
162
|
+
results = {}
|
163
|
+
|
164
|
+
databases.each do |db|
|
165
|
+
puts "\n" + "="*60
|
166
|
+
puts "#{db[:emoji]} Testing with #{db[:name]}..."
|
167
|
+
puts "="*60
|
168
|
+
|
169
|
+
begin
|
170
|
+
env = ENV.to_h.merge(db[:env])
|
171
|
+
success = system(env, "rails test:all")
|
172
|
+
|
173
|
+
results[db[:name]] = success ? "✅ PASSED" : "❌ FAILED"
|
174
|
+
rescue => e
|
175
|
+
results[db[:name]] = "❌ FAILED"
|
176
|
+
puts "Error: #{e.message}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Print summary
|
181
|
+
puts "\n" + "="*60
|
182
|
+
puts "🏁 FULL TEST MATRIX SUMMARY"
|
183
|
+
puts "="*60
|
184
|
+
results.each do |db, status|
|
185
|
+
puts "#{status} #{db}"
|
186
|
+
end
|
187
|
+
puts "="*60
|
188
|
+
|
189
|
+
# Fail if any tests failed
|
190
|
+
failed_count = results.values.count { |status| status.include?("FAILED") }
|
191
|
+
if failed_count > 0
|
192
|
+
puts "\n❌ #{failed_count} database(s) failed tests"
|
193
|
+
exit 1
|
194
|
+
else
|
195
|
+
puts "\n🎉 All databases passed!"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Helper methods
|
201
|
+
def mysql_available?
|
202
|
+
system("mysql --version > /dev/null 2>&1")
|
203
|
+
end
|
204
|
+
|
205
|
+
def postgresql_available?
|
206
|
+
system("psql --version > /dev/null 2>&1")
|
207
|
+
end
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg>
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,102 @@
|
|
1
|
+
* {
|
2
|
+
font-family: AvenirNextPro, sans-serif
|
3
|
+
}
|
4
|
+
|
5
|
+
a {
|
6
|
+
text-decoration: underline;
|
7
|
+
color: #0048b5;
|
8
|
+
}
|
9
|
+
|
10
|
+
#header {
|
11
|
+
background-color: #ffc91f;
|
12
|
+
}
|
13
|
+
|
14
|
+
#header a {
|
15
|
+
color: black
|
16
|
+
}
|
17
|
+
|
18
|
+
#header a:hover {
|
19
|
+
background-color: #ffe284;
|
20
|
+
}
|
21
|
+
|
22
|
+
a:hover {
|
23
|
+
cursor: pointer;
|
24
|
+
}
|
25
|
+
|
26
|
+
.hidden {
|
27
|
+
display: none;
|
28
|
+
}
|
29
|
+
|
30
|
+
/* REQUEST OPERATIONS GRAPH */
|
31
|
+
.operations-table {
|
32
|
+
width: 100%;
|
33
|
+
}
|
34
|
+
|
35
|
+
.operations-table tr {
|
36
|
+
cursor: pointer;
|
37
|
+
}
|
38
|
+
|
39
|
+
.operations-label-cell {
|
40
|
+
width: 380px;
|
41
|
+
max-width: 380px;
|
42
|
+
min-width: 120px;
|
43
|
+
padding-right: 10px;
|
44
|
+
overflow: hidden;
|
45
|
+
text-overflow: ellipsis;
|
46
|
+
white-space: nowrap;
|
47
|
+
vertical-align: middle;
|
48
|
+
}
|
49
|
+
.operations-label-cell span {
|
50
|
+
font-family: 'Times New Roman', Times, serif;
|
51
|
+
}
|
52
|
+
|
53
|
+
.operations-duration-cell {
|
54
|
+
width: 60px;
|
55
|
+
max-width: 100px;
|
56
|
+
}
|
57
|
+
|
58
|
+
.operations-event-cell {
|
59
|
+
position: relative;
|
60
|
+
background: none;
|
61
|
+
padding: 0;
|
62
|
+
}
|
63
|
+
|
64
|
+
.operations-event {
|
65
|
+
box-sizing: border-box;
|
66
|
+
height: 16px;
|
67
|
+
padding: 2px;
|
68
|
+
position: absolute;
|
69
|
+
top: 11px;
|
70
|
+
}
|
71
|
+
|
72
|
+
/* REQUEST OPERATIONS BAR */
|
73
|
+
.bar-container {
|
74
|
+
height:10px;
|
75
|
+
position:relative
|
76
|
+
}
|
77
|
+
.bar {
|
78
|
+
background-color:#727579;
|
79
|
+
height:100%;
|
80
|
+
position:absolute;
|
81
|
+
top:0
|
82
|
+
}
|
83
|
+
.bar.db {
|
84
|
+
background-color:#92c282
|
85
|
+
}
|
86
|
+
.bar.app {
|
87
|
+
background-color:#00adc4
|
88
|
+
}
|
89
|
+
.bar.gc {
|
90
|
+
background-color:#323333
|
91
|
+
}
|
92
|
+
.bar.view {
|
93
|
+
background-color:#b48da3
|
94
|
+
}
|
95
|
+
.bar:first-child {
|
96
|
+
border-bottom-left-radius:1px;
|
97
|
+
border-top-left-radius:1px
|
98
|
+
}
|
99
|
+
.bar:last-child {
|
100
|
+
border-bottom-right-radius:1px;
|
101
|
+
border-top-right-radius:1px
|
102
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
.alert {
|
2
|
+
border: 1px solid var(--alert-border-color, var(--color-border));
|
3
|
+
border-radius: var(--rounded-lg);
|
4
|
+
color: var(--alert-color, var(--color-text));
|
5
|
+
font-size: var(--text-sm);
|
6
|
+
inline-size: var(--size-full);
|
7
|
+
padding: var(--size-4);
|
8
|
+
|
9
|
+
img {
|
10
|
+
filter: var(--alert-icon-color, var(--color-filter-text));
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
.alert--positive {
|
15
|
+
--alert-border-color: var(--color-positive);
|
16
|
+
--alert-color: var(--color-positive);
|
17
|
+
--alert-icon-color: var(--color-filter-positive);
|
18
|
+
}
|
19
|
+
|
20
|
+
.alert--negative {
|
21
|
+
--alert-border-color: var(--color-negative);
|
22
|
+
--alert-color: var(--color-negative);
|
23
|
+
--alert-icon-color: var(--color-filter-negative);
|
24
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
.badge {
|
2
|
+
background-color: var(--badge-background, var(--color-bg));
|
3
|
+
border-radius: var(--rounded-md);
|
4
|
+
border: 1px solid var(--badge-border-color, var(--color-border));
|
5
|
+
box-shadow: var(--badge-box-shadow, none);
|
6
|
+
color: var(--badge-color, var(--color-text));
|
7
|
+
display: inline-flex;
|
8
|
+
font-size: var(--text-xs);
|
9
|
+
font-weight: var(--font-semibold);
|
10
|
+
line-height: var(--leading-4);
|
11
|
+
padding: var(--size-0_5) var(--size-2_5);
|
12
|
+
}
|
13
|
+
|
14
|
+
.badge--primary {
|
15
|
+
--badge-background: var(--color-primary);
|
16
|
+
--badge-border-color: transparent;
|
17
|
+
--badge-box-shadow: var(--shadow-sm);
|
18
|
+
--badge-color: var(--color-text-reversed);
|
19
|
+
}
|
20
|
+
|
21
|
+
.badge--secondary {
|
22
|
+
--badge-background: var(--color-secondary);
|
23
|
+
--badge-border-color: transparent;
|
24
|
+
--badge-box-shadow: none;
|
25
|
+
--badge-color: var(--color-text);
|
26
|
+
}
|
27
|
+
|
28
|
+
.badge--positive {
|
29
|
+
--badge-background: var(--color-positive);
|
30
|
+
--badge-border-color: transparent;
|
31
|
+
--badge-box-shadow: var(--shadow-sm);
|
32
|
+
--badge-color: white;
|
33
|
+
}
|
34
|
+
|
35
|
+
.badge--negative {
|
36
|
+
--badge-background: var(--color-negative);
|
37
|
+
--badge-border-color: transparent;
|
38
|
+
--badge-box-shadow: var(--shadow-sm);
|
39
|
+
--badge-color: white;
|
40
|
+
}
|
41
|
+
|
42
|
+
.badge--primary-inverse {
|
43
|
+
--badge-background: var(--color-bg);
|
44
|
+
--badge-border-color: transparent;
|
45
|
+
--badge-color: var(--color-positive);
|
46
|
+
}
|
47
|
+
|
48
|
+
.badge--positive-inverse {
|
49
|
+
--badge-background: var(--color-bg);
|
50
|
+
--badge-border-color: transparent;
|
51
|
+
--badge-color: var(--color-positive);
|
52
|
+
}
|
53
|
+
|
54
|
+
.badge--negative-inverse {
|
55
|
+
--badge-background: var(--color-bg);
|
56
|
+
--badge-border-color: transparent;
|
57
|
+
--badge-color: var(--color-negative);
|
58
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
:root {
|
2
|
+
/* Abstractions */
|
3
|
+
--color-bg: white;
|
4
|
+
--color-text: black;
|
5
|
+
--color-text-reversed: white;
|
6
|
+
--color-text-subtle: var(--zinc-500);
|
7
|
+
--color-link: var(--blue-700);
|
8
|
+
--color-border-light: var(--zinc-100);
|
9
|
+
--color-border: var(--zinc-200);
|
10
|
+
--color-border-dark: var(--zinc-400);
|
11
|
+
--color-selected: var(--blue-100);
|
12
|
+
--color-selected-dark: var(--blue-300);
|
13
|
+
--color-highlight: var(--yellow-200);
|
14
|
+
|
15
|
+
/* Accent colors */
|
16
|
+
--color-primary: var(--zinc-900);
|
17
|
+
--color-secondary: var(--zinc-100);
|
18
|
+
--color-negative: var(--red-600);
|
19
|
+
--color-positive: var(--green-600);
|
20
|
+
|
21
|
+
/* SVG color values */
|
22
|
+
--color-filter-text: invert(0);
|
23
|
+
--color-filter-text-reversed: invert(1);
|
24
|
+
--color-filter-negative: invert(22%) sepia(85%) saturate(1790%) hue-rotate(339deg) brightness(105%) contrast(108%);
|
25
|
+
--color-filter-positive: invert(44%) sepia(89%) saturate(409%) hue-rotate(89deg) brightness(94%) contrast(97%);
|
26
|
+
}
|
27
|
+
|
28
|
+
html[data-color-scheme="dark"] {
|
29
|
+
/* Abstractions */
|
30
|
+
--color-bg: var(--zinc-800);
|
31
|
+
--color-text: white;
|
32
|
+
--color-text-reversed: black;
|
33
|
+
--color-text-subtle: var(--zinc-400);
|
34
|
+
--color-link: var(--blue-400);
|
35
|
+
--color-border-light: var(--zinc-900);
|
36
|
+
--color-border: var(--zinc-800);
|
37
|
+
--color-border-dark: var(--zinc-600);
|
38
|
+
--color-selected: var(--blue-950);
|
39
|
+
--color-selected-dark: var(--blue-800);
|
40
|
+
--color-highlight: var(--yellow-900);
|
41
|
+
|
42
|
+
/* Accent colors */
|
43
|
+
--color-primary: var(--zinc-50);
|
44
|
+
--color-secondary: var(--zinc-800);
|
45
|
+
--color-negative: var(--red-900);
|
46
|
+
--color-positive: var(--green-900);
|
47
|
+
|
48
|
+
/* SVG color values */
|
49
|
+
--color-filter-text: invert(1);
|
50
|
+
--color-filter-text-reversed: invert(0);
|
51
|
+
--color-filter-negative: invert(15%) sepia(65%) saturate(2067%) hue-rotate(339deg) brightness(102%) contrast(97%);
|
52
|
+
--color-filter-positive: invert(23%) sepia(62%) saturate(554%) hue-rotate(91deg) brightness(93%) contrast(91%);
|
53
|
+
}
|
54
|
+
|
55
|
+
* {
|
56
|
+
border-color: var(--color-border);
|
57
|
+
scrollbar-color: #C1C1C1 transparent;
|
58
|
+
scrollbar-width: thin;
|
59
|
+
}
|
60
|
+
|
61
|
+
html {
|
62
|
+
scroll-behavior: smooth;
|
63
|
+
}
|
64
|
+
|
65
|
+
body {
|
66
|
+
background-color: var(--color-bg);
|
67
|
+
color: var(--color-text);
|
68
|
+
font-synthesis-weight: none;
|
69
|
+
overscroll-behavior: none;
|
70
|
+
text-rendering: optimizeLegibility;
|
71
|
+
}
|
72
|
+
|
73
|
+
.turbo-progress-bar {
|
74
|
+
background-color: #4a8136
|
75
|
+
}
|
76
|
+
|
77
|
+
::selection {
|
78
|
+
background-color: var(--color-selected);
|
79
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
.breadcrumb {
|
2
|
+
align-items: center;
|
3
|
+
color: var(--color-text-subtle);
|
4
|
+
column-gap: var(--size-1);
|
5
|
+
display: flex;
|
6
|
+
flex-wrap: wrap;
|
7
|
+
font-size: var(--text-sm);
|
8
|
+
overflow-wrap: break-word;
|
9
|
+
|
10
|
+
a {
|
11
|
+
padding-block-end: 2px;
|
12
|
+
}
|
13
|
+
|
14
|
+
img.breadcrumb-separator {
|
15
|
+
filter: var(--color-filter-text);
|
16
|
+
opacity: 0.5;
|
17
|
+
}
|
18
|
+
|
19
|
+
a:hover {
|
20
|
+
color: var(--color-text);
|
21
|
+
}
|
22
|
+
|
23
|
+
span[aria-current="page"] {
|
24
|
+
color: var(--color-text);
|
25
|
+
font-weight: 500;
|
26
|
+
}
|
27
|
+
|
28
|
+
@media (width >= 40rem) {
|
29
|
+
column-gap: var(--size-2);
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,99 @@
|
|
1
|
+
.btn {
|
2
|
+
--btn-background: var(--color-bg);
|
3
|
+
--hover-color: oklch(from var(--btn-background) calc(l * .95) c h);
|
4
|
+
|
5
|
+
align-items: center;
|
6
|
+
background-color: var(--btn-background);
|
7
|
+
block-size: var(--btn-block-size, auto);
|
8
|
+
border-radius: var(--btn-radius, var(--rounded-md));
|
9
|
+
border: 1px solid var(--btn-border-color, var(--color-border));
|
10
|
+
box-shadow: var(--btn-box-shadow, var(--shadow-xs));
|
11
|
+
color: var(--btn-color, var(--color-text));
|
12
|
+
column-gap: var(--size-2);
|
13
|
+
cursor: default;
|
14
|
+
display: inline-flex;
|
15
|
+
font-size: var(--btn-font-size, var(--text-sm));
|
16
|
+
font-weight: var(--btn-font-weight, var(--font-medium));
|
17
|
+
inline-size: var(--btn-inline-size, auto);
|
18
|
+
justify-content: var(--btn-justify-content, center);
|
19
|
+
padding: var(--btn-padding, .375rem 1rem);
|
20
|
+
position: relative;
|
21
|
+
text-align: var(--btn-text-align, center);
|
22
|
+
white-space: nowrap;
|
23
|
+
|
24
|
+
img:not([class]) {
|
25
|
+
filter: var(--btn-icon-color, var(--color-filter-text));
|
26
|
+
}
|
27
|
+
|
28
|
+
&:hover {
|
29
|
+
background-color: var(--btn-hover-color, var(--hover-color));
|
30
|
+
}
|
31
|
+
|
32
|
+
&:focus-visible {
|
33
|
+
outline: var(--btn-outline-size, 2px) solid var(--color-selected-dark);
|
34
|
+
}
|
35
|
+
|
36
|
+
&:is(:disabled, [aria-disabled]) {
|
37
|
+
opacity: var(--opacity-50); pointer-events: none;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
.btn--primary {
|
42
|
+
--btn-background: var(--color-primary);
|
43
|
+
--btn-border-color: transparent;
|
44
|
+
--btn-color: var(--color-text-reversed);
|
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;
|
51
|
+
}
|
52
|
+
|
53
|
+
.btn--borderless {
|
54
|
+
--btn-border-color: transparent;
|
55
|
+
--btn-box-shadow: none;
|
56
|
+
}
|
57
|
+
|
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
|
+
.btn--plain {
|
73
|
+
--btn-background: transparent;
|
74
|
+
--btn-border-color: transparent;
|
75
|
+
--btn-hover-color: transparent;
|
76
|
+
--btn-padding: 0;
|
77
|
+
--btn-box-shadow: none;
|
78
|
+
}
|
79
|
+
|
80
|
+
.btn--icon {
|
81
|
+
--btn-padding: var(--size-2);
|
82
|
+
}
|
83
|
+
|
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
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
.card {
|
2
|
+
background-color: var(--color-bg);
|
3
|
+
border-radius: var(--rounded-xl);
|
4
|
+
border-width: var(--border);
|
5
|
+
padding: var(--size-6);
|
6
|
+
box-shadow: var(--shadow-sm);
|
7
|
+
}
|
8
|
+
|
9
|
+
.card-selectable {
|
10
|
+
background-color: var(--color-bg);
|
11
|
+
border-radius: var(--rounded-xl);
|
12
|
+
border-width: var(--border);
|
13
|
+
padding: var(--size-3);
|
14
|
+
|
15
|
+
&:has(:checked) {
|
16
|
+
background-color: var(--color-secondary);
|
17
|
+
border-color: var(--color-primary);
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
.chart-container {
|
2
|
+
width: 100%;
|
3
|
+
aspect-ratio: 4 / 2;
|
4
|
+
}
|
5
|
+
|
6
|
+
.chart-container--slim {
|
7
|
+
aspect-ratio: 4 / 3;
|
8
|
+
}
|
9
|
+
|
10
|
+
@media (min-width: 64rem) {
|
11
|
+
.chart-container {
|
12
|
+
aspect-ratio: 16 / 5;
|
13
|
+
}
|
14
|
+
|
15
|
+
.chart-container--slim {
|
16
|
+
aspect-ratio: 16 / 5;
|
17
|
+
}
|
18
|
+
}
|