serialbench 0.1.2 → 0.1.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/.github/workflows/benchmark.yml +273 -228
- data/.github/workflows/rake.yml +11 -0
- data/.github/workflows/windows-debug.yml +171 -0
- data/.gitignore +32 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +274 -0
- data/Gemfile +13 -1
- data/README.adoc +36 -0
- data/data/schemas/result.yml +29 -0
- data/docs/PLATFORM_VALIDATION_FIX.md +79 -0
- data/docs/SYCK_YAML_FIX.md +91 -0
- data/docs/WEBSITE_COMPLETION_PLAN.md +440 -0
- data/docs/WINDOWS_LIBXML_FIX.md +136 -0
- data/docs/WINDOWS_SETUP.md +122 -0
- data/lib/serialbench/benchmark_runner.rb +3 -3
- data/lib/serialbench/cli/benchmark_cli.rb +74 -1
- data/lib/serialbench/cli/environment_cli.rb +3 -3
- data/lib/serialbench/cli/resultset_cli.rb +72 -26
- data/lib/serialbench/cli/ruby_build_cli.rb +75 -88
- data/lib/serialbench/cli/validate_cli.rb +88 -0
- data/lib/serialbench/cli.rb +6 -2
- data/lib/serialbench/config_manager.rb +15 -26
- data/lib/serialbench/models/benchmark_config.rb +12 -0
- data/lib/serialbench/models/benchmark_result.rb +39 -3
- data/lib/serialbench/models/environment_config.rb +3 -2
- data/lib/serialbench/models/platform.rb +56 -4
- data/lib/serialbench/models/result.rb +28 -1
- data/lib/serialbench/models/result_set.rb +8 -0
- data/lib/serialbench/ruby_build_manager.rb +19 -23
- data/lib/serialbench/runners/asdf_runner.rb +1 -1
- data/lib/serialbench/runners/docker_runner.rb +2 -4
- data/lib/serialbench/runners/local_runner.rb +71 -0
- data/lib/serialbench/serializers/base_serializer.rb +1 -1
- data/lib/serialbench/serializers/json/rapidjson_serializer.rb +1 -1
- data/lib/serialbench/serializers/toml/base_toml_serializer.rb +0 -2
- data/lib/serialbench/serializers/toml/toml_rb_serializer.rb +1 -1
- data/lib/serialbench/serializers/toml/tomlib_serializer.rb +1 -1
- data/lib/serialbench/serializers/xml/libxml_serializer.rb +4 -8
- data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +2 -2
- data/lib/serialbench/serializers/xml/oga_serializer.rb +4 -8
- data/lib/serialbench/serializers/xml/ox_serializer.rb +2 -2
- data/lib/serialbench/serializers/xml/rexml_serializer.rb +3 -3
- data/lib/serialbench/serializers/yaml/psych_serializer.rb +1 -1
- data/lib/serialbench/serializers/yaml/syck_serializer.rb +1 -1
- data/lib/serialbench/serializers.rb +2 -2
- data/lib/serialbench/site_generator.rb +180 -2
- data/lib/serialbench/templates/assets/css/format_based.css +1 -53
- data/lib/serialbench/templates/assets/css/themes.css +5 -4
- data/lib/serialbench/templates/assets/js/chart_helpers.js +44 -14
- data/lib/serialbench/templates/assets/js/dashboard.js +14 -15
- data/lib/serialbench/templates/format_based.liquid +480 -252
- data/lib/serialbench/version.rb +1 -1
- data/lib/serialbench/yaml_validator.rb +36 -0
- data/serialbench.gemspec +11 -2
- metadata +34 -23
- data/.github/workflows/ci.yml +0 -74
- data/.github/workflows/docker.yml +0 -272
|
@@ -58,26 +58,22 @@ module Serialbench
|
|
|
58
58
|
def build_xml_from_data(data, name = 'root')
|
|
59
59
|
require 'libxml'
|
|
60
60
|
|
|
61
|
+
element = LibXML::XML::Node.new(name.to_s)
|
|
61
62
|
case data
|
|
62
63
|
when Hash
|
|
63
|
-
element = LibXML::XML::Node.new(name.to_s)
|
|
64
64
|
data.each do |key, value|
|
|
65
65
|
child = build_xml_from_data(value, key.to_s)
|
|
66
66
|
element << child
|
|
67
67
|
end
|
|
68
|
-
element
|
|
69
68
|
when Array
|
|
70
|
-
element = LibXML::XML::Node.new(name.to_s)
|
|
71
69
|
data.each_with_index do |item, index|
|
|
72
70
|
child = build_xml_from_data(item, "item_#{index}")
|
|
73
71
|
element << child
|
|
74
72
|
end
|
|
75
|
-
element
|
|
76
73
|
else
|
|
77
|
-
element = LibXML::XML::Node.new(name.to_s)
|
|
78
74
|
element.content = data.to_s
|
|
79
|
-
element
|
|
80
75
|
end
|
|
76
|
+
element
|
|
81
77
|
end
|
|
82
78
|
|
|
83
79
|
# SAX handler for streaming
|
|
@@ -100,10 +96,10 @@ module Serialbench
|
|
|
100
96
|
@element_stack.push(@current_element)
|
|
101
97
|
end
|
|
102
98
|
|
|
103
|
-
def on_end_element(
|
|
99
|
+
def on_end_element(_element)
|
|
104
100
|
element_data = @element_stack.pop
|
|
105
101
|
if @element_stack.empty?
|
|
106
|
-
@block&.call(element_data)
|
|
102
|
+
@block&.call(element_data)
|
|
107
103
|
else
|
|
108
104
|
@element_stack.last[:children] << element_data
|
|
109
105
|
end
|
|
@@ -109,10 +109,10 @@ module Serialbench
|
|
|
109
109
|
@element_stack.push(@current_element)
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
-
def end_element(
|
|
112
|
+
def end_element(_name)
|
|
113
113
|
element = @element_stack.pop
|
|
114
114
|
if @element_stack.empty?
|
|
115
|
-
@block&.call(element)
|
|
115
|
+
@block&.call(element)
|
|
116
116
|
else
|
|
117
117
|
@element_stack.last[:children] << element
|
|
118
118
|
end
|
|
@@ -58,27 +58,23 @@ module Serialbench
|
|
|
58
58
|
def build_xml_from_data(data, name = 'root')
|
|
59
59
|
require 'oga'
|
|
60
60
|
|
|
61
|
+
element = Oga::XML::Element.new(name: name)
|
|
61
62
|
case data
|
|
62
63
|
when Hash
|
|
63
|
-
element = Oga::XML::Element.new(name: name)
|
|
64
64
|
data.each do |key, value|
|
|
65
65
|
child = build_xml_from_data(value, key.to_s)
|
|
66
66
|
element.children << child
|
|
67
67
|
end
|
|
68
|
-
element
|
|
69
68
|
when Array
|
|
70
|
-
element = Oga::XML::Element.new(name: name)
|
|
71
69
|
data.each_with_index do |item, index|
|
|
72
70
|
child = build_xml_from_data(item, "item_#{index}")
|
|
73
71
|
element.children << child
|
|
74
72
|
end
|
|
75
|
-
element
|
|
76
73
|
else
|
|
77
|
-
element = Oga::XML::Element.new(name: name)
|
|
78
74
|
text_node = Oga::XML::Text.new(text: data.to_s)
|
|
79
75
|
element.children << text_node
|
|
80
|
-
element
|
|
81
76
|
end
|
|
77
|
+
element
|
|
82
78
|
end
|
|
83
79
|
|
|
84
80
|
# SAX handler for streaming
|
|
@@ -108,10 +104,10 @@ module Serialbench
|
|
|
108
104
|
@element_stack.last[:text] += text if @element_stack.any?
|
|
109
105
|
end
|
|
110
106
|
|
|
111
|
-
def after_element(
|
|
107
|
+
def after_element(_namespace, _name)
|
|
112
108
|
element = @element_stack.pop
|
|
113
109
|
if @element_stack.empty?
|
|
114
|
-
@block&.call(element)
|
|
110
|
+
@block&.call(element)
|
|
115
111
|
else
|
|
116
112
|
@element_stack.last[:children] << element
|
|
117
113
|
end
|
|
@@ -61,10 +61,10 @@ module Serialbench
|
|
|
61
61
|
@element_stack.push(@current_element)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
def end_element(
|
|
64
|
+
def end_element(_name)
|
|
65
65
|
element = @element_stack.pop
|
|
66
66
|
if @element_stack.empty?
|
|
67
|
-
@block&.call(element)
|
|
67
|
+
@block&.call(element)
|
|
68
68
|
else
|
|
69
69
|
@element_stack.last[:children] << element
|
|
70
70
|
end
|
|
@@ -41,7 +41,7 @@ module Serialbench
|
|
|
41
41
|
|
|
42
42
|
indent = options.fetch(:indent, 0)
|
|
43
43
|
output = String.new
|
|
44
|
-
if indent
|
|
44
|
+
if indent.positive?
|
|
45
45
|
document.write(output, indent)
|
|
46
46
|
else
|
|
47
47
|
document.write(output)
|
|
@@ -120,7 +120,7 @@ module Serialbench
|
|
|
120
120
|
@result = nil
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
-
def start_element(
|
|
123
|
+
def start_element(_uri, _localname, qname, attributes)
|
|
124
124
|
@elements_processed += 1
|
|
125
125
|
@block&.call(:start_element, { name: qname, attributes: attributes })
|
|
126
126
|
end
|
|
@@ -132,7 +132,7 @@ module Serialbench
|
|
|
132
132
|
@block&.call(:text, text)
|
|
133
133
|
end
|
|
134
134
|
|
|
135
|
-
def end_element(
|
|
135
|
+
def end_element(_uri, _localname, qname)
|
|
136
136
|
@block&.call(:end_element, { name: qname })
|
|
137
137
|
end
|
|
138
138
|
end
|
|
@@ -80,11 +80,11 @@ module Serialbench
|
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
def self.available_for_format(format)
|
|
83
|
-
for_format(format).select
|
|
83
|
+
for_format(format).select(&:available?)
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def self.available
|
|
87
|
-
all.select
|
|
87
|
+
all.select(&:available?)
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
end
|
|
@@ -32,20 +32,29 @@ module Serialbench
|
|
|
32
32
|
|
|
33
33
|
def generate_site
|
|
34
34
|
target_name = @result ? @result.environment_config.name : @resultset.name
|
|
35
|
-
data = @result ? @result.to_json : @resultset.to_json
|
|
36
35
|
|
|
37
36
|
puts "🏗️ Generating HTML site for #{@result ? 'run' : 'resultset'}: #{target_name}"
|
|
38
37
|
puts "Output: #{@output_path}"
|
|
39
38
|
|
|
39
|
+
# Transform data for dashboard.js compatibility
|
|
40
|
+
data = if @result
|
|
41
|
+
transform_result_for_dashboard(@result)
|
|
42
|
+
else
|
|
43
|
+
transform_resultset_for_dashboard(@resultset)
|
|
44
|
+
end
|
|
45
|
+
|
|
40
46
|
prepare_output_directory
|
|
41
47
|
render_site(
|
|
42
48
|
{
|
|
43
|
-
'data' => data,
|
|
49
|
+
'data' => JSON.generate(data),
|
|
44
50
|
'kind' => @result ? 'run' : 'resultset'
|
|
45
51
|
},
|
|
46
52
|
'format_based.liquid'
|
|
47
53
|
)
|
|
48
54
|
|
|
55
|
+
# Export raw data files for download
|
|
56
|
+
export_raw_data
|
|
57
|
+
|
|
49
58
|
puts "✅ Site generated successfully at: #{@output_path}"
|
|
50
59
|
@output_path
|
|
51
60
|
end
|
|
@@ -101,5 +110,174 @@ module Serialbench
|
|
|
101
110
|
|
|
102
111
|
FileUtils.cp_r(assets_source, assets_dest)
|
|
103
112
|
end
|
|
113
|
+
|
|
114
|
+
# Transform a single Result into dashboard-compatible format
|
|
115
|
+
# Dashboard expects: { combined_results: {...}, environments: {...}, metadata: {...} }
|
|
116
|
+
def transform_result_for_dashboard(result)
|
|
117
|
+
env_key = "env-#{result.platform.ruby_version}"
|
|
118
|
+
|
|
119
|
+
# Build combined_results structure
|
|
120
|
+
combined_results = build_combined_results(result, env_key)
|
|
121
|
+
|
|
122
|
+
# Build environments structure
|
|
123
|
+
environments = {
|
|
124
|
+
env_key => {
|
|
125
|
+
'ruby_version' => result.platform.ruby_version,
|
|
126
|
+
'ruby_platform' => result.platform.ruby_platform || "#{result.platform.os}-#{result.platform.arch}",
|
|
127
|
+
'os' => result.platform.os,
|
|
128
|
+
'arch' => result.platform.arch,
|
|
129
|
+
'source_file' => result.metadata.environment_config_path,
|
|
130
|
+
'timestamp' => result.metadata.created_at
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
{
|
|
135
|
+
'combined_results' => combined_results,
|
|
136
|
+
'environments' => environments,
|
|
137
|
+
'metadata' => {
|
|
138
|
+
'generated_at' => Time.now.iso8601
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Transform a ResultSet (collection of Results) into dashboard-compatible format
|
|
144
|
+
# Combines all results into a single dashboard structure
|
|
145
|
+
def transform_resultset_for_dashboard(resultset)
|
|
146
|
+
combined_results = {}
|
|
147
|
+
environments = {}
|
|
148
|
+
|
|
149
|
+
resultset.results.each do |result|
|
|
150
|
+
# Create unique env key for this result
|
|
151
|
+
env_key = "#{result.platform.os}-#{result.platform.arch}-ruby-#{result.platform.ruby_version}"
|
|
152
|
+
|
|
153
|
+
# Merge this result's data into combined_results
|
|
154
|
+
result_combined = build_combined_results(result, env_key)
|
|
155
|
+
merge_combined_results!(combined_results, result_combined)
|
|
156
|
+
|
|
157
|
+
# Add environment info
|
|
158
|
+
environments[env_key] = {
|
|
159
|
+
'ruby_version' => result.platform.ruby_version,
|
|
160
|
+
'ruby_platform' => result.platform.ruby_platform || "#{result.platform.os}-#{result.platform.arch}",
|
|
161
|
+
'os' => result.platform.os,
|
|
162
|
+
'arch' => result.platform.arch,
|
|
163
|
+
'source_file' => result.metadata.environment_config_path,
|
|
164
|
+
'timestamp' => result.metadata.created_at
|
|
165
|
+
}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
{
|
|
169
|
+
'combined_results' => combined_results,
|
|
170
|
+
'environments' => environments,
|
|
171
|
+
'metadata' => {
|
|
172
|
+
'resultset_name' => resultset.name,
|
|
173
|
+
'resultset_description' => resultset.description,
|
|
174
|
+
'total_runs' => resultset.results.size,
|
|
175
|
+
'generated_at' => Time.now.iso8601
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Deep merge results from multiple runs
|
|
181
|
+
def merge_combined_results!(target, source)
|
|
182
|
+
source.each do |operation, sizes|
|
|
183
|
+
target[operation] ||= {}
|
|
184
|
+
sizes.each do |size, formats|
|
|
185
|
+
target[operation][size] ||= {}
|
|
186
|
+
formats.each do |format, serializers|
|
|
187
|
+
target[operation][size][format] ||= {}
|
|
188
|
+
serializers.each do |serializer, envs|
|
|
189
|
+
target[operation][size][format][serializer] ||= {}
|
|
190
|
+
target[operation][size][format][serializer].merge!(envs)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def build_combined_results(result, env_key)
|
|
198
|
+
combined = {}
|
|
199
|
+
|
|
200
|
+
%w[parsing generation streaming].each do |operation|
|
|
201
|
+
combined[operation] = {}
|
|
202
|
+
|
|
203
|
+
operation_results = result.benchmark_result.send(operation)
|
|
204
|
+
next if operation_results.nil? || operation_results.empty?
|
|
205
|
+
|
|
206
|
+
operation_results.each do |perf|
|
|
207
|
+
size = perf.data_size
|
|
208
|
+
format = perf.format
|
|
209
|
+
serializer = perf.adapter
|
|
210
|
+
|
|
211
|
+
combined[operation][size] ||= {}
|
|
212
|
+
combined[operation][size][format] ||= {}
|
|
213
|
+
combined[operation][size][format][serializer] ||= {}
|
|
214
|
+
combined[operation][size][format][serializer][env_key] = {
|
|
215
|
+
'iterations_per_second' => perf.iterations_per_second,
|
|
216
|
+
'time_per_iteration' => perf.time_per_iteration
|
|
217
|
+
}
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Handle memory separately
|
|
222
|
+
if result.benchmark_result.memory && !result.benchmark_result.memory.empty?
|
|
223
|
+
combined['memory'] = {}
|
|
224
|
+
|
|
225
|
+
result.benchmark_result.memory.each do |mem|
|
|
226
|
+
size = mem.data_size
|
|
227
|
+
format = mem.format
|
|
228
|
+
serializer = mem.adapter
|
|
229
|
+
|
|
230
|
+
combined['memory'][size] ||= {}
|
|
231
|
+
combined['memory'][size][format] ||= {}
|
|
232
|
+
combined['memory'][size][format][serializer] ||= {}
|
|
233
|
+
combined['memory'][size][format][serializer][env_key] = {
|
|
234
|
+
'allocated_memory' => mem.allocated_memory,
|
|
235
|
+
'retained_memory' => mem.retained_memory
|
|
236
|
+
}
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
combined
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Export raw data files for download
|
|
244
|
+
def export_raw_data
|
|
245
|
+
data_dir = File.join(@output_path, 'data')
|
|
246
|
+
FileUtils.mkdir_p(data_dir)
|
|
247
|
+
|
|
248
|
+
if @result
|
|
249
|
+
# Export single result
|
|
250
|
+
export_single_result(@result, data_dir)
|
|
251
|
+
elsif @resultset
|
|
252
|
+
# Export all results in resultset plus the complete resultset
|
|
253
|
+
export_resultset(@resultset, data_dir)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
puts "📦 Raw data files exported to: #{data_dir}"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def export_single_result(result, data_dir)
|
|
260
|
+
# Create filename based on platform info
|
|
261
|
+
env_key = "#{result.platform.os}-#{result.platform.arch}-ruby-#{result.platform.ruby_version}"
|
|
262
|
+
filename = "#{env_key}.yaml"
|
|
263
|
+
filepath = File.join(data_dir, filename)
|
|
264
|
+
|
|
265
|
+
# Write result as YAML
|
|
266
|
+
File.write(filepath, result.to_yaml)
|
|
267
|
+
puts " 📄 Exported: #{filename}"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def export_resultset(resultset, data_dir)
|
|
271
|
+
# Export each individual result
|
|
272
|
+
resultset.results.each do |result|
|
|
273
|
+
export_single_result(result, data_dir)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Export complete resultset
|
|
277
|
+
resultset_filename = 'resultset.yaml'
|
|
278
|
+
resultset_filepath = File.join(data_dir, resultset_filename)
|
|
279
|
+
File.write(resultset_filepath, resultset.to_yaml)
|
|
280
|
+
puts " 📄 Exported: #{resultset_filename} (complete dataset)"
|
|
281
|
+
end
|
|
104
282
|
end
|
|
105
283
|
end
|
|
@@ -13,59 +13,7 @@
|
|
|
13
13
|
flex-wrap: wrap;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
padding: 12px 24px;
|
|
18
|
-
border: none;
|
|
19
|
-
background: var(--card-bg);
|
|
20
|
-
color: var(--text-color);
|
|
21
|
-
border-radius: 25px;
|
|
22
|
-
cursor: pointer;
|
|
23
|
-
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
24
|
-
font-weight: 600;
|
|
25
|
-
font-size: 0.95em;
|
|
26
|
-
box-shadow: var(--shadow-soft);
|
|
27
|
-
border: 2px solid transparent;
|
|
28
|
-
position: relative;
|
|
29
|
-
overflow: hidden;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.format-tab::before {
|
|
33
|
-
content: '';
|
|
34
|
-
position: absolute;
|
|
35
|
-
top: 0;
|
|
36
|
-
left: -100%;
|
|
37
|
-
width: 100%;
|
|
38
|
-
height: 100%;
|
|
39
|
-
background: var(--gradient-accent);
|
|
40
|
-
transition: left 0.3s ease;
|
|
41
|
-
z-index: 0;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.format-tab:hover {
|
|
45
|
-
transform: translateY(-3px);
|
|
46
|
-
box-shadow: var(--shadow-medium);
|
|
47
|
-
border-color: var(--secondary-color);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.format-tab:hover::before {
|
|
51
|
-
left: 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.format-tab > * {
|
|
55
|
-
position: relative;
|
|
56
|
-
z-index: 1;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.format-tab.active {
|
|
60
|
-
background: var(--gradient-primary);
|
|
61
|
-
color: white;
|
|
62
|
-
transform: translateY(-2px);
|
|
63
|
-
box-shadow: var(--shadow-medium);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.format-tab.active::before {
|
|
67
|
-
display: none;
|
|
68
|
-
}
|
|
16
|
+
/* Format tab styles are defined in themes.css */
|
|
69
17
|
|
|
70
18
|
/* Format Content */
|
|
71
19
|
.format-content {
|
|
@@ -259,7 +259,7 @@ body {
|
|
|
259
259
|
|
|
260
260
|
.format-tab {
|
|
261
261
|
padding: var(--space-sm) var(--space-lg);
|
|
262
|
-
background:
|
|
262
|
+
background: var(--bg-card);
|
|
263
263
|
border: 1px solid var(--border-primary);
|
|
264
264
|
border-radius: var(--radius-md);
|
|
265
265
|
color: var(--text-secondary);
|
|
@@ -271,14 +271,15 @@ body {
|
|
|
271
271
|
letter-spacing: 0.05em;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
.format-tab:hover {
|
|
275
|
-
background: var(--bg-
|
|
274
|
+
.format-tab:hover:not(.active) {
|
|
275
|
+
background: var(--bg-tertiary);
|
|
276
276
|
color: var(--text-primary);
|
|
277
|
+
border-color: var(--border-secondary);
|
|
277
278
|
transform: translateY(-1px);
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
.format-tab.active {
|
|
281
|
-
background: var(--
|
|
282
|
+
background: var(--accent-primary);
|
|
282
283
|
border-color: var(--accent-primary);
|
|
283
284
|
color: white;
|
|
284
285
|
box-shadow: var(--shadow-md);
|
|
@@ -172,9 +172,15 @@ function createPerformanceChart(canvasId, title, data, metric, environments) {
|
|
|
172
172
|
console.log(`🌍 Environments:`, environments);
|
|
173
173
|
console.log(`📊 Raw data:`, data);
|
|
174
174
|
|
|
175
|
+
// Get theme-aware colors
|
|
176
|
+
const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark';
|
|
177
|
+
const textColor = getComputedStyle(document.documentElement).getPropertyValue('--text-primary').trim() || (isDarkMode ? '#F8FAFC' : '#0F172A');
|
|
178
|
+
const gridColor = getComputedStyle(document.documentElement).getPropertyValue('--border-primary').trim() || (isDarkMode ? '#475569' : '#E2E8F0');
|
|
179
|
+
|
|
180
|
+
// High contrast color palette
|
|
175
181
|
const colors = [
|
|
176
|
-
'#
|
|
177
|
-
'#
|
|
182
|
+
'#F97316', '#3B82F6', '#10B981', '#EF4444', '#8B5CF6',
|
|
183
|
+
'#F59E0B', '#EC4899', '#06B6D4', '#84CC16', '#6366F1'
|
|
178
184
|
];
|
|
179
185
|
|
|
180
186
|
const datasets = serializers.map((serializer, index) => {
|
|
@@ -224,14 +230,15 @@ function createPerformanceChart(canvasId, title, data, metric, environments) {
|
|
|
224
230
|
display: true,
|
|
225
231
|
text: title,
|
|
226
232
|
font: { size: 16, weight: 'bold' },
|
|
227
|
-
color:
|
|
233
|
+
color: textColor
|
|
228
234
|
},
|
|
229
235
|
legend: {
|
|
230
236
|
position: 'top',
|
|
231
237
|
labels: {
|
|
232
238
|
usePointStyle: true,
|
|
233
239
|
padding: 20,
|
|
234
|
-
font: { size: 12 }
|
|
240
|
+
font: { size: 12 },
|
|
241
|
+
color: textColor
|
|
235
242
|
}
|
|
236
243
|
}
|
|
237
244
|
},
|
|
@@ -241,17 +248,25 @@ function createPerformanceChart(canvasId, title, data, metric, environments) {
|
|
|
241
248
|
title: {
|
|
242
249
|
display: true,
|
|
243
250
|
text: 'Iterations per Second',
|
|
244
|
-
font: { size: 12, weight: 'bold' }
|
|
251
|
+
font: { size: 12, weight: 'bold' },
|
|
252
|
+
color: textColor
|
|
253
|
+
},
|
|
254
|
+
ticks: {
|
|
255
|
+
color: textColor
|
|
245
256
|
},
|
|
246
257
|
grid: {
|
|
247
|
-
color:
|
|
258
|
+
color: gridColor
|
|
248
259
|
}
|
|
249
260
|
},
|
|
250
261
|
x: {
|
|
251
262
|
title: {
|
|
252
263
|
display: true,
|
|
253
264
|
text: 'Ruby Versions',
|
|
254
|
-
font: { size: 12, weight: 'bold' }
|
|
265
|
+
font: { size: 12, weight: 'bold' },
|
|
266
|
+
color: textColor
|
|
267
|
+
},
|
|
268
|
+
ticks: {
|
|
269
|
+
color: textColor
|
|
255
270
|
},
|
|
256
271
|
grid: {
|
|
257
272
|
display: false
|
|
@@ -294,9 +309,15 @@ function createMemoryChart(canvasId, title, data, environments) {
|
|
|
294
309
|
return;
|
|
295
310
|
}
|
|
296
311
|
|
|
312
|
+
// Get theme-aware colors
|
|
313
|
+
const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark';
|
|
314
|
+
const textColor = getComputedStyle(document.documentElement).getPropertyValue('--text-primary').trim() || (isDarkMode ? '#F8FAFC' : '#0F172A');
|
|
315
|
+
const gridColor = getComputedStyle(document.documentElement).getPropertyValue('--border-primary').trim() || (isDarkMode ? '#475569' : '#E2E8F0');
|
|
316
|
+
|
|
317
|
+
// High contrast color palette
|
|
297
318
|
const colors = [
|
|
298
|
-
'#
|
|
299
|
-
'#
|
|
319
|
+
'#F97316', '#3B82F6', '#10B981', '#EF4444', '#8B5CF6',
|
|
320
|
+
'#F59E0B', '#EC4899', '#06B6D4', '#84CC16', '#6366F1'
|
|
300
321
|
];
|
|
301
322
|
|
|
302
323
|
const datasets = serializers.map((serializer, index) => {
|
|
@@ -338,14 +359,15 @@ function createMemoryChart(canvasId, title, data, environments) {
|
|
|
338
359
|
display: true,
|
|
339
360
|
text: title,
|
|
340
361
|
font: { size: 16, weight: 'bold' },
|
|
341
|
-
color:
|
|
362
|
+
color: textColor
|
|
342
363
|
},
|
|
343
364
|
legend: {
|
|
344
365
|
position: 'top',
|
|
345
366
|
labels: {
|
|
346
367
|
usePointStyle: true,
|
|
347
368
|
padding: 20,
|
|
348
|
-
font: { size: 12 }
|
|
369
|
+
font: { size: 12 },
|
|
370
|
+
color: textColor
|
|
349
371
|
}
|
|
350
372
|
}
|
|
351
373
|
},
|
|
@@ -355,17 +377,25 @@ function createMemoryChart(canvasId, title, data, environments) {
|
|
|
355
377
|
title: {
|
|
356
378
|
display: true,
|
|
357
379
|
text: 'Memory Usage (MB)',
|
|
358
|
-
font: { size: 12, weight: 'bold' }
|
|
380
|
+
font: { size: 12, weight: 'bold' },
|
|
381
|
+
color: textColor
|
|
382
|
+
},
|
|
383
|
+
ticks: {
|
|
384
|
+
color: textColor
|
|
359
385
|
},
|
|
360
386
|
grid: {
|
|
361
|
-
color:
|
|
387
|
+
color: gridColor
|
|
362
388
|
}
|
|
363
389
|
},
|
|
364
390
|
x: {
|
|
365
391
|
title: {
|
|
366
392
|
display: true,
|
|
367
393
|
text: 'Ruby Versions',
|
|
368
|
-
font: { size: 12, weight: 'bold' }
|
|
394
|
+
font: { size: 12, weight: 'bold' },
|
|
395
|
+
color: textColor
|
|
396
|
+
},
|
|
397
|
+
ticks: {
|
|
398
|
+
color: textColor
|
|
369
399
|
},
|
|
370
400
|
grid: {
|
|
371
401
|
display: false
|