Last active
October 1, 2025 13:56
-
-
Save StaffanBetner/24dbe48c4c7f5bcfa9afa19663df722b to your computer and use it in GitHub Desktop.
Interactive Confusion Matrix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <!-- Vibe coded with Gemini 2.5 Pro --> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Interactive Confusion Matrix</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| body { | |
| background-color: #f8f9fa; | |
| font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; | |
| color: #212529; | |
| } | |
| .calc-table-wrapper { | |
| overflow-x: auto; | |
| padding: 1rem; | |
| } | |
| .calc-table { | |
| border-collapse: collapse; | |
| width: 900px; | |
| margin: 0.5rem auto; | |
| font-size: 0.8rem; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| border: 1px solid #d1d5db; | |
| } | |
| .calc-table th, .calc-table td { | |
| border: 1px solid #d1d5db; | |
| padding: 0.1rem; | |
| text-align: center; | |
| vertical-align: middle; | |
| position: relative; | |
| } | |
| .calc-table td.no-border, .calc-table th.no-border { | |
| border: none !important; | |
| } | |
| .input-val { | |
| width: 100%; | |
| padding: 0.5rem; | |
| border: 1px solid #ccc; | |
| border-radius: 4px; | |
| text-align: center; | |
| font-size: 1rem; | |
| max-width: 120px; | |
| margin: 0 auto; | |
| background-color: #fff; | |
| } | |
| .input-val:focus { | |
| outline: 2px solid #3b82f6; | |
| border-color: #3b82f6; | |
| } | |
| .texhtml { | |
| font-family: serif; | |
| font-style: italic; | |
| } | |
| .sfrac { white-space: nowrap; } | |
| .sfrac .tion { display: inline-block; vertical-align: -0.5em; font-size: 85%; text-align: center; } | |
| .sfrac .num { display: block; line-height: 1em; margin: 0.0em 0.1em; border-bottom: 1px solid; } | |
| .sfrac .den { display: block; line-height: 1em; margin: 0.1em 0.1em; } | |
| .nowrap { white-space: nowrap; } | |
| .vertical-header { | |
| width: 40px; | |
| padding: 0.5rem; | |
| position: relative; | |
| } | |
| .vertical-header div { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%) rotate(-90deg); | |
| white-space: nowrap; | |
| } | |
| a { | |
| color: #0d6efd; | |
| text-decoration: none; | |
| } | |
| .calc-table a { | |
| color: #0d6efd; | |
| } | |
| a:hover { | |
| text-decoration: underline; | |
| } | |
| .result { | |
| font-weight: bold; | |
| font-size: 1rem; | |
| min-height: 1.5rem; | |
| display: inline-block; | |
| } | |
| /* --- NEW: TOOLTIP AND FOOTNOTE STYLES --- */ | |
| sup { | |
| font-size: 0.7em; | |
| vertical-align: super; | |
| margin-left: 2px; | |
| color: #0d6efd; | |
| cursor: help; | |
| } | |
| .tooltip-container { | |
| position: relative; | |
| display: inline-block; | |
| } | |
| .tooltip-text { | |
| visibility: hidden; | |
| width: 220px; | |
| background-color: #333; | |
| color: #fff; | |
| text-align: center; | |
| border-radius: 6px; | |
| padding: 8px; | |
| position: absolute; | |
| z-index: 1; | |
| bottom: 125%; | |
| left: 50%; | |
| margin-left: -110px; /* Use half of the width to center */ | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| font-size: 0.9rem; | |
| font-weight: normal; | |
| line-height: 1.4; | |
| } | |
| .tooltip-text::after { | |
| content: ""; | |
| position: absolute; | |
| top: 100%; | |
| left: 50%; | |
| margin-left: -5px; | |
| border-width: 5px; | |
| border-style: solid; | |
| border-color: #333 transparent transparent transparent; | |
| } | |
| .tooltip-container:hover .tooltip-text { | |
| visibility: visible; | |
| opacity: 1; | |
| } | |
| .footnotes { | |
| max-width: 900px; | |
| margin: 2rem auto; | |
| padding: 0 1rem; | |
| font-size: 0.9rem; | |
| color: #495057; | |
| } | |
| .footnotes p { | |
| margin-bottom: 0.5rem; | |
| line-height: 1.5; | |
| } | |
| /* --- END OF NEW STYLES --- */ | |
| </style> | |
| </head> | |
| <body class="antialiased"> | |
| <header class="text-center my-4 px-4"> | |
| <h1 class="text-4xl font-bold text-gray-900">Interactive Confusion Matrix</h1> | |
| <p class="text-lg text-gray-600 mt-2 mb-0">An interactive version of <a href="https://en.wikipedia.org/wiki/Confusion_matrix#Table_of_confusion" target="_blank" class="text-blue-600 hover:underline">the Wikipedia table</a> for evaluating binary classifiers.</p> | |
| </header> | |
| <div class="calc-table-wrapper"> | |
| <table class="calc-table"> | |
| <tbody> | |
| <tr> | |
| <td class="no-border" rowspan="2"></td> | |
| <td class="no-border"></td> | |
| <td style="background:#4ad2d260;" colspan="2"><b>Predicted condition</b></td> | |
| <td class="no-border" colspan="2"></td> | |
| </tr> | |
| <tr> | |
| <td style="background:#c9c9c950;">Total population <br><span class="texhtml">= P + N</span><br><span id="total_population" class="result"></span></td> | |
| <td style="background:#78ffff60;"><b>Predicted positive</b><br><span id="predicted_positive" class="result"></span></td> | |
| <td style="background:#1da5a560;"><b>Predicted negative</b><br><span id="predicted_negative" class="result"></span></td> | |
| <td style="border-left:double #a2a9b1; background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Youden%27s_J_statistic" target="_blank">Informedness</a>, <span style="font-size: 85%;">bookmaker informedness (BM)</span> <br><span class="texhtml">= TPR + TNR − 1</span><br><span id="informedness" class="result"></span></td> | |
| <td style="background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Prevalence_threshold" target="_blank">Prevalence threshold</a> (PT) <br><span class="texhtml"><span class="sfrac"><span class="tion"><span class="num"><span class="nowrap">√<span style="border-top:1px solid; padding:0 0.1em;">TPR × FPR</span></span> − FPR</span><span class="den">TPR − FPR</span></span></span></span><br><span id="pt" class="result"></span></td> | |
| </tr> | |
| <tr> | |
| <td class="vertical-header" rowspan="2" style="background:#d2d23d60;"><div><b>Actual condition</b></div></td> | |
| <!-- NEW: Footnote [a] added --> | |
| <td style="background:#ffff7860;"><b>Real Positive (P)</b><span class="tooltip-container"><sup>[a]</sup><span class="tooltip-text">the number of real positive cases in the data</span></span><br><span id="p_val" class="result"></span></td> | |
| <!-- NEW: Footnote [b] added --> | |
| <td style="background:#78ff7860;"><b><a href="https://en.wikipedia.org/wiki/True_and_false_positives#True_positive" target="_blank">True positive</a></b> (TP), <br><span style="font-size: 85%;">hit</span><span class="tooltip-container"><sup>[b]</sup><span class="tooltip-text">A test result that correctly indicates the presence of a condition or characteristic</span></span><br><input id="tp" type="number" min="0" class="input-val" oninput="calculate()"></td> | |
| <td style="background:#ffa5a560;"><b><a href="https://en.wikipedia.org/wiki/True_and_false_positives#False_negative" target="_blank">False negative</a></b> (FN), <br><span style="font-size: 85%;">miss, underestimation</span><br><input id="fn" type="number" min="0" class="input-val" oninput="calculate()"></td> | |
| <td style="background:#a5ffa530;"><a href="https://en.wikipedia.org/wiki/Sensitivity_(test)" target="_blank">True positive rate</a> (TPR), recall, sensitivity (SEN) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">TP</span><span class="den">P</span></span></span></span> <span class="texhtml">= 1 − FNR</span><br><span id="tpr" class="result"></span></td> | |
| <!-- NEW: Footnote [c] added --> | |
| <td style="background:#ffa5a530;"><a href="https://en.wikipedia.org/wiki/False_negative_rate" target="_blank">False negative rate</a> (FNR), miss rate <br> type II error<span class="tooltip-container"><sup>[c]</sup><span class="tooltip-text">Type II error: A test result which wrongly indicates that a particular condition or attribute is absent</span></span><br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">FN</span><span class="den">P</span></span></span></span> <span class="texhtml">= 1 − TPR</span><br><span id="fnr" class="result"></span></td> | |
| </tr> | |
| <tr> | |
| <!-- NEW: Footnote [d] added --> | |
| <td style="background:#a5a51d60;"><b>Real Negative (N)</b><span class="tooltip-container"><sup>[d]</sup><span class="tooltip-text">the number of real negative cases in the data</span></span><br><span id="n_val" class="result"></span></td> | |
| <td style="background:#ff787860;"><b><a href="https://en.wikipedia.org/wiki/True_and_false_positives#False_positive" target="_blank">False positive</a></b> (FP), <br><span style="font-size: 85%;">false alarm, overestimation</span><br><input id="fp" type="number" min="0" class="input-val" oninput="calculate()"></td> | |
| <!-- NEW: Footnote [e] added --> | |
| <td style="background:#3dd23d60;"><b><a href="https://en.wikipedia.org/wiki/True_and_false_positives#True_negative" target="_blank">True negative</a></b> (TN), <br><span style="font-size: 85%;">correct rejection</span><span class="tooltip-container"><sup>[e]</sup><span class="tooltip-text">A test result that correctly indicates the absence of a condition or characteristic</span></span><br><input id="tn" type="number" min="0" class="input-val" oninput="calculate()"></td> | |
| <!-- NEW: Footnote [f] added --> | |
| <td style="background:#a54a4a30;"><a href="https://en.wikipedia.org/wiki/False_positive_rate" target="_blank">False positive rate</a> (FPR), fall-out <br> type I error<span class="tooltip-container"><sup>[f]</sup><span class="tooltip-text">Type I error: A test result which wrongly indicates that a particular condition or attribute is present</span></span><br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">FP</span><span class="den">N</span></span></span></span> <span class="texhtml">= 1 − TNR</span><br><span id="fpr" class="result"></span></td> | |
| <td style="background:#4aa54a30;"><a href="https://en.wikipedia.org/wiki/Specificity_(test)" target="_blank">True negative rate</a> (TNR), specificity (SPC) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">TN</span><span class="den">N</span></span></span></span> <span class="texhtml">= 1 − FPR</span><br><span id="tnr" class="result"></span></td> | |
| </tr> | |
| <tr> | |
| <td class="no-border" rowspan="3"></td> | |
| <td style="border-top:double #a2a9b1; border-right:double #a2a9b1; background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Prevalence" target="_blank">Prevalence</a> <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">P</span><span class="den">P + N</span></span></span></span><br><span id="prevalence" class="result"></span></td> | |
| <td style="background:#a5ffa530;"><a href="https://en.wikipedia.org/wiki/Positive_and_negative_predictive_values#Positive_predictive_value" target="_blank">Positive predictive value</a> (PPV), precision <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">TP</span><span class="den">TP + FP</span></span></span></span> <span class="texhtml">= 1 − FDR</span><br><span id="ppv" class="result"></span></td> | |
| <td style="background:#ffa5a530; border-right:double #a2a9b1;"><a href="https://en.wikipedia.org/wiki/False_omission_rate" target="_blank">False omission rate</a> (FOR) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">FN</span><span class="den">TN + FN</span></span></span></span> <span class="texhtml">= 1 − NPV</span><br><span id="for_val" class="result"></span></td> | |
| <td style="background:#a5a5ff30;"><a href="https://en.wikipedia.org/wiki/Likelihood_ratios_in_diagnostic_testing#Positive_likelihood_ratio" target="_blank">Positive likelihood ratio</a> (LR+) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">TPR</span><span class="den">FPR</span></span></span></span><br><span id="lr_plus" class="result"></span></td> | |
| <td style="background:#a5a5ff30;"><a href="https://en.wikipedia.org/wiki/Likelihood_ratios_in_diagnostic_testing#Negative_likelihood_ratio" target="_blank">Negative likelihood ratio</a> (LR−) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">FNR</span><span class="den">TNR</span></span></span></span><br><span id="lr_minus" class="result"></span></td> | |
| </tr> | |
| <tr> | |
| <td style="border-right:double #a2a9b1; background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Accuracy_and_precision#In_binary_classification" target="_blank">Accuracy</a> (ACC) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">TP + TN</span><span class="den">P + N</span></span></span></span><br><span id="acc" class="result"></span></td> | |
| <td style="background:#a54a4a30;"><a href="https://en.wikipedia.org/wiki/False_discovery_rate" target="_blank">False discovery rate</a> (FDR) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">FP</span><span class="den">TP + FP</span></span></span></span> <span class="texhtml">= 1 − PPV</span><br><span id="fdr" class="result"></span></td> | |
| <td style="background:#4aa54a30;"><a href="https://en.wikipedia.org/wiki/Positive_and_negative_predictive_values#Negative_predictive_value" target="_blank">Negative predictive value</a> (NPV) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">TN</span><span class="den">TN + FN</span></span></span></span> <span class="texhtml">= 1 − FOR</span><br><span id="npv" class="result"></span></td> | |
| <td style="border-top:double #a2a9b1; border-right:double #a2a9b1; background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Markedness" target="_blank">Markedness</a> (MK) <br><span class="texhtml">= PPV + NPV − 1</span><br><span id="mk" class="result"></span></td> | |
| <td style="background:#a5a5ff30;"><a href="https://en.wikipedia.org/wiki/Diagnostic_odds_ratio" target="_blank">Diagnostic odds ratio</a> (DOR) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">LR+</span><span class="den">LR−</span></span></span></span><br><span id="dor" class="result"></span></td> | |
| </tr> | |
| <tr> | |
| <td style="background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Balanced_accuracy" target="_blank">Balanced accuracy</a> (BA) <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">TPR + TNR</span><span class="den">2</span></span></span></span><br><span id="ba" class="result"></span></td> | |
| <td style="border-top:double #a2a9b1; background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/F-score" target="_blank">F<sub>1</sub> score</a> <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">2 TP</span><span class="den">2 TP + FP + FN</span></span></span></span><br><span id="f1" class="result"></span></td> | |
| <td style="border-top:double #a2a9b1; background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Fowlkes%E2%80%93Mallows_index" target="_blank">Fowlkes–Mallows index</a> (FM) <br><span class="texhtml">= √<span style="border-top:1px solid; padding:0 0.1em;">PPV × TPR</span></span><br><span id="fm" class="result"></span></td> | |
| <td style="border-top:double #a2a9b1; background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Phi_coefficient" target="_blank">Matthews correlation coefficient</a> (MCC) <br><span class="texhtml" style="font-size:80%;">= <span class="sfrac"><span class="tion"><span class="num">TP×TN − FP×FN</span><span class="den">√(TP+FP)(TP+FN)(TN+FP)(TN+FN)</span></span></span></span><br><span id="mcc" class="result"></span></td> | |
| <td style="border-top:double #a2a9b1; background:#ffffff0c;"><a href="https://en.wikipedia.org/wiki/Jaccard_index#Jaccard_index_in_binary_classification_confusion_matrices" target="_blank">Threat score</a> (TS), Jaccard index <br><span class="texhtml">= <span class="sfrac"><span class="tion"><span class="num">TP</span><span class="den">TP + FN + FP</span></span></span></span><br><span id="ts" class="result"></span></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- NEW: VISIBLE FOOTNOTES LIST --> | |
| <div class="footnotes"> | |
| <p><b>a.</b> ^ the number of real positive cases in the data</p> | |
| <p><b>b.</b> ^ A test result that correctly indicates the presence of a condition or characteristic</p> | |
| <p><b>c.</b> ^ Type II error: A test result which wrongly indicates that a particular condition or attribute is absent</p> | |
| <p><b>d.</b> ^ the number of real negative cases in the data</p> | |
| <p><b>e.</b> ^ A test result that correctly indicates the absence of a condition or characteristic</p> | |
| <p><b>f.</b> ^ Type I error: A test result which wrongly indicates that a particular condition or attribute is present</p> | |
| </div> | |
| <footer class="text-center my-6"> | |
| <p class="text-gray-500 text-sm">Vibe coded with Gemini 2.5 Pro, by Staffan Betnér</p> | |
| </footer> | |
| <script> | |
| // Function to safely get a numeric value from an input field | |
| function getNumericValue(id) { | |
| const value = document.getElementById(id).value; | |
| // Return null if empty, otherwise parse as a float. | |
| return value.trim() === '' ? null : parseFloat(value); | |
| } | |
| // Function to format numbers for display | |
| function formatResult(value) { | |
| // If value is null, NaN, or infinite, display a dash. | |
| if (value === null || isNaN(value) || !isFinite(value)) { | |
| return '-'; | |
| } | |
| // If it's a whole number, display as is. Otherwise, show 3 decimal places. | |
| return value % 1 === 0 ? value.toString() : value.toFixed(3); | |
| } | |
| // Function to update a specific element's text content | |
| function updateElement(id, value) { | |
| document.getElementById(id).textContent = formatResult(value); | |
| } | |
| // Main calculation function | |
| function calculate() { | |
| const tp = getNumericValue('tp'); | |
| const fn = getNumericValue('fn'); | |
| const fp = getNumericValue('fp'); | |
| const tn = getNumericValue('tn'); | |
| // --- Intermediate calculations --- | |
| const p = (tp !== null && fn !== null) ? tp + fn : null; | |
| const n = (fp !== null && tn !== null) ? fp + tn : null; | |
| const predicted_positive = (tp !== null && fp !== null) ? tp + fp : null; | |
| const predicted_negative = (fn !== null && tn !== null) ? fn + tn : null; | |
| const total_population = (p !== null && n !== null) ? p + n : null; | |
| updateElement('p_val', p); | |
| updateElement('n_val', n); | |
| updateElement('predicted_positive', predicted_positive); | |
| updateElement('predicted_negative', predicted_negative); | |
| updateElement('total_population', total_population); | |
| // --- Core Metrics --- | |
| const tpr = (p !== null && p > 0 && tp !== null) ? tp / p : null; // Sensitivity, Recall | |
| const tnr = (n !== null && n > 0 && tn !== null) ? tn / n : null; // Specificity | |
| const ppv = (predicted_positive !== null && predicted_positive > 0 && tp !== null) ? tp / predicted_positive : null; // Precision | |
| const npv = (predicted_negative !== null && predicted_negative > 0 && tn !== null) ? tn / predicted_negative : null; | |
| const fnr = (p !== null && p > 0 && fn !== null) ? fn / p : null; | |
| const fpr = (n !== null && n > 0 && fp !== null) ? fp / n : null; | |
| const fdr = (predicted_positive !== null && predicted_positive > 0 && fp !== null) ? fp / predicted_positive : null; | |
| const for_val = (predicted_negative !== null && predicted_negative > 0 && fn !== null) ? fn / predicted_negative : null; | |
| const acc = (total_population !== null && total_population > 0 && tp !== null && tn !== null) ? (tp + tn) / total_population : null; | |
| const prevalence = (total_population !== null && total_population > 0 && p !== null) ? p / total_population : null; | |
| updateElement('tpr', tpr); | |
| updateElement('tnr', tnr); | |
| updateElement('ppv', ppv); | |
| updateElement('npv', npv); | |
| updateElement('fnr', fnr); | |
| updateElement('fpr', fpr); | |
| updateElement('fdr', fdr); | |
| updateElement('for_val', for_val); | |
| updateElement('acc', acc); | |
| updateElement('prevalence', prevalence); | |
| // --- Advanced Metrics --- | |
| const informedness = (tpr !== null && tnr !== null) ? tpr + tnr - 1 : null; | |
| const pt = (tpr !== null && fpr !== null && tpr - fpr !== 0) ? (Math.sqrt(tpr * fpr) - fpr) / (tpr - fpr) : null; | |
| const lr_plus = (fpr !== null && fpr > 0 && tpr !== null) ? tpr / fpr : null; | |
| const lr_minus = (tnr !== null && tnr > 0 && fnr !== null) ? fnr / tnr : null; | |
| const mk = (ppv !== null && npv !== null) ? ppv + npv - 1 : null; | |
| const dor = (lr_plus !== null && lr_minus !== null && lr_minus > 0) ? lr_plus / lr_minus : null; | |
| const ba = (tpr !== null && tnr !== null) ? (tpr + tnr) / 2 : null; | |
| const f1 = (tp !== null && fp !== null && fn !== null && (2 * tp + fp + fn > 0)) ? (2 * tp) / (2 * tp + fp + fn) : null; | |
| const fm = (ppv !== null && tpr !== null) ? Math.sqrt(ppv * tpr) : null; | |
| const mcc_numerator = (tp !== null && tn !== null && fp !== null && fn !== null) ? (tp * tn) - (fp * fn) : null; | |
| const mcc_denominator_val = (tp !== null && fp !== null && fn !== null && tn !== null) ? (tp + fp) * (tp + fn) * (tn + fp) * (tn + fn) : null; | |
| const mcc_denominator = (mcc_denominator_val !== null && mcc_denominator_val > 0) ? Math.sqrt(mcc_denominator_val) : null; | |
| const mcc = (mcc_numerator !== null && mcc_denominator !== null && mcc_denominator > 0) ? mcc_numerator / mcc_denominator : null; | |
| const ts_denominator = (tp !== null && fn !== null && fp !== null) ? tp + fn + fp : null; | |
| const ts = (ts_denominator !== null && ts_denominator > 0 && tp !== null) ? tp / ts_denominator : null; | |
| updateElement('informedness', informedness); | |
| updateElement('pt', pt); | |
| updateElement('lr_plus', lr_plus); | |
| updateElement('lr_minus', lr_minus); | |
| updateElement('mk', mk); | |
| updateElement('dor', dor); | |
| updateElement('ba', ba); | |
| updateElement('f1', f1); | |
| updateElement('fm', fm); | |
| updateElement('mcc', mcc); | |
| updateElement('ts', ts); | |
| } | |
| // Initial call to set the board with dashes on page load | |
| calculate(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment