Spaces:
Sleeping
Sleeping
Update templates/index.html
Browse files- templates/index.html +60 -9
templates/index.html
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
<!doctype html>
|
| 2 |
<html lang="en">
|
| 3 |
<head>
|
|
@@ -63,7 +64,7 @@
|
|
| 63 |
ol{ padding-left: 22px; }
|
| 64 |
ol li{ margin: 12px 0; }
|
| 65 |
a{ color: var(--accent2); }
|
| 66 |
-
.muted{ color
|
| 67 |
|
| 68 |
/* Sidebar: full merged text */
|
| 69 |
.sidebar-backdrop{ position: fixed; inset: 0; background: rgba(0,0,0,.35); opacity:0; pointer-events:none; transition:.18s ease; z-index:60; }
|
|
@@ -74,8 +75,22 @@
|
|
| 74 |
.sbar-hdr h3{ margin:0; font-size:16px; }
|
| 75 |
.sbar-body{ overflow:auto; padding:16px; }
|
| 76 |
.smallbtn{ font-size:12px; padding:6px 8px; border-radius:10px; background:#1a213f; color:#dbe2ff; border:1px solid #ffffff18; cursor:pointer; }
|
|
|
|
| 77 |
pre#fullText{ margin:0; white-space:pre-wrap; word-wrap:break-word; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; line-height:1.5; }
|
| 78 |
mark#centerMark{ background:#61e7ff55; padding:0 .5px; border-radius:3px; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
</style>
|
| 80 |
</head>
|
| 81 |
<body>
|
|
@@ -170,10 +185,29 @@
|
|
| 170 |
{% for r in results %}
|
| 171 |
<li>
|
| 172 |
<p>{{ r.text }}</p>
|
| 173 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
</li>
|
| 175 |
{% endfor %}
|
| 176 |
</ol>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
{% endif %}
|
| 178 |
</div>
|
| 179 |
</section>
|
|
@@ -279,8 +313,9 @@
|
|
| 279 |
btnClose.addEventListener('click', closeSidebar);
|
| 280 |
backdrop.addEventListener('click', closeSidebar);
|
| 281 |
|
| 282 |
-
|
| 283 |
-
|
|
|
|
| 284 |
|
| 285 |
function escapeHtml(s){
|
| 286 |
return s.replaceAll('&','&')
|
|
@@ -305,14 +340,11 @@
|
|
| 305 |
const after = escapeHtml(merged.slice(end));
|
| 306 |
pre.innerHTML = before + '<mark id="centerMark">' + middle + '</mark>' + after;
|
| 307 |
|
| 308 |
-
//
|
| 309 |
requestAnimationFrame(() => {
|
| 310 |
const mark = document.getElementById('centerMark');
|
| 311 |
if(mark){
|
| 312 |
-
|
| 313 |
-
const bodyRect = sidebarBody.getBoundingClientRect();
|
| 314 |
-
const offset = (rect.top + rect.height/2) - (bodyRect.top + bodyRect.height/2);
|
| 315 |
-
sidebarBody.scrollBy({ top: offset, behavior: 'smooth' });
|
| 316 |
}
|
| 317 |
});
|
| 318 |
}
|
|
@@ -326,6 +358,25 @@
|
|
| 326 |
alert('Error: ' + err.message);
|
| 327 |
}
|
| 328 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
</script>
|
| 330 |
</body>
|
| 331 |
</html>
|
|
|
|
| 1 |
+
<!-- templates/index.html -->
|
| 2 |
<!doctype html>
|
| 3 |
<html lang="en">
|
| 4 |
<head>
|
|
|
|
| 64 |
ol{ padding-left: 22px; }
|
| 65 |
ol li{ margin: 12px 0; }
|
| 66 |
a{ color: var(--accent2); }
|
| 67 |
+
.muted{ color:#b7c0ffcc; font-size:13px; }
|
| 68 |
|
| 69 |
/* Sidebar: full merged text */
|
| 70 |
.sidebar-backdrop{ position: fixed; inset: 0; background: rgba(0,0,0,.35); opacity:0; pointer-events:none; transition:.18s ease; z-index:60; }
|
|
|
|
| 75 |
.sbar-hdr h3{ margin:0; font-size:16px; }
|
| 76 |
.sbar-body{ overflow:auto; padding:16px; }
|
| 77 |
.smallbtn{ font-size:12px; padding:6px 8px; border-radius:10px; background:#1a213f; color:#dbe2ff; border:1px solid #ffffff18; cursor:pointer; }
|
| 78 |
+
|
| 79 |
pre#fullText{ margin:0; white-space:pre-wrap; word-wrap:break-word; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; line-height:1.5; }
|
| 80 |
mark#centerMark{ background:#61e7ff55; padding:0 .5px; border-radius:3px; }
|
| 81 |
+
|
| 82 |
+
/* New: results toolbuttons + file label */
|
| 83 |
+
.tools{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-top:6px; }
|
| 84 |
+
.iconbtn{
|
| 85 |
+
display:inline-flex; align-items:center; justify-content:center;
|
| 86 |
+
width:28px; height:28px; border-radius:8px; border:1px solid #ffffff18;
|
| 87 |
+
background:#1a213f; color:#e6ecff; cursor:pointer; font-size:14px;
|
| 88 |
+
}
|
| 89 |
+
.filetag{
|
| 90 |
+
display:inline-flex; align-items:center; gap:6px;
|
| 91 |
+
padding:4px 8px; border-radius:999px; border:1px solid #ffffff18;
|
| 92 |
+
background:#0d1333; color:#cfe1ff; font-size:12px;
|
| 93 |
+
}
|
| 94 |
</style>
|
| 95 |
</head>
|
| 96 |
<body>
|
|
|
|
| 185 |
{% for r in results %}
|
| 186 |
<li>
|
| 187 |
<p>{{ r.text }}</p>
|
| 188 |
+
<div class="tools">
|
| 189 |
+
<button type="button" class="smallbtn" onclick="openContext({{ r.idx }})" title="Open full context sidebar">π View context</button>
|
| 190 |
+
|
| 191 |
+
<!-- Clipboard icon: copies the 2nd line of the source file -->
|
| 192 |
+
<button type="button" class="iconbtn" onclick="copySecond({{ r.idx }}, this)" title="Copy 2nd line of this file">π</button>
|
| 193 |
+
|
| 194 |
+
<!-- File name label -->
|
| 195 |
+
<span class="filetag">π {{ r.file }}</span>
|
| 196 |
+
</div>
|
| 197 |
</li>
|
| 198 |
{% endfor %}
|
| 199 |
</ol>
|
| 200 |
+
|
| 201 |
+
<!-- Provide per-paragraph metadata for JS (2nd line + filename) -->
|
| 202 |
+
<script>
|
| 203 |
+
window.PARA_META = {};
|
| 204 |
+
{% for r in results %}
|
| 205 |
+
PARA_META[{{ r.idx }}] = {
|
| 206 |
+
second: {{ r.second_line | tojson }},
|
| 207 |
+
file: {{ r.file | tojson }}
|
| 208 |
+
};
|
| 209 |
+
{% endfor %}
|
| 210 |
+
</script>
|
| 211 |
{% endif %}
|
| 212 |
</div>
|
| 213 |
</section>
|
|
|
|
| 313 |
btnClose.addEventListener('click', closeSidebar);
|
| 314 |
backdrop.addEventListener('click', closeSidebar);
|
| 315 |
|
| 316 |
+
// Instant jumps for top/bottom
|
| 317 |
+
jumpTop.addEventListener('click', () => { sidebarBody.scrollTop = 0; });
|
| 318 |
+
jumpBottom.addEventListener('click', () => { sidebarBody.scrollTop = sidebarBody.scrollHeight; });
|
| 319 |
|
| 320 |
function escapeHtml(s){
|
| 321 |
return s.replaceAll('&','&')
|
|
|
|
| 340 |
const after = escapeHtml(merged.slice(end));
|
| 341 |
pre.innerHTML = before + '<mark id="centerMark">' + middle + '</mark>' + after;
|
| 342 |
|
| 343 |
+
// Instantly jump to the highlight
|
| 344 |
requestAnimationFrame(() => {
|
| 345 |
const mark = document.getElementById('centerMark');
|
| 346 |
if(mark){
|
| 347 |
+
mark.scrollIntoView({ block: 'center', inline: 'nearest' });
|
|
|
|
|
|
|
|
|
|
| 348 |
}
|
| 349 |
});
|
| 350 |
}
|
|
|
|
| 358 |
alert('Error: ' + err.message);
|
| 359 |
}
|
| 360 |
}
|
| 361 |
+
|
| 362 |
+
// Clipboard: copy the 2nd line of the source file for this paragraph
|
| 363 |
+
window.copySecond = function(idx, btn){
|
| 364 |
+
try{
|
| 365 |
+
const meta = window.PARA_META?.[idx];
|
| 366 |
+
if(!meta){ throw new Error("Missing metadata"); }
|
| 367 |
+
const text = meta.second || "";
|
| 368 |
+
navigator.clipboard.writeText(text).then(() => {
|
| 369 |
+
// simple visual feedback
|
| 370 |
+
const old = btn.textContent;
|
| 371 |
+
btn.textContent = 'β
';
|
| 372 |
+
setTimeout(()=>{ btn.textContent = 'π'; }, 900);
|
| 373 |
+
}, () => {
|
| 374 |
+
alert("Clipboard copy failed");
|
| 375 |
+
});
|
| 376 |
+
}catch(e){
|
| 377 |
+
alert("Clipboard error: " + e.message);
|
| 378 |
+
}
|
| 379 |
+
}
|
| 380 |
</script>
|
| 381 |
</body>
|
| 382 |
</html>
|