kaveh commited on
Commit
3dd7d71
·
1 Parent(s): 334ddc0

umap grid removed. tootip font size increase. text in home page aligned.

Browse files
streamlit_hf/home.py CHANGED
@@ -55,7 +55,11 @@ _APP_SUBTITLE = (
55
  )
56
 
57
  _EXPERIMENTAL_SYSTEM_MD = f"""
58
- Mouse embryonic fibroblasts (**MEFs**) were reprogrammed toward induced endoderm progenitors (**iEPs**) **in vitro** through *Foxa1* and *HNF4A* induction. This process produces **mixed outcomes**: some cells successfully reach the **iEP fate**, whereas others diverge into **off-target** trajectories and stall in **dead-end states**. Using [**CellTag-Multi**]({_CELLTAG_MULTI_ARTICLE_URL}) clonal barcoding, **early cells** could be linked to their **later fate**, which made it possible to ask a central biological question: which programs in **early-state cells**, coordinated **across transcriptional, chromatin, and metabolic layers**, drive successful reprogramming, which ones push cells toward off-target states, and which of these mechanisms could be targeted to improve reprogramming efficiency?
 
 
 
 
59
  """
60
 
61
  _BIOLOGY_CONTEXT_MARKDOWN = f"""
@@ -83,8 +87,14 @@ def _render_experiment_schematic(width_px: int) -> None:
83
  raw = raw.split("?>", 1)[1].lstrip()
84
  html_doc = f"""<!DOCTYPE html>
85
  <html><head><meta charset="utf-8"/><style>
86
- html, body {{ margin: 0; padding: 0; overflow: hidden; background: transparent; }}
87
- .ff-experiment-svg-wrap {{ width: {width_px}px; max-width: 100%; }}
 
 
 
 
 
 
88
  .ff-experiment-svg-wrap svg {{ width: 100%; height: auto; display: block; }}
