lodestones commited on
Commit
85a4088
verified
1 Parent(s): baf0167

Update tagger_ui/templates/index.html

Browse files
Files changed (1) hide show
  1. tagger_ui/templates/index.html +88 -2
tagger_ui/templates/index.html CHANGED
@@ -202,6 +202,36 @@
202
  .tag-pill:hover { opacity: .8; }
203
  .tag-pill .score { font-size: .66rem; opacity: .7; }
204
  .tag-pill.hidden { display: none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  </style>
206
  </head>
207
  <body>
@@ -239,12 +269,22 @@
239
 
240
  <div id="results-area">
241
 
242
- <!-- image full-width on top -->
243
  <div class="preview-wrap">
244
- <div style="width:100%">
245
  <img id="preview-img" src="" alt="preview" />
246
  <div class="img-meta" id="img-meta"></div>
247
  </div>
 
 
 
 
 
 
 
 
 
 
248
  </div>
249
 
250
  <!-- global copy bar -->
@@ -298,6 +338,48 @@
298
  if (el) el.value = Math.max(1, Math.min(99, parseInt(pct) || 1));
299
  }
300
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  // ---- drag & drop ----
302
  const dz = document.getElementById('drop-zone');
303
  dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('drag-over'); });
@@ -314,6 +396,8 @@
314
  const url = document.getElementById('url-input').value.trim();
315
  if (!url) return;
316
  setPreview(url, url);
 
 
317
  submitFetch(`/tag/url?max_size=${document.getElementById('maxsize-input').value}&url=${encodeURIComponent(url)}`,
318
  { method: 'POST' });
319
  }
@@ -325,6 +409,8 @@
325
  const reader = new FileReader();
326
  reader.onload = e => setPreview(e.target.result, file.name);
327
  reader.readAsDataURL(file);
 
 
328
  submitFetch(`/tag/upload?max_size=${maxSize}`, { method: 'POST', body: fd });
329
  }
330
 
 
202
  .tag-pill:hover { opacity: .8; }
203
  .tag-pill .score { font-size: .66rem; opacity: .7; }
204
  .tag-pill.hidden { display: none; }
205
+
206
+ /* ---- PCA panel ---- */
207
+ .preview-wrap { flex-wrap: wrap; }
208
+ .preview-col { flex: 1 1 0; min-width: 0; }
209
+ .pca-col {
210
+ flex: 1 1 0; min-width: 0;
211
+ display: flex; flex-direction: column; gap: .5rem;
212
+ }
213
+ .pca-label {
214
+ font-size: .72rem; color: var(--muted); text-align: center;
215
+ letter-spacing: .04em; text-transform: uppercase;
216
+ }
217
+ #pca-img {
218
+ border-radius: var(--radius); width: 100%; max-height: 420px;
219
+ object-fit: contain; border: 1px solid var(--border);
220
+ display: block; image-rendering: pixelated;
221
+ }
222
+ #pca-spinner {
223
+ display: none; width: 18px; height: 18px; margin: auto;
224
+ border: 3px solid var(--border); border-top-color: var(--accent);
225
+ border-radius: 50%; animation: spin .7s linear infinite;
226
+ }
227
+ .pca-toggle {
228
+ background: var(--bg); border: 1px solid var(--border);
229
+ border-radius: 6px; color: var(--muted); cursor: pointer;
230
+ font-size: .75rem; padding: .3rem .7rem; align-self: center;
231
+ transition: border-color .15s, color .15s;
232
+ }
233
+ .pca-toggle:hover { border-color: var(--accent); color: var(--text); }
234
+ .pca-toggle.active { border-color: var(--accent); color: #a78bfa; }
235
  </style>
236
  </head>
237
  <body>
 
269
 
270
  <div id="results-area">
271
 
272
+ <!-- image + PCA side by side -->
273
  <div class="preview-wrap">
274
+ <div class="preview-col">
275
  <img id="preview-img" src="" alt="preview" />
276
  <div class="img-meta" id="img-meta"></div>
277
  </div>
278
+ <div class="pca-col" id="pca-col" style="display:none">
279
+ <div class="pca-label">PCA 路 patch features (R=PC1, G=PC2, B=PC3)</div>
280
+ <div id="pca-spinner"></div>
281
+ <img id="pca-img" src="" alt="PCA" style="display:none" />
282
+ </div>
283
+ </div>
284
+
285
+ <!-- PCA toggle -->
286
+ <div style="display:flex;justify-content:flex-end;margin-bottom:.6rem">
287
+ <button class="pca-toggle" id="pca-toggle" onclick="togglePca()">Show PCA</button>
288
  </div>
289
 
290
  <!-- global copy bar -->
 
338
  if (el) el.value = Math.max(1, Math.min(99, parseInt(pct) || 1));
339
  }
