shreyask commited on
Commit
c25af82
·
verified ·
1 Parent(s): 3af292e

Update: Re-rank button label after ranking + new doc

Browse files
assets/index-C0wdVFKl.css ADDED
@@ -0,0 +1 @@
 
 
1
+ *,*:before,*:after{box-sizing:border-box;margin:0;padding:0}:root{--bg: #07080A;--surface: #0D0F12;--border: #1C1F26;--border-hi: #2E3340;--amber: #F59E0B;--amber-dim: #7C4F05;--amber-glow: rgba(245,158,11,.12);--green: #22C55E;--red: #EF4444;--text: #E2E8F0;--text-dim: #4A5568;--text-mid: #94A3B8;--mono: "JetBrains Mono", monospace;--display: "Syne", sans-serif}html{background:var(--bg);color:var(--text);font-family:var(--mono)}body{min-height:100vh;display:grid;grid-template-rows:auto 1fr auto;background:radial-gradient(ellipse 80% 40% at 50% -10%,rgba(245,158,11,.06) 0%,transparent 60%),var(--bg)}header{padding:2rem 2rem 0;max-width:860px;margin:0 auto;width:100%}.header-tag{font-size:.65rem;letter-spacing:.25em;text-transform:uppercase;color:var(--amber);font-weight:600;display:flex;align-items:center;gap:.5rem;margin-bottom:.75rem}.header-tag:before{content:"";width:6px;height:6px;border-radius:50%;background:var(--amber);box-shadow:0 0 8px var(--amber);animation:pulse-dot 2s ease-in-out infinite}@keyframes pulse-dot{0%,to{opacity:1;box-shadow:0 0 8px var(--amber)}50%{opacity:.4;box-shadow:0 0 3px var(--amber)}}.header-title{font-family:var(--display);font-size:clamp(1.8rem,4vw,2.6rem);font-weight:800;letter-spacing:-.02em;line-height:1.1;color:var(--text)}.header-title span{color:var(--amber)}.header-sub{margin-top:.6rem;font-size:.75rem;color:var(--text-dim);font-weight:300;letter-spacing:.02em}.header-sub a{color:var(--text-mid);text-decoration:none;border-bottom:1px solid var(--border-hi)}.header-sub a:hover{color:var(--amber);border-color:var(--amber)}main{max-width:860px;margin:0 auto;width:100%;padding:2rem 2rem 3rem}#status-bar{background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:.75rem 1rem;margin-bottom:1.5rem;display:flex;align-items:center;gap:.75rem;font-size:.72rem;transition:border-color .3s}#status-bar.loading{border-color:var(--amber-dim)}#status-bar.ready{border-color:#166534}#status-bar.error{border-color:#7f1d1d}.status-icon{width:8px;height:8px;border-radius:50%;background:var(--text-dim);flex-shrink:0}#status-bar.loading .status-icon{background:var(--amber);animation:pulse-dot 1s infinite}#status-bar.ready .status-icon{background:var(--green)}#status-bar.error .status-icon{background:var(--red)}#status-text{color:var(--text-mid);flex:1}#progress-bar-wrap{height:2px;flex:1;max-width:120px;background:var(--border);border-radius:1px;overflow:hidden;display:none}#progress-bar-wrap.visible{display:block}#progress-bar{height:100%;background:var(--amber);width:0%;transition:width .3s ease;border-radius:1px}.panel{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:1.25rem;margin-bottom:1rem}.panel-label{font-size:.6rem;font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--amber);margin-bottom:.6rem}#query-panel{border-color:var(--amber-dim)}#query-input{width:100%;background:transparent;border:none;outline:none;color:var(--text);font-family:var(--mono);font-size:.9rem;font-weight:400;resize:none;line-height:1.6;min-height:2.5rem;max-height:8rem;overflow-y:auto}#query-input::placeholder{color:var(--text-dim)}.instruction-row{display:flex;align-items:center;gap:.5rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--border)}.instruction-label{font-size:.6rem;letter-spacing:.15em;text-transform:uppercase;color:var(--text-dim);white-space:nowrap;font-weight:600}#instruction-input{flex:1;background:transparent;border:none;outline:none;color:var(--text-mid);font-family:var(--mono);font-size:.72rem;font-style:italic}#docs-list{display:flex;flex-direction:column;gap:.5rem}.doc-card{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:.85rem 1rem;display:grid;grid-template-columns:1.6rem 1fr auto;gap:.6rem;align-items:start;transition:border-color .2s,box-shadow .2s;will-change:transform;animation:card-in .25s ease both}@keyframes card-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.doc-card:focus-within{border-color:var(--border-hi)}.doc-index{font-size:.65rem;color:var(--text-dim);font-weight:600;padding-top:.15rem;user-select:none}.doc-text{width:100%;background:transparent;border:none;outline:none;color:var(--text);font-family:var(--mono);font-size:.8rem;font-weight:300;resize:none;line-height:1.65;min-height:2.4rem;max-height:6rem;overflow-y:auto}.doc-text::placeholder{color:var(--text-dim)}.doc-score-wrap{display:flex;flex-direction:column;align-items:flex-end;gap:.25rem;min-width:3.5rem}.doc-score-num{font-size:1.1rem;font-weight:700;color:var(--text-dim);font-variant-numeric:tabular-nums;transition:color .4s;line-height:1}.doc-score-bar-wrap{width:3rem;height:3px;background:var(--border);border-radius:1.5px;overflow:hidden}.doc-score-bar{height:100%;width:0%;background:var(--text-dim);border-radius:1.5px;transition:width .6s cubic-bezier(.16,1,.3,1),background .4s}.doc-card.ranked-1{border-color:#f59e0b66;background:#f59e0b0a}.doc-card.ranked-1 .doc-score-num{color:var(--amber)}.doc-card.ranked-1 .doc-score-bar{background:var(--amber)}.doc-card.ranked-2 .doc-score-num{color:#a78bfa}.doc-card.ranked-2 .doc-score-bar{background:#a78bfa}.doc-card.ranked-3 .doc-score-num{color:#60a5fa}.doc-card.ranked-3 .doc-score-bar{background:#60a5fa}.doc-card.computing{border-color:var(--amber-dim);box-shadow:inset 3px 0 0 var(--amber),0 0 12px #f59e0b14;animation:card-compute-pulse 1.4s ease-in-out infinite}@keyframes card-compute-pulse{0%,to{box-shadow:inset 3px 0 0 var(--amber),0 0 8px #f59e0b0f}50%{box-shadow:inset 3px 0 0 var(--amber),0 0 18px #f59e0b2e}}.doc-card.computing .doc-score-num{display:inline-block;color:var(--amber);font-size:1.2rem;animation:spin .7s linear infinite;transform-origin:center}@keyframes spin{to{transform:rotate(360deg)}}.doc-card.computing .doc-score-bar{background:var(--amber);width:100%!important;animation:bar-indeterminate 1.2s ease-in-out infinite;transform-origin:left}@keyframes bar-indeterminate{0%{transform:scaleX(0);opacity:.6}50%{transform:scaleX(1);opacity:1}to{transform:scaleX(0);opacity:.6}}.doc-card.needs-rank{border-color:var(--amber-dim);border-style:dashed}.doc-card.needs-rank .doc-score-num:after{content:" ↺";font-size:.65rem;color:var(--amber-dim)}.doc-card.flipping{transition:transform .45s cubic-bezier(.16,1,.3,1)}.actions-row{display:flex;gap:.6rem;margin-top:.75rem;align-items:center;flex-wrap:wrap}button{font-family:var(--mono);cursor:pointer;border:1px solid transparent;border-radius:5px;font-size:.72rem;font-weight:600;letter-spacing:.04em;padding:.5rem 1rem;transition:all .15s;display:inline-flex;align-items:center;gap:.4rem}#rank-btn{background:var(--amber);color:#0a0a0a;border-color:var(--amber);padding:.55rem 1.4rem;font-size:.78rem}#rank-btn:hover:not(:disabled){background:#fbbf24;box-shadow:0 0 16px #f59e0b59}#rank-btn:disabled{opacity:.45;cursor:not-allowed}#rank-btn.running{animation:btn-pulse 1.2s ease-in-out infinite}@keyframes btn-pulse{0%,to{box-shadow:0 0 #f59e0b00}50%{box-shadow:0 0 20px #f59e0b80}}#add-doc-btn{background:transparent;color:var(--text-mid);border-color:var(--border-hi)}#add-doc-btn:hover{border-color:var(--amber-dim);color:var(--amber)}#clear-btn{background:transparent;color:var(--text-dim);border-color:transparent;margin-left:auto;font-size:.68rem}#clear-btn:hover{color:var(--red)}#results-header{display:none;align-items:center;gap:.75rem;margin:1.5rem 0 .75rem;font-size:.6rem;letter-spacing:.2em;text-transform:uppercase;color:var(--text-dim);font-weight:600}#results-header.visible{display:flex}#results-header:after{content:"";flex:1;height:1px;background:var(--border)}footer{padding:1rem 2rem;max-width:860px;margin:0 auto;width:100%;display:flex;align-items:center;gap:1rem;font-size:.62rem;color:var(--text-dim);border-top:1px solid var(--border)}footer a{color:var(--text-dim);text-decoration:none}footer a:hover{color:var(--amber)}.sep{color:var(--border-hi)}#runtime-info{margin-left:auto}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border-hi);border-radius:2px}.dtype-select{background:var(--surface);border:1px solid var(--border-hi);border-radius:4px;color:var(--text-mid);font-family:var(--mono);font-size:.65rem;padding:.2rem .4rem;cursor:pointer;outline:none}.dtype-select:hover{border-color:var(--amber-dim)}
assets/index-CaEweXpE.js ADDED
@@ -0,0 +1 @@
 
 
1
+ (function(){const c=document.createElement("link").relList;if(c&&c.supports&&c.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))o(t);new MutationObserver(t=>{for(const s of t)if(s.type==="childList")for(const a of s.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&o(a)}).observe(document,{childList:!0,subtree:!0});function r(t){const s={};return t.integrity&&(s.integrity=t.integrity),t.referrerPolicy&&(s.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?s.credentials="include":t.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(t){if(t.ep)return;t.ep=!0;const s=r(t);fetch(t.href,s)}})();const I="onnx-community/Qwen3-Reranker-0.6B-ONNX",$="Given a web search query, retrieve relevant passages that answer the query",N=["Paris is the capital and largest city of France, located in northern France.","Berlin is the capital city of Germany.","France is a country in Western Europe known for its wine, cuisine, and art.","The Eiffel Tower is a famous landmark located in Paris, France."];let b=0;const M=document.getElementById("status-bar"),R=document.getElementById("status-text"),x=document.getElementById("progress-bar-wrap"),F=document.getElementById("progress-bar"),i=document.getElementById("rank-btn"),v=document.getElementById("docs-list"),S=document.getElementById("results-header"),g=document.getElementById("dtype-select"),y=document.getElementById("query-input");let h=w();function w(){const e=new Worker(new URL(""+new URL("worker-Cv6xZSLB.js",import.meta.url).href,import.meta.url),{type:"module"});return e.addEventListener("message",O),e}const f=new Map;function O({data:e}){const{type:c}=e;if(c==="progress"){const r=e.progress!=null?Math.round(e.progress):null;u("loading",`${e.file??"model"} — ${r!=null?r+"%":"…"}`,r);return}if(c==="ready"){document.getElementById("runtime-info").textContent=`dtype: ${e.dtype}`,u("ready",`Model ready · ${I}`),i.disabled=!1,g.disabled=!1;return}if(c==="load_error"){u("error",`Failed to load: ${e.message}`),g.disabled=!1;return}if(c==="scored"){const r=f.get(e.id);r&&(r.resolve(e.score),f.delete(e.id));return}if(c==="score_error"){const r=f.get(e.id);r&&(r.reject(new Error(e.message)),f.delete(e.id));return}}function A(e,c,r,o){return new Promise((t,s)=>{f.set(e,{resolve:t,reject:s}),h.postMessage({type:"score",id:e,query:c,doc:r,instruction:o})})}function u(e,c,r=null){M.className=e,R.textContent=c,r!=null?(x.classList.add("visible"),F.style.width=`${r}%`):x.classList.remove("visible")}function C(e){e.style.height="auto",e.style.height=Math.min(e.scrollHeight,128)+"px"}y.addEventListener("input",()=>C(y));function B(e=""){const c=String(++b),r=document.createElement("div");r.className="doc-card",r.dataset.id=c,r.style.animationDelay=`${Math.min((b-1)*30,100)}ms`;const o=document.createElement("span");o.className="doc-index",o.textContent=c.padStart(2,"0");const t=document.createElement("textarea");t.className="doc-text",t.rows=2,t.placeholder="Paste a document or passage…",t.value=e,t.addEventListener("input",()=>C(t));const s=document.createElement("div");s.className="doc-score-wrap";const a=document.createElement("span");a.className="doc-score-num",a.textContent="—";const d=document.createElement("div");d.className="doc-score-bar-wrap";const n=document.createElement("div");n.className="doc-score-bar",d.appendChild(n),s.appendChild(a),s.appendChild(d),r.appendChild(o),r.appendChild(t),r.appendChild(s),v.appendChild(r)}let k=!1;function D(){k&&(i.textContent="↺ Re-rank")}N.forEach(e=>B(e));document.getElementById("add-doc-btn").addEventListener("click",()=>{B(),D()});document.getElementById("clear-btn").addEventListener("click",()=>{document.querySelectorAll(".doc-card").forEach(e=>{e.className="doc-card",e.querySelector(".doc-score-num").textContent="—",e.querySelector(".doc-score-bar").style.width="0%"}),S.classList.remove("visible")});function P(e,c,r){const o=performance.now(),t=a=>(a*100).toFixed(1)+"%";function s(a){const d=Math.min((a-o)/600,1),n=1-Math.pow(1-d,3);e.textContent=t(n*r),c.style.width=`${n*r*100}%`,d<1?requestAnimationFrame(s):e.textContent=t(r)}requestAnimationFrame(s)}function T(e,c){const r=new Map(e.map(o=>[o.dataset.id,o.getBoundingClientRect().top]));c.forEach(o=>{const t=v.querySelector(`[data-id="${CSS.escape(o)}"]`);t&&v.appendChild(t)}),e.forEach(o=>{const t=r.get(o.dataset.id)-o.getBoundingClientRect().top;Math.abs(t)>1&&(o.style.transform=`translateY(${t}px)`,o.style.transition="none",requestAnimationFrame(()=>{o.classList.add("flipping"),o.style.transform="translateY(0)",setTimeout(()=>{o.classList.remove("flipping"),o.style.transition=""},500)}))})}async function L(){const e=y.value.trim(),c=document.getElementById("instruction-input").value.trim()||$;if(!e){y.focus();return}document.querySelectorAll(".doc-card.needs-rank").forEach(n=>n.classList.remove("needs-rank"));const r=[...document.querySelectorAll(".doc-card")],o=r.map(n=>({id:n.dataset.id,text:n.querySelector(".doc-text").value.trim(),card:n})),t=o.filter(n=>n.text);if(!t.length)return;i.disabled=!0,i.classList.add("running"),i.textContent="⟳ Ranking…",t.forEach(({card:n})=>{n.classList.add("computing"),n.querySelector(".doc-score-num").textContent="⟳"});const s=[];for(let n=0;n<t.length;n++){const{id:l,text:E,card:m}=t[n];u("loading",`Scoring document ${n+1} / ${t.length}…`,n/t.length*100);try{const p=await A(l,e,E,c);m.classList.remove("computing"),P(m.querySelector(".doc-score-num"),m.querySelector(".doc-score-bar"),p),s.push({id:l,score:p,card:m})}catch(p){console.error("Scoring failed for doc",l,p),m.classList.remove("computing"),m.querySelector(".doc-score-num").textContent="err",u("error",`Error: ${p.message}`),s.push({id:l,score:-1,card:m})}}s.sort((n,l)=>l.score-n.score),s.forEach(({card:n},l)=>{n.className="doc-card",l<3&&n.classList.add(`ranked-${l+1}`)});const a=o.filter(n=>!n.text).map(n=>n.id);T(r,[...s.map(n=>n.id),...a]);const d=o.filter(n=>n.card.querySelector(".doc-text").value.trim()&&!t.some(E=>E.id===n.id));d.forEach(({card:n})=>n.classList.add("needs-rank")),d.length>0?u("ready",`Scored ${t.length} · ${d.length} unscored — hit Rank again ↺`):u("ready",`Scored ${t.length} document${t.length!==1?"s":""} · model ready`),S.classList.add("visible"),k=!0,i.disabled=!1,i.classList.remove("running"),i.textContent="↺ Re-rank"}i.addEventListener("click",L);function q(){const e=g.value;g.disabled=!0,i.disabled=!0,u("loading",`Downloading model (${e})…`,0),h.postMessage({type:"load",dtype:e})}g.addEventListener("change",()=>{i.removeEventListener("click",L),h.terminate(),h=w(),i.addEventListener("click",L),q()});q();
assets/ort-wasm-simd-threaded.asyncify-DFMnNRgU.wasm ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:de8f373400e38d4c253f5c6535be22825b733f5238f50bd331427b6ecb872afd
3
+ size 22498509
assets/worker-Cv6xZSLB.js ADDED
The diff for this file is too large to render. See raw diff
 
index.html CHANGED
@@ -7,490 +7,8 @@
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;0,600;0,700;1,300&family=Syne:wght@700;800;900&display=swap" rel="stylesheet">
10
- <style>
11
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
12
-
13
- :root {
14
- --bg: #07080A;
15
- --surface: #0D0F12;
16
- --border: #1C1F26;
17
- --border-hi: #2E3340;
18
- --amber: #F59E0B;
19
- --amber-dim: #7C4F05;
20
- --amber-glow: rgba(245,158,11,0.12);
21
- --green: #22C55E;
22
- --red: #EF4444;
23
- --text: #E2E8F0;
24
- --text-dim: #4A5568;
25
- --text-mid: #94A3B8;
26
- --mono: 'JetBrains Mono', monospace;
27
- --display: 'Syne', sans-serif;
28
- }
29
-
30
- html { background: var(--bg); color: var(--text); font-family: var(--mono); }
31
-
32
- body {
33
- min-height: 100vh;
34
- display: grid;
35
- grid-template-rows: auto 1fr auto;
36
- background:
37
- radial-gradient(ellipse 80% 40% at 50% -10%, rgba(245,158,11,0.06) 0%, transparent 60%),
38
- var(--bg);
39
- }
40
-
41
- /* ── Header ───────────────────────────────────────── */
42
- header {
43
- padding: 2rem 2rem 0;
44
- max-width: 860px;
45
- margin: 0 auto;
46
- width: 100%;
47
- }
48
-
49
- .header-tag {
50
- font-size: 0.65rem;
51
- letter-spacing: 0.25em;
52
- text-transform: uppercase;
53
- color: var(--amber);
54
- font-weight: 600;
55
- display: flex;
56
- align-items: center;
57
- gap: 0.5rem;
58
- margin-bottom: 0.75rem;
59
- }
60
-
61
- .header-tag::before {
62
- content: '';
63
- width: 6px; height: 6px;
64
- border-radius: 50%;
65
- background: var(--amber);
66
- box-shadow: 0 0 8px var(--amber);
67
- animation: pulse-dot 2s ease-in-out infinite;
68
- }
69
-
70
- @keyframes pulse-dot {
71
- 0%, 100% { opacity: 1; box-shadow: 0 0 8px var(--amber); }
72
- 50% { opacity: 0.4; box-shadow: 0 0 3px var(--amber); }
73
- }
74
-
75
- .header-title {
76
- font-family: var(--display);
77
- font-size: clamp(1.8rem, 4vw, 2.6rem);
78
- font-weight: 800;
79
- letter-spacing: -0.02em;
80
- line-height: 1.1;
81
- color: var(--text);
82
- }
83
-
84
- .header-title span { color: var(--amber); }
85
-
86
- .header-sub {
87
- margin-top: 0.6rem;
88
- font-size: 0.75rem;
89
- color: var(--text-dim);
90
- font-weight: 300;
91
- letter-spacing: 0.02em;
92
- }
93
-
94
- .header-sub a { color: var(--text-mid); text-decoration: none; border-bottom: 1px solid var(--border-hi); }
95
- .header-sub a:hover { color: var(--amber); border-color: var(--amber); }
96
-
97
- /* ── Main ─────────────────────────────────────────── */
98
- main {
99
- max-width: 860px;
100
- margin: 0 auto;
101
- width: 100%;
102
- padding: 2rem 2rem 3rem;
103
- }
104
-
105
- /* ── Status bar ───────────────────────────────────── */
106
- #status-bar {
107
- background: var(--surface);
108
- border: 1px solid var(--border);
109
- border-radius: 6px;
110
- padding: 0.75rem 1rem;
111
- margin-bottom: 1.5rem;
112
- display: flex;
113
- align-items: center;
114
- gap: 0.75rem;
115
- font-size: 0.72rem;
116
- transition: border-color 0.3s;
117
- }
118
-
119
- #status-bar.loading { border-color: var(--amber-dim); }
120
- #status-bar.ready { border-color: #166534; }
121
- #status-bar.error { border-color: #7f1d1d; }
122
-
123
- .status-icon {
124
- width: 8px; height: 8px;
125
- border-radius: 50%;
126
- background: var(--text-dim);
127
- flex-shrink: 0;
128
- }
129
-
130
- #status-bar.loading .status-icon { background: var(--amber); animation: pulse-dot 1s infinite; }
131
- #status-bar.ready .status-icon { background: var(--green); }
132
- #status-bar.error .status-icon { background: var(--red); }
133
-
134
- #status-text { color: var(--text-mid); flex: 1; }
135
-
136
- #progress-bar-wrap {
137
- height: 2px;
138
- flex: 1;
139
- max-width: 120px;
140
- background: var(--border);
141
- border-radius: 1px;
142
- overflow: hidden;
143
- display: none;
144
- }
145
-
146
- #progress-bar-wrap.visible { display: block; }
147
-
148
- #progress-bar {
149
- height: 100%;
150
- background: var(--amber);
151
- width: 0%;
152
- transition: width 0.3s ease;
153
- border-radius: 1px;
154
- }
155
-
156
- /* ── Panels ───────────────────────────────────────── */
157
- .panel {
158
- background: var(--surface);
159
- border: 1px solid var(--border);
160
- border-radius: 8px;
161
- padding: 1.25rem;
162
- margin-bottom: 1rem;
163
- }
164
-
165
- .panel-label {
166
- font-size: 0.6rem;
167
- font-weight: 600;
168
- letter-spacing: 0.2em;
169
- text-transform: uppercase;
170
- color: var(--amber);
171
- margin-bottom: 0.6rem;
172
- }
173
-
174
- /* ── Query input ──────────────────────────────────── */
175
- #query-panel { border-color: var(--amber-dim); }
176
-
177
- #query-input {
178
- width: 100%;
179
- background: transparent;
180
- border: none;
181
- outline: none;
182
- color: var(--text);
183
- font-family: var(--mono);
184
- font-size: 0.9rem;
185
- font-weight: 400;
186
- resize: none;
187
- line-height: 1.6;
188
- min-height: 2.5rem;
189
- max-height: 8rem;
190
- overflow-y: auto;
191
- }
192
-
193
- #query-input::placeholder { color: var(--text-dim); }
194
-
195
- /* ── Instruction row ──────────────────────────────── */
196
- .instruction-row {
197
- display: flex;
198
- align-items: center;
199
- gap: 0.5rem;
200
- margin-top: 0.75rem;
201
- padding-top: 0.75rem;
202
- border-top: 1px solid var(--border);
203
- }
204
-
205
- .instruction-label {
206
- font-size: 0.6rem;
207
- letter-spacing: 0.15em;
208
- text-transform: uppercase;
209
- color: var(--text-dim);
210
- white-space: nowrap;
211
- font-weight: 600;
212
- }
213
-
214
- #instruction-input {
215
- flex: 1;
216
- background: transparent;
217
- border: none;
218
- outline: none;
219
- color: var(--text-mid);
220
- font-family: var(--mono);
221
- font-size: 0.72rem;
222
- font-style: italic;
223
- }
224
-
225
- /* ── Documents ────────────────────────────────────── */
226
- #docs-list {
227
- display: flex;
228
- flex-direction: column;
229
- gap: 0.5rem;
230
- }
231
-
232
- .doc-card {
233
- background: var(--surface);
234
- border: 1px solid var(--border);
235
- border-radius: 8px;
236
- padding: 0.85rem 1rem;
237
- display: grid;
238
- grid-template-columns: 1.6rem 1fr auto;
239
- gap: 0.6rem;
240
- align-items: start;
241
- transition: border-color 0.2s, box-shadow 0.2s;
242
- will-change: transform;
243
- animation: card-in 0.25s ease both;
244
- }
245
-
246
- @keyframes card-in {
247
- from { opacity: 0; transform: translateY(6px); }
248
- to { opacity: 1; transform: translateY(0); }
249
- }
250
-
251
- .doc-card:focus-within { border-color: var(--border-hi); }
252
-
253
- .doc-index {
254
- font-size: 0.65rem;
255
- color: var(--text-dim);
256
- font-weight: 600;
257
- padding-top: 0.15rem;
258
- user-select: none;
259
- }
260
-
261
- .doc-text {
262
- width: 100%;
263
- background: transparent;
264
- border: none;
265
- outline: none;
266
- color: var(--text);
267
- font-family: var(--mono);
268
- font-size: 0.8rem;
269
- font-weight: 300;
270
- resize: none;
271
- line-height: 1.65;
272
- min-height: 2.4rem;
273
- max-height: 6rem;
274
- overflow-y: auto;
275
- }
276
-
277
- .doc-text::placeholder { color: var(--text-dim); }
278
-
279
- .doc-score-wrap {
280
- display: flex;
281
- flex-direction: column;
282
- align-items: flex-end;
283
- gap: 0.25rem;
284
- min-width: 3.5rem;
285
- }
286
-
287
- .doc-score-num {
288
- font-size: 1.1rem;
289
- font-weight: 700;
290
- color: var(--text-dim);
291
- font-variant-numeric: tabular-nums;
292
- transition: color 0.4s;
293
- line-height: 1;
294
- }
295
-
296
- .doc-score-bar-wrap {
297
- width: 3rem;
298
- height: 3px;
299
- background: var(--border);
300
- border-radius: 1.5px;
301
- overflow: hidden;
302
- }
303
-
304
- .doc-score-bar {
305
- height: 100%;
306
- width: 0%;
307
- background: var(--text-dim);
308
- border-radius: 1.5px;
309
- transition: width 0.6s cubic-bezier(0.16,1,0.3,1), background 0.4s;
310
- }
311
-
312
- .doc-card.ranked-1 { border-color: rgba(245,158,11,0.4); background: rgba(245,158,11,0.04); }
313
- .doc-card.ranked-1 .doc-score-num { color: var(--amber); }
314
- .doc-card.ranked-1 .doc-score-bar { background: var(--amber); }
315
- .doc-card.ranked-2 .doc-score-num { color: #A78BFA; }
316
- .doc-card.ranked-2 .doc-score-bar { background: #A78BFA; }
317
- .doc-card.ranked-3 .doc-score-num { color: #60A5FA; }
318
- .doc-card.ranked-3 .doc-score-bar { background: #60A5FA; }
319
-
320
- /* Active card being scored — amber left-border + glow pulse */
321
- .doc-card.computing {
322
- border-color: var(--amber-dim);
323
- box-shadow: inset 3px 0 0 var(--amber), 0 0 12px rgba(245,158,11,0.08);
324
- animation: card-compute-pulse 1.4s ease-in-out infinite;
325
- }
326
-
327
- @keyframes card-compute-pulse {
328
- 0%, 100% { box-shadow: inset 3px 0 0 var(--amber), 0 0 8px rgba(245,158,11,0.06); }
329
- 50% { box-shadow: inset 3px 0 0 var(--amber), 0 0 18px rgba(245,158,11,0.18); }
330
- }
331
-
332
- /* CSS-only spinner — works even when WASM blocks the JS event loop */
333
- .doc-card.computing .doc-score-num {
334
- display: inline-block; /* required for transform */
335
- color: var(--amber);
336
- font-size: 1.2rem;
337
- animation: spin 0.7s linear infinite;
338
- transform-origin: center;
339
- }
340
-
341
- @keyframes spin {
342
- to { transform: rotate(360deg); }
343
- }
344
-
345
- /* Indeterminate progress bar (also CSS-only) */
346
- .doc-card.computing .doc-score-bar {
347
- background: var(--amber);
348
- width: 100% !important;
349
- animation: bar-indeterminate 1.2s ease-in-out infinite;
350
- transform-origin: left;
351
- }
352
-
353
- @keyframes bar-indeterminate {
354
- 0% { transform: scaleX(0); opacity: 0.6; }
355
- 50% { transform: scaleX(1); opacity: 1; }
356
- 100% { transform: scaleX(0); opacity: 0.6; }
357
- }
358
-
359
- /* ── Unscored doc (has text but wasn't in last rank run) ── */
360
- .doc-card.needs-rank {
361
- border-color: var(--amber-dim);
362
- border-style: dashed;
363
- }
364
-
365
- .doc-card.needs-rank .doc-score-num::after {
366
- content: ' ↺';
367
- font-size: 0.65rem;
368
- color: var(--amber-dim);
369
- }
370
-
371
- /* ── Actions ──────────────────────────────────────── */
372
- .actions-row {
373
- display: flex;
374
- gap: 0.6rem;
375
- margin-top: 0.75rem;
376
- align-items: center;
377
- flex-wrap: wrap;
378
- }
379
-
380
- button {
381
- font-family: var(--mono);
382
- cursor: pointer;
383
- border: 1px solid transparent;
384
- border-radius: 5px;
385
- font-size: 0.72rem;
386
- font-weight: 600;
387
- letter-spacing: 0.04em;
388
- padding: 0.5rem 1rem;
389
- transition: all 0.15s;
390
- display: inline-flex;
391
- align-items: center;
392
- gap: 0.4rem;
393
- }
394
-
395
- #rank-btn {
396
- background: var(--amber);
397
- color: #0A0A0A;
398
- border-color: var(--amber);
399
- padding: 0.55rem 1.4rem;
400
- font-size: 0.78rem;
401
- }
402
-
403
- #rank-btn:hover:not(:disabled) {
404
- background: #FBBF24;
405
- box-shadow: 0 0 16px rgba(245,158,11,0.35);
406
- }
407
-
408
- #rank-btn:disabled { opacity: 0.45; cursor: not-allowed; }
409
-
410
- #rank-btn.running { animation: btn-pulse 1.2s ease-in-out infinite; }
411
-
412
- @keyframes btn-pulse {
413
- 0%, 100% { box-shadow: 0 0 0 rgba(245,158,11,0); }
414
- 50% { box-shadow: 0 0 20px rgba(245,158,11,0.5); }
415
- }
416
-
417
- #add-doc-btn {
418
- background: transparent;
419
- color: var(--text-mid);
420
- border-color: var(--border-hi);
421
- }
422
-
423
- #add-doc-btn:hover { border-color: var(--amber-dim); color: var(--amber); }
424
-
425
- #clear-btn {
426
- background: transparent;
427
- color: var(--text-dim);
428
- border-color: transparent;
429
- margin-left: auto;
430
- font-size: 0.68rem;
431
- }
432
-
433
- #clear-btn:hover { color: var(--red); }
434
-
435
- /* ── Results header ───────────────────────────────── */
436
- #results-header {
437
- display: none;
438
- align-items: center;
439
- gap: 0.75rem;
440
- margin: 1.5rem 0 0.75rem;
441
- font-size: 0.6rem;
442
- letter-spacing: 0.2em;
443
- text-transform: uppercase;
444
- color: var(--text-dim);
445
- font-weight: 600;
446
- }
447
-
448
- #results-header.visible { display: flex; }
449
- #results-header::after { content: ''; flex: 1; height: 1px; background: var(--border); }
450
-
451
- /* ── FLIP helper ──────────────────────────────────── */
452
- .doc-card.flipping {
453
- transition: transform 0.45s cubic-bezier(0.16,1,0.3,1);
454
- }
455
-
456
- /* ── Footer ───────────────────────────────────────── */
457
- footer {
458
- padding: 1rem 2rem;
459
- max-width: 860px;
460
- margin: 0 auto;
461
- width: 100%;
462
- display: flex;
463
- align-items: center;
464
- gap: 1rem;
465
- font-size: 0.62rem;
466
- color: var(--text-dim);
467
- border-top: 1px solid var(--border);
468
- }
469
-
470
- footer a { color: var(--text-dim); text-decoration: none; }
471
- footer a:hover { color: var(--amber); }
472
- .sep { color: var(--border-hi); }
473
- #runtime-info { margin-left: auto; }
474
-
475
- ::-webkit-scrollbar { width: 4px; }
476
- ::-webkit-scrollbar-track { background: transparent; }
477
- ::-webkit-scrollbar-thumb { background: var(--border-hi); border-radius: 2px; }
478
-
479
- /* ── dtype selector ───────────────────────────────── */
480
- .dtype-select {
481
- background: var(--surface);
482
- border: 1px solid var(--border-hi);
483
- border-radius: 4px;
484
- color: var(--text-mid);
485
- font-family: var(--mono);
486
- font-size: 0.65rem;
487
- padding: 0.2rem 0.4rem;
488
- cursor: pointer;
489
- outline: none;
490
- }
491
-
492
- .dtype-select:hover { border-color: var(--amber-dim); }
493
- </style>
494
  </head>