89
  .ff-experiment-svg-wrap svg g[id] {{
90
  cursor: help;
@@ -105,10 +115,77 @@ html, body {{ margin: 0; padding: 0; overflow: hidden; background: transparent;
105
  .ff-experiment-svg-wrap svg text:hover {{
106
  filter: brightness(1.08);
107
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  </style></head><body>
109
  <div class="ff-experiment-svg-wrap">
110
  {raw}
111
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  </body></html>"""
113
  st.iframe(html_doc, width=width_px, height="content")
114
 
@@ -116,6 +193,21 @@ html, body {{ margin: 0; padding: 0; overflow: hidden; background: transparent;
116
  ui.inject_app_styles()
117
  ui.inject_home_landing_styles()
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  st.markdown(
120
  f"""<div class="ff-hero"><div class="ff-hero-inner"><div class="ff-hero-text">
121
  <div class="ff-hero-title-row">
@@ -128,7 +220,11 @@ st.markdown(
128
  )
129
 
130
  with st.container(border=True):
131
- fig_col, text_col = st.columns([0.42, 0.58], gap="large")
 
 
 
 
132
  with fig_col:
133
  if _EXPERIMENT_SVG.is_file():
134
  _render_experiment_schematic(_EXPERIMENT_FIGURE_WIDTH_PX)
 
55
  )
56
 
57
  _EXPERIMENTAL_SYSTEM_MD = f"""
58
+ Mouse embryonic fibroblasts (**MEFs**) were reprogrammed toward induced endoderm progenitors (**iEPs**) **in vitro** through *Foxa1* and *HNF4A* induction.
59
+
60
+ This process produces **mixed outcomes**: some cells successfully reach the **iEP fate**, whereas others diverge into **off-target** trajectories and stall in **dead-end states**.
61
+
62
+ Using [**CellTag-Multi**]({_CELLTAG_MULTI_ARTICLE_URL}) clonal barcoding, **early cells** could be linked to their **later fate**, which made it possible to ask a central biological question: which programs in **early-state cells**, coordinated **across transcriptional, chromatin, and metabolic layers**, drive successful reprogramming, which ones push cells toward off-target states, and which of these mechanisms could be targeted to improve reprogramming efficiency?
63
  """
64
 
65
  _BIOLOGY_CONTEXT_MARKDOWN = f"""
 
87
  raw = raw.split("?>", 1)[1].lstrip()
88
  html_doc = f"""<!DOCTYPE html>
89
  <html><head><meta charset="utf-8"/><style>
90
+ html, body {{
91
+ margin: 0;
92
+ padding: 0;
93
+ background: transparent;
94
+ overflow: visible;
95
+ box-sizing: border-box;
96
+ }}
97
+ .ff-experiment-svg-wrap {{ width: {width_px}px; max-width: 100%; overflow: visible; }}
98
  .ff-experiment-svg-wrap svg {{ width: 100%; height: auto; display: block; }}
99
  .ff-experiment-svg-wrap svg g[id] {{
100
  cursor: help;
 
115
  .ff-experiment-svg-wrap svg text:hover {{
116
  filter: brightness(1.08);
117
  }}
118
+ #ff-svgtip {{
119
+ position: fixed;
120
+ left: 0;
121
+ top: 0;
122
+ z-index: 2147483647;
123
+ display: none;
124
+ max-width: min(22rem, calc(100vw - 20px));
125
+ padding: 10px 14px;
126
+ font-size: 15px;
127
+ line-height: 1.5;
128
+ font-family: system-ui, -apple-system, Segoe UI, sans-serif;
129
+ color: #f1f5f9;
130
+ background: #0f172a;
131
+ border-radius: 8px;
132
+ box-shadow: 0 4px 18px rgba(0,0,0,.25);
133
+ pointer-events: none;
134
+ }}
135
  </style></head><body>
136
  <div class="ff-experiment-svg-wrap">
137
  {raw}
138
  </div>
139
+ <script>
140
+ (function () {{
141
+ const tip = document.createElement("div");
142
+ tip.id = "ff-svgtip";
143
+ document.body.appendChild(tip);
144
+ const OFFSET = 14;
145
+ const PAD = 10;
146
+ function placeTip(e) {{
147
+ if (tip.style.display !== "block") return;
148
+ tip.style.visibility = "hidden";
149
+ tip.style.left = "0";
150
+ tip.style.top = "0";
151
+ const w = tip.offsetWidth;
152
+ const h = tip.offsetHeight;
153
+ tip.style.visibility = "visible";
154
+ const vw = window.innerWidth;
155
+ const vh = window.innerHeight;
156
+ const O = OFFSET;
157
+ const P = PAD;
158
+ let x = e.clientX + O;
159
+ let y = e.clientY + O;
160
+ if (y + h + P > vh) y = e.clientY - h - O;
161
+ if (x + w + P > vw) x = e.clientX - w - O;
162
+ if (x + w + P > vw) x = Math.max(P, vw - w - P);
163
+ if (y + h + P > vh) y = Math.max(P, vh - h - P);
164
+ if (x < P) x = P;
165
+ if (y < P) y = P;
166
+ tip.style.left = x + "px";
167
+ tip.style.top = y + "px";
168
+ }}
169
+ function bind(el) {{
170
+ if (el.closest && el.closest("#microscope")) return;
171
+ const t = el.querySelector(":scope > title");
172
+ if (!t || !t.textContent.trim()) return;
173
+ const txt = t.textContent.trim();
174
+ t.remove();
175
+ el.addEventListener("mouseenter", function (e) {{
176
+ tip.textContent = txt;
177
+ tip.style.display = "block";
178
+ requestAnimationFrame(function () {{ placeTip(e); }});
179
+ }});
180
+ el.addEventListener("mousemove", function (e) {{ placeTip(e); }});
181
+ el.addEventListener("mouseleave", function () {{
182
+ tip.style.display = "none";
183
+ }});
184
+ }}
185
+ document.querySelectorAll(".ff-experiment-svg-wrap svg g[id]").forEach(bind);
186
+ document.querySelectorAll(".ff-experiment-svg-wrap svg text").forEach(bind);
187
+ }})();
188
+ </script>
189
  </body></html>"""
190
  st.iframe(html_doc, width=width_px, height="content")
191
 
 
193
  ui.inject_app_styles()
194
  ui.inject_home_landing_styles()
195
 
196
+ # Bordered Streamlit blocks use overflow that clips iframe tooltips; allow paint past the card edge.
197
+ st.markdown(
198
+ """
199
+ <style>
200
+ section[data-testid="stMain"] div[data-testid="stVerticalBlockBorderWrapper"]:has(iframe) {
201
+ overflow: visible !important;
202
+ }
203
+ section[data-testid="stMain"] div[data-testid="stVerticalBlockBorderWrapper"]:has(iframe) > div {
204
+ overflow: visible !important;
205
+ }
206
+ </style>
207
+ """,
208
+ unsafe_allow_html=True,
209
+ )
210
+
211
  st.markdown(
212
  f"""<div class="ff-hero"><div class="ff-hero-inner"><div class="ff-hero-text">
213
  <div class="ff-hero-title-row">
 
220
  )
221
 
222
  with st.container(border=True):
223
+ # Wider text column fewer wrapped lines; tighter gap; center figure vs text when heights differ.
224
+ try:
225
+ fig_col, text_col = st.columns([0.33, 0.67], gap="medium", vertical_alignment="center")
226
+ except TypeError:
227
+ fig_col, text_col = st.columns([0.33, 0.67], gap="medium")
228
  with fig_col:
229
  if _EXPERIMENT_SVG.is_file():
230
  _render_experiment_schematic(_EXPERIMENT_FIGURE_WIDTH_PX)
streamlit_hf/lib/plots.py CHANGED
@@ -211,8 +211,8 @@ def latent_scatter(
211
  )
212
  elif not title:
213
  fig.update_layout(title=None)
214
- fig.update_xaxes(showticklabels=False, showgrid=True, gridcolor="rgba(0,0,0,0.06)", zeroline=False)
215
- fig.update_yaxes(showticklabels=False, showgrid=True, gridcolor="rgba(0,0,0,0.06)", zeroline=False)
216
  return fig
217
 
218
 
 
211
  )
212
  elif not title:
213
  fig.update_layout(title=None)
214
+ fig.update_xaxes(showticklabels=False, showgrid=False, zeroline=False)
215
+ fig.update_yaxes(showticklabels=False, showgrid=False, zeroline=False)
216
  return fig
217
 
218
 
streamlit_hf/pages/1_Single_Cell_Explorer.py CHANGED
@@ -27,7 +27,7 @@ _UMAP_EXPLORER_SUBTITLE = "Hover points for details · drag on the plot to selec
27
  _UMAP_EXPLORER_HELP = f"""
28
  **What this is:** The same **2‑D UMAP** as on **Home**: validation **single cells** in **FateFormer**’s **latent space** (**context vector token representation**), summarised across **5-fold cross-validation** (**2,110** cells before filters). Here you **choose what to colour** and **filter** the cloud.
29
 
30
- **How to read it:** Each point is one cell. **Colour** comes from **Colour by**: e.g. [**CellTag-Multi**]({_CELLTAG_MULTI_ARTICLE_URL}) **label**, **predicted fate**, **prediction correct / wrong**, **CV fold**, **batch**, which **modalities** are present, or **dominant fate %**. **Axes are unitless** (UMAP preserves *local* neighbourhoods only). **Hover** a point for per-cell fields.
31
 
32
  **Using this page:** Use **Filters** to keep modality combinations, restrict **prediction outcome** (all / correct only / wrong only), choose **CV folds**, and set a **dominant fate %** range. In the plot **toolbar** (top right), pick **Box select** or **Lasso select**, then **drag** on the canvas; the app **reruns** and the **Selected points** table fills with those rows. To inspect **one** cell without a selection, scroll to **Inspect by dataset index**.
33
  """
@@ -58,7 +58,6 @@ with left:
58
  "correct",
59
  "fold",
60
  "batch_no",
61
- "modality_label",
62
  "pct",
63
  ],
64
  format_func=lambda x: {
@@ -67,7 +66,6 @@ with left:
67
  "correct": "Prediction correct",
68
  "fold": "CV fold",
69
  "batch_no": "Batch",
70
- "modality_label": "Available modalities",
71
  "pct": "Dominant fate %",
72
  }[x],
73
  label_visibility="collapsed",
 
27
  _UMAP_EXPLORER_HELP = f"""
28
  **What this is:** The same **2‑D UMAP** as on **Home**: validation **single cells** in **FateFormer**’s **latent space** (**context vector token representation**), summarised across **5-fold cross-validation** (**2,110** cells before filters). Here you **choose what to colour** and **filter** the cloud.
29
 
30
+ **How to read it:** Each point is one cell. **Colour** comes from **Colour by**: e.g. [**CellTag-Multi**]({_CELLTAG_MULTI_ARTICLE_URL}) **label**, **predicted fate**, **prediction correct / wrong**, **CV fold**, **batch**, or **dominant fate %**. **Axes are unitless** (UMAP preserves *local* neighbourhoods only). **Hover** a point for per-cell fields.
31
 
32
  **Using this page:** Use **Filters** to keep modality combinations, restrict **prediction outcome** (all / correct only / wrong only), choose **CV folds**, and set a **dominant fate %** range. In the plot **toolbar** (top right), pick **Box select** or **Lasso select**, then **drag** on the canvas; the app **reruns** and the **Selected points** table fills with those rows. To inspect **one** cell without a selection, scroll to **Inspect by dataset index**.
33
  """
 
58
  "correct",
59
  "fold",
60
  "batch_no",
 
61
  "pct",
62
  ],
63
  format_func=lambda x: {
 
66
  "correct": "Prediction correct",
67
  "fold": "CV fold",
68
  "batch_no": "Batch",
 
69
  "pct": "Dominant fate %",
70
  }[x],
71
  label_visibility="collapsed",