update
Browse files
modular_graph_and_candidates.py
CHANGED
|
@@ -1181,6 +1181,43 @@ function linkPath(d) {
|
|
| 1181 |
return `M${sourceX},${sourceY}A${dr},${dr} 0 0,1 ${targetX},${targetY}`;
|
| 1182 |
}
|
| 1183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1184 |
sim.on('tick', () => {
|
| 1185 |
link.attr('d', linkPath);
|
| 1186 |
node.attr('transform', d => `translate(${d.x},${d.y})`);
|
|
@@ -1224,10 +1261,13 @@ TIMELINE_HTML = """
|
|
| 1224 |
<html lang='en'><head><meta charset='UTF-8'>
|
| 1225 |
<title>Transformers Chronological Timeline</title>
|
| 1226 |
<style>__TIMELINE_CSS__</style></head><body>
|
|
|
|
| 1227 |
<div id='controls'>
|
| 1228 |
<div style='font-weight:600; margin-bottom:8px;'>Chronological Timeline</div>
|
| 1229 |
🟡 base<br>🔵 modular<br>🔴 candidate<br>
|
| 1230 |
<label><input type="checkbox" id="toggleRed" checked> Show candidates</label>
|
|
|
|
|
|
|
| 1231 |
<div style='margin-top:10px; font-size:13px; color:var(--muted);'>
|
| 1232 |
Models positioned by creation date<br>
|
| 1233 |
Scroll & zoom to explore timeline
|
|
|
|
| 1181 |
return `M${sourceX},${sourceY}A${dr},${dr} 0 0,1 ${targetX},${targetY}`;
|
| 1182 |
}
|
| 1183 |
|
| 1184 |
+
function idOf(x){ return typeof x === 'object' ? x.id : x; }
|
| 1185 |
+
|
| 1186 |
+
function neighborsOf(id){
|
| 1187 |
+
const out = new Set([id]);
|
| 1188 |
+
Object.keys(linkedByIndex).forEach(k=>{
|
| 1189 |
+
const [a,b] = k.split(',');
|
| 1190 |
+
if(a===id) out.add(b);
|
| 1191 |
+
if(b===id) out.add(a);
|
| 1192 |
+
});
|
| 1193 |
+
return out;
|
| 1194 |
+
}
|
| 1195 |
+
|
| 1196 |
+
// Highlight matches + neighbors; empty query resets
|
| 1197 |
+
function applySearch(q){
|
| 1198 |
+
q = (q || '').trim().toLowerCase();
|
| 1199 |
+
if(!q){
|
| 1200 |
+
node.select("circle").style("opacity", 1);
|
| 1201 |
+
g.selectAll(".node-label").style("opacity", 1);
|
| 1202 |
+
link.style("opacity", 1);
|
| 1203 |
+
return;
|
| 1204 |
+
}
|
| 1205 |
+
const matches = new Set(timeline.nodes.filter(n => n.id.toLowerCase().includes(q)).map(n=>n.id));
|
| 1206 |
+
const keep = new Set();
|
| 1207 |
+
matches.forEach(m => neighborsOf(m).forEach(x => keep.add(x)));
|
| 1208 |
+
|
| 1209 |
+
node.select("circle").style("opacity", d => keep.has(d.id) ? 1 : 0.08);
|
| 1210 |
+
g.selectAll(".node-label").style("opacity", d => keep.has(d.id) ? 1 : 0.08);
|
| 1211 |
+
link.style("opacity", d => {
|
| 1212 |
+
const s = idOf(d.source), t = idOf(d.target);
|
| 1213 |
+
return (keep.has(s) && keep.has(t)) ? 1 : 0.08;
|
| 1214 |
+
});
|
| 1215 |
+
}
|
| 1216 |
+
|
| 1217 |
+
// wire it up
|
| 1218 |
+
document.getElementById('searchBox').addEventListener('input', e => applySearch(e.target.value));
|
| 1219 |
+
|
| 1220 |
+
|
| 1221 |
sim.on('tick', () => {
|
| 1222 |
link.attr('d', linkPath);
|
| 1223 |
node.attr('transform', d => `translate(${d.x},${d.y})`);
|
|
|
|
| 1261 |
<html lang='en'><head><meta charset='UTF-8'>
|
| 1262 |
<title>Transformers Chronological Timeline</title>
|
| 1263 |
<style>__TIMELINE_CSS__</style></head><body>
|
| 1264 |
+
|
| 1265 |
<div id='controls'>
|
| 1266 |
<div style='font-weight:600; margin-bottom:8px;'>Chronological Timeline</div>
|
| 1267 |
🟡 base<br>🔵 modular<br>🔴 candidate<br>
|
| 1268 |
<label><input type="checkbox" id="toggleRed" checked> Show candidates</label>
|
| 1269 |
+
<label><input type="checkbox" id="toggleRed" checked> Show candidates</label>
|
| 1270 |
+
<input id="searchBox" type="text" placeholder="Search model…" style="margin-top:10px;width:100%;padding:6px 8px;border-radius:8px;border:1px solid #ccc;background:transparent;color:inherit;">
|
| 1271 |
<div style='margin-top:10px; font-size:13px; color:var(--muted);'>
|
| 1272 |
Models positioned by creation date<br>
|
| 1273 |
Scroll & zoom to explore timeline
|