CodeSoft commited on
Commit
4e8b17d
·
verified ·
1 Parent(s): 1ff0e70

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1100 -56
index.html CHANGED
@@ -1,57 +1,1101 @@
1
  <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Gradio-Lite: Serverless Gradio Running Entirely in Your Browser</title>
7
- <meta name="description" content="Gradio-Lite: Serverless Gradio Running Entirely in Your Browser">
8
-
9
- <script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script>
10
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
11
-
12
- <style>
13
- html, body {
14
- margin: 0;
15
- padding: 0;
16
- height: 100%;
17
- }
18
- </style>
19
- </head>
20
- <body>
21
- <gradio-lite>
22
- <gradio-file name="app.py" entrypoint>
23
- import gradio as gr
24
-
25
- from filters import as_gray
26
-
27
- def process(input_image):
28
- output_image = as_gray(input_image)
29
- return output_image
30
-
31
- demo = gr.Interface(
32
- process,
33
- "image",
34
- "image",
35
- examples=["lion.jpg", "logo.png"],
36
- )
37
-
38
- demo.launch()
39
- </gradio-file>
40
-
41
- <gradio-file name="filters.py">
42
- from skimage.color import rgb2gray
43
-
44
- def as_gray(image):
45
- return rgb2gray(image)
46
- </gradio-file>
47
-
48
- <gradio-file name="lion.jpg" url="https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/test_data/lion.jpg" />
49
- <gradio-file name="logo.png" url="https://raw.githubusercontent.com/gradio-app/gradio/main/guides/assets/logo.png" />
50
-
51
- <gradio-requirements>
52
- # Same syntax as requirements.txt
53
- scikit-image
54
- </gradio-requirements>
55
- </gradio-lite>
56
- </body>
57
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>MetaDiffusion-150M - Live Denoising</title>
7
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%2313141a'/%3E%3Crect x='4' y='4' width='8' height='8' rx='1.5' fill='%236e9fd4'/%3E%3C/svg%3E">
8
+ <style>
9
+ * { box-sizing: border-box; }
10
+ body {
11
+ font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
12
+ max-width: 980px;
13
+ margin: 0 auto;
14
+ padding: 24px 20px 40px;
15
+ color: #c9d1d9;
16
+ line-height: 1.55;
17
+ background: #13141a;
18
+ }
19
+ .subtitle { color: #7d8590; font-size: 0.85em; margin-bottom: 14px; }
20
+ .subtitle code {
21
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
22
+ font-size: 0.95em; color: #9cc4ee; background: #1c1d26;
23
+ padding: 1px 5px; border-radius: 3px;
24
+ }
25
+ .status-line {
26
+ font-size: 0.78em; color: #7d8590; margin-bottom: 18px;
27
+ padding: 6px 10px; background: #1c1d26; border: 1px solid #2d2e3a;
28
+ border-radius: 4px; display: inline-block; max-width: 100%;
29
+ }
30
+ .status-line span {
31
+ margin-right: 16px; max-width: 280px; overflow: hidden;
32
+ text-overflow: ellipsis; white-space: nowrap;
33
+ display: inline-block; vertical-align: bottom;
34
+ }
35
+ .status-line span:last-child { margin-right: 0; }
36
+
37
+ .layout { display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-start; }
38
+ .col-left { width: 310px; flex-shrink: 0; }
39
+ .col-right { flex: 1; min-width: 320px; }
40
+
41
+ fieldset {
42
+ border: 1px solid #2d2e3a; border-radius: 6px;
43
+ padding: 16px; margin: 0 0 14px; background: #1a1b23;
44
+ }
45
+ legend { font-weight: 600; font-size: 0.82em; padding: 0 6px; color: #9ca3af; }
46
+
47
+ label { display: block; font-size: 0.8em; margin: 12px 0 3px; color: #9ca3af; font-weight: 500; }
48
+ label .val { float: right; font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; font-weight: 400; color: #e6edf3; }
49
+ input[type=range] { width: 100%; margin: 2px 0; cursor: pointer; accent-color: #6e9fd4; }
50
+ textarea {
51
+ width: 100%; height: 72px; resize: vertical;
52
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
53
+ font-size: 13px; padding: 8px 10px;
54
+ border: 1px solid #2d2e3a; border-radius: 4px;
55
+ background: #111218; color: #c9d1d9; line-height: 1.5;
56
+ }
57
+ textarea:focus { outline: none; border-color: #6e9fd4; box-shadow: 0 0 0 2px rgba(110,159,212,0.15); }
58
+
59
+ .seg { display: flex; border: 1px solid #2d2e3a; border-radius: 4px; overflow: hidden; margin-top: 4px; }
60
+ .seg button {
61
+ flex: 1; padding: 6px 0; font-size: 0.78em; font-weight: 600;
62
+ background: #1e1f28; color: #9ca3af; border: 0; cursor: pointer;
63
+ border-right: 1px solid #2d2e3a; transition: background 0.12s, color 0.12s;
64
+ }
65
+ .seg button:last-child { border-right: 0; }
66
+ .seg button:hover { background: #282936; color: #c9d1d9; }
67
+ .seg button.active { background: #2b3a52; color: #bcd4ee; }
68
+ .seg button:disabled { opacity: 0.38; cursor: not-allowed; }
69
+ .seg button:disabled:hover { background: #1e1f28; color: #9ca3af; }
70
+
71
+ .btn-row { margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap; }
72
+ .btn-row button {
73
+ padding: 8px 16px; cursor: pointer; font-size: 0.83em; font-weight: 600;
74
+ border: 1px solid #2d2e3a; border-radius: 4px; background: #22232e; color: #c9d1d9;
75
+ transition: background 0.12s;
76
+ }
77
+ .btn-row button:hover { background: #2a2b38; }
78
+ .btn-row button:disabled { opacity: 0.4; cursor: not-allowed; }
79
+ #runBtn { background: #3b6ea5; color: #e6edf3; border-color: #3b6ea5; }
80
+ #runBtn:hover { background: #4a7fb8; }
81
+ #runBtn:disabled { background: #3b6ea5; }
82
+ #stopBtn { color: #e57373; border-color: #5c3333; background: #1e1518; }
83
+ #stopBtn:hover { background: #2a1c20; }
84
+ #getWeightsBtn { background: #3b6ea5; color: #e6edf3; border-color: #3b6ea5; }
85
+ #getWeightsBtn:hover { background: #4a7fb8; }
86
+
87
+ #presets { margin-top: 12px; }
88
+ #presets button {
89
+ font-size: 0.75em; padding: 3px 9px; margin: 0 5px 5px 0;
90
+ cursor: pointer; border: 1px solid #2d2e3a; border-radius: 3px;
91
+ background: #1e1f28; color: #9ca3af; transition: background 0.12s, color 0.12s;
92
+ }
93
+ #presets button:hover { background: #282936; border-color: #4a4b5c; color: #c9d1d9; }
94
+
95
+ #status { font-size: 0.78em; color: #7d8590; margin-top: 12px; font-family: Consolas, Menlo, monospace; }
96
+ #status .ok { color: #7ee787; font-weight: 600; }
97
+ #status .err { color: #f47067; font-weight: 600; }
98
+ #dlbar { margin-top: 8px; height: 4px; background: #22232e; border-radius: 2px; overflow: hidden; }
99
+ #dlbar > div { height: 100%; width: 0%; background: #6e9fd4; transition: width 0.2s; }
100
+
101
+ #cacheInfo {
102
+ font-size: 0.78em; color: #7d8590; font-family: Consolas, Menlo, monospace;
103
+ margin-bottom: 4px; min-height: 2.6em;
104
+ }
105
+ #cacheInfo b { color: #e6edf3; font-weight: 600; }
106
+
107
+ #probe {
108
+ min-height: 140px; max-height: 300px; overflow-y: auto;
109
+ border: 1px solid #2d2e3a; border-radius: 4px;
110
+ padding: 12px 14px; margin-bottom: 10px;
111
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
112
+ font-size: 13px; line-height: 1.8;
113
+ background: #111218;
114
+ white-space: pre-wrap;
115
+ word-wrap: break-word;
116
+ overflow-wrap: break-word;
117
+ transition: border-color 0.15s;
118
+ }
119
+ #probe:hover { border-color: #3a3c4d; }
120
+ .tok { display: inline; white-space: pre-wrap; word-break: break-word; }
121
+ .tok.prompt { color: #7ee787; }
122
+ .tok.mask { color: #3d3f50; }
123
+ .tok.fresh { background: #263352; border-radius: 2px; }
124
+
125
+ #meta {
126
+ font-size: 0.76em; color: #7d8590;
127
+ font-family: Consolas, Menlo, monospace;
128
+ margin: 8px 0; padding: 0 2px;
129
+ }
130
+ #meta b { color: #e6edf3; font-weight: 600; }
131
+ #progressWrap { height: 5px; background: #22232e; border-radius: 3px; margin: 8px 0; overflow: hidden; }
132
+ #progressFill { height: 100%; width: 0%; background: #6e9fd4; border-radius: 3px; transition: width 0.15s; }
133
+
134
+ .sparkhead {
135
+ display: flex; justify-content: space-between;
136
+ font-size: 0.72em; color: #7d8590;
137
+ font-family: Consolas, Menlo, monospace;
138
+ margin-top: 10px; padding: 0 2px;
139
+ }
140
+ .sparkhead b { color: #e6edf3; font-weight: 600; }
141
+ #spark {
142
+ width: 100%; height: 40px; display: block;
143
+ background: #111218; border: 1px solid #2d2e3a; border-radius: 4px;
144
+ margin-top: 4px;
145
+ }
146
+
147
+ #finalText {
148
+ display: none; border: 1px solid #2d2e3a; border-radius: 4px;
149
+ padding: 12px 14px; margin-top: 12px;
150
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
151
+ font-size: 13px; line-height: 1.7;
152
+ white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word;
153
+ max-height: 240px; overflow-y: auto; background: #111218; color: #c9d1d9;
154
+ }
155
+ #copyBtn {
156
+ display: none; margin-top: 8px; font-size: 0.78em;
157
+ padding: 5px 12px; cursor: pointer;
158
+ border: 1px solid #2d2e3a; border-radius: 3px; background: #22232e; color: #9ca3af;
159
+ transition: background 0.12s, color 0.12s;
160
+ }
161
+ #copyBtn:hover { background: #2a2b38; color: #c9d1d9; }
162
+
163
+ footer {
164
+ margin-top: 32px; padding-top: 14px;
165
+ border-top: 1px solid #2d2e3a;
166
+ font-size: 0.76em; color: #555b66;
167
+ }
168
+ footer a { color: #6e9fd4; text-decoration: none; }
169
+ footer a:hover { text-decoration: underline; }
170
+
171
+ @media (max-width: 700px) {
172
+ .layout { flex-direction: column; }
173
+ .col-left { width: 100%; }
174
+ }
175
+ </style>
176
+ </head>
177
+ <body>
178
+
179
+ <div class="subtitle"><code>CodeSoft/MetaDiffusion-150M-exp</code> &mdash; masked diffusion language model</div>
180
+ <div class="status-line">
181
+ <span id="badgeEngine">engine: &mdash;</span>
182
+ <span id="badgeGpu">gpu: probing&hellip;</span>
183
+ <span id="badgeWeights">tokenizer: &mdash;</span>
184
+ </div>
185
+
186
+ <div class="layout">
187
+ <div class="col-left">
188
+ <fieldset>
189
+ <legend>Model weights</legend>
190
+ <div id="cacheInfo">Checking local cache&hellip;</div>
191
+ <div class="btn-row">
192
+ <button id="getWeightsBtn" style="display:none">Download weights</button>
193
+ <button id="clearCacheBtn" style="display:none">Clear cache</button>
194
+ </div>
195
+ <div id="dlbar"><div id="dlfill"></div></div>
196
+ </fieldset>
197
+
198
+ <fieldset>
199
+ <legend>Prompt &amp; Controls</legend>
200
+
201
+ <label for="prompt">Prompt text</label>
202
+ <textarea id="prompt" spellcheck="false">The cat sat on the</textarea>
203
+
204
+ <label>Sequence length <span class="val" id="vSeq">96</span></label>
205
+ <input type="range" id="seqLen" min="32" max="256" step="16" value="96">
206
+
207
+ <label>Denoising steps <span class="val" id="vSteps">64</span></label>
208
+ <input type="range" id="numSteps" min="16" max="128" step="8" value="64">
209
+
210
+ <label>Temperature <span class="val" id="vTemp">0.60</span></label>
211
+ <input type="range" id="temperature" min="0.3" max="1.5" step="0.05" value="0.6">
212
+
213
+ <label>Repetition penalty <span class="val" id="vRep">1.50</span></label>
214
+ <input type="range" id="repPenalty" min="1.0" max="2.0" step="0.05" value="1.5">
215
+
216
+ <label>Backend</label>
217
+ <div class="seg" id="backendSeg">
218
+ <button type="button" data-backend="auto" class="active">Auto</button>
219
+ <button type="button" data-backend="webgpu">WebGPU</button>
220
+ <button type="button" data-backend="wasm">WASM</button>
221
+ </div>
222
+
223
+ <div id="presets">
224
+ <button data-preset='{"prompt":"Once upon a time,","seqLen":96,"numSteps":64,"temperature":0.6,"repPenalty":1.5}'>Fairy tale</button>
225
+ <button data-preset='{"prompt":"The science of","seqLen":128,"numSteps":96,"temperature":0.5,"repPenalty":1.6}'>Science</button>
226
+ <button data-preset='{"prompt":"def fibonacci(n):","seqLen":96,"numSteps":64,"temperature":0.2,"repPenalty":1.8}'>Code</button>
227
+ <button data-preset='{"prompt":"The cat sat on the","seqLen":160,"numSteps":112,"temperature":0.8,"repPenalty":1.3}'>Creative</button>
228
+ </div>
229
+
230
+ <div class="btn-row">
231
+ <button id="runBtn" disabled>&#9654; Denoise</button>
232
+ <button id="stopBtn" disabled>&#9632; Stop</button>
233
+ </div>
234
+
235
+ <div id="status">Initializing&hellip;</div>
236
+ </fieldset>
237
+ </div>
238
+
239
+ <div class="col-right">
240
+ <fieldset>
241
+ <legend>Live denoising output</legend>
242
+ <div id="probe" aria-live="polite"></div>
243
+ <div id="meta">
244
+ step <b id="mStep">0</b>/<span id="mSteps">64</span> &nbsp;&middot;&nbsp;
245
+ noise <b id="mT">1.00</b> &nbsp;&middot;&nbsp;
246
+ masks <b id="mMasks">0</b> &nbsp;&middot;&nbsp;
247
+ <b id="mTps">&ndash;</b> tok/s &nbsp;
248
+ <span id="mEta"></span>
249
+ </div>
250
+ <div id="progressWrap"><div id="progressFill"></div></div>
251
+ <div class="sparkhead"><span>step time</span><b id="mMs">&ndash;</b></div>
252
+ <canvas id="spark"></canvas>
253
+ <div id="finalText"></div>
254
+ <button id="copyBtn">Copy output</button>
255
+ </fieldset>
256
+ </div>
257
+ </div>
258
+
259
+ <footer>
260
+ <a href="https://huggingface.co/CodeSoft/MetaDiffusion-150M-exp" target="_blank" rel="noopener">CodeSoft/MetaDiffusion-150M-exp</a>
261
+ &middot; Apache-2.0 &middot; int8 ONNX &middot;
262
+ <a href="https://onnxruntime.ai" target="_blank" rel="noopener">onnxruntime-web</a>
263
+ </footer>
264
+
265
+ <!-- The ORT bundle is loaded dynamically in loadModel(): ort.webgpu.min.js
266
+ (wasm + webgpu EP) when navigator.gpu exists, else the lean ort.min.js.
267
+ The default ort.min.js does NOT contain the WebGPU EP. -->
268
+
269
+ <script id="engine">
270
+ "use strict";
271
+
272
+ const MD_MASK_TOKEN = 32000;
273
+ const MD_SPECIAL_IDS = new Set([0, 1, 2, 3]);
274
+
275
+ function buildByteMap() {
276
+ const map = new Array(256);
277
+ const bs = [];
278
+ for (let b = 33; b <= 126; b++) bs.push(b);
279
+ for (let b = 161; b <= 172; b++) bs.push(b);
280
+ for (let b = 174; b <= 255; b++) bs.push(b);
281
+ const cs = bs.slice();
282
+ let n = 0;
283
+ for (let b = 0; b < 256; b++) {
284
+ if (!bs.includes(b)) { bs.push(b); cs.push(256 + n); n++; }
285
+ }
286
+ for (let i = 0; i < 256; i++) map[bs[i]] = String.fromCharCode(cs[i]);
287
+ return map;
288
+ }
289
+ const BYTE_MAP = buildByteMap();
290
+ const CHAR_TO_BYTE = new Map();
291
+ for (let b = 0; b < 256; b++) CHAR_TO_BYTE.set(BYTE_MAP[b], b);
292
+
293
+ function strToUtf8Bytes(s) {
294
+ return Array.from(new TextEncoder().encode(s));
295
+ }
296
+ function bytesToStr(bytes) {
297
+ return new TextDecoder("utf-8", { fatal: false }).decode(new Uint8Array(bytes));
298
+ }
299
+
300
+ class ByteLevelTokenizer {
301
+ constructor(json) {
302
+ const model = json.model;
303
+ this.vocab = new Map(Object.entries(model.vocab));
304
+ this.idToToken = new Array(model.vocab ? Object.keys(model.vocab).length : 0);
305
+ for (const [tok, id] of this.vocab) this.idToToken[id] = tok;
306
+ this.unkId = model.unk_token ? this.vocab.get(model.unk_token) : 3;
307
+ this.merges = new Map();
308
+ (model.merges || []).forEach((m, rank) => {
309
+ const pair = Array.isArray(m) ? m[0] + m[1] : m.indexOf(" ") >= 0 ? m.replace(" ", "") : m;
310
+ this.merges.set(pair, rank);
311
+ });
312
+ const pt = (json.pre_tokenizer || {});
313
+ this.regex = pt.use_regex === false
314
+ ? null
315
+ : /'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/gu;
316
+ }
317
+
318
+ byteEncode(str) {
319
+ let out = "";
320
+ for (const b of strToUtf8Bytes(str)) out += BYTE_MAP[b];
321
+ return out;
322
+ }
323
+
324
+ bpe(word) {
325
+ if (word.length <= 1) return [word];
326
+ let parts = Array.from(word);
327
+ while (parts.length > 1) {
328
+ let bestRank = Infinity, bestIdx = -1;
329
+ for (let i = 0; i < parts.length - 1; i++) {
330
+ const rank = this.merges.get(parts[i] + parts[i + 1]);
331
+ if (rank !== undefined && rank < bestRank) { bestRank = rank; bestIdx = i; }
332
+ }
333
+ if (bestIdx === -1) break;
334
+ parts.splice(bestIdx, 2, parts[bestIdx] + parts[bestIdx + 1]);
335
+ }
336
+ return parts;
337
+ }
338
+
339
+ encode(text) {
340
+ const ids = [];
341
+ if (this.regex) {
342
+ const matches = text.match(this.regex);
343
+ if (matches) {
344
+ for (const piece of matches) {
345
+ const encoded = this.byteEncode(piece);
346
+ for (const sub of this.bpe(encoded)) {
347
+ const id = this.vocab.get(sub);
348
+ ids.push(id === undefined ? this.unkId : id);
349
+ }
350
+ }
351
+ }
352
+ } else {
353
+ for (const sub of this.bpe(this.byteEncode(text))) {
354
+ const id = this.vocab.get(sub);
355
+ ids.push(id === undefined ? this.unkId : id);
356
+ }
357
+ }
358
+ return ids;
359
+ }
360
+
361
+ decode(ids, skipSpecial = true) {
362
+ const bytes = [];
363
+ for (const id of ids) {
364
+ if (skipSpecial && MD_SPECIAL_IDS.has(id)) continue;
365
+ const tok = this.idToToken[id];
366
+ if (tok === undefined) continue;
367
+ for (const ch of tok) {
368
+ const b = CHAR_TO_BYTE.get(ch);
369
+ if (b !== undefined) bytes.push(b);
370
+ }
371
+ }
372
+ return bytesToStr(bytes);
373
+ }
374
+ }
375
+
376
+ function cumulativeUnmaskFrac(i, n) {
377
+ return 0.5 * (1 - Math.cos(Math.PI * i / n));
378
+ }
379
+
380
+ async function* denoise(env, params) {
381
+ const { prompt, seqLen, numSteps, temperature, repPenalty, runStep, greedy } = params;
382
+ const tok = env.tokenizer;
383
+ const promptIds = tok.encode(prompt);
384
+ if (promptIds.length >= seqLen) {
385
+ throw new Error("Prompt is " + promptIds.length + " tokens, must be < seq len " + seqLen + ".");
386
+ }
387
+ const total = seqLen;
388
+ const genLen = total - promptIds.length;
389
+ const vocab = env.vocabSize;
390
+ const inputIds = new Int32Array(total).fill(MD_MASK_TOKEN);
391
+ for (let i = 0; i < promptIds.length; i++) inputIds[i] = promptIds[i];
392
+ const positionIds = new Int32Array(total);
393
+ for (let i = 0; i < total; i++) positionIds[i] = i;
394
+
395
+ const logits = new Float32Array(total * vocab);
396
+ const startTime = performance.now();
397
+
398
+ for (let step = 0; step < numSteps; step++) {
399
+ if (env.cancelled) throw new Error("cancelled");
400
+
401
+ const fracNow = cumulativeUnmaskFrac(step, numSteps);
402
+ const fracNext = cumulativeUnmaskFrac(step + 1, numSteps);
403
+ let nMasked = 0;
404
+ for (let i = promptIds.length; i < total; i++) if (inputIds[i] === MD_MASK_TOKEN) nMasked++;
405
+ const nTotalToUnmask = Math.round((fracNext - fracNow) * genLen);
406
+ let nUnmask;
407
+ if (step === numSteps - 1) nUnmask = nMasked;
408
+ else nUnmask = nMasked > 0 ? Math.max(nTotalToUnmask, 1) : 0;
409
+ const t = 1 - fracNow;
410
+
411
+ await runStep(inputIds, positionIds, t, logits);
412
+
413
+ const maskPositions = [];
414
+ const seenTokens = new Set();
415
+ for (let i = 0; i < total; i++) {
416
+ const row = i * vocab;
417
+ logits[row + MD_MASK_TOKEN] = -1e9;
418
+ if (inputIds[i] === MD_MASK_TOKEN) maskPositions.push(i);
419
+ else seenTokens.add(inputIds[i]);
420
+ }
421
+ if (repPenalty !== 1.0 && seenTokens.size > 0) {
422
+ for (const tokId of seenTokens) {
423
+ for (let i = 0; i < total; i++) {
424
+ const v = logits[i * vocab + tokId];
425
+ logits[i * vocab + tokId] = v < 0 ? v * repPenalty : v / repPenalty;
426
+ }
427
+ }
428
+ }
429
+
430
+ const nMask = maskPositions.length;
431
+ const rowMaxProb = new Float32Array(total);
432
+ const invTemp = 1 / temperature;
433
+ for (let i = 0; i < total; i++) {
434
+ const row = i * vocab;
435
+ let mx = -Infinity;
436
+ for (let k = 0; k < vocab; k++) mx = Math.max(mx, logits[row + k]);
437
+ let sum = 0;
438
+ for (let k = 0; k < vocab; k++) {
439
+ const v = Math.exp((logits[row + k] - mx) * invTemp);
440
+ logits[row + k] = v;
441
+ sum += v;
442
+ }
443
+ const invSum = 1 / sum;
444
+ let pmax = 0;
445
+ for (let k = 0; k < vocab; k++) {
446
+ const p = logits[row + k] * invSum;
447
+ logits[row + k] = p;
448
+ if (p > pmax) pmax = p;
449
+ }
450
+ rowMaxProb[i] = pmax;
451
+ }
452
+
453
+ const sampled = new Int32Array(nMask);
454
+ const confidence = new Float32Array(nMask);
455
+ for (let j = 0; j < nMask; j++) {
456
+ const row = maskPositions[j] * vocab;
457
+ if (greedy) {
458
+ let best = 0;
459
+ for (let k = 1; k < vocab; k++) if (logits[row + k] > logits[row + best]) best = k;
460
+ sampled[j] = best;
461
+ } else {
462
+ const u = Math.random();
463
+ let cdf = 0;
464
+ let chosen = vocab - 1;
465
+ for (let k = 0; k < vocab; k++) {
466
+ cdf += logits[row + k];
467
+ if (cdf >= u) { chosen = k; break; }
468
+ }
469
+ sampled[j] = chosen;
470
+ }
471
+ confidence[j] = rowMaxProb[maskPositions[j]];
472
+ }
473
+
474
+ const order = Array.from({ length: nMask }, (_, j) => j);
475
+ order.sort((a, b) => confidence[b] - confidence[a]);
476
+ const nFill = Math.min(nUnmask, nMask);
477
+ for (let k = 0; k < nFill; k++) {
478
+ inputIds[maskPositions[order[k]]] = sampled[order[k]];
479
+ }
480
+
481
+ yield {
482
+ step, numSteps, t, nMasked: nMask,
483
+ inputIds: inputIds.slice(),
484
+ promptLen: promptIds.length,
485
+ elapsed: performance.now() - startTime,
486
+ };
487
+ }
488
+ }
489
+ </script>
490
+
491
+ <script>
492
+ "use strict";
493
+ var $ = function(id) { return document.getElementById(id); };
494
+ var els = {
495
+ prompt: $("prompt"), seqLen: $("seqLen"), numSteps: $("numSteps"),
496
+ temperature: $("temperature"), repPenalty: $("repPenalty"),
497
+ vSeq: $("vSeq"), vSteps: $("vSteps"), vTemp: $("vTemp"), vRep: $("vRep"),
498
+ runBtn: $("runBtn"), stopBtn: $("stopBtn"), status: $("status"),
499
+ dlfill: $("dlfill"), badgeEngine: $("badgeEngine"), badgeWeights: $("badgeWeights"),
500
+ badgeGpu: $("badgeGpu"),
501
+ cacheInfo: $("cacheInfo"), getWeightsBtn: $("getWeightsBtn"), clearCacheBtn: $("clearCacheBtn"),
502
+ probe: $("probe"), mStep: $("mStep"), mSteps: $("mSteps"), mT: $("mT"),
503
+ mMasks: $("mMasks"), mEta: $("mEta"), mTps: $("mTps"), mMs: $("mMs"),
504
+ spark: $("spark"),
505
+ progressFill: $("progressFill"), finalText: $("finalText"), copyBtn: $("copyBtn"),
506
+ };
507
+ var ORT_CDN = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.27.0/dist/";
508
+
509
+ var env = {
510
+ tokenizer: null, vocabSize: 32001, session: null, engine: "loading", cancelled: false,
511
+ modelBuf: null, backend: "auto", engineName: "-", threads: 1,
512
+ hasWebGpuBundle: false, idbOk: true,
513
+ cacheState: "checking", cachedSize: 0, cachedSavedAt: 0, modelSize: 0,
514
+ stepTimes: [], lastNumSteps: 64,
515
+ };
516
+
517
+ function setStatus(html) {
518
+ els.status.innerHTML = html;
519
+ }
520
+
521
+ function fmtMB(n) { return (n / 1e6).toFixed(0) + " MB"; }
522
+
523
+ /* ================= IndexedDB weight cache ================= */
524
+ var IDB_NAME = "metadiffusion-cache", IDB_STORE = "weights", IDB_VER = 1;
525
+
526
+ function openDB() {
527
+ return new Promise(function(ok, no) {
528
+ var rq = indexedDB.open(IDB_NAME, IDB_VER);
529
+ rq.onupgradeneeded = function() {
530
+ var db = rq.result;
531
+ if (!db.objectStoreNames.contains(IDB_STORE)) db.createObjectStore(IDB_STORE);
532
+ };
533
+ rq.onsuccess = function() { ok(rq.result); };
534
+ rq.onerror = function() { no(rq.error); };
535
+ });
536
+ }
537
+ function idbGet(key) {
538
+ return openDB().then(function(db) {
539
+ return new Promise(function(ok, no) {
540
+ var rq = db.transaction(IDB_STORE, "readonly").objectStore(IDB_STORE).get(key);
541
+ rq.onsuccess = function() { ok(rq.result); };
542
+ rq.onerror = function() { no(rq.error); };
543
+ });
544
+ });
545
+ }
546
+ function idbPut(key, val) {
547
+ return openDB().then(function(db) {
548
+ return new Promise(function(ok, no) {
549
+ var tx = db.transaction(IDB_STORE, "readwrite");
550
+ tx.objectStore(IDB_STORE).put(val, key);
551
+ tx.oncomplete = function() { ok(); };
552
+ tx.onerror = function() { no(tx.error); };
553
+ });
554
+ });
555
+ }
556
+ function idbDel(key) {
557
+ return openDB().then(function(db) {
558
+ return new Promise(function(ok, no) {
559
+ var tx = db.transaction(IDB_STORE, "readwrite");
560
+ tx.objectStore(IDB_STORE).delete(key);
561
+ tx.oncomplete = function() { ok(); };
562
+ tx.onerror = function() { no(tx.error); };
563
+ });
564
+ });
565
+ }
566
+ // 'meta' is a tiny record checked on every load; 'model' is the big ArrayBuffer,
567
+ // only read when the user explicitly loads from cache.
568
+ function getCachedMeta() { return idbGet("meta"); }
569
+ function getWeightsBuffer() { return idbGet("model"); }
570
+ function putCached(buf) {
571
+ return idbPut("model", buf).then(function() {
572
+ return idbPut("meta", { size: buf.byteLength, savedAt: Date.now() });
573
+ });
574
+ }
575
+ function clearCached() {
576
+ return idbDel("model").then(function() { return idbDel("meta"); });
577
+ }
578
+
579
+ /* ================= cache UI state machine ================= */
580
+ function updateCacheUI() {
581
+ var info = els.cacheInfo, btn = els.getWeightsBtn, clr = els.clearCacheBtn;
582
+ var canClear = env.idbOk && (env.cacheState === "cached" || env.cacheState === "ready");
583
+ clr.style.display = canClear ? "" : "none";
584
+
585
+ switch (env.cacheState) {
586
+ case "checking":
587
+ info.textContent = "Checking local cache\u2026";
588
+ btn.style.display = "none";
589
+ break;
590
+ case "absent":
591
+ info.innerHTML = env.idbOk
592
+ ? "Not cached." + (env.modelSize ? " ~" + fmtMB(env.modelSize) + " download." : "")
593
+ : "Local cache unavailable here; weights re-download each visit.";
594
+ btn.style.display = "";
595
+ btn.textContent = "Download weights" + (env.modelSize ? " (" + fmtMB(env.modelSize) + ")" : "");
596
+ break;
597
+ case "cached":
598
+ var when = env.cachedSavedAt ? " \u00b7 saved " + new Date(env.cachedSavedAt).toLocaleDateString() : "";
599
+ info.innerHTML = "Cached locally" + (env.cachedSize ? " (" + fmtMB(env.cachedSize) + ")" : "") + when + ". Not loaded yet.";
600
+ btn.style.display = "";
601
+ btn.textContent = "Load from cache";
602
+ break;
603
+ case "downloading":
604
+ info.textContent = "Downloading\u2026";
605
+ btn.style.display = "none";
606
+ break;
607
+ case "loading":
608
+ info.textContent = "Building inference session\u2026";
609
+ btn.style.display = "none";
610
+ break;
611
+ case "ready":
612
+ info.innerHTML = "Loaded on <b>" + env.engineName + "</b>.";
613
+ btn.style.display = "none";
614
+ break;
615
+ }
616
+ }
617
+
618
+ /* ================= ORT helpers ================= */
619
+ function loadScript(src) {
620
+ return new Promise(function(ok, no) {
621
+ var s = document.createElement("script");
622
+ s.src = src;
623
+ s.onload = function() { ok(); };
624
+ s.onerror = function() { no(new Error("failed to load " + src)); };
625
+ document.head.appendChild(s);
626
+ });
627
+ }
628
+
629
+ function engineLabel() {
630
+ var base = env.engineName;
631
+ if (base.indexOf("wasm") >= 0) base += " \u00b7 " + env.threads + "t";
632
+ return "engine: " + base;
633
+ }
634
+
635
+ async function detectGpu() {
636
+ if (!navigator.gpu) { els.badgeGpu.textContent = "gpu: not available"; return; }
637
+ try {
638
+ var adapter = await navigator.gpu.requestAdapter();
639
+ if (!adapter) { els.badgeGpu.textContent = "gpu: no adapter"; return; }
640
+ var info = adapter.info || (adapter.requestAdapterInfo ? await adapter.requestAdapterInfo() : null);
641
+ var label = "";
642
+ if (info) label = info.description || [info.vendor, info.architecture].filter(Boolean).join(" ");
643
+ els.badgeGpu.textContent = "gpu: " + (label || "available");
644
+ } catch (e) {
645
+ els.badgeGpu.textContent = "gpu: available";
646
+ }
647
+ }
648
+
649
+ async function createSession(backend) {
650
+ var buf = env.modelBuf;
651
+ if (backend === "wasm") {
652
+ var s = await ort.InferenceSession.create(buf, { executionProviders: ["wasm"] });
653
+ env.engineName = "wasm";
654
+ return s;
655
+ }
656
+ if (backend === "webgpu") {
657
+ if (!env.hasWebGpuBundle) throw new Error("WebGPU backend not loaded (no navigator.gpu, or page not in a secure context)");
658
+ var s2 = await ort.InferenceSession.create(buf, { executionProviders: ["webgpu"] });
659
+ env.engineName = "webgpu";
660
+ return s2;
661
+ }
662
+ // auto
663
+ if (env.hasWebGpuBundle) {
664
+ try {
665
+ var s3 = await ort.InferenceSession.create(buf, { executionProviders: ["webgpu", "wasm"] });
666
+ env.engineName = "webgpu+wasm";
667
+ return s3;
668
+ } catch (e) {
669
+ console.warn("webgpu session init failed, using wasm:", e);
670
+ var s4 = await ort.InferenceSession.create(buf, { executionProviders: ["wasm"] });
671
+ env.engineName = "wasm";
672
+ return s4;
673
+ }
674
+ }
675
+ var s5 = await ort.InferenceSession.create(buf, { executionProviders: ["wasm"] });
676
+ env.engineName = "wasm";
677
+ return s5;
678
+ }
679
+
680
+ async function headSize(url) {
681
+ try {
682
+ var r = await fetch(url, { method: "HEAD" });
683
+ var cl = r.headers.get("content-length");
684
+ return cl ? Number(cl) : 0;
685
+ } catch (e) { return 0; }
686
+ }
687
+
688
+ async function fetchWithProgress(url) {
689
+ var res = await fetch(url);
690
+ if (!res.ok) throw new Error("HTTP " + res.status + " for " + url);
691
+ var total = Number(res.headers.get("content-length")) || 0;
692
+ if (!total || !res.body) return res.arrayBuffer();
693
+ var reader = res.body.getReader();
694
+ var chunks = [];
695
+ var received = 0;
696
+ while (true) {
697
+ var r = await reader.read();
698
+ if (r.done) break;
699
+ chunks.push(r.value);
700
+ received += r.value.length;
701
+ els.dlfill.style.width = Math.min(100, (received / total) * 100).toFixed(1) + "%";
702
+ setStatus("Downloading model weights\u2026 " + (received / 1e6).toFixed(0) + " / " + (total / 1e6).toFixed(0) + " MB");
703
+ }
704
+ var buf = new Uint8Array(received);
705
+ var off = 0;
706
+ for (var i = 0; i < chunks.length; i++) { buf.set(chunks[i], off); off += chunks[i].length; }
707
+ return buf.buffer;
708
+ }
709
+
710
+ /* ================= weight acquisition (user-initiated) ================= */
711
+ async function buildSessionFromBuffer() {
712
+ env.cacheState = "loading"; updateCacheUI();
713
+ setStatus("Initializing runtime (" + env.backend + ")\u2026");
714
+ env.session = await createSession(env.backend);
715
+ els.badgeEngine.textContent = engineLabel();
716
+ env.engine = "ready";
717
+ env.cacheState = "ready"; updateCacheUI();
718
+ els.runBtn.disabled = false;
719
+
720
+ var note = "";
721
+ if (env.engineName.indexOf("webgpu") >= 0) note = " GPU active.";
722
+ else if (navigator.gpu) note = " <span style='color:#7d8590'>(webgpu present but session fell back to wasm)</span>";
723
+ if (!window.crossOriginIsolated) note += " <span style='color:#555b66'>(serve with COOP/COEP for multi-threaded wasm)</span>";
724
+ setStatus('<span class="ok">Ready.</span> ' + env.engineName + '.' + note);
725
+ }
726
+
727
+ async function downloadWeights() {
728
+ env.cacheState = "downloading"; updateCacheUI();
729
+ els.dlfill.style.width = "0%";
730
+ setStatus("Downloading model weights\u2026");
731
+ try {
732
+ var buf = await fetchWithProgress("model.onnx");
733
+ env.modelBuf = buf;
734
+ els.dlfill.style.width = "100%";
735
+ if (env.idbOk) {
736
+ setStatus("Saving to local cache\u2026");
737
+ try {
738
+ await putCached(buf);
739
+ env.cachedSize = buf.byteLength;
740
+ env.cachedSavedAt = Date.now();
741
+ } catch (e2) {
742
+ console.warn("cache save failed:", e2);
743
+ env.idbOk = false;
744
+ }
745
+ }
746
+ await buildSessionFromBuffer();
747
+ } catch (e) {
748
+ console.error(e);
749
+ env.cacheState = "absent"; updateCacheUI();
750
+ setStatus('<span class="err">Download failed: ' + e.message + '</span>');
751
+ }
752
+ }
753
+
754
+ async function loadFromCache() {
755
+ env.cacheState = "loading"; updateCacheUI();
756
+ setStatus("Reading weights from local cache\u2026");
757
+ try {
758
+ var buf = await getWeightsBuffer();
759
+ if (!buf) {
760
+ env.cacheState = "absent"; updateCacheUI();
761
+ setStatus("Cache entry missing. Download the weights instead.");
762
+ return;
763
+ }
764
+ env.modelBuf = buf;
765
+ await buildSessionFromBuffer();
766
+ } catch (e) {
767
+ console.error(e);
768
+ env.cacheState = "absent"; updateCacheUI();
769
+ setStatus('<span class="err">Cache load failed: ' + e.message + '</span>');
770
+ }
771
+ }
772
+
773
+ els.getWeightsBtn.addEventListener("click", function() {
774
+ if (env.cacheState === "absent") downloadWeights();
775
+ else if (env.cacheState === "cached") loadFromCache();
776
+ });
777
+
778
+ els.clearCacheBtn.addEventListener("click", async function() {
779
+ try { await clearCached(); } catch (e) { console.warn(e); }
780
+ env.modelBuf = null;
781
+ env.session = null;
782
+ env.engine = "awaiting-weights";
783
+ env.cacheState = "absent";
784
+ env.cachedSize = 0; env.cachedSavedAt = 0;
785
+ els.runBtn.disabled = true;
786
+ els.badgeEngine.textContent = "engine: \u2014";
787
+ els.modelSize = await headSize("model.onnx");
788
+ updateCacheUI();
789
+ setStatus("Cache cleared. Download the weights to continue.");
790
+ });
791
+
792
+ /* ================= init (no auto weight download) ================= */
793
+ async function loadModel() {
794
+ detectGpu();
795
+
796
+ var gpuPresent = !!navigator.gpu;
797
+ if (!gpuPresent) {
798
+ var wg = document.querySelector('#backendSeg button[data-backend="webgpu"]');
799
+ if (wg) { wg.disabled = true; wg.title = "WebGPU unavailable (needs https or localhost)"; }
800
+ }
801
+
802
+ setStatus("Loading tokenizer\u2026");
803
+ var tokRes = await fetch("tokenizer.json");
804
+ env.tokenizer = new ByteLevelTokenizer(await tokRes.json());
805
+ env.vocabSize = env.tokenizer.idToToken.length + 1;
806
+ els.badgeWeights.textContent = "tokenizer: ok";
807
+
808
+ env.hasWebGpuBundle = false;
809
+ setStatus("Loading ONNX runtime (" + (gpuPresent ? "webgpu+wasm" : "wasm") + ")\u2026");
810
+ try {
811
+ if (gpuPresent) {
812
+ await loadScript(ORT_CDN + "ort.webgpu.min.js");
813
+ env.hasWebGpuBundle = true;
814
+ } else {
815
+ await loadScript(ORT_CDN + "ort.min.js");
816
+ }
817
+ } catch (e1) {
818
+ console.warn("primary ORT bundle failed, falling back to wasm build:", e1);
819
+ await loadScript(ORT_CDN + "ort.min.js");
820
+ env.hasWebGpuBundle = false;
821
+ }
822
+ if (typeof ort === "undefined") throw new Error("onnxruntime-web failed to load from CDN");
823
+
824
+ ort.env.wasm.wasmPaths = ORT_CDN;
825
+ if (window.crossOriginIsolated) {
826
+ env.threads = Math.min(navigator.hardwareConcurrency || 4, 8);
827
+ ort.env.wasm.numThreads = env.threads;
828
+ } else {
829
+ env.threads = 1;
830
+ }
831
+
832
+ // Check the cache (cheap meta read only). Do NOT download automatically.
833
+ env.cacheState = "checking"; updateCacheUI();
834
+ try {
835
+ await openDB();
836
+ env.idbOk = true;
837
+ } catch (e) {
838
+ env.idbOk = false;
839
+ }
840
+
841
+ els.runBtn.disabled = true;
842
+ env.engine = "awaiting-weights";
843
+
844
+ if (env.idbOk) {
845
+ var meta = await getCachedMeta();
846
+ if (meta && meta.size) {
847
+ env.cachedSize = meta.size;
848
+ env.cachedSavedAt = meta.savedAt || 0;
849
+ env.cacheState = "cached";
850
+ updateCacheUI();
851
+ setStatus("Weights are cached. <b>Load from cache</b> to start (no download).");
852
+ return;
853
+ }
854
+ }
855
+
856
+ env.cacheState = "absent";
857
+ env.modelSize = await headSize("model.onnx");
858
+ updateCacheUI();
859
+ setStatus(env.idbOk
860
+ ? "Weights not cached. Download them once; they'll be stored locally for next time."
861
+ : "Local cache unavailable here. Download the weights to begin.");
862
+ }
863
+
864
+ /* ================= backend switcher ================= */
865
+ document.querySelectorAll("#backendSeg button").forEach(function(btn) {
866
+ btn.addEventListener("click", async function() {
867
+ if (running || btn.disabled || btn.classList.contains("active")) return;
868
+ var prev = document.querySelector("#backendSeg .active");
869
+ if (prev) prev.classList.remove("active");
870
+ btn.classList.add("active");
871
+ env.backend = btn.dataset.backend;
872
+
873
+ if (!env.modelBuf) {
874
+ setStatus("Backend set to <b>" + env.backend + "</b> \u2014 applies when weights are loaded.");
875
+ return;
876
+ }
877
+ env.engine = "loading";
878
+ setStatus("Switching backend to <b>" + env.backend + "</b>\u2026");
879
+ try {
880
+ env.session = await createSession(env.backend);
881
+ els.badgeEngine.textContent = engineLabel();
882
+ env.engine = "ready";
883
+ env.cacheState = "ready"; updateCacheUI();
884
+ setStatus('<span class="ok">Ready.</span> Running on <b>' + env.engineName + '</b>.');
885
+ } catch (e) {
886
+ console.warn(e);
887
+ env.engine = "ready";
888
+ setStatus('<span class="err">' + env.backend + ' failed: ' + e.message + '</span> \u2014 still on ' + env.engineName + '.');
889
+ }
890
+ });
891
+ });
892
+
893
+ /* ================= live view ================= */
894
+ function renderTokens(state) {
895
+ var tok = env.tokenizer;
896
+ var ids = state.inputIds;
897
+ var frag = document.createDocumentFragment();
898
+ var fresh = new Set();
899
+ if (state.prevMasked) {
900
+ for (var i = 0; i < ids.length; i++) {
901
+ if (state.prevMasked[i] && ids[i] !== MD_MASK_TOKEN) fresh.add(i);
902
+ }
903
+ }
904
+ var prevMasked = new Array(ids.length);
905
+ for (var i = 0; i < ids.length; i++) {
906
+ var id = ids[i];
907
+ var span = document.createElement("span");
908
+ if (id === MD_MASK_TOKEN) {
909
+ span.className = "tok mask";
910
+ span.textContent = "\u25ae";
911
+ prevMasked[i] = true;
912
+ } else {
913
+ var isPrompt = i < state.promptLen;
914
+ span.className = "tok" + (isPrompt ? " prompt" : "") + (fresh.has(i) ? " fresh" : "");
915
+ span.textContent = tok.idToToken[id] !== undefined ? tok.decode([id], true) : "\ufffd";
916
+ prevMasked[i] = false;
917
+ }
918
+ frag.appendChild(span);
919
+ }
920
+ els.probe.replaceChildren(frag);
921
+ return prevMasked;
922
+ }
923
+
924
+ function updateMeta(state) {
925
+ els.mStep.textContent = String(state.step + 1);
926
+ els.mSteps.textContent = String(state.numSteps);
927
+ els.mT.textContent = state.t.toFixed(3);
928
+ els.mMasks.textContent = String(state.nMasked);
929
+ els.progressFill.style.width = ((state.step + 1) / state.numSteps * 100).toFixed(1) + "%";
930
+ var perStep = state.elapsed / (state.step + 1);
931
+ var left = (state.numSteps - state.step - 1) * perStep;
932
+ els.mEta.textContent = left > 0 ? "~" + Math.round(left / 1000) + "s left" : "";
933
+ }
934
+
935
+ function updatePerf(state) {
936
+ var times = env.stepTimes;
937
+ if (times.length) {
938
+ var recent = times.slice(-5);
939
+ var avg = 0;
940
+ for (var i = 0; i < recent.length; i++) avg += recent[i];
941
+ avg /= recent.length;
942
+ els.mMs.textContent = avg.toFixed(1) + " ms";
943
+ }
944
+ var genLen = state.inputIds.length - state.promptLen;
945
+ var revealed = genLen - state.nMasked;
946
+ var tps = state.elapsed > 0 ? revealed / (state.elapsed / 1000) : 0;
947
+ els.mTps.textContent = tps.toFixed(1);
948
+ }
949
+
950
+ function drawSparkline() {
951
+ var c = els.spark;
952
+ var dpr = window.devicePixelRatio || 1;
953
+ var w = c.clientWidth || 300, h = c.clientHeight || 40;
954
+ if (c.width !== Math.round(w * dpr) || c.height !== Math.round(h * dpr)) {
955
+ c.width = Math.round(w * dpr);
956
+ c.height = Math.round(h * dpr);
957
+ }
958
+ var ctx = c.getContext("2d");
959
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
960
+ ctx.clearRect(0, 0, w, h);
961
+
962
+ var times = env.stepTimes;
963
+ if (!times.length) return;
964
+
965
+ var slots = Math.max(times.length, env.lastNumSteps);
966
+ var slot = (w - 8) / slots;
967
+ var bw = Math.max(1.5, Math.min(slot - 1.5, 8));
968
+ var max = 0;
969
+ for (var i = 0; i < times.length; i++) if (times[i] > max) max = times[i];
970
+ if (max <= 0) max = 1;
971
+
972
+ for (var i = 0; i < times.length; i++) {
973
+ var bh = Math.max(2, (times[i] / max) * (h - 8));
974
+ ctx.fillStyle = (i === times.length - 1) ? "#9cc4ee" : "#4f7bb0";
975
+ ctx.fillRect(4 + i * slot, h - 4 - bh, bw, bh);
976
+ }
977
+ }
978
+
979
+ /* ================= run ================= */
980
+ var running = false;
981
+
982
+ async function run() {
983
+ if (running) return;
984
+ if (env.engine !== "ready") { setStatus('Weights not loaded yet \u2014 download or load from cache first.'); return; }
985
+
986
+ var params = {
987
+ prompt: els.prompt.value,
988
+ seqLen: parseInt(els.seqLen.value, 10),
989
+ numSteps: parseInt(els.numSteps.value, 10),
990
+ temperature: parseFloat(els.temperature.value),
991
+ repPenalty: parseFloat(els.repPenalty.value),
992
+ };
993
+ running = true;
994
+ env.cancelled = false;
995
+ env.stepTimes = [];
996
+ env.lastNumSteps = params.numSteps;
997
+ els.runBtn.disabled = true;
998
+ els.stopBtn.disabled = false;
999
+ els.finalText.style.display = "none";
1000
+ els.copyBtn.style.display = "none";
1001
+ els.probe.replaceChildren();
1002
+ els.progressFill.style.width = "0%";
1003
+ els.mTps.textContent = "\u2013";
1004
+ els.mMs.textContent = "\u2013";
1005
+ drawSparkline();
1006
+
1007
+ var session = env.session;
1008
+ var prevMasked = null;
1009
+ var lastElapsed = 0;
1010
+
1011
+ var runStep = async function(inputIds, positionIds, t, logitsOut) {
1012
+ var seq = inputIds.length;
1013
+ var idsBig = new BigInt64Array(seq);
1014
+ for (var i = 0; i < seq; i++) idsBig[i] = BigInt(inputIds[i]);
1015
+ var posBig = new BigInt64Array(seq);
1016
+ for (var i = 0; i < seq; i++) posBig[i] = BigInt(positionIds[i]);
1017
+ var feeds = {
1018
+ input_ids: new ort.Tensor("int64", idsBig, [1, seq]),
1019
+ timesteps: new ort.Tensor("float32", new Float32Array([t]), [1]),
1020
+ position_ids: new ort.Tensor("int64", posBig, [1, seq]),
1021
+ };
1022
+ var out = await session.run(feeds);
1023
+ logitsOut.set(out.logits.data);
1024
+ };
1025
+
1026
+ try {
1027
+ var state;
1028
+ var gen = denoise(env, Object.assign({}, params, { runStep: runStep }));
1029
+ var first = true;
1030
+ while (true) {
1031
+ var result = await gen.next();
1032
+ if (result.done) break;
1033
+ state = result.value;
1034
+ if (first) { els.mSteps.textContent = String(state.numSteps); first = false; }
1035
+ state.prevMasked = prevMasked;
1036
+ prevMasked = renderTokens(state);
1037
+ updateMeta(state);
1038
+
1039
+ var stepMs = state.elapsed - lastElapsed;
1040
+ lastElapsed = state.elapsed;
1041
+ env.stepTimes.push(stepMs);
1042
+ drawSparkline();
1043
+ updatePerf(state);
1044
+
1045
+ setStatus("Denoising on <b>" + env.engineName + "</b>\u2026 step <b>" + (state.step + 1) + "</b>/" + state.numSteps + " \u00b7 noise <b>" + state.t.toFixed(2) + "</b>");
1046
+ await new Promise(function(r) { setTimeout(r, 0); });
1047
+ }
1048
+ var finalText = env.tokenizer.decode(state.inputIds, true);
1049
+ els.finalText.textContent = finalText;
1050
+ els.finalText.style.display = "block";
1051
+ els.copyBtn.style.display = "inline-block";
1052
+ setStatus('<span class="ok">Done.</span> ' + env.engineName + ' \u00b7 ' + els.mMs.textContent + '/step avg \u00b7 denoised text below.');
1053
+ } catch (e) {
1054
+ if (e && e.message === "cancelled") setStatus("Stopped.");
1055
+ else { console.error(e); setStatus('<span class="err">Error: ' + e.message + '</span>'); }
1056
+ } finally {
1057
+ running = false;
1058
+ els.runBtn.disabled = false;
1059
+ els.stopBtn.disabled = true;
1060
+ }
1061
+ }
1062
+
1063
+ els.runBtn.addEventListener("click", run);
1064
+ els.stopBtn.addEventListener("click", function() { env.cancelled = true; });
1065
+
1066
+ function bindSlider(input, label, fmt) {
1067
+ var upd = function() { label.textContent = fmt ? fmt(parseFloat(input.value)) : input.value; };
1068
+ input.addEventListener("input", upd);
1069
+ upd();
1070
+ }
1071
+ bindSlider(els.seqLen, els.vSeq, function(v) { return String(v); });
1072
+ bindSlider(els.numSteps, els.vSteps, function(v) { return String(v); });
1073
+ bindSlider(els.temperature, els.vTemp, function(v) { return v.toFixed(2); });
1074
+ bindSlider(els.repPenalty, els.vRep, function(v) { return v.toFixed(2); });
1075
+
1076
+ document.querySelectorAll("#presets button").forEach(function(btn) {
1077
+ btn.addEventListener("click", function() {
1078
+ var p = JSON.parse(btn.dataset.preset);
1079
+ els.prompt.value = p.prompt;
1080
+ els.seqLen.value = p.seqLen; els.vSeq.textContent = p.seqLen;
1081
+ els.numSteps.value = p.numSteps; els.vSteps.textContent = p.numSteps;
1082
+ els.temperature.value = p.temperature; els.vTemp.textContent = p.temperature.toFixed(2);
1083
+ els.repPenalty.value = p.repPenalty; els.vRep.textContent = p.repPenalty.toFixed(2);
1084
+ });
1085
+ });
1086
+
1087
+ els.copyBtn.addEventListener("click", async function() {
1088
+ try {
1089
+ await navigator.clipboard.writeText(els.finalText.textContent);
1090
+ els.copyBtn.textContent = "Copied!";
1091
+ setTimeout(function() { els.copyBtn.textContent = "Copy output"; }, 1500);
1092
+ } catch (e) {}
1093
+ });
1094
+
1095
+ loadModel().catch(function(e) {
1096
+ console.error(e);
1097
+ setStatus('<span class="err">Failed to initialize: ' + e.message + '</span>');
1098
+ });
1099
+ </script>
1100
+ </body>
1101
+ </html>