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
|
@@ -1,279 +1,507 @@
|
|
|
1
|
-
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
12
|
-
|
|
13
|
-
<!-- Chart.js -->
|
|
14
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js" defer></script>
|
|
15
|
-
|
|
16
|
-
<!-- Theme CSS -->
|
|
17
|
-
<link rel="stylesheet" href="assets/css/themes.css">
|
|
18
|
-
|
|
19
|
-
<!-- Favicon -->
|
|
20
|
-
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23F97316' viewBox='0 0 24 24'%3e%3cpath d='M12 2L2 7v10c0 5.55 3.84 9.95 9 11 5.16-1.05 9-5.45 9-11V7l-10-5z'/%3e%3c/svg%3e">
|
|
21
|
-
</head>
|
|
22
|
-
<body>
|
|
23
|
-
<!-- Navigation Bar -->
|
|
24
|
-
<nav class="navbar">
|
|
25
|
-
<div class="navbar-container">
|
|
26
|
-
<!-- Header -->
|
|
27
|
-
<div class="navbar-header">
|
|
28
|
-
<a href="#" class="navbar-brand">
|
|
29
|
-
<div class="navbar-brand-icon">
|
|
30
|
-
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
31
|
-
<path d="M12 2L2 7v10c0 5.55 3.84 9.95 9 11 5.16-1.05 9-5.45 9-11V7l-10-5z"/>
|
|
32
|
-
</svg>
|
|
33
|
-
</div>
|
|
34
|
-
SerialBench
|
|
35
|
-
</a>
|
|
36
|
-
|
|
37
|
-
<div class="navbar-controls">
|
|
38
|
-
<button class="theme-toggle" title="Toggle theme">
|
|
39
|
-
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
40
|
-
<circle cx="12" cy="12" r="5"></circle>
|
|
41
|
-
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
42
|
-
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
43
|
-
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
44
|
-
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
45
|
-
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
46
|
-
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
47
|
-
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
48
|
-
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
49
|
-
</svg>
|
|
50
|
-
</button>
|
|
51
|
-
</div>
|
|
1
|
+
<!-- Filter Bar -->
|
|
2
|
+
<div class="filter-bar">
|
|
3
|
+
<div class="filter-container">
|
|
4
|
+
<!-- Platform Filter -->
|
|
5
|
+
<div class="filter-group">
|
|
6
|
+
<label class="filter-label" for="platform-filter">Platform:</label>
|
|
7
|
+
<div class="custom-select">
|
|
8
|
+
<select id="platform-filter">
|
|
9
|
+
<option value="">All Platforms</option>
|
|
10
|
+
</select>
|
|
52
11
|
</div>
|
|
12
|
+
</div>
|
|
53
13
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
</select>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
14
|
+
<!-- Ruby Type Filter -->
|
|
15
|
+
<div class="filter-group">
|
|
16
|
+
<label class="filter-label" for="ruby-type-filter">Ruby Type:</label>
|
|
17
|
+
<div class="custom-select">
|
|
18
|
+
<select id="ruby-type-filter">
|
|
19
|
+
<option value="">All Types</option>
|
|
20
|
+
</select>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
66
23
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
24
|
+
<!-- Ruby Version Filter -->
|
|
25
|
+
<div class="filter-group">
|
|
26
|
+
<label class="filter-label" for="ruby-version-filter">Version:</label>
|
|
27
|
+
<div class="custom-select">
|
|
28
|
+
<select id="ruby-version-filter">
|
|
29
|
+
<option value="">All Versions</option>
|
|
30
|
+
</select>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
76
33
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
34
|
+
<!-- Format Tabs -->
|
|
35
|
+
<div class="format-tabs">
|
|
36
|
+
<button class="format-tab active" data-format="xml">XML</button>
|
|
37
|
+
<button class="format-tab" data-format="json">JSON</button>
|
|
38
|
+
<button class="format-tab" data-format="yaml">YAML</button>
|
|
39
|
+
<button class="format-tab" data-format="toml">TOML</button>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
86
43
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
44
|
+
<!-- Dashboard Header -->
|
|
45
|
+
<header class="dashboard-header">
|
|
46
|
+
<h1>Performance Dashboard</h1>
|
|
47
|
+
<p class="dashboard-subtitle">
|
|
48
|
+
Comprehensive serialization performance analysis across formats, platforms, and Ruby versions
|
|
49
|
+
</p>
|
|
50
|
+
<div class="dashboard-meta">
|
|
51
|
+
<span><strong>Generated:</strong> <span id="metadata-timestamp">Unknown</span></span>
|
|
52
|
+
<span><strong>Ruby Versions:</strong> <span id="metadata-ruby-versions">Unknown</span></span>
|
|
53
|
+
<span><strong>Platforms:</strong> <span id="metadata-platforms">Unknown</span></span>
|
|
54
|
+
</div>
|
|
55
|
+
</header>
|
|
56
|
+
|
|
57
|
+
<!-- Charts Grid -->
|
|
58
|
+
<section class="dashboard-grid">
|
|
59
|
+
<!-- Parsing Performance Chart -->
|
|
60
|
+
<div class="chart-card fade-in-up">
|
|
61
|
+
<div class="chart-header">
|
|
62
|
+
<h2 class="chart-title">Parsing Performance</h2>
|
|
63
|
+
<p class="chart-subtitle">Operations per second across different file sizes</p>
|
|
96
64
|
</div>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<!-- Dashboard Header -->
|
|
102
|
-
<header class="dashboard-header">
|
|
103
|
-
<h1>Performance Dashboard</h1>
|
|
104
|
-
<p class="dashboard-subtitle">
|
|
105
|
-
Comprehensive serialization performance analysis across formats, platforms, and Ruby versions
|
|
106
|
-
</p>
|
|
107
|
-
<div class="dashboard-meta">
|
|
108
|
-
<span><strong>Generated:</strong> {{ metadata.timestamp | default: "Unknown" }}</span>
|
|
109
|
-
<span><strong>Ruby Versions:</strong> {{ metadata.ruby_versions | join: ", " | default: "Unknown" }}</span>
|
|
110
|
-
<span><strong>Platforms:</strong> {{ metadata.platforms | join: ", " | default: "Unknown" }}</span>
|
|
111
|
-
</div>
|
|
112
|
-
</header>
|
|
113
|
-
|
|
114
|
-
<!-- Charts Grid -->
|
|
115
|
-
<section class="dashboard-grid">
|
|
116
|
-
<!-- Parsing Performance Chart -->
|
|
117
|
-
<div class="chart-card fade-in-up">
|
|
118
|
-
<div class="chart-header">
|
|
119
|
-
<h2 class="chart-title">Parsing Performance</h2>
|
|
120
|
-
<p class="chart-subtitle">Operations per second across different file sizes</p>
|
|
121
|
-
</div>
|
|
122
|
-
<div class="chart-container">
|
|
123
|
-
<canvas id="chart-parsing"></canvas>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
65
|
+
<div class="chart-container">
|
|
66
|
+
<canvas id="chart-parsing"></canvas>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
126
69
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
70
|
+
<!-- Generation Performance Chart -->
|
|
71
|
+
<div class="chart-card fade-in-up">
|
|
72
|
+
<div class="chart-header">
|
|
73
|
+
<h2 class="chart-title">Generation Performance</h2>
|
|
74
|
+
<p class="chart-subtitle">Serialization speed across different file sizes</p>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="chart-container">
|
|
77
|
+
<canvas id="chart-generation"></canvas>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
137
80
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
81
|
+
<!-- Memory Usage Chart -->
|
|
82
|
+
<div class="chart-card fade-in-up">
|
|
83
|
+
<div class="chart-header">
|
|
84
|
+
<h2 class="chart-title">Memory Usage</h2>
|
|
85
|
+
<p class="chart-subtitle">Memory consumption during operations</p>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="chart-container">
|
|
88
|
+
<canvas id="chart-memory"></canvas>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
148
91
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
92
|
+
<!-- Streaming Performance Chart -->
|
|
93
|
+
<div class="chart-card fade-in-up">
|
|
94
|
+
<div class="chart-header">
|
|
95
|
+
<h2 class="chart-title">Streaming Performance</h2>
|
|
96
|
+
<p class="chart-subtitle">Streaming operations performance</p>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="chart-container">
|
|
99
|
+
<canvas id="chart-streaming"></canvas>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</section>
|
|
160
103
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
</main>
|
|
169
|
-
|
|
170
|
-
<!-- Embedded Data -->
|
|
171
|
-
<script>
|
|
172
|
-
window.benchmarkData = {{ data }};
|
|
173
|
-
</script>
|
|
174
|
-
|
|
175
|
-
<!-- JavaScript -->
|
|
176
|
-
<script src="assets/js/dashboard.js" defer></script>
|
|
177
|
-
|
|
178
|
-
<!-- Loading indicator -->
|
|
179
|
-
<style>
|
|
180
|
-
.loading-overlay {
|
|
181
|
-
position: fixed;
|
|
182
|
-
top: 0;
|
|
183
|
-
left: 0;
|
|
184
|
-
right: 0;
|
|
185
|
-
bottom: 0;
|
|
186
|
-
background: var(--bg-primary);
|
|
187
|
-
display: flex;
|
|
188
|
-
align-items: center;
|
|
189
|
-
justify-content: center;
|
|
190
|
-
z-index: 9999;
|
|
191
|
-
transition: opacity 0.3s ease;
|
|
192
|
-
}
|
|
104
|
+
<!-- Environment Information -->
|
|
105
|
+
<section class="environment-section fade-in-up">
|
|
106
|
+
<h2 class="summary-title">Environment Information</h2>
|
|
107
|
+
<div class="environment-grid" id="environment-info">
|
|
108
|
+
<!-- Environment cards will be populated by JavaScript -->
|
|
109
|
+
</div>
|
|
110
|
+
</section>
|
|
193
111
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
112
|
+
<!-- Raw Data Downloads -->
|
|
113
|
+
<section class="downloads-section fade-in-up">
|
|
114
|
+
<h2 class="summary-title">Raw Data Downloads</h2>
|
|
115
|
+
<p class="downloads-subtitle">Download benchmark data in YAML format for further analysis</p>
|
|
116
|
+
<div class="downloads-grid" id="downloads-grid">
|
|
117
|
+
<!-- Download links will be populated by JavaScript -->
|
|
118
|
+
</div>
|
|
119
|
+
</section>
|
|
120
|
+
|
|
121
|
+
<!-- Embedded Data -->
|
|
122
|
+
<script>
|
|
123
|
+
window.benchmarkData = {{ data }};
|
|
124
|
+
|
|
125
|
+
// Populate metadata on load
|
|
126
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
127
|
+
try {
|
|
128
|
+
const data = window.benchmarkData;
|
|
129
|
+
|
|
130
|
+
// Extract metadata
|
|
131
|
+
const timestamps = [];
|
|
132
|
+
const rubyVersions = new Set();
|
|
133
|
+
const platforms = new Set();
|
|
134
|
+
|
|
135
|
+
// Handle both single results (with environments) and resultsets (with results)
|
|
136
|
+
if (data.environments) {
|
|
137
|
+
// Single result format
|
|
138
|
+
Object.values(data.environments).forEach(env => {
|
|
139
|
+
if (env.timestamp) {
|
|
140
|
+
timestamps.push(new Date(env.timestamp));
|
|
141
|
+
}
|
|
142
|
+
if (env.ruby_version) {
|
|
143
|
+
rubyVersions.add(env.ruby_version);
|
|
144
|
+
}
|
|
145
|
+
if (env.os && env.arch) {
|
|
146
|
+
platforms.add(`${env.os} (${env.arch})`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
} else if (data.results) {
|
|
150
|
+
// Resultset format
|
|
151
|
+
data.results.forEach(result => {
|
|
152
|
+
if (result.metadata && result.metadata.created_at) {
|
|
153
|
+
timestamps.push(new Date(result.metadata.created_at));
|
|
154
|
+
}
|
|
155
|
+
if (result.platform) {
|
|
156
|
+
if (result.platform.ruby_version) {
|
|
157
|
+
rubyVersions.add(result.platform.ruby_version);
|
|
158
|
+
}
|
|
159
|
+
if (result.platform.os) {
|
|
160
|
+
platforms.add(result.platform.os);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Update timestamp
|
|
167
|
+
const timestampEl = document.getElementById('metadata-timestamp');
|
|
168
|
+
if (timestampEl) {
|
|
169
|
+
if (timestamps.length > 0) {
|
|
170
|
+
const latestDate = new Date(Math.max(...timestamps));
|
|
171
|
+
timestampEl.textContent = latestDate.toLocaleString();
|
|
172
|
+
} else if (data.metadata && data.metadata.generated_at) {
|
|
173
|
+
timestampEl.textContent = new Date(data.metadata.generated_at).toLocaleString();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
202
176
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
177
|
+
// Update Ruby versions
|
|
178
|
+
const versionsEl = document.getElementById('metadata-ruby-versions');
|
|
179
|
+
if (versionsEl && rubyVersions.size > 0) {
|
|
180
|
+
versionsEl.textContent = Array.from(rubyVersions).sort().join(', ');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Update platforms
|
|
184
|
+
const platformsEl = document.getElementById('metadata-platforms');
|
|
185
|
+
if (platformsEl && platforms.size > 0) {
|
|
186
|
+
platformsEl.textContent = Array.from(platforms).sort().join(', ');
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('Error populating metadata:', error);
|
|
206
190
|
}
|
|
207
191
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
192
|
+
// Populate downloads grid
|
|
193
|
+
try {
|
|
194
|
+
const downloadsGrid = document.getElementById('downloads-grid');
|
|
195
|
+
if (downloadsGrid && data.environments) {
|
|
196
|
+
// Create download cards for individual environment results from environments object
|
|
197
|
+
Object.entries(data.environments).forEach(([envKey, env]) => {
|
|
198
|
+
const filename = `${envKey}.yaml`;
|
|
199
|
+
|
|
200
|
+
const card = document.createElement('div');
|
|
201
|
+
card.className = 'download-card';
|
|
202
|
+
card.innerHTML = `
|
|
203
|
+
<div class="download-card-title">
|
|
204
|
+
${env.os} ${env.arch}
|
|
205
|
+
</div>
|
|
206
|
+
<div class="download-card-meta">
|
|
207
|
+
Ruby ${env.ruby_version}
|
|
208
|
+
</div>
|
|
209
|
+
<a href="data/${filename}" class="download-link" download>
|
|
210
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
211
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
|
|
212
|
+
</svg>
|
|
213
|
+
Download YAML
|
|
214
|
+
</a>
|
|
215
|
+
`;
|
|
216
|
+
downloadsGrid.appendChild(card);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Add complete resultset download
|
|
220
|
+
const resultsetCard = document.createElement('div');
|
|
221
|
+
resultsetCard.className = 'download-card';
|
|
222
|
+
resultsetCard.style.gridColumn = 'span 1';
|
|
223
|
+
resultsetCard.innerHTML = `
|
|
224
|
+
<div class="download-card-title">
|
|
225
|
+
Complete Dataset
|
|
226
|
+
</div>
|
|
227
|
+
<div class="download-card-meta">
|
|
228
|
+
All environments and results
|
|
229
|
+
</div>
|
|
230
|
+
<a href="data/resultset.yaml" class="download-link" download>
|
|
231
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
232
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
|
|
233
|
+
</svg>
|
|
234
|
+
Download YAML
|
|
235
|
+
</a>
|
|
236
|
+
`;
|
|
237
|
+
downloadsGrid.appendChild(resultsetCard);
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('Error populating downloads:', error);
|
|
212
241
|
}
|
|
242
|
+
});
|
|
243
|
+
</script>
|
|
213
244
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
245
|
+
<!-- Additional JavaScript -->
|
|
246
|
+
<script src="assets/js/dashboard.js" defer></script>
|
|
247
|
+
|
|
248
|
+
<!-- Theme CSS (additional to base) -->
|
|
249
|
+
<link rel="stylesheet" href="assets/css/themes.css">
|
|
250
|
+
<link rel="stylesheet" href="assets/css/format_based.css">
|
|
251
|
+
|
|
252
|
+
<!-- Theme Toggle Button -->
|
|
253
|
+
<script>
|
|
254
|
+
// Add theme toggle to navbar
|
|
255
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
256
|
+
const navbar = document.querySelector('.navbar-header');
|
|
257
|
+
if (navbar) {
|
|
258
|
+
const controls = document.createElement('div');
|
|
259
|
+
controls.className = 'navbar-controls';
|
|
260
|
+
controls.innerHTML = `
|
|
261
|
+
<button class="theme-toggle" title="Toggle theme">
|
|
262
|
+
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
263
|
+
<circle cx="12" cy="12" r="5"></circle>
|
|
264
|
+
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
265
|
+
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
266
|
+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
267
|
+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
268
|
+
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
269
|
+
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
270
|
+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
271
|
+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
272
|
+
</svg>
|
|
273
|
+
</button>
|
|
274
|
+
`;
|
|
275
|
+
navbar.appendChild(controls);
|
|
223
276
|
}
|
|
277
|
+
});
|
|
278
|
+
</script>
|
|
279
|
+
|
|
280
|
+
<!-- Loading indicator -->
|
|
281
|
+
<style>
|
|
282
|
+
.loading-overlay {
|
|
283
|
+
position: fixed;
|
|
284
|
+
top: 0;
|
|
285
|
+
left: 0;
|
|
286
|
+
right: 0;
|
|
287
|
+
bottom: 0;
|
|
288
|
+
background: var(--bg-primary);
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
justify-content: center;
|
|
292
|
+
z-index: 9999;
|
|
293
|
+
transition: opacity 0.3s ease;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.loading-spinner {
|
|
297
|
+
width: 40px;
|
|
298
|
+
height: 40px;
|
|
299
|
+
border: 3px solid var(--border-primary);
|
|
300
|
+
border-top: 3px solid var(--accent-primary);
|
|
301
|
+
border-radius: 50%;
|
|
302
|
+
animation: spin 1s linear infinite;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
@keyframes spin {
|
|
306
|
+
0% { transform: rotate(0deg); }
|
|
307
|
+
100% { transform: rotate(360deg); }
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.dashboard-header {
|
|
311
|
+
text-align: center;
|
|
312
|
+
margin-bottom: var(--space-2xl, 2rem);
|
|
313
|
+
padding: var(--space-xl, 1.5rem) 0;
|
|
314
|
+
background: transparent;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.dashboard-header h1 {
|
|
318
|
+
font-size: 2.5rem;
|
|
319
|
+
font-weight: 700;
|
|
320
|
+
margin-bottom: var(--space-md, 1rem);
|
|
321
|
+
color: var(--text-primary);
|
|
322
|
+
position: relative;
|
|
323
|
+
z-index: 1;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.dashboard-subtitle {
|
|
327
|
+
font-size: 1.125rem;
|
|
328
|
+
color: var(--text-secondary);
|
|
329
|
+
margin-bottom: var(--space-lg, 1.25rem);
|
|
330
|
+
max-width: 600px;
|
|
331
|
+
margin-left: auto;
|
|
332
|
+
margin-right: auto;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.dashboard-meta {
|
|
336
|
+
display: flex;
|
|
337
|
+
justify-content: center;
|
|
338
|
+
gap: var(--space-lg, 1.25rem);
|
|
339
|
+
flex-wrap: wrap;
|
|
340
|
+
font-size: 0.875rem;
|
|
341
|
+
color: var(--text-muted);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.filter-bar {
|
|
345
|
+
background: var(--bg-secondary, #f9fafb);
|
|
346
|
+
border-bottom: 1px solid var(--border-primary, #e5e7eb);
|
|
347
|
+
padding: 1rem;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.filter-container {
|
|
351
|
+
max-width: 1200px;
|
|
352
|
+
margin: 0 auto;
|
|
353
|
+
display: flex;
|
|
354
|
+
gap: 1rem;
|
|
355
|
+
flex-wrap: wrap;
|
|
356
|
+
align-items: center;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.filter-group {
|
|
360
|
+
display: flex;
|
|
361
|
+
align-items: center;
|
|
362
|
+
gap: 0.5rem;
|
|
363
|
+
}
|
|
224
364
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
365
|
+
.filter-label {
|
|
366
|
+
font-size: 0.875rem;
|
|
367
|
+
font-weight: 500;
|
|
368
|
+
color: var(--text-secondary);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.custom-select select {
|
|
372
|
+
padding: 0.5rem 2rem 0.5rem 0.75rem;
|
|
373
|
+
border: 1px solid var(--border-primary);
|
|
374
|
+
border-radius: 0.375rem;
|
|
375
|
+
background: var(--bg-card);
|
|
376
|
+
color: var(--text-primary);
|
|
377
|
+
font-size: 0.875rem;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.custom-select select option {
|
|
381
|
+
color: var(--text-primary);
|
|
382
|
+
background: var(--bg-card);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/* Format tabs styles are in themes.css */
|
|
386
|
+
|
|
387
|
+
/* Downloads Section Styles */
|
|
388
|
+
.downloads-section {
|
|
389
|
+
margin-top: var(--space-2xl, 2rem);
|
|
390
|
+
padding: var(--space-xl, 1.5rem);
|
|
391
|
+
background: var(--bg-card);
|
|
392
|
+
border-radius: 0.75rem;
|
|
393
|
+
border: 1px solid var(--border-primary);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.downloads-subtitle {
|
|
397
|
+
text-align: center;
|
|
398
|
+
font-size: 1rem;
|
|
399
|
+
color: var(--text-secondary);
|
|
400
|
+
margin-bottom: var(--space-lg, 1.25rem);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.downloads-grid {
|
|
404
|
+
display: grid;
|
|
405
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
406
|
+
gap: var(--space-lg, 1.25rem);
|
|
407
|
+
margin-top: var(--space-lg, 1.25rem);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.download-card {
|
|
411
|
+
background: var(--bg-secondary);
|
|
412
|
+
border: 1px solid var(--border-primary);
|
|
413
|
+
border-radius: 0.5rem;
|
|
414
|
+
padding: var(--space-lg, 1.25rem);
|
|
415
|
+
transition: all 0.2s ease;
|
|
416
|
+
display: flex;
|
|
417
|
+
flex-direction: column;
|
|
418
|
+
gap: var(--space-sm, 0.5rem);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.download-card:hover {
|
|
422
|
+
border-color: var(--accent-primary);
|
|
423
|
+
transform: translateY(-2px);
|
|
424
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.download-card-title {
|
|
428
|
+
font-size: 1rem;
|
|
429
|
+
font-weight: 600;
|
|
430
|
+
color: var(--text-primary);
|
|
431
|
+
margin-bottom: var(--space-xs, 0.25rem);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.download-card-meta {
|
|
435
|
+
font-size: 0.875rem;
|
|
436
|
+
color: var(--text-muted);
|
|
437
|
+
margin-bottom: var(--space-sm, 0.5rem);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.download-link {
|
|
441
|
+
display: inline-flex;
|
|
442
|
+
align-items: center;
|
|
443
|
+
gap: 0.5rem;
|
|
444
|
+
padding: 0.625rem 1rem;
|
|
445
|
+
background: var(--accent-primary);
|
|
446
|
+
color: white;
|
|
447
|
+
text-decoration: none;
|
|
448
|
+
border-radius: 0.375rem;
|
|
449
|
+
font-size: 0.875rem;
|
|
450
|
+
font-weight: 500;
|
|
451
|
+
transition: background 0.2s ease;
|
|
452
|
+
justify-content: center;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.download-link:hover {
|
|
456
|
+
background: var(--accent-dark);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.download-link svg {
|
|
460
|
+
width: 16px;
|
|
461
|
+
height: 16px;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
@media (max-width: 768px) {
|
|
465
|
+
.dashboard-header h1 {
|
|
466
|
+
font-size: 2rem;
|
|
232
467
|
}
|
|
233
468
|
|
|
234
469
|
.dashboard-meta {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
gap: var(--space-lg);
|
|
238
|
-
flex-wrap: wrap;
|
|
239
|
-
font-size: 0.875rem;
|
|
240
|
-
color: var(--text-muted);
|
|
470
|
+
flex-direction: column;
|
|
471
|
+
gap: var(--space-sm, 0.5rem);
|
|
241
472
|
}
|
|
242
473
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
474
|
+
.filter-container {
|
|
475
|
+
flex-direction: column;
|
|
476
|
+
align-items: stretch;
|
|
477
|
+
}
|
|
247
478
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
gap: var(--space-sm);
|
|
251
|
-
}
|
|
479
|
+
.format-tabs {
|
|
480
|
+
margin-left: 0;
|
|
252
481
|
}
|
|
253
|
-
|
|
482
|
+
}
|
|
483
|
+
</style>
|
|
254
484
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
485
|
+
<div class="loading-overlay" id="loading-overlay">
|
|
486
|
+
<div class="loading-spinner"></div>
|
|
487
|
+
</div>
|
|
258
488
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
</body>
|
|
279
|
-
</html>
|
|
489
|
+
<script>
|
|
490
|
+
// Hide loading overlay once everything is loaded
|
|
491
|
+
window.addEventListener('load', () => {
|
|
492
|
+
const overlay = document.getElementById('loading-overlay');
|
|
493
|
+
if (overlay) {
|
|
494
|
+
overlay.style.opacity = '0';
|
|
495
|
+
setTimeout(() => overlay.remove(), 300);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// Fallback to hide loading after 5 seconds
|
|
500
|
+
setTimeout(() => {
|
|
501
|
+
const overlay = document.getElementById('loading-overlay');
|
|
502
|
+
if (overlay) {
|
|
503
|
+
overlay.style.opacity = '0';
|
|
504
|
+
setTimeout(() => overlay.remove(), 300);
|
|
505
|
+
}
|
|
506
|
+
}, 5000);
|
|
507
|
+
</script>
|