340
 
341
+ // ---- PCA state ----
342
+ let _pcaEnabled = false;
343
+ let _lastPcaRequest = null; // { type: 'url'|'file', url?: string, file?: File }
344
+
345
+ function togglePca() {
346
+ _pcaEnabled = !_pcaEnabled;
347
+ const btn = document.getElementById('pca-toggle');
348
+ btn.textContent = _pcaEnabled ? 'Hide PCA' : 'Show PCA';
349
+ btn.classList.toggle('active', _pcaEnabled);
350
+ document.getElementById('pca-col').style.display = _pcaEnabled ? 'flex' : 'none';
351
+ if (_pcaEnabled && _lastPcaRequest) runPca(_lastPcaRequest);
352
+ }
353
+
354
+ function runPca(req) {
355
+ const spinner = document.getElementById('pca-spinner');
356
+ const img = document.getElementById('pca-img');
357
+ spinner.style.display = 'block';
358
+ img.style.display = 'none';
359
+
360
+ const maxSize = document.getElementById('maxsize-input').value;
361
+ let fetchPromise;
362
+ if (req.type === 'url') {
363
+ fetchPromise = fetch(
364
+ `/pca/url?max_size=${maxSize}&url=${encodeURIComponent(req.url)}`,
365
+ { method: 'POST' }
366
+ );
367
+ } else {
368
+ const fd = new FormData();
369
+ fd.append('file', req.file);
370
+ fetchPromise = fetch(`/pca/upload?max_size=${maxSize}`, { method: 'POST', body: fd });
371
+ }
372
+
373
+ fetchPromise
374
+ .then(r => r.ok ? r.blob() : Promise.reject('PCA failed'))
375
+ .then(blob => {
376
+ img.src = URL.createObjectURL(blob);
377
+ img.style.display = 'block';
378
+ })
379
+ .catch(() => { img.style.display = 'none'; })
380
+ .finally(() => { spinner.style.display = 'none'; });
381
+ }
382
+
383
  // ---- drag & drop ----
384
  const dz = document.getElementById('drop-zone');
385
  dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('drag-over'); });
 
396
  const url = document.getElementById('url-input').value.trim();
397
  if (!url) return;
398
  setPreview(url, url);
399
+ _lastPcaRequest = { type: 'url', url };
400
+ if (_pcaEnabled) runPca(_lastPcaRequest);
401
  submitFetch(`/tag/url?max_size=${document.getElementById('maxsize-input').value}&url=${encodeURIComponent(url)}`,
402
  { method: 'POST' });
403
  }
 
409
  const reader = new FileReader();
410
  reader.onload = e => setPreview(e.target.result, file.name);
411
  reader.readAsDataURL(file);
412
+ _lastPcaRequest = { type: 'file', file };
413
+ if (_pcaEnabled) runPca(_lastPcaRequest);
414
  submitFetch(`/tag/upload?max_size=${maxSize}`, { method: 'POST', body: fd });
415
  }
416