Switch to Chart.js to fix chart display

Frappe Charts is broken on recent browsers, and upstream is inactive.

This changes rendering from stacked horizontal bar graphs to donut charts,
as stacked horizontal bar graphs aren't available in Chart.js to my knowledge.
This commit is contained in:
Hugo Locurcio
2025-12-16 04:26:53 +01:00
parent 043826956e
commit 9b17935f79
4 changed files with 192 additions and 76 deletions

View File

@@ -44,7 +44,7 @@ but they allow seeing what kind of hardware and software is popular among issue
- The frontend is a single [`index.html`](/index.html) page, plus the
third-party dependencies mentioned below. No frontend building is required.
- [Frappe Charts](https://frappe.io/charts) is used to display charts.
- [Chart.js](https://www.chartjs.org/) is used to display charts.
- [Ky](https://github.com/sindresorhus/ky) is used to make an HTTP request to the JSON file.
- [Water.css](https://watercss.kognise.dev/) is used for styling the page.

View File

@@ -5,7 +5,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="Godot Engine">
<meta name="description" content="View hardware and software information for issues reported on the main Godot GitHub repository.">
<meta name="description"
content="View hardware and software information for issues reported on the main Godot GitHub repository.">
<meta name="theme-color" content="#3d8fcc">
<title>Godot GitHub issue statistics</title>
<link rel="icon" href="favicon.png">
@@ -28,18 +29,29 @@
margin-top: -3rem;
}
details {
align-items: unset;
}
summary {
/* Make accordion titles look more like headings. */
font-weight: bold;
font-size: 1.25rem;
}
.chart-container {
/* Limit size of donut charts on desktop. */
position: relative;
max-width: 20rem;
margin: 0 auto 2rem auto;
}
.chart-caption {
font-size: 0.9rem;
font-style: italic;
}
</style>
<script defer src="thirdparty/frappe-charts.umd.min.js"></script>
<script defer src="thirdparty/chart.umd.min.js"></script>
<script defer src="thirdparty/ky.umd.min.js"></script>
<script>
const capitalizeStringRemaps = {
@@ -280,6 +292,8 @@
INDIVIDUAL: 1,
}
const prefersDark = (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches);
function getRemappedString(label) {
// Capitalize string (like_this => Like This) if not found in the remaps table.
// Replace "Dedicated" and "Integrated" with shorter forms if not using the remaps table
@@ -303,20 +317,60 @@
// Creates a Frappe chart (automatically added to the DOM).
function createChart(id, chartDatatype, dataset) {
new frappe.Chart(id, {
data: {
labels: Object.keys(dataset).map(label => getRemappedString(label)),
datasets: [{
values: chartDatatype == ChartDatatype.INDIVIDUAL
const options = {
animation: false,
plugins: {
tooltip: {
callbacks: {
// Display percentage share when hovering a value in the donut chart.
label: function (context) {
try {
const data = context.dataset.data;
const dataIndex = context.dataIndex;
let label = ' ' + data[dataIndex] || '';
if (label) {
label += ': ';
}
const sum = data.reduce((accumulator, curValue) => {
return accumulator + curValue;
});
const value = data[dataIndex];
label += Number((value / sum) * 100).toFixed(1) + "%";
return label;
} catch (error) {
console.log(error);
}
}
}
}
}
};
const data = {
labels: Object.keys(dataset).map(label => getRemappedString(label)),
datasets: [
{
data: chartDatatype == ChartDatatype.INDIVIDUAL
? Object.values(dataset)
: Object.values(dataset).map(subObject => Object.values(subObject).reduce(sum, 0)),
}],
},
// Make charts slightly more compact (default height is 168 in our case).
height: 145,
type: "percentage",
colors: Object.keys(dataset).map(label => getChartColor(label)),
});
backgroundColor: Object.keys(dataset).map(label => getChartColor(label)),
// Match background color to create a visible, but transparent distinction between chart segments.
borderColor: prefersDark ? "#1a242f" : "#f7f7f7",
}
]
};
new Chart(
document.getElementById(id),
{
type: "doughnut",
options,
data,
}
);
}
function setAllDetailsOpen(open) {
@@ -326,6 +380,9 @@
}
document.addEventListener("DOMContentLoaded", async function () {
// Match water.css theme font colors for Chart.js labels.
Chart.defaults.color = prefersDark ? "#dbdbdb" : "#363636";
const statistics = await ky.get('statistics.json').json();
document.getElementById("num-reports").innerText = statistics.num_reports;
@@ -333,36 +390,36 @@
document.getElementById("first-report-date").innerText = statistics.first_report_date.substr(0, 10);
document.getElementById("last-report-date").innerText = statistics.last_report_date.substr(0, 10);
createChart("#chart-operating-system", ChartDatatype.AGGREGATE, statistics.os);
createChart("#chart-windows-version", ChartDatatype.INDIVIDUAL, statistics.os.windows);
createChart("#chart-macos-version", ChartDatatype.INDIVIDUAL, statistics.os.macos);
createChart("#chart-linux-distribution", ChartDatatype.INDIVIDUAL, statistics.os.linux);
createChart("#chart-android-version", ChartDatatype.INDIVIDUAL, statistics.os.android);
createChart("#chart-ios-version", ChartDatatype.INDIVIDUAL, statistics.os.ios);
createChart("#chart-web-browser", ChartDatatype.INDIVIDUAL, statistics.os.web);
createChart("chart-operating-system", ChartDatatype.AGGREGATE, statistics.os);
createChart("chart-windows-version", ChartDatatype.INDIVIDUAL, statistics.os.windows);
createChart("chart-macos-version", ChartDatatype.INDIVIDUAL, statistics.os.macos);
createChart("chart-linux-distribution", ChartDatatype.INDIVIDUAL, statistics.os.linux);
createChart("chart-android-version", ChartDatatype.INDIVIDUAL, statistics.os.android);
createChart("chart-ios-version", ChartDatatype.INDIVIDUAL, statistics.os.ios);
createChart("chart-web-browser", ChartDatatype.INDIVIDUAL, statistics.os.web);
createChart("#chart-cpu-vendor", ChartDatatype.AGGREGATE, statistics.cpu);
createChart("#chart-cpu-amd", ChartDatatype.INDIVIDUAL, statistics.cpu.amd);
createChart("#chart-cpu-intel", ChartDatatype.INDIVIDUAL, statistics.cpu.intel);
createChart("chart-cpu-vendor", ChartDatatype.AGGREGATE, statistics.cpu);
createChart("chart-cpu-amd", ChartDatatype.INDIVIDUAL, statistics.cpu.amd);
createChart("chart-cpu-intel", ChartDatatype.INDIVIDUAL, statistics.cpu.intel);
createChart("#chart-cpu-core-count", ChartDatatype.INDIVIDUAL, statistics.cpu_core_count);
createChart("#chart-cpu-x86-features", ChartDatatype.INDIVIDUAL, statistics.cpu_x86_features);
createChart("#chart-cpu-passmark-multi", ChartDatatype.INDIVIDUAL, statistics.cpu_passmark_score.multi_thread);
createChart("#chart-cpu-passmark-single", ChartDatatype.INDIVIDUAL, statistics.cpu_passmark_score.single_thread);
createChart("chart-cpu-core-count", ChartDatatype.INDIVIDUAL, statistics.cpu_core_count);
createChart("chart-cpu-x86-features", ChartDatatype.INDIVIDUAL, statistics.cpu_x86_features);
createChart("chart-cpu-passmark-multi", ChartDatatype.INDIVIDUAL, statistics.cpu_passmark_score.multi_thread);
createChart("chart-cpu-passmark-single", ChartDatatype.INDIVIDUAL, statistics.cpu_passmark_score.single_thread);
createChart("#chart-gpu-vendor", ChartDatatype.AGGREGATE, statistics.gpu);
createChart("#chart-gpu-amd", ChartDatatype.INDIVIDUAL, statistics.gpu.amd);
createChart("#chart-gpu-intel", ChartDatatype.INDIVIDUAL, statistics.gpu.intel);
createChart("#chart-gpu-nvidia", ChartDatatype.INDIVIDUAL, statistics.gpu.nvidia);
createChart("chart-gpu-vendor", ChartDatatype.AGGREGATE, statistics.gpu);
createChart("chart-gpu-amd", ChartDatatype.INDIVIDUAL, statistics.gpu.amd);
createChart("chart-gpu-intel", ChartDatatype.INDIVIDUAL, statistics.gpu.intel);
createChart("chart-gpu-nvidia", ChartDatatype.INDIVIDUAL, statistics.gpu.nvidia);
createChart("#chart-gpu-vram", ChartDatatype.INDIVIDUAL, statistics.gpu_vram);
createChart("#chart-gpu-raytracing-dedicated", ChartDatatype.INDIVIDUAL, statistics.gpu_raytracing.dedicated);
createChart("#chart-gpu-raytracing-integrated", ChartDatatype.INDIVIDUAL, statistics.gpu_raytracing.integrated);
createChart("#chart-gpu-vrs-dedicated", ChartDatatype.INDIVIDUAL, statistics.gpu_vrs.dedicated);
createChart("#chart-gpu-vrs-integrated", ChartDatatype.INDIVIDUAL, statistics.gpu_vrs.integrated);
createChart("#chart-gpu-mesh-shaders-dedicated", ChartDatatype.INDIVIDUAL, statistics.gpu_mesh_shaders.dedicated);
createChart("#chart-gpu-mesh-shaders-integrated", ChartDatatype.INDIVIDUAL, statistics.gpu_mesh_shaders.integrated);
createChart("#chart-gpu-passmark", ChartDatatype.INDIVIDUAL, statistics.gpu_passmark_score);
createChart("chart-gpu-vram", ChartDatatype.INDIVIDUAL, statistics.gpu_vram);
createChart("chart-gpu-raytracing-dedicated", ChartDatatype.INDIVIDUAL, statistics.gpu_raytracing.dedicated);
createChart("chart-gpu-raytracing-integrated", ChartDatatype.INDIVIDUAL, statistics.gpu_raytracing.integrated);
createChart("chart-gpu-vrs-dedicated", ChartDatatype.INDIVIDUAL, statistics.gpu_vrs.dedicated);
createChart("chart-gpu-vrs-integrated", ChartDatatype.INDIVIDUAL, statistics.gpu_vrs.integrated);
createChart("chart-gpu-mesh-shaders-dedicated", ChartDatatype.INDIVIDUAL, statistics.gpu_mesh_shaders.dedicated);
createChart("chart-gpu-mesh-shaders-integrated", ChartDatatype.INDIVIDUAL, statistics.gpu_mesh_shaders.integrated);
createChart("chart-gpu-passmark", ChartDatatype.INDIVIDUAL, statistics.gpu_passmark_score);
});
</script>
</head>
@@ -386,49 +443,57 @@
metrics does not add up to this number, as a user may report several
issues with identical hardware/software configuration.
</p>
<p>
<strong>
⚠️ There are <a href="https://github.com/frappe/charts/issues/295">known issues</a>
with chart display in Chromium-based browsers and Safari. Please use Firefox to view charts.
</strong>
</p>
<button onclick="setAllDetailsOpen(true)"><strong>+</strong>&nbsp;&nbsp;Expand all</button>
<button onclick="setAllDetailsOpen(false)"><strong>-</strong>&nbsp;&nbsp;Collapse all</button>
<details open>
<summary>Operating system</summary>
<div id="chart-operating-system"></div>
<div class="chart-container">
<canvas id="chart-operating-system"></canvas>
</div>
</details>
<details open>
<summary>Windows version</summary>
<div id="chart-windows-version"></div>
<div class="chart-container">
<canvas id="chart-windows-version"></canvas>
</div>
</details>
<details open>
<summary>macOS version</summary>
<div id="chart-macos-version"></div>
<div class="chart-container">
<canvas id="chart-macos-version"></canvas>
</div>
</details>
<details open>
<summary>Linux distribution</summary>
<div id="chart-linux-distribution"></div>
<div class="chart-container">
<canvas id="chart-linux-distribution"></canvas>
</div>
</details>
<details open>
<summary>Android version</summary>
<div id="chart-android-version"></div>
<div class="chart-container">
<canvas id="chart-android-version"></canvas>
</div>
</details>
<details open>
<summary>iOS version</summary>
<div id="chart-ios-version"></div>
<div class="chart-container">
<canvas id="chart-ios-version"></canvas>
</div>
</details>
<details open>
<summary>Web browser</summary>
<div id="chart-web-browser"></div>
<div class="chart-container">
<canvas id="chart-web-browser"></canvas>
</div>
<div class="chart-caption">
The web browser used when reporting issues related to the web editor or web export.
</div>
@@ -436,12 +501,16 @@
<details open>
<summary>CPU vendor</summary>
<div id="chart-cpu-vendor"></div>
<div class="chart-container">
<canvas id="chart-cpu-vendor"></canvas>
</div>
</details>
<details open>
<summary>AMD CPU generation</summary>
<div id="chart-cpu-amd"></div>
<div class="chart-container">
<canvas id="chart-cpu-amd"></canvas>
</div>
<div class="chart-caption">
Laptop CPUs, FXs, Athlons, Phenoms, Threadrippers and EPYCs are currently categorized as "Other/Unknown".
</div>
@@ -449,7 +518,9 @@
<details open>
<summary>Intel CPU generation</summary>
<div id="chart-cpu-intel"></div>
<div class="chart-container">
<canvas id="chart-cpu-intel"></canvas>
</div>
<div class="chart-caption">
Laptop CPUs, Celerons, Pentiums and Xeons are currently categorized as "Other/Unknown".
</div>
@@ -457,7 +528,9 @@
<details open>
<summary>CPU physical core count</summary>
<div id="chart-cpu-core-count"></div>
<div class="chart-container">
<canvas id="chart-cpu-core-count"></canvas>
</div>
<div class="chart-caption">
For CPUs with hybrid (big.LITTLE) topologies, both "big" cores and "little" cores are summed together.
</div>
@@ -465,7 +538,9 @@
<details open>
<summary>Highest supported x86 CPU instruction set</summary>
<div id="chart-cpu-x86-features"></div>
<div class="chart-container">
<canvas id="chart-cpu-x86-features"></canvas>
</div>
<div class="chart-caption">
Support for a new instruction set implies support for older instruction sets. For instance, supporting
AVX-512 implies supporting AVX2, AVX and SSE4.2.<br>
@@ -475,7 +550,9 @@
<details open>
<summary>CPU multi-thread performance score</summary>
<div id="chart-cpu-passmark-multi"></div>
<div class="chart-container">
<canvas id="chart-cpu-passmark-multi"></canvas>
</div>
<div class="chart-caption">
Scores are sourced from <a href="https://www.cpubenchmark.net/">PassMark</a>.<br>
For reference, here are some example CPUs that roughly match each score threshold:
@@ -493,7 +570,9 @@
<details open>
<summary>CPU single-thread performance score</summary>
<div id="chart-cpu-passmark-single"></div>
<div class="chart-container">
<canvas id="chart-cpu-passmark-single"></canvas>
</div>
<div class="chart-caption">
Scores are sourced from <a href="https://www.cpubenchmark.net/">PassMark</a>.<br>
For reference, here are some example CPUs that roughly match each score threshold:
@@ -511,27 +590,37 @@
<details open>
<summary>GPU vendor</summary>
<div id="chart-gpu-vendor"></div>
<div class="chart-container">
<canvas id="chart-gpu-vendor"></canvas>
</div>
</details>
<details open>
<summary>AMD GPU generation</summary>
<div id="chart-gpu-amd"></div>
<div class="chart-container">
<canvas id="chart-gpu-amd"></canvas>
</div>
</details>
<details open>
<summary>Intel GPU generation</summary>
<div id="chart-gpu-intel"></div>
<div class="chart-container">
<canvas id="chart-gpu-intel"></canvas>
</div>
</details>
<details open>
<summary>NVIDIA GPU generation</summary>
<div id="chart-gpu-nvidia"></div>
<div class="chart-container">
<canvas id="chart-gpu-nvidia"></canvas>
</div>
</details>
<details open>
<summary>GPU video memory</summary>
<div id="chart-gpu-vram"></div>
<div class="chart-container">
<canvas id="chart-gpu-vram"></canvas>
</div>
<div class="chart-caption">
Only dedicated GPUs are taken into account.
</div>
@@ -540,9 +629,13 @@
<details open>
<summary>GPU hardware-accelerated raytracing support</summary>
<h3>Dedicated GPUs</h3>
<div id="chart-gpu-raytracing-dedicated"></div>
<div class="chart-container">
<canvas id="chart-gpu-raytracing-dedicated"></canvas>
</div>
<h3>Integrated GPUs</h3>
<div id="chart-gpu-raytracing-integrated"></div>
<div class="chart-container">
<canvas id="chart-gpu-raytracing-integrated"></canvas>
</div>
<div class="chart-caption">
Raytracing is currently not used in Godot.
<br><br>
@@ -554,20 +647,29 @@
<details open>
<summary>GPU variable rate shading support</summary>
<h3>Dedicated GPUs</h3>
<div id="chart-gpu-vrs-dedicated"></div>
<div class="chart-container">
<canvas id="chart-gpu-vrs-dedicated"></canvas>
</div>
<h3>Integrated GPUs</h3>
<div id="chart-gpu-vrs-integrated"></div>
<div class="chart-container">
<canvas id="chart-gpu-vrs-integrated"></canvas>
</div>
<div class="chart-caption">
<a href="https://docs.godotengine.org/en/stable/tutorials/3d/variable_rate_shading.html">Variable rate shading</a> can be optionally used in Godot 4.
<a href="https://docs.godotengine.org/en/stable/tutorials/3d/variable_rate_shading.html">Variable rate
shading</a> can be optionally used in Godot 4.
</div>
</details>
<details open>
<summary>GPU mesh shaders support</summary>
<h3>Dedicated GPUs</h3>
<div id="chart-gpu-mesh-shaders-dedicated"></div>
<div class="chart-container">
<canvas id="chart-gpu-mesh-shaders-dedicated"></canvas>
</div>
<h3>Integrated GPUs</h3>
<div id="chart-gpu-mesh-shaders-integrated"></div>
<div class="chart-container">
<canvas id="chart-gpu-mesh-shaders-integrated"></canvas>
</div>
<div class="chart-caption">
Mesh shaders are currently not used in Godot.
</div>
@@ -575,7 +677,9 @@
<details open>
<summary>GPU performance score</summary>
<div id="chart-gpu-passmark"></div>
<div class="chart-container">
<canvas id="chart-gpu-passmark"></canvas>
</div>
<div class="chart-caption">
Scores are sourced from <a href="https://www.videocardbenchmark.net/">PassMark</a>.<br>
For reference, here are some example GPUs that roughly match each score threshold:

14
thirdparty/chart.umd.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long