495
  <body>
496
 
@@ -506,7 +24,6 @@
506
  </header>
507
 
508
  <main>
509
- <!-- Status bar -->
510
  <div id="status-bar">
511
  <div class="status-icon"></div>
512
  <span id="status-text">Loading model…</span>
@@ -521,32 +38,20 @@
521
  </select>
522
  </div>
523
 
524
- <!-- Query -->
525
  <div class="panel" id="query-panel">
526
  <div class="panel-label">Query</div>
527
- <textarea
528
- id="query-input"
529
- rows="1"
530
- placeholder="Enter your search query…"
531
- >What is the capital of France?</textarea>
532
  <div class="instruction-row">
533
  <span class="instruction-label">Instruct</span>
534
- <input
535
- type="text"
536
- id="instruction-input"
537
  placeholder="Given a web search query, retrieve relevant passages that answer the query"
538
- value="Given a web search query, retrieve relevant passages that answer the query"
539
- >
540
  </div>
541
  </div>
542
 
543
- <!-- Results header -->
544
  <div id="results-header">ranked results</div>
545
-
546
- <!-- Documents -->
547
  <div id="docs-list"></div>
548
 
549
- <!-- Actions -->
550
  <div class="actions-row">
551
  <button id="rank-btn" disabled>▶ Rank Documents</button>
