Spaces:
Running
Running
Update templates/index.html
Browse files- templates/index.html +92 -10
templates/index.html
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
|
|
| 6 |
<title>{{ app_title }}</title>
|
| 7 |
<style>
|
| 8 |
:root {
|
|
@@ -57,9 +58,10 @@
|
|
| 57 |
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
|
| 58 |
.logo {
|
| 59 |
width: 30px; height: 30px; border-radius: 10px;
|
| 60 |
-
display:
|
| 61 |
-
|
| 62 |
box-shadow: 0 6px 18px rgba(108,131,255,.25);
|
|
|
|
| 63 |
flex: 0 0 auto;
|
| 64 |
}
|
| 65 |
.brand-title { font-weight: 700; letter-spacing: -.03em; font-size: 15px; }
|
|
@@ -351,6 +353,10 @@
|
|
| 351 |
background: rgba(255,255,255,.02);
|
| 352 |
animation: fadeUp 200ms ease both;
|
| 353 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
.other-answer-head {
|
| 355 |
display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
|
| 356 |
color: var(--muted); font-family: var(--mono); font-size: 10px;
|
|
@@ -472,10 +478,42 @@
|
|
| 472 |
|
| 473 |
/* ── Diffusion ── */
|
| 474 |
.diffusion-text {
|
| 475 |
-
|
| 476 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
}
|
| 478 |
-
.diffusion-text.revealed { filter: blur(0); opacity: 1; }
|
| 479 |
|
| 480 |
/* ── Composer (questions only) ── */
|
| 481 |
.compose {
|
|
@@ -621,7 +659,7 @@
|
|
| 621 |
<div id="app">
|
| 622 |
<div id="topbar">
|
| 623 |
<div class="brand">
|
| 624 |
-
<
|
| 625 |
<div>
|
| 626 |
<div class="brand-title">Human Intelligence</div>
|
| 627 |
<div class="brand-sub">community answers</div>
|
|
@@ -694,6 +732,7 @@
|
|
| 694 |
clientId: null,
|
| 695 |
conversation: null,
|
| 696 |
currentQuestion: "",
|
|
|
|
| 697 |
loading: false,
|
| 698 |
animMode: localStorage.getItem("hi_anim") || "none",
|
| 699 |
};
|
|
@@ -740,6 +779,16 @@
|
|
| 740 |
requestAnimationFrame(() => { c.scrollTop = c.scrollHeight; });
|
| 741 |
}
|
| 742 |
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 743 |
|
| 744 |
async function animateText(el, text) {
|
| 745 |
const mode = S.animMode;
|
|
@@ -764,10 +813,14 @@
|
|
| 764 |
await sleep(d);
|
| 765 |
}
|
| 766 |
} else if (mode === "diffusion") {
|
| 767 |
-
el.innerHTML = `<span class="diffusion-text">${
|
| 768 |
scrollBottom();
|
| 769 |
-
|
| 770 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 771 |
} else {
|
| 772 |
el.innerHTML = nl2br(text);
|
| 773 |
}
|
|
@@ -910,6 +963,28 @@
|
|
| 910 |
</div>`;
|
| 911 |
}
|
| 912 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 913 |
/* ── Main render ── */
|
| 914 |
async function renderConversation(questionText, doAnimate) {
|
| 915 |
const tr = $("transcript");
|
|
@@ -948,6 +1023,7 @@
|
|
| 948 |
<div class="bubble no-answer-bubble">No answer yet. Be the first to write one.</div>
|
| 949 |
<div class="turn-meta"><span class="chip warn">awaiting answer</span></div>
|
| 950 |
${renderWriteAnswer()}
|
|
|
|
| 951 |
</div>
|
| 952 |
</div>`);
|
| 953 |
} else {
|
|
@@ -960,6 +1036,7 @@
|
|
| 960 |
${renderAnswerBlock(best, 0, true)}
|
| 961 |
${renderWriteAnswer()}
|
| 962 |
${renderOtherAnswers(answers)}
|
|
|
|
| 963 |
</div>
|
| 964 |
</div>`);
|
| 965 |
|
|
@@ -1089,7 +1166,9 @@
|
|
| 1089 |
ws.textContent = "Saving…";
|
| 1090 |
showStatus("Saving answer…");
|
| 1091 |
const res = await callAPI("answer", {
|
| 1092 |
-
conversation_id: S.conversation.id,
|
|
|
|
|
|
|
| 1093 |
});
|
| 1094 |
hideStatus();
|
| 1095 |
ws.disabled = false; ws.textContent = orig;
|
|
@@ -1129,6 +1208,7 @@
|
|
| 1129 |
if (!res.ok) { toast(res.error||"Error","bad"); return; }
|
| 1130 |
S.conversation = res.conversation;
|
| 1131 |
S.currentQuestion = q;
|
|
|
|
| 1132 |
save();
|
| 1133 |
toast(res.matched ? "Existing answer found" : "New question created", "good");
|
| 1134 |
await renderConversation(q, true);
|
|
@@ -1159,6 +1239,7 @@
|
|
| 1159 |
if (res.ok && res.conversation) {
|
| 1160 |
S.conversation = res.conversation;
|
| 1161 |
S.currentQuestion = res.conversation.question || "";
|
|
|
|
| 1162 |
renderConversation(S.currentQuestion, false);
|
| 1163 |
}
|
| 1164 |
}
|
|
@@ -1166,6 +1247,7 @@
|
|
| 1166 |
function newChat() {
|
| 1167 |
S.conversation = null;
|
| 1168 |
S.currentQuestion = "";
|
|
|
|
| 1169 |
localStorage.removeItem("hi_last_cid");
|
| 1170 |
$("transcript").innerHTML = "";
|
| 1171 |
$("welcome").style.display = "block";
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<link rel="icon" type="image/png" href="/logo.png" />
|
| 7 |
<title>{{ app_title }}</title>
|
| 8 |
<style>
|
| 9 |
:root {
|
|
|
|
| 58 |
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
|
| 59 |
.logo {
|
| 60 |
width: 30px; height: 30px; border-radius: 10px;
|
| 61 |
+
display: block;
|
| 62 |
+
object-fit: cover;
|
| 63 |
box-shadow: 0 6px 18px rgba(108,131,255,.25);
|
| 64 |
+
border: 1px solid rgba(255,255,255,.08);
|
| 65 |
flex: 0 0 auto;
|
| 66 |
}
|
| 67 |
.brand-title { font-weight: 700; letter-spacing: -.03em; font-size: 15px; }
|
|
|
|
| 353 |
background: rgba(255,255,255,.02);
|
| 354 |
animation: fadeUp 200ms ease both;
|
| 355 |
}
|
| 356 |
+
.other-answer-card.related {
|
| 357 |
+
background: linear-gradient(180deg, rgba(108,131,255,.05), rgba(255,255,255,.02));
|
| 358 |
+
border-color: rgba(108,131,255,.16);
|
| 359 |
+
}
|
| 360 |
.other-answer-head {
|
| 361 |
display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
|
| 362 |
color: var(--muted); font-family: var(--mono); font-size: 10px;
|
|
|
|
| 478 |
|
| 479 |
/* ── Diffusion ── */
|
| 480 |
.diffusion-text {
|
| 481 |
+
display: inline;
|
| 482 |
+
}
|
| 483 |
+
.diffusion-token {
|
| 484 |
+
display: inline-block;
|
| 485 |
+
filter: blur(var(--blur, 8px));
|
| 486 |
+
opacity: var(--opacity, .18);
|
| 487 |
+
transform: translateY(var(--shift, 0px));
|
| 488 |
+
transition:
|
| 489 |
+
filter 420ms cubic-bezier(.22,.61,.36,1),
|
| 490 |
+
opacity 320ms ease,
|
| 491 |
+
transform 360ms cubic-bezier(.22,.61,.36,1);
|
| 492 |
+
will-change: filter, opacity, transform;
|
| 493 |
+
}
|
| 494 |
+
.diffusion-token.revealed {
|
| 495 |
+
filter: blur(0);
|
| 496 |
+
opacity: 1;
|
| 497 |
+
transform: translateY(0);
|
| 498 |
+
}
|
| 499 |
+
.related-stack {
|
| 500 |
+
margin-top: 14px;
|
| 501 |
+
padding-top: 12px;
|
| 502 |
+
border-top: 1px solid rgba(255,255,255,.06);
|
| 503 |
+
}
|
| 504 |
+
.related-stack .chip {
|
| 505 |
+
margin-bottom: 6px;
|
| 506 |
+
}
|
| 507 |
+
.related-score {
|
| 508 |
+
color: var(--accent);
|
| 509 |
+
border-color: rgba(108,131,255,.22);
|
| 510 |
+
}
|
| 511 |
+
.related-note {
|
| 512 |
+
color: var(--muted);
|
| 513 |
+
font-size: 11px;
|
| 514 |
+
line-height: 1.5;
|
| 515 |
+
margin-top: 6px;
|
| 516 |
}
|
|
|
|
| 517 |
|
| 518 |
/* ── Composer (questions only) ── */
|
| 519 |
.compose {
|
|
|
|
| 659 |
<div id="app">
|
| 660 |
<div id="topbar">
|
| 661 |
<div class="brand">
|
| 662 |
+
<img class="logo" src="/logo.png" alt="Human Intelligence logo" />
|
| 663 |
<div>
|
| 664 |
<div class="brand-title">Human Intelligence</div>
|
| 665 |
<div class="brand-sub">community answers</div>
|
|
|
|
| 732 |
clientId: null,
|
| 733 |
conversation: null,
|
| 734 |
currentQuestion: "",
|
| 735 |
+
relatedAnswers: [],
|
| 736 |
loading: false,
|
| 737 |
animMode: localStorage.getItem("hi_anim") || "none",
|
| 738 |
};
|
|
|
|
| 779 |
requestAnimationFrame(() => { c.scrollTop = c.scrollHeight; });
|
| 780 |
}
|
| 781 |
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
| 782 |
+
function diffusionTokens(text) {
|
| 783 |
+
return String(text || "").split(/(\s+)/).map((part, i) => {
|
| 784 |
+
if (!part) return "";
|
| 785 |
+
if (/^\s+$/.test(part)) return part.replace(/ /g, " ").replace(/\n/g, "<br>");
|
| 786 |
+
const blur = (5 + (i % 4) * 1.4).toFixed(1);
|
| 787 |
+
const opacity = (0.14 + (i % 3) * 0.08).toFixed(2);
|
| 788 |
+
const shift = ((i % 2 === 0 ? 1 : -1) * ((i % 3) + 1)).toFixed(0);
|
| 789 |
+
return `<span class="diffusion-token" style="--blur:${blur}px;--opacity:${opacity};--shift:${shift}px;">${esc(part)}</span>`;
|
| 790 |
+
}).join("");
|
| 791 |
+
}
|
| 792 |
|
| 793 |
async function animateText(el, text) {
|
| 794 |
const mode = S.animMode;
|
|
|
|
| 813 |
await sleep(d);
|
| 814 |
}
|
| 815 |
} else if (mode === "diffusion") {
|
| 816 |
+
el.innerHTML = `<span class="diffusion-text">${diffusionTokens(text)}</span>`;
|
| 817 |
scrollBottom();
|
| 818 |
+
const tokens = Array.from(el.querySelectorAll(".diffusion-token"));
|
| 819 |
+
for (let i = 0; i < tokens.length; i++) {
|
| 820 |
+
tokens[i].classList.add("revealed");
|
| 821 |
+
if (i % 5 === 0) scrollBottom();
|
| 822 |
+
await sleep(26 + Math.random() * 30);
|
| 823 |
+
}
|
| 824 |
} else {
|
| 825 |
el.innerHTML = nl2br(text);
|
| 826 |
}
|
|
|
|
| 963 |
</div>`;
|
| 964 |
}
|
| 965 |
|
| 966 |
+
function renderRelated(rel) {
|
| 967 |
+
if (!rel || !rel.length) return "";
|
| 968 |
+
|
| 969 |
+
return `
|
| 970 |
+
<div class="related-stack">
|
| 971 |
+
<div class="chip muted">from similar questions</div>
|
| 972 |
+
${rel.map(r => `
|
| 973 |
+
<div class="other-answer-card related">
|
| 974 |
+
<div class="other-answer-head">
|
| 975 |
+
<span class="chip matched">related</span>
|
| 976 |
+
<span>${esc(r.question || "")}</span>
|
| 977 |
+
<span>·</span>
|
| 978 |
+
<span class="chip related-score">score ${(Number(r.score || 0)).toFixed(2)}</span>
|
| 979 |
+
</div>
|
| 980 |
+
<div class="other-answer-text">${nl2br(r.answer || "")}</div>
|
| 981 |
+
</div>
|
| 982 |
+
`).join("")}
|
| 983 |
+
<div class="related-note">These are read-only answers from semantically similar questions, so you can reuse context without writing into the wrong thread.</div>
|
| 984 |
+
</div>
|
| 985 |
+
`;
|
| 986 |
+
}
|
| 987 |
+
|
| 988 |
/* ── Main render ── */
|
| 989 |
async function renderConversation(questionText, doAnimate) {
|
| 990 |
const tr = $("transcript");
|
|
|
|
| 1023 |
<div class="bubble no-answer-bubble">No answer yet. Be the first to write one.</div>
|
| 1024 |
<div class="turn-meta"><span class="chip warn">awaiting answer</span></div>
|
| 1025 |
${renderWriteAnswer()}
|
| 1026 |
+
${renderRelated(S.relatedAnswers)}
|
| 1027 |
</div>
|
| 1028 |
</div>`);
|
| 1029 |
} else {
|
|
|
|
| 1036 |
${renderAnswerBlock(best, 0, true)}
|
| 1037 |
${renderWriteAnswer()}
|
| 1038 |
${renderOtherAnswers(answers)}
|
| 1039 |
+
${renderRelated(S.relatedAnswers)}
|
| 1040 |
</div>
|
| 1041 |
</div>`);
|
| 1042 |
|
|
|
|
| 1166 |
ws.textContent = "Saving…";
|
| 1167 |
showStatus("Saving answer…");
|
| 1168 |
const res = await callAPI("answer", {
|
| 1169 |
+
conversation_id: S.conversation.id,
|
| 1170 |
+
text,
|
| 1171 |
+
question: S.currentQuestion,
|
| 1172 |
});
|
| 1173 |
hideStatus();
|
| 1174 |
ws.disabled = false; ws.textContent = orig;
|
|
|
|
| 1208 |
if (!res.ok) { toast(res.error||"Error","bad"); return; }
|
| 1209 |
S.conversation = res.conversation;
|
| 1210 |
S.currentQuestion = q;
|
| 1211 |
+
S.relatedAnswers = Array.isArray(res.related) ? res.related : [];
|
| 1212 |
save();
|
| 1213 |
toast(res.matched ? "Existing answer found" : "New question created", "good");
|
| 1214 |
await renderConversation(q, true);
|
|
|
|
| 1239 |
if (res.ok && res.conversation) {
|
| 1240 |
S.conversation = res.conversation;
|
| 1241 |
S.currentQuestion = res.conversation.question || "";
|
| 1242 |
+
S.relatedAnswers = [];
|
| 1243 |
renderConversation(S.currentQuestion, false);
|
| 1244 |
}
|
| 1245 |
}
|
|
|
|
| 1247 |
function newChat() {
|
| 1248 |
S.conversation = null;
|
| 1249 |
S.currentQuestion = "";
|
| 1250 |
+
S.relatedAnswers = [];
|
| 1251 |
localStorage.removeItem("hi_last_cid");
|
| 1252 |
$("transcript").innerHTML = "";
|
| 1253 |
$("welcome").style.display = "block";
|