@@ -434,7 +434,7 @@ def _to_list(x: Any) -> Any:
434434 motif_auc_table_html = ""
435435 if motif_auc_df is not None :
436436 # append motif logo column if exists
437- if motif_logo_dict is not None :
437+ if motif_file_fmt == 'meme' and ( len ( motif_logo_dict ) > 0 ) :
438438 motif_auc_df ['motif_logo' ] = motif_auc_df .apply (lambda x : "<img src=\" " + motif_logo_dict [x ['concept' ]] + "\" width=\" 100\" >" , axis = 1 )
439439 motif_auc_table_html = _render_df_table (motif_auc_df , max_rows = 5000 )
440440
@@ -493,7 +493,7 @@ def _to_list(x: Any) -> Any:
493493 motif_df = pd .DataFrame (motif_concept_rows )
494494 motif_df = motif_df [[c for c in motif_cols if c in motif_df .columns ]]
495495 # append motif logo column if exists
496- if motif_logo_dict is not None :
496+ if motif_file_fmt == 'meme' and ( len ( motif_logo_dict ) > 0 ) :
497497 motif_df ['motif_logo' ] = motif_df .apply (lambda x : "<img src=\" " + motif_logo_dict [x ['concept' ]] + "\" width=\" 100\" >" , axis = 1 )
498498 motif_table_html = _render_df_table (motif_df , max_rows = 5000 )
499499 if non_motif_concept_rows :
@@ -542,6 +542,7 @@ def _png_html(path: Path, alt: str) -> str:
542542 :root {{
543543 --bg: #0b1020;
544544 --panel: #111936;
545+ --panel2: rgba(255,255,255,0.03);
545546 --text: #e7ecff;
546547 --muted: #b7c0e1;
547548 --warn: #f6c177;
@@ -550,6 +551,16 @@ def _png_html(path: Path, alt: str) -> str:
550551 --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
551552 --sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
552553 }}
554+ [data-theme="light"] {{
555+ --bg: #ffffff;
556+ --panel: #f7f8fc;
557+ --panel2: #ffffff;
558+ --text: #0b1020;
559+ --muted: #4b5563;
560+ --warn: #a16207;
561+ --err: #b91c1c;
562+ --border: rgba(11, 16, 32, 0.14);
563+ }}
553564 body {{
554565 margin: 0;
555566 background: var(--bg);
@@ -567,6 +578,15 @@ def _png_html(path: Path, alt: str) -> str:
567578 border-radius: 12px;
568579 padding: 18px 18px 14px;
569580 }}
581+ [data-theme="light"] header {{
582+ background: linear-gradient(180deg, rgba(247,248,252,0.95), rgba(247,248,252,0.65));
583+ }}
584+ .header-row {{
585+ display: flex;
586+ align-items: center;
587+ justify-content: space-between;
588+ gap: 12px;
589+ }}
570590 h1 {{
571591 margin: 0 0 8px;
572592 font-size: 22px;
@@ -576,7 +596,7 @@ def _png_html(path: Path, alt: str) -> str:
576596 margin-top: 18px;
577597 padding: 14px 14px 18px;
578598 border: 1px solid var(--border);
579- background: rgba(17,25,54,0.65 );
599+ background: var(--panel );
580600 border-radius: 12px;
581601 }}
582602 .trainer {{
@@ -612,7 +632,7 @@ def _png_html(path: Path, alt: str) -> str:
612632 height: auto;
613633 border-radius: 10px;
614634 border: 1px solid var(--border);
615- background: rgba(255,255,255,0.03 );
635+ background: var(--panel2 );
616636 margin: 12px 0 6px;
617637 }}
618638 .figure--half {{
@@ -623,7 +643,7 @@ def _png_html(path: Path, alt: str) -> str:
623643 min-height: 520px;
624644 border-radius: 10px;
625645 border: 1px solid var(--border);
626- background: rgba(255,255,255,0.03 );
646+ background: var(--panel2 );
627647 margin: 12px 0 6px;
628648 }}
629649 .plot--short {{
@@ -644,9 +664,15 @@ def _png_html(path: Path, alt: str) -> str:
644664 cursor: pointer;
645665 font-size: 12px;
646666 }}
667+ [data-theme="light"] .btn {{
668+ background: rgba(0,0,0,0.03);
669+ }}
647670 .btn:hover {{
648671 background: rgba(255,255,255,0.08);
649672 }}
673+ [data-theme="light"] .btn:hover {{
674+ background: rgba(0,0,0,0.06);
675+ }}
650676 .btn:disabled {{
651677 opacity: 0.5;
652678 cursor: not-allowed;
@@ -681,7 +707,7 @@ def _png_html(path: Path, alt: str) -> str:
681707 border: 1px solid var(--border);
682708 border-radius: 10px;
683709 margin-top: 10px;
684- background: rgba(0,0,0,0.12 );
710+ background: var(--panel2 );
685711 }}
686712 .table-scroll table.df {{
687713 margin-top: 0;
@@ -726,7 +752,7 @@ def _png_html(path: Path, alt: str) -> str:
726752 text-align: left;
727753 color: var(--muted);
728754 font-weight: 600;
729- background: rgba(255,255,255,0.03 );
755+ background: var(--panel2 );
730756 }}
731757 table.kv {{
732758 border-collapse: collapse;
@@ -745,13 +771,13 @@ def _png_html(path: Path, alt: str) -> str:
745771 width: 180px;
746772 text-align: left;
747773 color: var(--muted);
748- background: rgba(255,255,255,0.03 );
774+ background: var(--panel2 );
749775 font-weight: 600;
750776 }}
751777 pre {{
752778 font-family: var(--mono);
753779 font-size: 12px;
754- background: rgba(0,0,0,0.25 );
780+ background: var(--panel2 );
755781 border: 1px solid var(--border);
756782 border-radius: 10px;
757783 padding: 10px 12px;
@@ -762,13 +788,16 @@ def _png_html(path: Path, alt: str) -> str:
762788 </style>
763789</head>
764790<body>
765- <div class="container">
766- <header>
767- <h1>{ _html .escape (title )} </h1>
768- <div class="meta">Generated: { now } </div>
769- { params_html }
770- { assets_note }
771- </header>
791+ <div class="container">
792+ <header>
793+ <div class="header-row">
794+ <h1>{ _html .escape (title )} </h1>
795+ <button id="theme_toggle" class="btn" type="button">Toggle theme</button>
796+ </div>
797+ <div class="meta">Generated: { now } </div>
798+ { params_html }
799+ { assets_note }
800+ </header>
772801
773802 <section>
774803 <h2>Concepts</h2>
@@ -819,6 +848,68 @@ def _png_html(path: Path, alt: str) -> str:
819848 <script type="application/json" id="tpcav-report-data">{ payload_json } </script>
820849 <script>
821850 (function() {{
851+ // Theme toggle (dark default).
852+ const themeBtn = document.getElementById("theme_toggle");
853+ function cssVar(name) {{
854+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
855+ }}
856+ function updatePlotlyTheme() {{
857+ if (!window.Plotly) return;
858+ const text = cssVar("--text") || "#000000";
859+ const muted = cssVar("--muted") || text;
860+ const border = cssVar("--border") || "rgba(0,0,0,0.15)";
861+ document.querySelectorAll(".js-plotly-plot").forEach((div) => {{
862+ const updates = {{
863+ "font.color": text,
864+ "paper_bgcolor": "rgba(0,0,0,0)",
865+ "plot_bgcolor": "rgba(0,0,0,0)",
866+ "title.font.color": text,
867+ }};
868+ const layout = div.layout || null;
869+ if (layout) {{
870+ Object.keys(layout).forEach((k) => {{
871+ if (!k.startsWith("xaxis") && !k.startsWith("yaxis")) return;
872+ updates[`${{k}}.tickfont.color`] = text;
873+ updates[`${{k}}.title.font.color`] = text;
874+ updates[`${{k}}.linecolor`] = border;
875+ updates[`${{k}}.gridcolor`] = border;
876+ updates[`${{k}}.zerolinecolor`] = border;
877+ updates[`${{k}}.tickcolor`] = border;
878+ updates[`${{k}}.color`] = text;
879+ }});
880+ if (Array.isArray(layout.annotations)) {{
881+ const anns = layout.annotations.map((a) => {{
882+ const font = Object.assign({{}}, a.font || {{}}, {{ color: text }});
883+ return Object.assign({{}}, a, {{ font }});
884+ }});
885+ updates["annotations"] = anns;
886+ }}
887+ }}
888+ try {{
889+ window.Plotly.relayout(div, updates);
890+ }} catch (e) {{
891+ // ignore
892+ }}
893+ }});
894+ }}
895+ function applyTheme(theme) {{
896+ document.documentElement.setAttribute("data-theme", theme);
897+ if (themeBtn) {{
898+ themeBtn.textContent = theme === "dark" ? "Light mode" : "Dark mode";
899+ }}
900+ updatePlotlyTheme();
901+ }}
902+ const savedTheme = localStorage.getItem("tpcav_theme") || "light";
903+ applyTheme(savedTheme);
904+ if (themeBtn) {{
905+ themeBtn.addEventListener("click", () => {{
906+ const cur = document.documentElement.getAttribute("data-theme") || "dark";
907+ const next = cur === "dark" ? "light" : "dark";
908+ localStorage.setItem("tpcav_theme", next);
909+ applyTheme(next);
910+ }});
911+ }}
912+
822913 const node = document.getElementById("tpcav-report-data");
823914 if (!node) return;
824915 const payload = JSON.parse(node.textContent);
@@ -1033,6 +1124,7 @@ def _png_html(path: Path, alt: str) -> str:
10331124 }});
10341125 Plotly.newPlot("motif_auc_plots", traces, layout, {{displayModeBar: false}}).then(() => {{
10351126 _ensureDownloadBar("motif_auc_plots", "motif_auc_plots");
1127+ updatePlotlyTheme();
10361128 }});
10371129 }}
10381130
@@ -1111,6 +1203,7 @@ def _png_html(path: Path, alt: str) -> str:
11111203 {{displayModeBar: false}}
11121204 ).then(() => {{
11131205 _ensureDownloadBar(entry.heatmap_div_id, "heatmap_" + kind);
1206+ updatePlotlyTheme();
11141207 }});
11151208
11161209 const hoverDiv = document.getElementById(entry.hover_div_id);
@@ -1160,8 +1253,9 @@ def _png_html(path: Path, alt: str) -> str:
11601253 yaxis: {{title: "Log TPCAV score", range: [-5, 5]}},
11611254 }},
11621255 {{displayModeBar: false}}
1163- ).then(() => {{
1256+ ).then(() => {{
11641257 _ensureDownloadBar(divId, "bar_" + kind + "_attr_" + k);
1258+ updatePlotlyTheme();
11651259 }});
11661260 }});
11671261 }}
0 commit comments