552
  <button id="add-doc-btn">+ Add Document</button>
@@ -565,336 +70,5 @@
565
  <span id="runtime-info"></span>
566
  </footer>
567
 
568
- <script type="module">
569
- import {
570
- AutoTokenizer,
571
- AutoModelForCausalLM,
572
- } from "https://cdn.jsdelivr.net/npm/@huggingface/transformers@next";
573
-
574
- const MODEL_ID = "onnx-community/Qwen3-Reranker-0.6B-ONNX"; // https://huggingface.co/onnx-community/Qwen3-Reranker-0.6B-ONNX
575
-
576
- const SYSTEM =
577
- 'Judge whether the Document meets the requirements based on the Query and the Instruct provided. ' +
578
- 'Note that the answer can only be "yes" or "no".';
579
-
580
- const DEFAULT_INSTRUCTION =
581
- "Given a web search query, retrieve relevant passages that answer the query";
582
-
583
- const DEFAULT_DOCS = [
584
- "Paris is the capital and largest city of France, located in northern France.",
585
- "Berlin is the capital city of Germany.",
586
- "France is a country in Western Europe known for its wine, cuisine, and art.",
587
- "The Eiffel Tower is a famous landmark located in Paris, France.",
588
- ];
589
-
590
- // ── State ─────────────────────────────────────────────
591
- let tokenizer = null;
592
- let model = null;
593
- let tokenYes = -1;
594
- let tokenNo = -1;
595
- let docCount = 0;
596
-
597
- // ── DOM ───────────────────────────────────────────────
598
- const statusBar = document.getElementById("status-bar");
599
- const statusText = document.getElementById("status-text");
600
- const progressWrap = document.getElementById("progress-bar-wrap");
601
- const progressBar = document.getElementById("progress-bar");
602
- const rankBtn = document.getElementById("rank-btn");
603
- const docsList = document.getElementById("docs-list");
604
- const resultsHdr = document.getElementById("results-header");
605
- const dtypeSelect = document.getElementById("dtype-select");
606
- const queryInput = document.getElementById("query-input");
607
-
608
- // ── Status helper ─────────────────────────────────────
609
- function setStatus(type, text, progress = null) {
610
- statusBar.className = type;
611
- statusText.textContent = text;
612
- if (progress !== null) {
613
- progressWrap.classList.add("visible");
614
- progressBar.style.width = `${progress}%`;
615
- } else {
616
- progressWrap.classList.remove("visible");
617
- }
618
- }
619
-
620
- // Auto-resize textareas
621
- function autoResize(el) {
622
- el.style.height = "auto";
623
- el.style.height = Math.min(el.scrollHeight, 128) + "px";
624
- }
625
-
626
- queryInput.addEventListener("input", () => autoResize(queryInput));
627
-
628
- // ── Build prompt (no user data in template) ───────────
629
- function buildPrompt(query, doc, instruction) {
630
- return (
631
- `<|im_start|>system\n${SYSTEM}<|im_end|>\n` +
632
- `<|im_start|>user\n<Instruct>: ${instruction}\n\n<Query>: ${query}\n\n<Document>: ${doc}<|im_end|>\n` +
633
- `<|im_start|>assistant\n<think>\n\n</think>\n`
634
- );
635
- }
636
-
637
- // ── Add document card (safe DOM construction) ─────────
638
- function addDoc(text = "") {
639
- const id = String(++docCount);
640
-
641
- const card = document.createElement("div");
642
- card.className = "doc-card";
643
- card.dataset.id = id;
644
- card.style.animationDelay = `${Math.min((docCount - 1) * 30, 100)}ms`;
645
-
646
- const idxSpan = document.createElement("span");
647
- idxSpan.className = "doc-index";
648
- idxSpan.textContent = id.padStart(2, "0");
649
-
650
- const textarea = document.createElement("textarea");
651
- textarea.className = "doc-text";
652
- textarea.rows = 2;
653
- textarea.placeholder = "Paste a document or passage…";
654
- textarea.value = text; // safe: sets value, not HTML
655
- textarea.addEventListener("input", () => autoResize(textarea));
656
-
657
- const scoreWrap = document.createElement("div");
658
- scoreWrap.className = "doc-score-wrap";
659
-
660
- const scoreNum = document.createElement("span");
661
- scoreNum.className = "doc-score-num";
662
- scoreNum.textContent = "—";
663
-
664
- const barWrap = document.createElement("div");
665
- barWrap.className = "doc-score-bar-wrap";
666
-
667
- const bar = document.createElement("div");
668
- bar.className = "doc-score-bar";
669
-
670
- barWrap.appendChild(bar);
671
- scoreWrap.appendChild(scoreNum);
672
- scoreWrap.appendChild(barWrap);
673
-
674
- card.appendChild(idxSpan);
675
- card.appendChild(textarea);
676
- card.appendChild(scoreWrap);
677
-
678
- docsList.appendChild(card);
679
- }
680
-
681
- // Seed defaults
682
- DEFAULT_DOCS.forEach(d => addDoc(d));
683
-
684
- document.getElementById("add-doc-btn").addEventListener("click", () => addDoc());
685
-
686
- document.getElementById("clear-btn").addEventListener("click", () => {
687
- document.querySelectorAll(".doc-card").forEach(card => {
688
- card.className = "doc-card";
689
- card.querySelector(".doc-score-num").textContent = "—";
690
- card.querySelector(".doc-score-bar").style.width = "0%";
691
- });
692
- resultsHdr.classList.remove("visible");
693
- });
694
-
695
- // ── Animated score counter ────────────────────────────
696
- function animateScore(numEl, barEl, target) {
697
- const start = performance.now();
698
- const duration = 600;
699
- const fmt = v => (v * 100).toFixed(1) + "%";
700
-
701
- function tick(now) {
702
- const t = Math.min((now - start) / duration, 1);
703
- const ease = 1 - Math.pow(1 - t, 3);
704
- numEl.textContent = fmt(ease * target);
705
- barEl.style.width = `${ease * target * 100}%`;
706
- if (t < 1) requestAnimationFrame(tick);
707
- else numEl.textContent = fmt(target);
708
- }
709
-
710
- requestAnimationFrame(tick);
711
- }
712
-
713
- // ── FLIP reorder animation ────────────────────────────
714
- function flipReorder(cards, sortedIds) {
715
- const before = new Map(cards.map(c => [c.dataset.id, c.getBoundingClientRect().top]));
716
-
717
- sortedIds.forEach(id => {
718
- const card = docsList.querySelector(`[data-id="${CSS.escape(id)}"]`);
719
- if (card) docsList.appendChild(card);
720
- });
721
-
722
- cards.forEach(card => {
723
- const delta = before.get(card.dataset.id) - card.getBoundingClientRect().top;
724
- if (Math.abs(delta) > 1) {
725
- card.style.transform = `translateY(${delta}px)`;
726
- card.style.transition = "none";
727
- requestAnimationFrame(() => {
728
- card.classList.add("flipping");
729
- card.style.transform = "translateY(0)";
730
- setTimeout(() => {
731
- card.classList.remove("flipping");
732
- card.style.transition = "";
733
- }, 500);
734
- });
735
- }
736
- });
737
- }
738
-
739
- // ── Main inference ────────────────────────────────────
740
- async function runReranking() {
741
- const query = queryInput.value.trim();
742
- const instruction = document.getElementById("instruction-input").value.trim()
743
- || DEFAULT_INSTRUCTION;
744
-
745
- if (!query) { queryInput.focus(); return; }
746
-
747
- // Clear any previous "needs re-rank" markers
748
- document.querySelectorAll(".doc-card.needs-rank").forEach(c => c.classList.remove("needs-rank"));
749
-
750
- const cards = [...document.querySelectorAll(".doc-card")];
751
- const entries = cards.map(card => ({
752
- id: card.dataset.id,
753
- text: card.querySelector(".doc-text").value.trim(),
754
- card,
755
- }));
756
- const toScore = entries.filter(e => e.text);
757
- if (!toScore.length) return;
758
-
759
- rankBtn.disabled = true;
760
- rankBtn.classList.add("running");
761
- rankBtn.textContent = "⟳ Ranking…";
762
-
763
- // Set ⟳ once — CSS rotates it on the compositor thread,
764
- // so it spins even while WASM inference blocks the JS event loop.
765
- toScore.forEach(({ card }) => {
766
- card.classList.add("computing");
767
- card.querySelector(".doc-score-num").textContent = "⟳";
768
- });
769
-
770
- const scored = [];
771
-
772
- for (let i = 0; i < toScore.length; i++) {
773
- const { id, text, card } = toScore[i];
774
- setStatus("loading", `Scoring document ${i + 1} / ${toScore.length}…`, (i / toScore.length) * 100);
775
-
776
- try {
777
- const prompt = buildPrompt(query, text, instruction);
778
- const inputs = tokenizer(prompt, { truncation: true, max_length: 8192 });
779
- const output = await model(inputs);
780
-
781
- const [, seqLen, vocabSize] = output.logits.dims.map(Number);
782
- const offset = (seqLen - 1) * vocabSize;
783
- const data = output.logits.data;
784
-
785
- // Numerically stable softmax over yes/no
786
- const ly = data[offset + tokenYes];
787
- const ln = data[offset + tokenNo];
788
- const m = Math.max(ly, ln);
789
- const ey = Math.exp(ly - m);
790
- const en = Math.exp(ln - m);
791
- const score = ey / (ey + en);
792
-
793
- card.classList.remove("computing");
794
- animateScore(
795
- card.querySelector(".doc-score-num"),
796
- card.querySelector(".doc-score-bar"),
797
- score,
798
- );
799
- scored.push({ id, score, card });
800
-
801
- } catch (err) {
802
- console.error("Inference error on doc", id, err);
803
- card.classList.remove("computing");
804
- card.querySelector(".doc-score-num").textContent = "err";
805
- setStatus("error", `Error: ${err?.message ?? String(err)}`);
806
- scored.push({ id, score: -1, card });
807
- }
808
- }
809
-
810
- // Rank + reorder
811
- scored.sort((a, b) => b.score - a.score);
812
- scored.forEach(({ card }, rank) => {
813
- card.className = "doc-card";
814
- if (rank < 3) card.classList.add(`ranked-${rank + 1}`);
815
- });
816
-
817
- const unscoredEntries = entries.filter(e => !e.text);
818
- const unscoredIds = unscoredEntries.map(e => e.id);
819
- flipReorder(cards, [...scored.map(s => s.id), ...unscoredIds]);
820
-
821
- // Highlight any cards that now have text but weren't scored this run
822
- // (e.g. user typed into a new card after hitting Rank)
823
- const newlyFilled = entries.filter(e => {
824
- const currentText = e.card.querySelector(".doc-text").value.trim();
825
- return currentText && !toScore.some(s => s.id === e.id);
826
- });
827
- newlyFilled.forEach(({ card }) => card.classList.add("needs-rank"));
828
-
829
- const needsRankCount = newlyFilled.length;
830
- if (needsRankCount > 0) {
831
- setStatus("ready", `Scored ${toScore.length} · ${needsRankCount} unscored doc${needsRankCount > 1 ? "s" : ""} — hit Rank again ↺`);
832
- } else {
833
- setStatus("ready", `Scored ${toScore.length} document${toScore.length !== 1 ? "s" : ""} · model ready`);
834
- }
835
-
836
- resultsHdr.classList.add("visible");
837
- rankBtn.disabled = false;
838
- rankBtn.classList.remove("running");
839
- rankBtn.textContent = "▶ Rank Documents";
840
- }
841
-
842
- // ── Load model ────────────────────────────────────────
843
- //
844
- // NOTE on scoring approach:
845
- // Traditional BERT cross-encoders (see github.com/huggingface/transformers.js/issues/497)
846
- // use text-classification + logits.sigmoid(). Qwen3-Reranker is a CausalLM — it
847
- // outputs "yes"/"no" at the next-token position instead. We do a numerically stable
848
- // softmax over just those two token logits rather than sigmoid over a classification head.
849
- async function loadModel() {
850
- const dtype = dtypeSelect.value;
851
- dtypeSelect.disabled = true;
852
- rankBtn.disabled = true;
853
-
854
- setStatus("loading", `Downloading model (${dtype})…`, 0);
855
-
856
- try {
857
- tokenizer = await AutoTokenizer.from_pretrained(MODEL_ID);
858
-
859
- // Resolve token IDs for "yes" / "no" (last subword of each)
860
- const yesIds = tokenizer("yes", { add_special_tokens: false }).input_ids.data;
861
- const noIds = tokenizer("no", { add_special_tokens: false }).input_ids.data;
862
- tokenYes = Number(yesIds[yesIds.length - 1]);
863
- tokenNo = Number(noIds[noIds.length - 1]);
864
-
865
- model = await AutoModelForCausalLM.from_pretrained(MODEL_ID, {
866
- dtype,
867
- progress_callback(info) {
868
- if (info.status === "progress" && info.progress != null) {
869
- const pct = Math.round(info.progress);
870
- setStatus("loading", `${info.file ?? "model"} — ${pct}%`, pct);
871
- }
872
- },
873
- });
874
-
875
- document.getElementById("runtime-info").textContent = `dtype: ${dtype}`;
876
- setStatus("ready", `Model ready · ${MODEL_ID}`);
877
- rankBtn.disabled = false;
878
- dtypeSelect.disabled = false;
879
-
880
- } catch (err) {
881
- console.error(err);
882
- setStatus("error", `Failed to load: ${err.message}`);
883
- dtypeSelect.disabled = false;
884
- }
885
- }
886
-
887
- // Single stable listener — removed/re-added on dtype reload to avoid double-firing
888
- rankBtn.addEventListener("click", runReranking);
889
-
890
- dtypeSelect.addEventListener("change", () => {
891
- model = null;
892
- tokenizer = null;
893
- rankBtn.removeEventListener("click", runReranking);
894
- loadModel().then(() => rankBtn.addEventListener("click", runReranking));
895
- });
896
-
897
- loadModel();
898
- </script>
899
  </body>
