| <!doctype html>
|
| <html lang="en">
|
| <head>
|
| <meta charset="utf-8" />
|
| <meta name="viewport" content="width=device-width, initial-scale=1" />
|
| <title>Shit Detector</title>
|
| <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.22.0/dist/ort.min.js"></script>
|
| <script>
|
| window.SHIT_DETECTOR_MODEL_REPO = "cstria0106/shit-detector";
|
| window.SHIT_DETECTOR_ORT_VERSION = "1.22.0";
|
| </script>
|
| <script type="module" src="./app.js"></script>
|
| <style>
|
| :root {
|
| color-scheme: light dark;
|
| --bg: #f5f7f8;
|
| --panel: #ffffff;
|
| --text: #182024;
|
| --muted: #637078;
|
| --line: #d8dee2;
|
| --accent: #b52727;
|
| --accent-soft: #f7e3e3;
|
| --ok: #1f7a4d;
|
| --shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
|
| }
|
|
|
| @media (prefers-color-scheme: dark) {
|
| :root {
|
| --bg: #111518;
|
| --panel: #181f23;
|
| --text: #eff4f6;
|
| --muted: #a6b1b8;
|
| --line: #303b41;
|
| --accent: #ff6b5f;
|
| --accent-soft: #331f20;
|
| --ok: #62d394;
|
| --shadow: none;
|
| }
|
| }
|
|
|
| * {
|
| box-sizing: border-box;
|
| }
|
|
|
| body {
|
| margin: 0;
|
| min-height: 100vh;
|
| font-family:
|
| Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
| sans-serif;
|
| background: var(--bg);
|
| color: var(--text);
|
| }
|
|
|
| main {
|
| width: min(1120px, calc(100% - 32px));
|
| margin: 0 auto;
|
| padding: 28px 0;
|
| }
|
|
|
| header {
|
| display: flex;
|
| align-items: end;
|
| justify-content: space-between;
|
| gap: 18px;
|
| margin-bottom: 20px;
|
| }
|
|
|
| h1 {
|
| margin: 0;
|
| font-size: 28px;
|
| line-height: 1.1;
|
| font-weight: 760;
|
| }
|
|
|
| .model-link {
|
| color: var(--muted);
|
| font-size: 14px;
|
| text-decoration: none;
|
| }
|
|
|
| .surface {
|
| display: grid;
|
| grid-template-columns: minmax(0, 1.05fr) minmax(300px, 0.95fr);
|
| gap: 16px;
|
| }
|
|
|
| .panel {
|
| background: var(--panel);
|
| border: 1px solid var(--line);
|
| border-radius: 8px;
|
| box-shadow: var(--shadow);
|
| }
|
|
|
| .dropzone {
|
| min-height: 520px;
|
| display: grid;
|
| place-items: center;
|
| padding: 18px;
|
| position: relative;
|
| overflow: hidden;
|
| }
|
|
|
| .dropzone.dragging {
|
| border-color: var(--accent);
|
| background: var(--accent-soft);
|
| }
|
|
|
| .empty {
|
| display: grid;
|
| gap: 14px;
|
| justify-items: center;
|
| text-align: center;
|
| color: var(--muted);
|
| }
|
|
|
| .upload-button {
|
| border: 1px solid var(--line);
|
| background: var(--text);
|
| color: var(--panel);
|
| border-radius: 6px;
|
| padding: 10px 14px;
|
| font-weight: 700;
|
| cursor: pointer;
|
| }
|
|
|
| .preview {
|
| width: 100%;
|
| max-height: 620px;
|
| object-fit: contain;
|
| display: none;
|
| }
|
|
|
| .side {
|
| display: grid;
|
| grid-template-rows: auto 1fr;
|
| gap: 16px;
|
| }
|
|
|
| .result {
|
| padding: 18px;
|
| }
|
|
|
| .label-row {
|
| display: flex;
|
| align-items: center;
|
| justify-content: space-between;
|
| gap: 12px;
|
| margin-bottom: 18px;
|
| }
|
|
|
| .label {
|
| font-size: 30px;
|
| line-height: 1;
|
| font-weight: 820;
|
| }
|
|
|
| .badge {
|
| border-radius: 999px;
|
| padding: 7px 10px;
|
| font-size: 13px;
|
| font-weight: 750;
|
| background: var(--accent-soft);
|
| color: var(--accent);
|
| }
|
|
|
| .badge.ok {
|
| background: rgba(31, 122, 77, 0.14);
|
| color: var(--ok);
|
| }
|
|
|
| .class-scores {
|
| display: grid;
|
| gap: 14px;
|
| margin-bottom: 18px;
|
| }
|
|
|
| .class-score {
|
| display: grid;
|
| gap: 7px;
|
| }
|
|
|
| .class-score-head {
|
| display: flex;
|
| align-items: center;
|
| justify-content: space-between;
|
| gap: 12px;
|
| font-size: 14px;
|
| }
|
|
|
| .class-name {
|
| color: var(--text);
|
| font-weight: 760;
|
| }
|
|
|
| .class-value {
|
| color: var(--muted);
|
| font-variant-numeric: tabular-nums;
|
| }
|
|
|
| .meter {
|
| height: 10px;
|
| border-radius: 999px;
|
| background: color-mix(in srgb, var(--line), transparent 20%);
|
| overflow: hidden;
|
| }
|
|
|
| .meter > div {
|
| height: 100%;
|
| width: 0%;
|
| background: var(--muted);
|
| transition: width 160ms ease;
|
| }
|
|
|
| .meter > div.positive {
|
| background: var(--accent);
|
| }
|
|
|
| .meter > div.negative {
|
| background: var(--ok);
|
| }
|
|
|
| dl {
|
| display: grid;
|
| grid-template-columns: 1fr auto;
|
| gap: 10px 18px;
|
| margin: 0;
|
| font-size: 14px;
|
| }
|
|
|
| dt {
|
| color: var(--muted);
|
| }
|
|
|
| dd {
|
| margin: 0;
|
| font-variant-numeric: tabular-nums;
|
| }
|
|
|
| .status {
|
| padding: 14px 18px;
|
| color: var(--muted);
|
| font-size: 14px;
|
| min-height: 52px;
|
| }
|
|
|
| .details {
|
| padding: 18px;
|
| color: var(--muted);
|
| font-size: 14px;
|
| line-height: 1.55;
|
| }
|
|
|
| input[type="file"] {
|
| position: absolute;
|
| width: 1px;
|
| height: 1px;
|
| opacity: 0;
|
| }
|
|
|
| @media (max-width: 780px) {
|
| main {
|
| width: min(100% - 20px, 1120px);
|
| padding: 16px 0;
|
| }
|
|
|
| header,
|
| .surface {
|
| display: block;
|
| }
|
|
|
| h1 {
|
| font-size: 24px;
|
| margin-bottom: 8px;
|
| }
|
|
|
| .dropzone {
|
| min-height: 360px;
|
| margin-bottom: 12px;
|
| }
|
|
|
| .side {
|
| gap: 12px;
|
| }
|
| }
|
| </style>
|
| </head>
|
| <body>
|
| <main>
|
| <header>
|
| <h1>Shit Detector</h1>
|
| <a class="model-link" href="https://huggingface.co/cstria0106/shit-detector" target="_blank" rel="noreferrer">
|
| cstria0106/shit-detector
|
| </a>
|
| </header>
|
|
|
| <section class="surface">
|
| <label class="panel dropzone" id="dropzone">
|
| <input id="fileInput" type="file" accept="image/*" />
|
| <img id="preview" class="preview" alt="" />
|
| <div id="empty" class="empty">
|
| <button class="upload-button" type="button" id="chooseButton">Choose image</button>
|
| <span>Drop an image here or choose one from this device.</span>
|
| </div>
|
| </label>
|
|
|
| <aside class="side">
|
| <section class="panel result">
|
| <div class="label-row">
|
| <div class="label" id="label">Ready</div>
|
| <div class="badge ok" id="badge">local</div>
|
| </div>
|
| <div class="class-scores" aria-label="Class probabilities">
|
| <div class="class-score">
|
| <div class="class-score-head">
|
| <span class="class-name">shit</span>
|
| <span class="class-value" id="shitProbability">-</span>
|
| </div>
|
| <div class="meter" aria-hidden="true"><div class="positive" id="shitMeterFill"></div></div>
|
| </div>
|
| <div class="class-score">
|
| <div class="class-score-head">
|
| <span class="class-name">not_shit</span>
|
| <span class="class-value" id="notShitProbability">-</span>
|
| </div>
|
| <div class="meter" aria-hidden="true"><div class="negative" id="notShitMeterFill"></div></div>
|
| </div>
|
| </div>
|
| <dl>
|
| <dt>Confidence</dt>
|
| <dd id="confidence">-</dd>
|
| <dt>Threshold</dt>
|
| <dd id="threshold">-</dd>
|
| <dt>Runtime</dt>
|
| <dd id="runtime">WASM</dd>
|
| </dl>
|
| </section>
|
| <section class="panel status" id="status">Loading ONNX Runtime Web...</section>
|
| <section class="panel details">
|
| Images are processed in your browser. The model file is fetched from Hugging Face, then inference runs locally with ONNX Runtime Web.
|
| </section>
|
| </aside>
|
| </section>
|
| </main>
|
| </body>
|
| </html>
|
|
|