Download templates/index.html from CyberAl/Image_Classification_Parfait_TOLEFO: direct link, hf CLI and curl.
- Browser
- Download file 31.1 kB
-
https://huggingface.co/spaces/CyberAl/Image_Classification_Parfait_TOLEFO/resolve/main/templates/index.html
- Command line
-
hf download hf://spaces/CyberAl/Image_Classification_Parfait_TOLEFO/templates/index.html
-
curl -L -o index.html https://huggingface.co/spaces/CyberAl/Image_Classification_Parfait_TOLEFO/resolve/main/templates/index.html
31.1 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <title>SCENEIQ — Intel Scene Classifier | Enhanced Readability</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Rajdhani:wght@400;500;600;700&family=Exo+2:wght@300;400;600&display=swap" rel="stylesheet"> | |
| <style> | |
| /* ══ TOKENS — SCALED FOR READABILITY ══ */ | |
| :root { | |
| --g0:#000000; --g1:#071007; --g2:#0f1a0f; --g3:#152215; | |
| --g4:#1f331f; | |
| --green:#00ff41; --green2:#00dd33; --green3:#00aa22; | |
| --green-dim:#00661a; --green-glow:rgba(0,255,65,0.2); | |
| --border:rgba(0,255,65,0.22); --border2:rgba(0,255,65,0.45); | |
| --muted:rgba(0,255,65,0.55); | |
| --ff-mono:'Share Tech Mono',monospace; | |
| --ff-head:'Rajdhani',sans-serif; | |
| --ff-body:'Exo 2',sans-serif; | |
| --r:8px; | |
| } | |
| *, *::before, *::after { box-sizing:border-box; margin:0; padding:0; } | |
| html, body { | |
| height: 100vh; | |
| overflow: hidden; | |
| font-size: 18px; /* base boost for readability */ | |
| } | |
| body { | |
| background: var(--g0); | |
| color: var(--green); | |
| font-family: var(--ff-body); | |
| display: flex; | |
| flex-direction: column; | |
| line-height: 1.45; | |
| } | |
| /* ── Background layers (enhanced depth) ── */ | |
| #plexus-bg { | |
| position: fixed; inset: 0; z-index: 0; pointer-events: none; | |
| } | |
| body::before { | |
| content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none; | |
| background-image: | |
| linear-gradient(rgba(0,255,65,0.045) 1px, transparent 1px), | |
| linear-gradient(90deg, rgba(0,255,65,0.045) 1px, transparent 1px); | |
| background-size: 40px 40px; | |
| } | |
| body::after { | |
| content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none; | |
| background: repeating-linear-gradient( | |
| 0deg, transparent, transparent 2px, | |
| rgba(0,0,0,0.1) 2px, rgba(0,0,0,0.1) 5px | |
| ); | |
| } | |
| /* ══ WRAPPER — full height, spacious inner layout ══ */ | |
| .wrapper { | |
| width: 100%; | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 0 1.5rem; | |
| height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.25rem; | |
| position: relative; | |
| z-index: 2; | |
| } | |
| /* ══ HEADER — bold, crisp ══ */ | |
| header { | |
| flex-shrink: 0; | |
| border-bottom: 2px solid var(--border); | |
| padding: 1.2rem 0 0.8rem 0; | |
| display: flex; align-items: center; justify-content: space-between; | |
| animation: fadeIn .5s ease both; | |
| } | |
| .logo { display:flex; align-items:center; gap:1rem; } | |
| .logo-icon { | |
| width:50px; height:50px; border:1.8px solid var(--green); | |
| display:grid; place-items:center; font-size:1.4rem; | |
| box-shadow: 0 0 18px var(--green-glow), inset 0 0 12px var(--green-glow); | |
| animation: pulse-box 3s ease-in-out infinite; | |
| background: rgba(0,255,65,0.02); | |
| } | |
| @keyframes pulse-box { | |
| 0%,100%{box-shadow:0 0 14px var(--green-glow),inset 0 0 8px var(--green-glow);} | |
| 50%{box-shadow:0 0 28px rgba(0,255,65,.4),inset 0 0 18px rgba(0,255,65,.2);} | |
| } | |
| .logo-text { | |
| font-family:var(--ff-head); font-size:2.5rem; font-weight:700; | |
| letter-spacing:.12em; text-shadow:0 0 22px rgba(0,255,65,.7); | |
| } | |
| .logo-text span { color:var(--green3); } | |
| .header-right { display:flex; align-items:center; gap:1.6rem; } | |
| .status-dot { | |
| display:flex; align-items:center; gap:.9rem; | |
| font-family:var(--ff-mono); font-size:0.85rem; color:var(--muted); | |
| letter-spacing: 0.5px; | |
| } | |
| .dot { | |
| width:9px; height:9px; border-radius:50%; | |
| background:var(--green); box-shadow:0 0 12px var(--green); | |
| animation:blink 2s step-end infinite; | |
| } | |
| @keyframes blink{0%,100%{opacity:1}50%{opacity:.2}} | |
| .version { font-family:var(--ff-mono); font-size:.85rem; color:var(--green-dim); letter-spacing:.12em; } | |
| /* ── CLASSES BAR — bigger pills, readable ── */ | |
| .classes-bar { | |
| flex-shrink: 0; | |
| display: flex; align-items: center; gap: 1rem; | |
| padding: 0.7rem 0; | |
| border-bottom: 1px solid var(--border); | |
| animation: fadeIn .6s ease .1s both; | |
| } | |
| .cs-label { | |
| font-family:var(--ff-mono); font-size:0.85rem; | |
| color:var(--green-dim); letter-spacing:.2em; | |
| white-space:nowrap; font-weight:500; | |
| } | |
| .cs-pills { display:flex; gap:0.6rem; flex-wrap:wrap; } | |
| .cs-pill { | |
| font-family:var(--ff-mono); font-size:0.85rem; | |
| padding:0.3rem 0.9rem; | |
| border:1px solid var(--border); border-radius:20px; | |
| color:var(--green-dim); transition:all .2s; cursor:default; | |
| background: rgba(0,20,0,0.4); | |
| backdrop-filter: blur(1px); | |
| } | |
| .cs-pill:hover { border-color:var(--green2); color:var(--green); background:rgba(0,255,65,0.08); } | |
| /* ══ MAIN GRID — generous spacing, bigger cards ══ */ | |
| .main-grid { | |
| flex: 1; | |
| min-height: 0; | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 1.5rem; | |
| padding: 1rem 0 0.75rem 0; | |
| animation: fadeIn .7s ease .15s both; | |
| } | |
| /* ══ PANELS — elevated, more padding, readable containers ══ */ | |
| .panel { | |
| background: rgba(10, 21, 10, 0.92); | |
| backdrop-filter: blur(2px); | |
| border: 1px solid var(--border); | |
| border-radius: var(--r); | |
| padding: 1.5rem 1.6rem; | |
| position: relative; | |
| overflow: hidden; | |
| display: flex; | |
| flex-direction: column; | |
| box-shadow: 0 8px 20px rgba(0,0,0,0.6), 0 0 0 1px rgba(0,255,65,0.05) inset; | |
| transition: box-shadow 0.2s; | |
| } | |
| .panel:hover { | |
| box-shadow: 0 10px 28px rgba(0,0,0,0.7), 0 0 0 1px rgba(0,255,65,0.15) inset; | |
| } | |
| .panel::before { | |
| content:''; position:absolute; top:0; left:0; right:0; height:3px; | |
| background:linear-gradient(90deg,transparent,var(--green2),transparent); | |
| } | |
| .panel-title { | |
| flex-shrink: 0; | |
| font-family:var(--ff-mono); font-size:0.9rem; | |
| letter-spacing:.25em; color:var(--green3); | |
| text-transform:uppercase; margin-bottom:1rem; | |
| display:flex; align-items:center; gap:.8rem; | |
| font-weight: 500; | |
| } | |
| .panel-title::after { content:''; flex:1; height:1.5px; background:var(--border); } | |
| /* scrollable zones — smooth, no visible scrollbar */ | |
| .panel-scroll { | |
| flex: 1; | |
| min-height: 0; | |
| overflow-y: auto; | |
| overflow-x: hidden; | |
| scrollbar-width: thin; | |
| scrollbar-color: var(--green3) var(--g2); | |
| padding-right: 3px; | |
| } | |
| .panel-scroll::-webkit-scrollbar { width: 5px; } | |
| .panel-scroll::-webkit-scrollbar-track { background: var(--g2); border-radius: 4px; } | |
| .panel-scroll::-webkit-scrollbar-thumb { background: var(--green-dim); border-radius: 4px; } | |
| /* ── MODEL SELECTOR (larger cards) ── */ | |
| .model-grid { | |
| display:grid; grid-template-columns:1fr 1fr; gap:1rem; | |
| margin-bottom:1.4rem; | |
| } | |
| .model-card { | |
| border:1.5px solid var(--border); border-radius:12px; | |
| padding:0.9rem 0.8rem; cursor:pointer; transition:all .2s; | |
| background:var(--g1); | |
| display:flex; flex-direction:column; gap:0.4rem; | |
| } | |
| .model-card:hover { border-color:var(--green2); background:rgba(0,255,65,0.08); transform:translateY(-2px); } | |
| .model-card.active { | |
| border-color:var(--green); background:rgba(0,255,65,.1); | |
| box-shadow:0 0 20px rgba(0,255,65,.18),inset 0 0 12px rgba(0,255,65,.06); | |
| } | |
| .model-card.active .mc-name { color:var(--green); text-shadow:0 0 5px var(--green-glow);} | |
| .mc-icon { font-size:1.6rem; } | |
| .mc-name { font-family:var(--ff-head); font-weight:700; font-size:1.3rem; color:var(--green2); transition:color .2s; } | |
| .mc-sub { font-family:var(--ff-mono); font-size:0.75rem; color:var(--muted); letter-spacing: -0.2px;} | |
| /* ── INPUT TABS (bigger click area) ── */ | |
| .input-tabs { | |
| display:flex; margin-bottom:1rem; | |
| border:1px solid var(--border); border-radius:12px; overflow:hidden; | |
| } | |
| .tab-btn { | |
| flex:1; padding:0.7rem 0.5rem; background:var(--g1); | |
| border:none; color:var(--muted); font-family:var(--ff-mono); | |
| font-size:0.85rem; letter-spacing:.1em; cursor:pointer; | |
| transition:all .2s; text-transform:uppercase; font-weight:500; | |
| } | |
| .tab-btn:hover { background:var(--g3); color:var(--green2); } | |
| .tab-btn.active { | |
| background:rgba(0,255,65,.12); color:var(--green); | |
| box-shadow:inset 0 -3px 0 var(--green); | |
| } | |
| /* ── DROP ZONE — taller, clearer text ── */ | |
| .drop-zone { | |
| border:2px dashed var(--border2); border-radius:16px; | |
| height: 210px; | |
| display:flex; flex-direction:column; | |
| align-items:center; justify-content:center; | |
| gap:.7rem; cursor:pointer; | |
| transition:all .25s; background:var(--g1); | |
| position:relative; overflow:hidden; | |
| } | |
| .drop-zone:hover,.drop-zone.drag { | |
| border-color:var(--green); background:rgba(0,255,65,.08); | |
| box-shadow:0 0 28px rgba(0,255,65,.12); | |
| } | |
| .drop-zone.has-img .dz-ph { display:none; } | |
| #preview-img { | |
| position:absolute; inset:0; width:100%; height:100%; | |
| object-fit:cover; display:none; | |
| border-radius:14px; opacity:.9; | |
| } | |
| .drop-zone.has-img #preview-img { display:block; } | |
| .dz-corner { position:absolute; width:14px; height:14px; border-color:var(--green2); border-style:solid; } | |
| .dz-corner.tl{top:10px;left:10px;border-width:2px 0 0 2px;} | |
| .dz-corner.tr{top:10px;right:10px;border-width:2px 2px 0 0;} | |
| .dz-corner.bl{bottom:10px;left:10px;border-width:0 0 2px 2px;} | |
| .dz-corner.br{bottom:10px;right:10px;border-width:0 2px 2px 0;} | |
| .dz-ph { display:flex; flex-direction:column; align-items:center; gap:.5rem; pointer-events:none; } | |
| .dz-icon { | |
| width:48px; height:48px; border:1.5px solid var(--border2); | |
| border-radius:50%; display:grid; place-items:center; | |
| font-size:1.5rem; color:var(--green2); | |
| } | |
| .dz-ph p { font-family:var(--ff-mono); font-size:0.95rem; color:var(--muted); font-weight:500; } | |
| .dz-ph em { font-family:var(--ff-mono); font-size:0.75rem; color:var(--green-dim); font-style:normal; } | |
| #file-input { display:none; } | |
| /* ── URL INPUT — bigger controls ── */ | |
| .url-input-wrap { display:none; flex-direction:column; gap:.8rem; } | |
| .url-input-wrap.show { display:flex; } | |
| .url-field { | |
| display:flex; align-items:center; | |
| border:1.5px solid var(--border2); border-radius:12px; | |
| background:var(--g1); overflow:hidden; | |
| } | |
| .url-prefix { font-family:var(--ff-mono); font-size:0.85rem; color:var(--green2); padding:0 0.8rem; white-space:nowrap; border-right:1.5px solid var(--border); } | |
| .url-input { | |
| flex:1; background:transparent; border:none; outline:none; | |
| color:var(--green); font-family:var(--ff-mono); font-size:0.9rem; | |
| padding:0.8rem 1rem; caret-color:var(--green); | |
| } | |
| .url-input::placeholder { color:var(--green-dim); font-size:0.8rem; } | |
| .url-load-btn { | |
| padding:0.7rem 1rem; background:rgba(0,255,65,.12); border:none; | |
| border-left:1.5px solid var(--border); | |
| color:var(--green2); font-family:var(--ff-mono); font-size:0.85rem; | |
| cursor:pointer; transition:background .2s; white-space:nowrap; | |
| font-weight:600; | |
| } | |
| .url-load-btn:hover { background:rgba(0,255,65,.25); } | |
| .url-preview { | |
| width:100%; height:130px; object-fit:cover; | |
| border-radius:12px; border:1px solid var(--border); display:none; | |
| } | |
| .url-preview.show { display:block; } | |
| /* ── CLASSIFY BTN — bold, interactive ── */ | |
| .classify-btn { | |
| flex-shrink: 0; | |
| width:100%; margin-top:1rem; | |
| padding:0.9rem 0.5rem; | |
| background:linear-gradient(135deg,var(--green3),var(--green-dim)); | |
| border:1.5px solid var(--green3); border-radius:60px; | |
| color:var(--green); font-family:var(--ff-head); | |
| font-size:1.3rem; font-weight:700; letter-spacing:.12em; | |
| cursor:pointer; position:relative; overflow:hidden; | |
| transition:all .25s; text-transform:uppercase; | |
| box-shadow:0 2px 8px rgba(0,0,0,0.3); | |
| } | |
| .classify-btn::before { | |
| content:''; position:absolute; top:-2px; left:-100%; | |
| width:60%; height:calc(100% + 4px); | |
| background:linear-gradient(90deg,transparent,rgba(0,255,65,.25),transparent); | |
| transform:skewX(-15deg); | |
| } | |
| .classify-btn.loading::before { animation:sweep 1.2s linear infinite; } | |
| @keyframes sweep { to { left:150%; } } | |
| .classify-btn:not(:disabled):hover { | |
| background:linear-gradient(135deg,var(--green2),var(--green3)); | |
| box-shadow:0 0 30px rgba(0,255,65,.45); transform:translateY(-2px); | |
| border-color: var(--green); | |
| } | |
| .classify-btn:disabled { opacity:.4; cursor:not-allowed; transform: none; } | |
| /* ══ RIGHT PANEL — results, big class display, crisp probabilities ══ */ | |
| .result-panel-inner { | |
| flex: 1; | |
| min-height: 0; | |
| overflow-y: auto; | |
| overflow-x: hidden; | |
| scrollbar-width: thin; | |
| padding-right: 4px; | |
| } | |
| .result-panel-inner::-webkit-scrollbar { width: 5px; } | |
| .result-waiting { | |
| display:flex; flex-direction:column; | |
| align-items:flex-start; justify-content:center; | |
| flex:1; gap:.8rem; padding:0.6rem 0; | |
| } | |
| .result-waiting .rw-title { font-family:var(--ff-head); font-size:1.5rem; font-weight:600; color:var(--green2); letter-spacing:-0.2px; } | |
| .result-waiting .rw-sub { font-family:var(--ff-mono); font-size:1rem; color:var(--green-dim); line-height:1.9; } | |
| .terminal-cursor { | |
| display:inline-block; width:9px; height:18px; | |
| background:var(--green); margin-left:4px; | |
| animation:blink-c .8s step-end infinite; vertical-align:middle; | |
| } | |
| @keyframes blink-c{0%,100%{opacity:1}50%{opacity:0}} | |
| .result-content { display:none; flex-direction:column; gap:1.4rem; } | |
| .result-content.show { display:flex; animation:scanIn .45s ease; } | |
| @keyframes scanIn { | |
| from{opacity:0;clip-path:inset(0 0 100% 0)} | |
| to{opacity:1;clip-path:inset(0 0 0% 0)} | |
| } | |
| .rc-header { display:flex; flex-direction:column; gap:.5rem; } | |
| .rc-label { font-family:var(--ff-mono); font-size:0.7rem; letter-spacing:.25em; color:var(--green3); font-weight:500; } | |
| .rc-class { | |
| font-family:var(--ff-head); font-size:4rem; font-weight:800; | |
| letter-spacing:-0.02em; line-height:1.1; color:var(--green); | |
| text-shadow:0 0 28px rgba(0,255,65,.7); | |
| word-break: break-word; | |
| } | |
| .rc-conf { font-family:var(--ff-mono); font-size:1rem; color:var(--muted); margin-top:.2rem; } | |
| .rc-conf strong { color:var(--green2); font-size:1.2rem; } | |
| .conf-track { | |
| height:8px; background:var(--g4); border-radius:40px; overflow:hidden; margin-top:.6rem; | |
| } | |
| .conf-fill { | |
| height:100%; | |
| background:linear-gradient(90deg,var(--green3),var(--green)); | |
| border-radius:40px; width:0%; | |
| transition:width 1s cubic-bezier(.2,.9,.3,1.1); | |
| box-shadow:0 0 12px var(--green); | |
| } | |
| .rc-divider { height:1.5px; background:var(--border); flex-shrink:0; margin:.2rem 0; } | |
| .prob-list { display:flex; flex-direction:column; gap:1rem; margin-top:0.3rem; } | |
| .prob-row { display:flex; flex-direction:column; gap:.3rem; } | |
| .prob-meta { display:flex; justify-content:space-between; align-items:baseline; } | |
| .prob-name { | |
| font-family:var(--ff-body); font-size:1rem; font-weight:500; | |
| color:var(--muted); display:flex; align-items:center; gap:.5rem; text-transform:capitalize; | |
| } | |
| .prob-row.top .prob-name { color:var(--green); font-weight:700; text-shadow:0 0 3px rgba(0,255,65,0.3);} | |
| .prob-pct { font-family:var(--ff-mono); font-size:0.85rem; color:var(--green-dim); font-weight:500; } | |
| .prob-row.top .prob-pct { color:var(--green2); font-weight:600; } | |
| .prob-track { height:7px; background:var(--g4); border-radius:40px; overflow:hidden; } | |
| .prob-fill { | |
| height:100%; background:var(--green-dim); | |
| border-radius:40px; width:0%; | |
| transition:width .8s cubic-bezier(.3,.8,.4,1); | |
| } | |
| .prob-row.top .prob-fill { | |
| background:linear-gradient(90deg,var(--green3),var(--green)); | |
| box-shadow:0 0 10px rgba(0,255,65,.5); | |
| } | |
| .result-error { | |
| display:none; padding:1rem; | |
| border:1px solid rgba(255,70,70,.5); border-radius:12px; | |
| background:rgba(255,30,30,.08); | |
| font-family:var(--ff-mono); font-size:0.85rem; color:#ff7a7a; line-height:1.7; | |
| } | |
| .result-error.show { display:block; animation:fadeIn .3s ease; } | |
| /* ══ FOOTER BAR — readable, relaxed ══ */ | |
| .footer-bar { | |
| flex-shrink: 0; | |
| border-top: 1.5px solid var(--border); | |
| padding: 0.5rem 0; | |
| display: flex; justify-content: space-between; align-items: center; | |
| animation: fadeIn .8s ease .25s both; | |
| } | |
| .footer-bar p { font-family:var(--ff-mono); font-size:0.8rem; color:var(--green-dim); letter-spacing:0.3px; } | |
| @keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}} | |
| </style> | |
| </head> | |
| <body> | |
| <canvas id="plexus-bg"></canvas> | |
| <div class="wrapper"> | |
| <!-- HEADER --> | |
| <header> | |
| <div class="logo"> | |
| <div class="logo-icon">⬡</div> | |
| <div class="logo-text">SCENE<span>IQ</span></div> | |
| </div> | |
| <div class="header-right"> | |
| <div class="status-dot"><div class="dot"></div> SYSTEM ONLINE</div> | |
| <div class="version">v4.0 · PARFAIT</div> | |
| </div> | |
| </header> | |
| <!-- CLASSES BAR --> | |
| <div class="classes-bar"> | |
| <span class="cs-label">CLASSES //</span> | |
| <div class="cs-pills"> | |
| <span class="cs-pill">🏙 buildings</span> | |
| <span class="cs-pill">🌲 forest</span> | |
| <span class="cs-pill">🧊 glacier</span> | |
| <span class="cs-pill">⛰ mountain</span> | |
| <span class="cs-pill">🌊 sea</span> | |
| <span class="cs-pill">🛣 street</span> | |
| </div> | |
| </div> | |
| <!-- MAIN GRID --> | |
| <div class="main-grid"> | |
| <!-- LEFT : inputs --> | |
| <div class="panel"> | |
| <div class="panel-title">01 // MODEL_SELECT</div> | |
| <div class="panel-scroll"> | |
| <div class="model-grid"> | |
| <div class="model-card active" data-fw="pytorch" onclick="selectModel(this)"> | |
| <span class="mc-icon">⚡</span> | |
| <span class="mc-name">PyTorch</span> | |
| <span class="mc-sub">CNN_Torch · .pth</span> | |
| </div> | |
| <div class="model-card" data-fw="tensorflow" onclick="selectModel(this)"> | |
| <span class="mc-icon">🧠</span> | |
| <span class="mc-name">TensorFlow</span> | |
| <span class="mc-sub">CNN_TF · .keras</span> | |
| </div> | |
| </div> | |
| <div class="panel-title">02 // INPUT_IMAGE</div> | |
| <div class="input-tabs"> | |
| <button class="tab-btn active" onclick="switchTab('file', this)">↑ FILE_UPLOAD</button> | |
| <button class="tab-btn" onclick="switchTab('url', this)">⬡ IMAGE_URL</button> | |
| </div> | |
| <!-- File drop zone --> | |
| <div id="tab-file"> | |
| <div class="drop-zone" id="drop-zone" onclick="document.getElementById('file-input').click()"> | |
| <div class="dz-corner tl"></div><div class="dz-corner tr"></div> | |
| <div class="dz-corner bl"></div><div class="dz-corner br"></div> | |
| <img id="preview-img" src="" alt="preview"/> | |
| <div class="dz-ph"> | |
| <div class="dz-icon">↑</div> | |
| <p>Drop image here or click</p> | |
| <em>JPG · PNG · WEBP · GIF</em> | |
| </div> | |
| </div> | |
| <input type="file" id="file-input" accept="image/*"/> | |
| </div> | |
| <!-- URL input --> | |
| <div id="tab-url" class="url-input-wrap"> | |
| <div class="url-field"> | |
| <span class="url-prefix">URL://</span> | |
| <input type="text" class="url-input" id="url-input" | |
| placeholder="https://example.com/image.jpg"/> | |
| <button class="url-load-btn" onclick="loadFromUrl()">LOAD</button> | |
| </div> | |
| <img id="url-preview" class="url-preview" src="" alt="URL preview"/> | |
| </div> | |
| <div class="panel-title" style="margin-top:1rem;">03 // ANALYZE</div> | |
| </div><!-- /panel-scroll --> | |
| <button class="classify-btn" id="classify-btn" disabled onclick="classify()"> | |
| RUN CLASSIFICATION → | |
| </button> | |
| </div> | |
| <!-- RIGHT : results --> | |
| <div class="panel"> | |
| <div class="panel-title">04 // SCAN_RESULTS</div> | |
| <div class="result-panel-inner"> | |
| <div class="result-waiting" id="result-waiting"> | |
| <div class="rw-title">AWAITING INPUT</div> | |
| <div class="rw-sub"> | |
| > select_model()<br> | |
| > load_image() <span class="terminal-cursor"></span><br> | |
| > predict() | |
| </div> | |
| </div> | |
| <div class="result-content" id="result-content"> | |
| <div class="rc-header"> | |
| <div class="rc-label">// PREDICTED_CLASS</div> | |
| <div class="rc-class" id="rc-class">—</div> | |
| <div class="rc-conf">confidence : <strong id="rc-conf">—</strong></div> | |
| <div class="conf-track"><div class="conf-fill" id="conf-fill"></div></div> | |
| </div> | |
| <div class="rc-divider"></div> | |
| <div> | |
| <div class="panel-title" style="margin-bottom:.8rem;">// CLASS_SCORES</div> | |
| <div class="prob-list" id="prob-list"></div> | |
| </div> | |
| </div> | |
| <div class="result-error" id="result-error"></div> | |
| </div><!-- /result-panel-inner --> | |
| </div> | |
| </div><!-- /main-grid --> | |
| <!-- FOOTER BAR --> | |
| <div class="footer-bar"> | |
| <p>SCENEIQ · Intel Image Classification · CNN PyTorch & TensorFlow</p> | |
| <p>by PARFAIT · seed=42 · reproducible</p> | |
| </div> | |
| </div><!-- /wrapper --> | |
| <script> | |
| const EMOJIS = {buildings:"🏙",forest:"🌲",glacier:"🧊",mountain:"⛰",sea:"🌊",street:"🛣"}; | |
| const CLASSES = ["buildings","forest","glacier","mountain","sea","street"]; | |
| let selectedFw = "pytorch"; | |
| let selectedFile = null; | |
| let urlImageReady = false; | |
| let currentTab = "file"; | |
| function selectModel(card) { | |
| document.querySelectorAll(".model-card").forEach(c => c.classList.remove("active")); | |
| card.classList.add("active"); | |
| selectedFw = card.dataset.fw; | |
| } | |
| function switchTab(tab, btn) { | |
| currentTab = tab; | |
| document.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active")); | |
| btn.classList.add("active"); | |
| document.getElementById("tab-file").style.display = tab === "file" ? "block" : "none"; | |
| document.getElementById("tab-url").classList.toggle("show", tab === "url"); | |
| updateBtn(); | |
| } | |
| const dropZone = document.getElementById("drop-zone"); | |
| const fileInput = document.getElementById("file-input"); | |
| fileInput.addEventListener("change", () => { if (fileInput.files[0]) loadFile(fileInput.files[0]); }); | |
| dropZone.addEventListener("dragover", e => { e.preventDefault(); dropZone.classList.add("drag"); }); | |
| dropZone.addEventListener("dragleave", () => dropZone.classList.remove("drag")); | |
| dropZone.addEventListener("drop", e => { | |
| e.preventDefault(); dropZone.classList.remove("drag"); | |
| const f = e.dataTransfer.files[0]; | |
| if (f && f.type.startsWith("image/")) loadFile(f); | |
| }); | |
| function loadFile(file) { | |
| selectedFile = file; | |
| const reader = new FileReader(); | |
| reader.onload = e => { | |
| document.getElementById("preview-img").src = e.target.result; | |
| dropZone.classList.add("has-img"); | |
| }; | |
| reader.readAsDataURL(file); | |
| resetResult(); updateBtn(); | |
| } | |
| function loadFromUrl() { | |
| const url = document.getElementById("url-input").value.trim(); | |
| if (!url) return; | |
| const preview = document.getElementById("url-preview"); | |
| preview.onload = () => { preview.classList.add("show"); urlImageReady = true; resetResult(); updateBtn(); }; | |
| preview.onerror = () => { preview.classList.remove("show"); urlImageReady = false; updateBtn(); }; | |
| preview.src = url; | |
| } | |
| document.getElementById("url-input").addEventListener("keydown", e => { if (e.key === "Enter") loadFromUrl(); }); | |
| function updateBtn() { | |
| const ready = (currentTab === "file" && selectedFile) || (currentTab === "url" && urlImageReady); | |
| document.getElementById("classify-btn").disabled = !ready; | |
| } | |
| async function classify() { | |
| const btn = document.getElementById("classify-btn"); | |
| btn.disabled = true; btn.textContent = "SCANNING…"; btn.classList.add("loading"); | |
| const form = new FormData(); | |
| form.append("model", selectedFw); | |
| if (currentTab === "file" && selectedFile) form.append("image", selectedFile); | |
| else form.append("image_url", document.getElementById("url-input").value.trim()); | |
| try { | |
| const res = await fetch("/predict", { method: "POST", body: form }); | |
| const data = await res.json(); | |
| if (data.error) throw new Error(data.error); | |
| showResult(data); | |
| } catch(err) { | |
| showError(err.message); | |
| } finally { | |
| btn.textContent = "RUN CLASSIFICATION →"; btn.classList.remove("loading"); btn.disabled = false; | |
| } | |
| } | |
| function showResult(data) { | |
| document.getElementById("result-waiting").style.display = "none"; | |
| document.getElementById("result-error").classList.remove("show"); | |
| const pct = Math.round(data.confidence * 100); | |
| document.getElementById("rc-class").textContent = | |
| (EMOJIS[data.class]||"") + " " + data.class.charAt(0).toUpperCase() + data.class.slice(1); | |
| document.getElementById("rc-conf").textContent = pct + "%"; | |
| const content = document.getElementById("result-content"); | |
| content.classList.remove("show"); void content.offsetWidth; content.classList.add("show"); | |
| setTimeout(() => { document.getElementById("conf-fill").style.width = pct + "%"; }, 60); | |
| const list = document.getElementById("prob-list"); | |
| list.innerHTML = ""; | |
| const sorted = [...CLASSES].sort((a,b) => data.probabilities[b]-data.probabilities[a]); | |
| sorted.forEach((cls, i) => { | |
| const p = Math.round(data.probabilities[cls] * 100); | |
| const top = cls === data.class; | |
| const row = document.createElement("div"); | |
| row.className = "prob-row" + (top ? " top" : ""); | |
| row.innerHTML = ` | |
| <div class="prob-meta"> | |
| <span class="prob-name">${EMOJIS[cls]||""} ${cls}</span> | |
| <span class="prob-pct" id="pct-${cls}">0%</span> | |
| </div> | |
| <div class="prob-track"><div class="prob-fill" id="bar-${cls}"></div></div>`; | |
| list.appendChild(row); | |
| setTimeout(() => { | |
| document.getElementById("bar-"+cls).style.width = p+"%"; | |
| document.getElementById("pct-"+cls).textContent = p+"%"; | |
| }, 100 + i*50); | |
| }); | |
| } | |
| function showError(msg) { | |
| document.getElementById("result-waiting").style.display = "none"; | |
| document.getElementById("result-content").classList.remove("show"); | |
| const err = document.getElementById("result-error"); | |
| err.textContent = "ERROR > " + msg; err.classList.add("show"); | |
| } | |
| function resetResult() { | |
| document.getElementById("result-waiting").style.display = "flex"; | |
| document.getElementById("result-content").classList.remove("show"); | |
| document.getElementById("result-error").classList.remove("show"); | |
| document.getElementById("conf-fill").style.width = "0%"; | |
| } | |
| document.getElementById("tab-file").style.display = "block"; | |
| </script> | |
| <!-- PLEXUS BACKGROUND (enhanced) --> | |
| <script> | |
| (function(){ | |
| const canvas = document.getElementById('plexus-bg'); | |
| const ctx = canvas.getContext('2d'); | |
| const NODE_COLOR='rgba(0,255,65,', LINE_COLOR='rgba(0,255,65,'; | |
| const MAX_DIST=160, MOUSE_DIST=220, ATTRACT_FORCE=0.012, NODE_COUNT=110, SPEED=0.4; | |
| let W, H, nodes=[], mouse={x:-9999,y:-9999}; | |
| window.addEventListener('mousemove', e=>{mouse.x=e.clientX;mouse.y=e.clientY;}); | |
| window.addEventListener('mouseleave',()=>{mouse.x=-9999;mouse.y=-9999;}); | |
| function resize(){W=canvas.width=window.innerWidth;H=canvas.height=window.innerHeight;} | |
| function Node(){this.x=Math.random()*W;this.y=Math.random()*H;this.vx=(Math.random()-.5)*SPEED;this.vy=(Math.random()-.5)*SPEED;this.r=Math.random()*2+1.5;} | |
| function init(){nodes=[];for(let i=0;i<NODE_COUNT;i++)nodes.push(new Node());} | |
| function draw(){ | |
| ctx.clearRect(0,0,W,H); | |
| for(const n of nodes){ | |
| const dxM=mouse.x-n.x,dyM=mouse.y-n.y,dM=Math.sqrt(dxM*dxM+dyM*dyM); | |
| if(dM<MOUSE_DIST&&dM>0){const f=(1-dM/MOUSE_DIST)*ATTRACT_FORCE;n.vx+=dxM/dM*f;n.vy+=dyM/dM*f;} | |
| const spd=Math.sqrt(n.vx*n.vx+n.vy*n.vy),maxSpd=SPEED*3; | |
| if(spd>maxSpd){n.vx=(n.vx/spd)*maxSpd;n.vy=(n.vy/spd)*maxSpd;} | |
| if(dM>=MOUSE_DIST){n.vx*=.996;n.vy*=.996;const s=Math.sqrt(n.vx*n.vx+n.vy*n.vy);if(s<SPEED*.3){n.vx+=(Math.random()-.5)*.06;n.vy+=(Math.random()-.5)*.06;}} | |
| n.x+=n.vx;n.y+=n.vy; | |
| if(n.x<0||n.x>W)n.vx*=-1;if(n.y<0||n.y>H)n.vy*=-1; | |
| } | |
| for(let i=0;i<nodes.length;i++)for(let j=i+1;j<nodes.length;j++){ | |
| const a=nodes[i],b=nodes[j],dx=a.x-b.x,dy=a.y-b.y,d=Math.sqrt(dx*dx+dy*dy); | |
| if(d<MAX_DIST){const al=(1-d/MAX_DIST)*.28;ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.strokeStyle=LINE_COLOR+al+')';ctx.lineWidth=.9;ctx.stroke();} | |
| } | |
| for(const n of nodes){ | |
| const dxM=mouse.x-n.x,dyM=mouse.y-n.y,dM=Math.sqrt(dxM*dxM+dyM*dyM); | |
| if(dM<MOUSE_DIST){const al=(1-dM/MOUSE_DIST)*.35;ctx.beginPath();ctx.moveTo(mouse.x,mouse.y);ctx.lineTo(n.x,n.y);ctx.strokeStyle=LINE_COLOR+al+')';ctx.lineWidth=.7;ctx.stroke();} | |
| } | |
| if(mouse.x>0){ | |
| const g=ctx.createRadialGradient(mouse.x,mouse.y,0,mouse.x,mouse.y,22); | |
| g.addColorStop(0,'rgba(0,255,65,0.45)');g.addColorStop(1,'rgba(0,255,65,0)'); | |
| ctx.beginPath();ctx.arc(mouse.x,mouse.y,22,0,Math.PI*2);ctx.fillStyle=g;ctx.fill(); | |
| ctx.beginPath();ctx.arc(mouse.x,mouse.y,3,0,Math.PI*2);ctx.fillStyle='rgba(0,255,65,0.95)';ctx.fill(); | |
| } | |
| for(const n of nodes){ | |
| const g=ctx.createRadialGradient(n.x,n.y,0,n.x,n.y,n.r*6); | |
| g.addColorStop(0,NODE_COLOR+'0.35)');g.addColorStop(1,NODE_COLOR+'0)'); | |
| ctx.beginPath();ctx.arc(n.x,n.y,n.r*6,0,Math.PI*2);ctx.fillStyle=g;ctx.fill(); | |
| ctx.beginPath();ctx.arc(n.x,n.y,n.r,0,Math.PI*2);ctx.fillStyle=NODE_COLOR+'0.9)';ctx.fill(); | |
| } | |
| requestAnimationFrame(draw); | |
| } | |
| resize();init();draw(); | |
| window.addEventListener('resize',()=>{resize();init();}); | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |
| ``` |