rails_pulse 0.1.4 → 0.2.3
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 +4 -4
- data/README.md +78 -0
- data/Rakefile +152 -3
- data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
- data/app/assets/stylesheets/rails_pulse/components/datepicker.css +191 -0
- data/app/assets/stylesheets/rails_pulse/components/switch.css +36 -0
- data/app/assets/stylesheets/rails_pulse/components/tags.css +98 -0
- data/app/assets/stylesheets/rails_pulse/components/utilities.css +26 -0
- data/app/controllers/concerns/chart_table_concern.rb +2 -0
- data/app/controllers/concerns/response_range_concern.rb +15 -2
- data/app/controllers/concerns/tag_filter_concern.rb +26 -0
- data/app/controllers/concerns/time_range_concern.rb +27 -8
- data/app/controllers/rails_pulse/application_controller.rb +74 -0
- data/app/controllers/rails_pulse/dashboard_controller.rb +12 -8
- data/app/controllers/rails_pulse/queries_controller.rb +15 -7
- data/app/controllers/rails_pulse/requests_controller.rb +48 -12
- data/app/controllers/rails_pulse/routes_controller.rb +14 -5
- data/app/controllers/rails_pulse/tags_controller.rb +51 -0
- data/app/helpers/rails_pulse/application_helper.rb +2 -0
- data/app/helpers/rails_pulse/form_helper.rb +75 -0
- data/app/helpers/rails_pulse/tags_helper.rb +29 -0
- data/app/javascript/rails_pulse/application.js +6 -0
- data/app/javascript/rails_pulse/controllers/custom_range_controller.js +115 -0
- data/app/javascript/rails_pulse/controllers/datepicker_controller.js +48 -0
- data/app/javascript/rails_pulse/controllers/global_filters_controller.js +110 -0
- data/app/models/concerns/rails_pulse/taggable.rb +63 -0
- data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +12 -5
- data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +12 -5
- data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +7 -0
- data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +6 -0
- data/app/models/rails_pulse/queries/cards/average_query_times.rb +10 -6
- data/app/models/rails_pulse/queries/cards/execution_rate.rb +16 -10
- data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +10 -6
- data/app/models/rails_pulse/queries/charts/average_query_times.rb +5 -2
- data/app/models/rails_pulse/queries/tables/index.rb +20 -2
- data/app/models/rails_pulse/query.rb +2 -0
- data/app/models/rails_pulse/request.rb +9 -1
- data/app/models/rails_pulse/requests/charts/average_response_times.rb +12 -6
- data/app/models/rails_pulse/route.rb +2 -0
- data/app/models/rails_pulse/routes/cards/average_response_times.rb +10 -6
- data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +10 -6
- data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +10 -6
- data/app/models/rails_pulse/routes/cards/request_count_totals.rb +10 -6
- data/app/models/rails_pulse/routes/charts/average_response_times.rb +9 -5
- data/app/models/rails_pulse/routes/tables/index.rb +20 -2
- data/app/models/rails_pulse/summary.rb +55 -0
- data/app/services/rails_pulse/summary_service.rb +2 -0
- data/app/views/layouts/rails_pulse/_global_filters.html.erb +91 -0
- data/app/views/layouts/rails_pulse/_menu_items.html.erb +5 -5
- data/app/views/layouts/rails_pulse/application.html.erb +8 -5
- data/app/views/rails_pulse/components/_active_filters.html.erb +36 -0
- data/app/views/rails_pulse/components/_page_header.html.erb +24 -0
- data/app/views/rails_pulse/dashboard/index.html.erb +4 -0
- data/app/views/rails_pulse/operations/show.html.erb +1 -1
- data/app/views/rails_pulse/queries/_table.html.erb +3 -1
- data/app/views/rails_pulse/queries/index.html.erb +3 -7
- data/app/views/rails_pulse/queries/show.html.erb +3 -7
- data/app/views/rails_pulse/requests/_table.html.erb +3 -1
- data/app/views/rails_pulse/requests/index.html.erb +44 -62
- data/app/views/rails_pulse/requests/show.html.erb +1 -1
- data/app/views/rails_pulse/routes/_requests_table.html.erb +3 -1
- data/app/views/rails_pulse/routes/_table.html.erb +3 -1
- data/app/views/rails_pulse/routes/index.html.erb +4 -8
- data/app/views/rails_pulse/routes/show.html.erb +3 -7
- data/app/views/rails_pulse/tags/_tag_manager.html.erb +73 -0
- data/config/initializers/rails_charts_csp_patch.rb +9 -9
- data/config/routes.rb +5 -0
- data/db/rails_pulse_schema.rb +3 -0
- data/lib/generators/rails_pulse/install_generator.rb +21 -2
- data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +3 -0
- data/lib/generators/rails_pulse/templates/rails_pulse.rb +21 -0
- data/lib/generators/rails_pulse/upgrade_generator.rb +145 -29
- data/lib/rails_pulse/cleanup_service.rb +8 -0
- data/lib/rails_pulse/configuration.rb +16 -1
- data/lib/rails_pulse/engine.rb +25 -0
- data/lib/rails_pulse/version.rb +1 -1
- data/public/rails-pulse-assets/rails-pulse-icons.js +16 -15
- data/public/rails-pulse-assets/rails-pulse-icons.js.map +1 -1
- data/public/rails-pulse-assets/rails-pulse.css +1 -1
- data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
- data/public/rails-pulse-assets/rails-pulse.js +73 -69
- data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
- metadata +18 -3
- data/app/views/rails_pulse/components/_breadcrumbs.html.erb +0 -12
|
@@ -41,17 +41,36 @@ module RailsPulse
|
|
|
41
41
|
# Check for existing Rails Pulse tables
|
|
42
42
|
tables_exist = rails_pulse_tables_exist?
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
root_path = respond_to?(:destination_root) ? destination_root : Rails.root
|
|
45
|
+
schema_path = File.join(root_path, "db/rails_pulse_schema.rb")
|
|
46
|
+
|
|
47
|
+
if !tables_exist && File.exist?(schema_path)
|
|
45
48
|
:schema_only
|
|
46
49
|
elsif !tables_exist
|
|
47
50
|
:not_installed
|
|
48
|
-
elsif
|
|
51
|
+
elsif has_separate_database_config?
|
|
49
52
|
:separate
|
|
50
53
|
else
|
|
51
54
|
:single
|
|
52
55
|
end
|
|
53
56
|
end
|
|
54
57
|
|
|
58
|
+
def has_separate_database_config?
|
|
59
|
+
root_path = respond_to?(:destination_root) ? destination_root : Rails.root
|
|
60
|
+
config_path = File.join(root_path, "config/database.yml")
|
|
61
|
+
|
|
62
|
+
return false unless File.exist?(config_path)
|
|
63
|
+
|
|
64
|
+
require "yaml"
|
|
65
|
+
db_config = YAML.load_file(config_path)
|
|
66
|
+
|
|
67
|
+
# Check if any environment has a rails_pulse database configuration
|
|
68
|
+
db_config.values.any? { |env| env.is_a?(Hash) && env.key?("rails_pulse") }
|
|
69
|
+
rescue => e
|
|
70
|
+
# If we can't read the file, assume single database
|
|
71
|
+
false
|
|
72
|
+
end
|
|
73
|
+
|
|
55
74
|
def rails_pulse_tables_exist?
|
|
56
75
|
return false unless defined?(ActiveRecord::Base)
|
|
57
76
|
|
|
@@ -65,7 +84,8 @@ module RailsPulse
|
|
|
65
84
|
|
|
66
85
|
def get_rails_pulse_table_names
|
|
67
86
|
# Load the schema file to get the table names dynamically
|
|
68
|
-
|
|
87
|
+
root_path = respond_to?(:destination_root) ? destination_root : Rails.root
|
|
88
|
+
schema_file = File.join(root_path, "db/rails_pulse_schema.rb")
|
|
69
89
|
|
|
70
90
|
if File.exist?(schema_file)
|
|
71
91
|
# Read the schema file and extract the required_tables array
|
|
@@ -84,44 +104,114 @@ module RailsPulse
|
|
|
84
104
|
end
|
|
85
105
|
|
|
86
106
|
def upgrade_single_database
|
|
87
|
-
|
|
107
|
+
# Check for new migrations in gem
|
|
108
|
+
gem_migrations = get_gem_migrations
|
|
109
|
+
existing_migrations = get_user_migrations("db/migrate")
|
|
110
|
+
new_migrations = gem_migrations - existing_migrations
|
|
111
|
+
|
|
112
|
+
if new_migrations.any?
|
|
113
|
+
say "Found #{new_migrations.size} new migration(s) to copy:", :blue
|
|
114
|
+
new_migrations.each do |migration|
|
|
115
|
+
say " - #{migration}", :blue
|
|
116
|
+
copy_gem_migration_to(migration, "db/migrate")
|
|
117
|
+
end
|
|
88
118
|
|
|
89
|
-
|
|
90
|
-
say "
|
|
91
|
-
|
|
92
|
-
|
|
119
|
+
say "\nMigrations copied successfully!", :green
|
|
120
|
+
say "\nNext steps:", :green
|
|
121
|
+
say "1. Run: rails db:migrate"
|
|
122
|
+
say "2. Restart your Rails server"
|
|
123
|
+
else
|
|
124
|
+
# Fall back to detecting missing columns
|
|
125
|
+
missing_columns = detect_missing_columns
|
|
93
126
|
|
|
94
|
-
|
|
95
|
-
|
|
127
|
+
if missing_columns.empty?
|
|
128
|
+
say "Rails Pulse is up to date! No migration needed.", :green
|
|
129
|
+
return
|
|
130
|
+
end
|
|
96
131
|
|
|
97
|
-
|
|
132
|
+
# Format missing columns by table for the template
|
|
133
|
+
missing_by_table = format_missing_columns_by_table(missing_columns)
|
|
98
134
|
|
|
99
|
-
|
|
100
|
-
@migration_version = ActiveRecord::Migration.current_version
|
|
101
|
-
@missing_columns = missing_by_table
|
|
135
|
+
say "Creating upgrade migration for missing columns: #{missing_columns.keys.join(', ')}", :blue
|
|
102
136
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
)
|
|
137
|
+
# Set instance variables for template
|
|
138
|
+
@migration_version = ActiveRecord::Migration.current_version
|
|
139
|
+
@missing_columns = missing_by_table
|
|
107
140
|
|
|
108
|
-
|
|
141
|
+
migration_template(
|
|
142
|
+
"migrations/upgrade_rails_pulse_tables.rb",
|
|
143
|
+
"db/migrate/upgrade_rails_pulse_tables.rb"
|
|
144
|
+
)
|
|
109
145
|
|
|
110
|
-
|
|
146
|
+
say <<~MESSAGE
|
|
111
147
|
|
|
112
|
-
|
|
113
|
-
1. Run: rails db:migrate
|
|
114
|
-
2. Restart your Rails server
|
|
148
|
+
Upgrade migration created successfully!
|
|
115
149
|
|
|
116
|
-
|
|
150
|
+
Next steps:
|
|
151
|
+
1. Run: rails db:migrate
|
|
152
|
+
2. Restart your Rails server
|
|
117
153
|
|
|
118
|
-
|
|
154
|
+
This migration will add: #{missing_columns.keys.join(', ')}
|
|
155
|
+
|
|
156
|
+
MESSAGE
|
|
157
|
+
end
|
|
119
158
|
end
|
|
120
159
|
|
|
121
160
|
def upgrade_separate_database
|
|
122
|
-
#
|
|
123
|
-
|
|
124
|
-
|
|
161
|
+
# Check for new migrations in gem
|
|
162
|
+
gem_migrations = get_gem_migrations
|
|
163
|
+
existing_migrations = get_user_migrations("db/rails_pulse_migrate")
|
|
164
|
+
new_migrations = gem_migrations - existing_migrations
|
|
165
|
+
|
|
166
|
+
if new_migrations.any?
|
|
167
|
+
say "Found #{new_migrations.size} new migration(s) to copy:", :blue
|
|
168
|
+
new_migrations.each do |migration|
|
|
169
|
+
say " - #{migration}", :blue
|
|
170
|
+
copy_gem_migration_to(migration, "db/rails_pulse_migrate")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
say "\nMigrations copied successfully!", :green
|
|
174
|
+
say "\nNext steps:", :green
|
|
175
|
+
say "1. Run migrations for the rails_pulse database:"
|
|
176
|
+
say " rails db:migrate (will run migrations for all databases)"
|
|
177
|
+
say " OR manually run the migration files in db/rails_pulse_migrate/"
|
|
178
|
+
say "2. Restart your Rails server"
|
|
179
|
+
else
|
|
180
|
+
# Fall back to detecting missing columns
|
|
181
|
+
missing_columns = detect_missing_columns
|
|
182
|
+
|
|
183
|
+
if missing_columns.empty?
|
|
184
|
+
say "Rails Pulse is up to date! No migrations needed.", :green
|
|
185
|
+
else
|
|
186
|
+
# Format missing columns by table for the template
|
|
187
|
+
missing_by_table = format_missing_columns_by_table(missing_columns)
|
|
188
|
+
|
|
189
|
+
say "Creating upgrade migration for missing columns: #{missing_columns.keys.join(', ')}", :blue
|
|
190
|
+
|
|
191
|
+
# Set instance variables for template
|
|
192
|
+
@migration_version = ActiveRecord::Migration.current_version
|
|
193
|
+
@missing_columns = missing_by_table
|
|
194
|
+
|
|
195
|
+
migration_template(
|
|
196
|
+
"migrations/upgrade_rails_pulse_tables.rb",
|
|
197
|
+
"db/rails_pulse_migrate/upgrade_rails_pulse_tables.rb"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
say <<~MESSAGE
|
|
201
|
+
|
|
202
|
+
Upgrade migration created successfully!
|
|
203
|
+
|
|
204
|
+
Next steps:
|
|
205
|
+
1. Run migrations for the rails_pulse database:
|
|
206
|
+
rails db:migrate (will run migrations for all databases)
|
|
207
|
+
OR manually run the migration files in db/rails_pulse_migrate/
|
|
208
|
+
2. Restart your Rails server
|
|
209
|
+
|
|
210
|
+
This migration will add: #{missing_columns.keys.join(', ')}
|
|
211
|
+
|
|
212
|
+
MESSAGE
|
|
213
|
+
end
|
|
214
|
+
end
|
|
125
215
|
end
|
|
126
216
|
|
|
127
217
|
def offer_conversion_to_migrations
|
|
@@ -165,7 +255,8 @@ module RailsPulse
|
|
|
165
255
|
end
|
|
166
256
|
|
|
167
257
|
def get_expected_schema_from_file
|
|
168
|
-
|
|
258
|
+
root_path = respond_to?(:destination_root) ? destination_root : Rails.root
|
|
259
|
+
schema_file = File.join(root_path, "db/rails_pulse_schema.rb")
|
|
169
260
|
return {} unless File.exist?(schema_file)
|
|
170
261
|
|
|
171
262
|
schema_content = File.read(schema_file)
|
|
@@ -221,6 +312,31 @@ module RailsPulse
|
|
|
221
312
|
|
|
222
313
|
missing_by_table
|
|
223
314
|
end
|
|
315
|
+
|
|
316
|
+
def get_gem_migrations
|
|
317
|
+
gem_migrations_path = File.expand_path("../../../db/rails_pulse_migrate", __dir__)
|
|
318
|
+
return [] unless File.directory?(gem_migrations_path)
|
|
319
|
+
|
|
320
|
+
Dir.glob("#{gem_migrations_path}/*.rb").map { |f| File.basename(f) }
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def get_user_migrations(directory)
|
|
324
|
+
# Use destination_root in tests, Rails.root in production
|
|
325
|
+
root_path = respond_to?(:destination_root) ? destination_root : Rails.root
|
|
326
|
+
full_directory = File.join(root_path, directory)
|
|
327
|
+
|
|
328
|
+
return [] unless File.directory?(full_directory)
|
|
329
|
+
|
|
330
|
+
Dir.glob("#{full_directory}/*.rb").map { |f| File.basename(f) }
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def copy_gem_migration_to(migration_name, destination)
|
|
334
|
+
gem_migrations_path = File.expand_path("../../../db/rails_pulse_migrate", __dir__)
|
|
335
|
+
source_file = File.join(gem_migrations_path, migration_name)
|
|
336
|
+
destination_file = File.join(destination, migration_name)
|
|
337
|
+
|
|
338
|
+
copy_file source_file, destination_file
|
|
339
|
+
end
|
|
224
340
|
end
|
|
225
341
|
end
|
|
226
342
|
end
|
|
@@ -69,6 +69,11 @@ module RailsPulse
|
|
|
69
69
|
return 0 unless defined?(RailsPulse::Request)
|
|
70
70
|
|
|
71
71
|
count = RailsPulse::Request.where("occurred_at < ?", cutoff_time).count
|
|
72
|
+
request_ids = RailsPulse::Request.where("occurred_at < ?", cutoff_time).pluck(:id)
|
|
73
|
+
|
|
74
|
+
# Delete associated operations first to respect foreign key constraints
|
|
75
|
+
RailsPulse::Operation.where(request_id: request_ids).delete_all
|
|
76
|
+
|
|
72
77
|
RailsPulse::Request.where("occurred_at < ?", cutoff_time).delete_all
|
|
73
78
|
count
|
|
74
79
|
end
|
|
@@ -140,6 +145,9 @@ module RailsPulse
|
|
|
140
145
|
.limit(records_to_delete)
|
|
141
146
|
.pluck(:id)
|
|
142
147
|
|
|
148
|
+
# Delete associated operations first to respect foreign key constraints
|
|
149
|
+
RailsPulse::Operation.where(request_id: ids_to_delete).delete_all
|
|
150
|
+
|
|
143
151
|
RailsPulse::Request.where(id: ids_to_delete).delete_all
|
|
144
152
|
records_to_delete
|
|
145
153
|
end
|
|
@@ -16,7 +16,8 @@ module RailsPulse
|
|
|
16
16
|
:connects_to,
|
|
17
17
|
:authentication_enabled,
|
|
18
18
|
:authentication_method,
|
|
19
|
-
:authentication_redirect_path
|
|
19
|
+
:authentication_redirect_path,
|
|
20
|
+
:tags
|
|
20
21
|
|
|
21
22
|
def initialize
|
|
22
23
|
@enabled = true
|
|
@@ -41,6 +42,7 @@ module RailsPulse
|
|
|
41
42
|
@authentication_enabled = Rails.env.production?
|
|
42
43
|
@authentication_method = nil
|
|
43
44
|
@authentication_redirect_path = "/"
|
|
45
|
+
@tags = [ "ignored", "critical", "experimental" ]
|
|
44
46
|
|
|
45
47
|
validate_configuration!
|
|
46
48
|
end
|
|
@@ -64,6 +66,7 @@ module RailsPulse
|
|
|
64
66
|
validate_patterns!
|
|
65
67
|
validate_database_settings!
|
|
66
68
|
validate_authentication_settings!
|
|
69
|
+
validate_tags!
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
# Revalidate configuration after changes
|
|
@@ -135,6 +138,18 @@ module RailsPulse
|
|
|
135
138
|
end
|
|
136
139
|
end
|
|
137
140
|
|
|
141
|
+
def validate_tags!
|
|
142
|
+
unless @tags.is_a?(Array)
|
|
143
|
+
raise ArgumentError, "tags must be an array, got #{@tags.class}"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
@tags.each do |tag|
|
|
147
|
+
unless tag.is_a?(String)
|
|
148
|
+
raise ArgumentError, "tags must be strings, got #{tag.class}"
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
138
153
|
# Default patterns for common asset types and paths
|
|
139
154
|
def default_asset_patterns
|
|
140
155
|
[
|
data/lib/rails_pulse/engine.rb
CHANGED
|
@@ -14,6 +14,17 @@ module RailsPulse
|
|
|
14
14
|
class Engine < ::Rails::Engine
|
|
15
15
|
isolate_namespace RailsPulse
|
|
16
16
|
|
|
17
|
+
# Prevent rails_charts from polluting the global ActionView namespace
|
|
18
|
+
# This MUST happen before any initializers run to avoid conflicts with host apps
|
|
19
|
+
# that use Chartkick or other chart libraries
|
|
20
|
+
if defined?(RailsCharts::Engine)
|
|
21
|
+
# Find and remove the rails_charts.helpers initializer
|
|
22
|
+
RailsCharts::Engine.initializers.delete_if do |init|
|
|
23
|
+
init.name == "rails_charts.helpers"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
17
28
|
# Load Rake tasks
|
|
18
29
|
rake_tasks do
|
|
19
30
|
Dir.glob(File.expand_path("../tasks/**/*.rake", __FILE__)).each { |file| load file }
|
|
@@ -50,6 +61,20 @@ module RailsPulse
|
|
|
50
61
|
RailsCharts.options[:theme] = "railspulse"
|
|
51
62
|
end
|
|
52
63
|
|
|
64
|
+
# Manually include RailsCharts helpers only in RailsPulse views
|
|
65
|
+
# This ensures rails_charts methods are only available in RailsPulse namespace,
|
|
66
|
+
# not in the host application
|
|
67
|
+
initializer "rails_pulse.include_rails_charts_helpers" do
|
|
68
|
+
ActiveSupport.on_load :action_view do
|
|
69
|
+
if defined?(RailsCharts::Helpers) && defined?(RailsPulse::ChartHelper)
|
|
70
|
+
unless RailsPulse::ChartHelper.include?(RailsCharts::Helpers)
|
|
71
|
+
RailsPulse::ChartHelper.include(RailsCharts::Helpers)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
|
|
53
78
|
initializer "rails_pulse.ransack", after: "ransack.initialize" do
|
|
54
79
|
# Ensure Ransack is loaded before our models
|
|
55
80
|
end
|
data/lib/rails_pulse/version.rb
CHANGED
|
@@ -1,44 +1,45 @@
|
|
|
1
1
|
// Rails Pulse Icons Bundle - Auto-generated
|
|
2
|
-
// Contains
|
|
2
|
+
// Contains 34 SVG icons for Rails Pulse
|
|
3
3
|
|
|
4
4
|
(function() {
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
7
|
// Icon data
|
|
8
8
|
const icons = {
|
|
9
|
-
"menu": "<
|
|
9
|
+
"menu": "<path d=\"M4 5h16\" /><path d=\"M4 12h16\" /><path d=\"M4 19h16\" />",
|
|
10
10
|
"sun": "<circle cx=\"12\" cy=\"12\" r=\"4\" /><path d=\"M12 2v2\" /><path d=\"M12 20v2\" /><path d=\"m4.93 4.93 1.41 1.41\" /><path d=\"m17.66 17.66 1.41 1.41\" /><path d=\"M2 12h2\" /><path d=\"M20 12h2\" /><path d=\"m6.34 17.66-1.41 1.41\" /><path d=\"m19.07 4.93-1.41 1.41\" />",
|
|
11
|
-
"moon": "<path d=\"
|
|
11
|
+
"moon": "<path d=\"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401\" />",
|
|
12
12
|
"chevron-right": "<path d=\"m9 18 6-6-6-6\" />",
|
|
13
13
|
"chevron-left": "<path d=\"m15 18-6-6 6-6\" />",
|
|
14
14
|
"chevron-down": "<path d=\"m6 9 6 6 6-6\" />",
|
|
15
15
|
"chevron-up": "<path d=\"m18 15-6-6-6 6\" />",
|
|
16
16
|
"chevrons-left": "<path d=\"m11 17-5-5 5-5\" /><path d=\"m18 17-5-5 5-5\" />",
|
|
17
17
|
"chevrons-right": "<path d=\"m6 17 5-5-5-5\" /><path d=\"m13 17 5-5-5-5\" />",
|
|
18
|
-
"loader-circle": "<
|
|
19
|
-
"search": "<circle cx=\"11\" cy=\"11\" r=\"8\"
|
|
20
|
-
"filter": "<
|
|
18
|
+
"loader-circle": "<path d=\"M12 2v4\" /><path d=\"m16.2 7.8 2.9-2.9\" /><path d=\"M18 12h4\" /><path d=\"m16.2 16.2 2.9 2.9\" /><path d=\"M12 18v4\" /><path d=\"m4.9 19.1 2.9-2.9\" /><path d=\"M2 12h4\" /><path d=\"m4.9 4.9 2.9 2.9\" />",
|
|
19
|
+
"search": "<path d=\"m21 21-4.34-4.34\" /><circle cx=\"11\" cy=\"11\" r=\"8\" />",
|
|
20
|
+
"list-filter": "<path d=\"M2 5h20\" /><path d=\"M6 12h12\" /><path d=\"M9 19h6\" />",
|
|
21
|
+
"list-filter-plus": "<path d=\"M12 5H2\" /><path d=\"M6 12h12\" /><path d=\"M9 19h6\" /><path d=\"M16 5h6\" /><path d=\"M19 8V2\" />",
|
|
21
22
|
"x": "<path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />",
|
|
22
23
|
"x-circle": "<circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" />",
|
|
23
24
|
"check": "<path d=\"M20 6 9 17l-5-5\" />",
|
|
24
25
|
"alert-circle": "<circle cx=\"12\" cy=\"12\" r=\"10\" /><line x1=\"12\" x2=\"12\" y1=\"8\" y2=\"12\" /><line x1=\"12\" x2=\"12.01\" y1=\"16\" y2=\"16\" />",
|
|
25
|
-
"alert-triangle": "<path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-
|
|
26
|
+
"alert-triangle": "<path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" />",
|
|
26
27
|
"info": "<circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" />",
|
|
27
|
-
"external-link": "<path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"
|
|
28
|
-
"download": "<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" /><
|
|
28
|
+
"external-link": "<path d=\"M15 3h6v6\" /><path d=\"M10 14 21 3\" /><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />",
|
|
29
|
+
"download": "<path d=\"M12 15V3\" /><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" /><path d=\"m7 10 5 5 5-5\" />",
|
|
29
30
|
"refresh-cw": "<path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" /><path d=\"M21 3v5h-5\" /><path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" /><path d=\"M8 16H3v5\" />",
|
|
30
|
-
"clock": "<circle cx=\"12\" cy=\"12\" r=\"10\"
|
|
31
|
+
"clock": "<path d=\"M12 6v6l4 2\" /><circle cx=\"12\" cy=\"12\" r=\"10\" />",
|
|
31
32
|
"database": "<ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" /><path d=\"M3 5V19A9 3 0 0 0 21 19V5\" /><path d=\"M3 12A9 3 0 0 0 21 12\" />",
|
|
32
33
|
"server": "<rect width=\"20\" height=\"8\" x=\"2\" y=\"2\" rx=\"2\" ry=\"2\" /><rect width=\"20\" height=\"8\" x=\"2\" y=\"14\" rx=\"2\" ry=\"2\" /><line x1=\"6\" x2=\"6.01\" y1=\"6\" y2=\"6\" /><line x1=\"6\" x2=\"6.01\" y1=\"18\" y2=\"18\" />",
|
|
33
|
-
"activity": "<path d=\"M22 12h-
|
|
34
|
+
"activity": "<path d=\"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2\" />",
|
|
34
35
|
"layout-dashboard": "<rect width=\"7\" height=\"9\" x=\"3\" y=\"3\" rx=\"1\" /><rect width=\"7\" height=\"5\" x=\"14\" y=\"3\" rx=\"1\" /><rect width=\"7\" height=\"9\" x=\"14\" y=\"12\" rx=\"1\" /><rect width=\"7\" height=\"5\" x=\"3\" y=\"16\" rx=\"1\" />",
|
|
35
36
|
"audio-lines": "<path d=\"M2 10v3\" /><path d=\"M6 6v11\" /><path d=\"M10 3v18\" /><path d=\"M14 8v7\" /><path d=\"M18 5v13\" /><path d=\"M22 10v3\" />",
|
|
36
|
-
"message-circle-question": "<path d=\"
|
|
37
|
+
"message-circle-question": "<path d=\"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719\" /><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\" /><path d=\"M12 17h.01\" />",
|
|
37
38
|
"route": "<circle cx=\"6\" cy=\"19\" r=\"3\" /><path d=\"M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15\" /><circle cx=\"18\" cy=\"5\" r=\"3\" />",
|
|
38
|
-
"trending-up": "<
|
|
39
|
-
"trending-down": "<
|
|
39
|
+
"trending-up": "<path d=\"M16 7h6v6\" /><path d=\"m22 7-8.5 8.5-5-5L2 17\" />",
|
|
40
|
+
"trending-down": "<path d=\"M16 17h6v-6\" /><path d=\"m22 17-8.5-8.5-5 5L2 7\" />",
|
|
40
41
|
"move-right": "<path d=\"M18 8L22 12L18 16\" /><path d=\"M2 12H22\" />",
|
|
41
|
-
"eye": "<path d=\"M2
|
|
42
|
+
"eye": "<path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\" /><circle cx=\"12\" cy=\"12\" r=\"3\" />"
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
// Global icon registry
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"names": [],
|
|
9
9
|
"mappings": "",
|
|
10
10
|
"sourcesContent": [
|
|
11
|
-
"// Rails Pulse Icons Bundle - Auto-generated\n// Contains
|
|
11
|
+
"// Rails Pulse Icons Bundle - Auto-generated\n// Contains 34 SVG icons for Rails Pulse\n\n(function() {\n 'use strict';\n\n // Icon data\n const icons = {\n \"menu\": \"<path d=\\\"M4 5h16\\\" /><path d=\\\"M4 12h16\\\" /><path d=\\\"M4 19h16\\\" />\",\n \"sun\": \"<circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"4\\\" /><path d=\\\"M12 2v2\\\" /><path d=\\\"M12 20v2\\\" /><path d=\\\"m4.93 4.93 1.41 1.41\\\" /><path d=\\\"m17.66 17.66 1.41 1.41\\\" /><path d=\\\"M2 12h2\\\" /><path d=\\\"M20 12h2\\\" /><path d=\\\"m6.34 17.66-1.41 1.41\\\" /><path d=\\\"m19.07 4.93-1.41 1.41\\\" />\",\n \"moon\": \"<path d=\\\"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401\\\" />\",\n \"chevron-right\": \"<path d=\\\"m9 18 6-6-6-6\\\" />\",\n \"chevron-left\": \"<path d=\\\"m15 18-6-6 6-6\\\" />\",\n \"chevron-down\": \"<path d=\\\"m6 9 6 6 6-6\\\" />\",\n \"chevron-up\": \"<path d=\\\"m18 15-6-6-6 6\\\" />\",\n \"chevrons-left\": \"<path d=\\\"m11 17-5-5 5-5\\\" /><path d=\\\"m18 17-5-5 5-5\\\" />\",\n \"chevrons-right\": \"<path d=\\\"m6 17 5-5-5-5\\\" /><path d=\\\"m13 17 5-5-5-5\\\" />\",\n \"loader-circle\": \"<path d=\\\"M12 2v4\\\" /><path d=\\\"m16.2 7.8 2.9-2.9\\\" /><path d=\\\"M18 12h4\\\" /><path d=\\\"m16.2 16.2 2.9 2.9\\\" /><path d=\\\"M12 18v4\\\" /><path d=\\\"m4.9 19.1 2.9-2.9\\\" /><path d=\\\"M2 12h4\\\" /><path d=\\\"m4.9 4.9 2.9 2.9\\\" />\",\n \"search\": \"<path d=\\\"m21 21-4.34-4.34\\\" /><circle cx=\\\"11\\\" cy=\\\"11\\\" r=\\\"8\\\" />\",\n \"list-filter\": \"<path d=\\\"M2 5h20\\\" /><path d=\\\"M6 12h12\\\" /><path d=\\\"M9 19h6\\\" />\",\n \"list-filter-plus\": \"<path d=\\\"M12 5H2\\\" /><path d=\\\"M6 12h12\\\" /><path d=\\\"M9 19h6\\\" /><path d=\\\"M16 5h6\\\" /><path d=\\\"M19 8V2\\\" />\",\n \"x\": \"<path d=\\\"M18 6 6 18\\\" /><path d=\\\"m6 6 12 12\\\" />\",\n \"x-circle\": \"<circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"10\\\" /><path d=\\\"m15 9-6 6\\\" /><path d=\\\"m9 9 6 6\\\" />\",\n \"check\": \"<path d=\\\"M20 6 9 17l-5-5\\\" />\",\n \"alert-circle\": \"<circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"10\\\" /><line x1=\\\"12\\\" x2=\\\"12\\\" y1=\\\"8\\\" y2=\\\"12\\\" /><line x1=\\\"12\\\" x2=\\\"12.01\\\" y1=\\\"16\\\" y2=\\\"16\\\" />\",\n \"alert-triangle\": \"<path d=\\\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\\\" /><path d=\\\"M12 9v4\\\" /><path d=\\\"M12 17h.01\\\" />\",\n \"info\": \"<circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"10\\\" /><path d=\\\"M12 16v-4\\\" /><path d=\\\"M12 8h.01\\\" />\",\n \"external-link\": \"<path d=\\\"M15 3h6v6\\\" /><path d=\\\"M10 14 21 3\\\" /><path d=\\\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\\\" />\",\n \"download\": \"<path d=\\\"M12 15V3\\\" /><path d=\\\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\\\" /><path d=\\\"m7 10 5 5 5-5\\\" />\",\n \"refresh-cw\": \"<path d=\\\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\\\" /><path d=\\\"M21 3v5h-5\\\" /><path d=\\\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\\\" /><path d=\\\"M8 16H3v5\\\" />\",\n \"clock\": \"<path d=\\\"M12 6v6l4 2\\\" /><circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"10\\\" />\",\n \"database\": \"<ellipse cx=\\\"12\\\" cy=\\\"5\\\" rx=\\\"9\\\" ry=\\\"3\\\" /><path d=\\\"M3 5V19A9 3 0 0 0 21 19V5\\\" /><path d=\\\"M3 12A9 3 0 0 0 21 12\\\" />\",\n \"server\": \"<rect width=\\\"20\\\" height=\\\"8\\\" x=\\\"2\\\" y=\\\"2\\\" rx=\\\"2\\\" ry=\\\"2\\\" /><rect width=\\\"20\\\" height=\\\"8\\\" x=\\\"2\\\" y=\\\"14\\\" rx=\\\"2\\\" ry=\\\"2\\\" /><line x1=\\\"6\\\" x2=\\\"6.01\\\" y1=\\\"6\\\" y2=\\\"6\\\" /><line x1=\\\"6\\\" x2=\\\"6.01\\\" y1=\\\"18\\\" y2=\\\"18\\\" />\",\n \"activity\": \"<path d=\\\"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2\\\" />\",\n \"layout-dashboard\": \"<rect width=\\\"7\\\" height=\\\"9\\\" x=\\\"3\\\" y=\\\"3\\\" rx=\\\"1\\\" /><rect width=\\\"7\\\" height=\\\"5\\\" x=\\\"14\\\" y=\\\"3\\\" rx=\\\"1\\\" /><rect width=\\\"7\\\" height=\\\"9\\\" x=\\\"14\\\" y=\\\"12\\\" rx=\\\"1\\\" /><rect width=\\\"7\\\" height=\\\"5\\\" x=\\\"3\\\" y=\\\"16\\\" rx=\\\"1\\\" />\",\n \"audio-lines\": \"<path d=\\\"M2 10v3\\\" /><path d=\\\"M6 6v11\\\" /><path d=\\\"M10 3v18\\\" /><path d=\\\"M14 8v7\\\" /><path d=\\\"M18 5v13\\\" /><path d=\\\"M22 10v3\\\" />\",\n \"message-circle-question\": \"<path d=\\\"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719\\\" /><path d=\\\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\\\" /><path d=\\\"M12 17h.01\\\" />\",\n \"route\": \"<circle cx=\\\"6\\\" cy=\\\"19\\\" r=\\\"3\\\" /><path d=\\\"M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15\\\" /><circle cx=\\\"18\\\" cy=\\\"5\\\" r=\\\"3\\\" />\",\n \"trending-up\": \"<path d=\\\"M16 7h6v6\\\" /><path d=\\\"m22 7-8.5 8.5-5-5L2 17\\\" />\",\n \"trending-down\": \"<path d=\\\"M16 17h6v-6\\\" /><path d=\\\"m22 17-8.5-8.5-5 5L2 7\\\" />\",\n \"move-right\": \"<path d=\\\"M18 8L22 12L18 16\\\" /><path d=\\\"M2 12H22\\\" />\",\n \"eye\": \"<path d=\\\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\\\" /><circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"3\\\" />\"\n};\n\n // Global icon registry\n window.RailsPulseIcons = {\n icons: icons,\n\n // Get icon SVG content\n get: function(name) {\n return icons[name] || null;\n },\n\n // Check if icon exists\n has: function(name) {\n return name in icons;\n },\n\n // Get all icon names\n list: function() {\n return Object.keys(icons);\n },\n\n // Render icon to element (CSP-safe)\n render: function(name, element, options = {}) {\n const svgContent = this.get(name);\n if (!svgContent || !element) return false;\n\n // Create SVG element\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('width', options.width || '24');\n svg.setAttribute('height', options.height || '24');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke', 'currentColor');\n svg.setAttribute('stroke-width', '2');\n svg.setAttribute('stroke-linecap', 'round');\n svg.setAttribute('stroke-linejoin', 'round');\n\n // Add icon content\n svg.innerHTML = svgContent;\n\n // Replace element content\n element.innerHTML = '';\n element.appendChild(svg);\n\n return true;\n }\n };\n})();\n"
|
|
12
12
|
]
|
|
13
13
|
}
|