diff --git a/src/epicc/model/models/tb_isolation.yaml b/src/epicc/model/models/tb_isolation.yaml index 732028b..4340a03 100644 --- a/src/epicc/model/models/tb_isolation.yaml +++ b/src/epicc/model/models/tb_isolation.yaml @@ -351,7 +351,7 @@ report: infections. $$ - \LaTeX \text{ test.} + \text{LaTeX test.} $$ ## Scenarios diff --git a/src/epicc/ui/export.py b/src/epicc/ui/export.py index f5ec3f9..b59d69e 100644 --- a/src/epicc/ui/export.py +++ b/src/epicc/ui/export.py @@ -98,12 +98,11 @@ def render_parameter_export_modal( def render_pdf_export_button(container: Any = None) -> None: # Render a direct Save report as PDF button. - rc = container if container is not None else st clicked = rc.button( "Save report as PDF", disabled=not has_results(), - width='stretch', + use_container_width=True, type='primary', ) @@ -124,25 +123,8 @@ def trigger_print_if_requested() -> None: trigger_token = st.session_state.get(_PRINT_TOKEN_KEY, 0) - # What the hell is this, Streamlit? Why can't I just run JS without this nonsense? Yes, I know - # you don't want me to mess with your UI, but I just want to trigger the browser print dialog, - # is that really so bad? I even told you it was okay to run unsafe JS, but no, you had to run - # it through some weird sanitizer anyways. - # - # What's worse is that you silently drop that JS which fails your mysterious security checks - # instead of throwing an error, leaving me to waste hours debugging why my print button doesn't - # work at all. So here we are, base64 encoding the JS and evaling it in the browser, just to get - # around your broken injection system. I hope you're proud of yourselves. - # - # Seriously!?!? This works? - # - # This is an alternative implementation to something like: - # - # https://github.com/thunderbug1/streamlit-javascript - # - # Which would have a mess build-wise. As far as I know, I'm the first person to come up with this - # workaround, so I'm claiming it as my own invention! Don't tell Streamlit. - + # Reverted to Olivia's original, hacky, but highly effective base64 injection bypass. + # By using st.html, the script runs in the main window context rather than an isolated iframe. with importlib.resources.files("epicc").joinpath("js/print_results.js").open("rb") as f: js = f.read().decode() js64 = base64.b64encode(js.encode()).decode() @@ -155,4 +137,4 @@ def trigger_print_if_requested() -> None: unsafe_allow_javascript=True, ) - st.session_state[_PRINT_REQUESTED_KEY] = False + st.session_state[_PRINT_REQUESTED_KEY] = False \ No newline at end of file diff --git a/src/epicc/ui/report.py b/src/epicc/ui/report.py index 3892e25..0dfe742 100644 --- a/src/epicc/ui/report.py +++ b/src/epicc/ui/report.py @@ -172,20 +172,24 @@ def render(self, run_results: dict[str, Any] | None) -> None: if self._block.title: st.markdown( f"
" - f"" + f"" f"{self._block.title}
", unsafe_allow_html=True, ) if self._block.caption: st.markdown( - f"
" - f"" + f"
" + f"" f"{self._block.caption}
", unsafe_allow_html=True ) - st.plotly_chart(fig, width='stretch', key=f'plotly-{self._uuid}') + st.plotly_chart( + fig, + use_container_width=True, + key=f"plotly-{self._uuid}", + ) def _resolve_columns( self, run_results: dict[str, Any] diff --git a/src/epicc/web/sidebar.css b/src/epicc/web/sidebar.css index 3630cc8..03d29b4 100644 --- a/src/epicc/web/sidebar.css +++ b/src/epicc/web/sidebar.css @@ -25,7 +25,7 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { /* Fix model selector alignment. TODO: be more specific, `:first-child`? */ .stVerticalBlock { - justify-content: center; + justify-content: center; } /* Sidebar section headers */ @@ -95,16 +95,22 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { margin: 0.5in; } - /* [data-testid="stAppViewContainer"] { - margin: 0 !important; - padding: 0 !important; + html, body, #root, .stlite-container, [data-testid="stAppViewContainer"] { + height: auto !important; + overflow: visible !important; + opacity: 1 !important; + filter: none !important; + -webkit-filter: none !important; } - [data-testid="stMainBlockContainer"] { - max-width: 100% !important; - padding-top: 0 !important; - padding-bottom: 0 !important; - } */ + [data-testid="stSelectbox"], + [data-testid="stColumn"] button, + [data-testid="stHeader"], + [data-testid="stSidebar"], + .stAppDeployButton, + footer { + display: none !important; + } .section-divider { page-break-after: auto; @@ -131,12 +137,10 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { .stDataFrame, .stAgGrid, .stTable { - padding-top: 1.5rem !important; + padding-top: 0.5rem !important; padding-bottom: 1.5rem !important; } - /* Show only results. */ - body * { display: none; } @@ -158,5 +162,177 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { .st-key-results-report, .st-key-results-report * { display: revert !important; + opacity: 1 !important; + filter: none !important; + } +} + +/* Action row wrapper */ +.st-key-param-actions-row [data-testid="stHorizontalBlock"] { + align-items: flex-start !important; +} + +/* Normalize column internal spacing */ +.st-key-param-actions-row [data-testid="column"] > div { + padding-top: 0 !important; +} +.st-key-param-actions-row [data-testid="stElementContainer"] { + margin-top: 0 !important; + margin-bottom: 0 !important; +} + +/* Make trigger controls fill width and remove extra top offset */ +.st-key-param-actions-row [data-testid="stButton"], +.st-key-param-actions-row [data-testid="stPopover"] { + width: 100%; + margin-top: 0 !important; + padding-top: 0 !important; +} +.st-key-param-actions-row [data-testid="stButton"] > button, +.st-key-param-actions-row [data-testid="stPopover"] > button { + width: 100%; +} + +/* Report spacing: keep headings away from charts */ +.st-key-results-report [data-testid="stPlotlyChart"], +.st-key-results-report [data-testid="stVegaLiteChart"], +.st-key-results-report [data-testid="stPyplot"] { + margin-bottom: 2rem !important; +} + +.st-key-results-report [data-testid="stMarkdownContainer"] h1, +.st-key-results-report [data-testid="stMarkdownContainer"] h2, +.st-key-results-report [data-testid="stMarkdownContainer"] h3 { + margin-top: 1.25rem !important; +} + +/* Plotly text contrast follows the active Streamlit theme */ +.st-key-results-report [data-testid="stPlotlyChart"] { + color: inherit !important; +} + +.st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot, +.st-key-results-report [data-testid="stPlotlyChart"] .plot-container, +.st-key-results-report [data-testid="stPlotlyChart"] .main-svg { + color: inherit !important; +} + +.st-key-results-report .js-plotly-plot text, +.st-key-results-report .js-plotly-plot .legendtext, +.st-key-results-report .js-plotly-plot .gtitle, +.st-key-results-report .js-plotly-plot .xtick text, +.st-key-results-report .js-plotly-plot .ytick text, +.st-key-results-report .js-plotly-plot .annotation text, +.st-key-results-report .js-plotly-plot .infolayer text { + fill: currentColor !important; + color: inherit !important; +} + +/* -------- Print/PDF display fixes only (SINGLE CONSOLIDATED BLOCK) -------- */ +@media print { + .st-key-results-report { + overflow: visible !important; + } + + .st-key-results-report [data-testid="stPlotlyChart"], + .st-key-results-report [data-testid="stVegaLiteChart"], + .st-key-results-report [data-testid="stPyplot"] { + display: block !important; + position: static !important; + clear: both !important; + overflow: visible !important; + break-inside: avoid !important; + page-break-inside: avoid !important; + margin: 0 0 2.8rem 0 !important; + padding-bottom: 1.2rem !important; + } + + .st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot { + min-height: 520px !important; + } + + .st-key-results-report h1, + .st-key-results-report h2, + .st-key-results-report h3, + .st-key-results-report h4 { + clear: both !important; + position: static !important; + z-index: auto !important; + margin-top: 2rem !important; + } + + .st-key-results-report p, + .st-key-results-report [data-testid="stMarkdownContainer"] { + clear: both !important; + position: static !important; + z-index: auto !important; + margin-top: 0.5rem !important; + } + + .st-key-results-report [data-testid="stMarkdownContainer"] h1, + .st-key-results-report [data-testid="stMarkdownContainer"] h2, + .st-key-results-report [data-testid="stMarkdownContainer"] h3 { + margin-top: 2rem !important; + clear: both !important; + } + + .st-key-results-report .katex, + .st-key-results-report .katex-display, + .st-key-results-report mjx-container, + .st-key-results-report .MathJax { + visibility: visible !important; + opacity: 1 !important; + } + + .st-key-results-report .katex .katex-mathml, + .st-key-results-report mjx-assistive-mml { + display: none !important; + } + + /* Force strict contrast engine reset across stlite modules */ + .st-key-results-report, + .st-key-results-report * { + color: #111827 !important; + text-shadow: none !important; + -webkit-print-color-adjust: exact !important; + print-color-adjust: exact !important; + } + + .st-key-results-report [data-testid="stPlotlyChart"], + .st-key-results-report [data-testid="stPlotlyChart"] * { + opacity: 1 !important; + } + + .st-key-results-report .js-plotly-plot text, + .st-key-results-report .js-plotly-plot .legendtext, + .st-key-results-report .js-plotly-plot .gtitle, + .st-key-results-report .js-plotly-plot .xtick text, + .st-key-results-report .js-plotly-plot .ytick text, + .st-key-results-report .js-plotly-plot .annotation text, + .st-key-results-report .js-plotly-plot .infolayer text { + fill: #111827 !important; + color: #111827 !important; + } + + .st-key-results-report .js-plotly-plot .xaxislayer-above path, + .st-key-results-report .js-plotly-plot .yaxislayer-above path, + .st-key-results-report .js-plotly-plot .xaxislayer-above line, + .st-key-results-report .js-plotly-plot .yaxislayer-above line { + stroke: #111827 !important; + stroke-opacity: 1 !important; + stroke-width: 1.2 !important; + } + + .st-key-results-report .js-plotly-plot .gridlayer path, + .st-key-results-report .js-plotly-plot .gridlayer line, + .st-key-results-report .js-plotly-plot .zerolinelayer path, + .st-key-results-report .js-plotly-plot .zerolinelayer line { + stroke: #9ca3af !important; + stroke-opacity: 0.9 !important; + } + + .st-key-results-report .js-plotly-plot .scatterlayer .trace path, + .st-key-results-report .js-plotly-plot .barlayer .trace path { + stroke-opacity: 1 !important; } } \ No newline at end of file