From ed59e75245dd296ce73c5a765fb5bdc8e81802f1 Mon Sep 17 00:00:00 2001 From: Q Kim Date: Fri, 24 Jul 2026 11:37:01 +0900 Subject: [PATCH 1/2] fix: dark mode text contrast in Acceptance Criteria section (web/index.html) Labels, select boxes, and the section divider under "Acceptance Criteria" had no dark: variant classes, so text rendered in default (light-mode) colors and became unreadable against the dark background. Live site reproduced this in dark mode. Co-Authored-By: Claude Sonnet 5 --- web/index.html | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/web/index.html b/web/index.html index eae80c7..06a5f41 100644 --- a/web/index.html +++ b/web/index.html @@ -447,17 +447,17 @@

-
- Acceptance Criteria -

Required failures produce FAIL; preferred warnings produce CONDITIONAL PASS.

+
+ Acceptance Criteria +

Required failures produce FAIL; preferred warnings produce CONDITIONAL PASS.

-
-
-
-
-
-
-
+
+
+
+
+
+
+
From 3acd7f2a7c0ddfde29aee3db42b20fcc04ac6493 Mon Sep 17 00:00:00 2001 From: Q Kim Date: Fri, 24 Jul 2026 11:55:14 +0900 Subject: [PATCH 2/2] feat: add candidate-strategy tooltips to Candidate Comparison table Feasibility Best / GC Target / High CAI rows showed a bare name with no explanation of what each candidate strategy actually does. Reuses the existing descriptions already shown on the profile-selection cards (Optimization Settings) via the same tooltip-container/info-icon pattern, keyed by candidate.id so it works regardless of label text. Co-Authored-By: Claude Sonnet 5 --- web/js/app.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/web/js/app.js b/web/js/app.js index fc7b855..ad074b1 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -942,6 +942,15 @@ function getPrimaryResult(res) { }; } +// Same wording as the profile-selection cards above (Optimization Settings), +// reused here so the comparison table explains each candidate strategy +// instead of showing a bare name. +const CANDIDATE_DESCRIPTIONS = { + feasibility_best: 'Dynamic-programming candidate with maximum CAI inside the requested GC range when feasible.', + gc_target: 'Profile-engine comparison that prioritizes proximity to the active host GC band.', + high_cai: 'Profile-engine comparison that favors high codon adaptation index.' +}; + function renderCandidateComparison(res) { if (!elements.candidateComparisonContainer || !elements.candidateComparisonBody) return; if (!Array.isArray(res.candidates) || res.candidates.length === 0) { @@ -955,9 +964,13 @@ function renderCandidateComparison(res) { const isRecommended = candidate.id === recommendedId; const status = candidate.automated_decision || (candidate.validator_status === 'pass' ? 'PASS' : 'REVIEW'); const aaStatus = candidate.validator_status === 'pass' ? '100%' : 'Review'; + const description = CANDIDATE_DESCRIPTIONS[candidate.id]; + const nameCell = description + ? `${escapeHtml(candidate.label || candidate.id)}i${escapeHtml(description)}` + : escapeHtml(candidate.label || candidate.id); return ` - ${candidate.label || candidate.id}${isRecommended ? ' ★' : ''} + ${nameCell}${isRecommended ? ' ★' : ''} ${Number(candidate.cai || 0).toFixed(3)} ${Number(candidate.gc_percent || 0).toFixed(1)} ${Number(candidate.gc_window_min || 0).toFixed(1)}-${Number(candidate.gc_window_max || 0).toFixed(1)}