900
  </html>
 
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;0,600;0,700;1,300&family=Syne:wght@700;800;900&display=swap" rel="stylesheet">
10
+ <script type="module" crossorigin src="./assets/index-CaEweXpE.js"></script>
11
+ <link rel="stylesheet" crossorigin href="./assets/index-C0wdVFKl.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  </head>
13
  <body>
14
 
 
24
  </header>
25
 
26
  <main>
 
27
  <div id="status-bar">
28
  <div class="status-icon"></div>
29
  <span id="status-text">Loading model…</span>
 
38
  </select>
39
  </div>
40
 
 
41
  <div class="panel" id="query-panel">
42
  <div class="panel-label">Query</div>
43
+ <textarea id="query-input" rows="1" placeholder="Enter your search query…">What is the capital of France?</textarea>
 
 
 
 
44
  <div class="instruction-row">
45
  <span class="instruction-label">Instruct</span>
46
+ <input type="text" id="instruction-input"
 
 
47
  placeholder="Given a web search query, retrieve relevant passages that answer the query"
48
+ value="Given a web search query, retrieve relevant passages that answer the query">
 
49
  </div>
50
  </div>
51
 
 
52
  <div id="results-header">ranked results</div>
 
 
53
  <div id="docs-list"></div>
54
 
 
55
  <div class="actions-row">
56
  <button id="rank-btn" disabled>▶ Rank Documents</button>
57
  <button id="add-doc-btn">+ Add Document</button>
 
70
  <span id="runtime-info"></span>
71
  </footer>
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  </body>
74
  </html>