Spaces:
Sleeping
Sleeping
| <html lang="en" data-theme="light"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Tweet Sentiment Classifier</title> | |
| <link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.10/dist/full.min.css" rel="stylesheet" type="text/css" /> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}"> | |
| <script src="https://unpkg.com/htmx.org@1.9.10"></script> | |
| </head> | |
| <body class="min-h-screen text-slate-700"> | |
| <main class="flex min-h-screen items-start justify-center px-4 py-8 sm:py-12 lg:items-center lg:max-h-screen"> | |
| <div class="w-full max-w-5xl"> | |
| <div class="rounded-3xl bg-white/95 shadow-xl ring-1 ring-slate-100 backdrop-blur lg:max-h-[90vh] lg:overflow-y-auto"> | |
| <div class="grid gap-8 p-6 md:p-8 lg:grid-cols-3 lg:p-10"> | |
| <section class="space-y-6 lg:col-span-2"> | |
| <header class="text-center lg:text-left"> | |
| <h1 class="text-3xl font-semibold text-slate-800"> | |
| <img class="mr-2 inline h-8 w-8 align-middle" src="../{{ url_for('static', filename='images/bird.png') }}" alt="bird icon"> | |
| Tweet Sentiment Classifier | |
| </h1> | |
| </header> | |
| <form | |
| class="space-y-4" | |
| hx-post="/classify" | |
| hx-target="#result" | |
| hx-trigger="input changed delay:300ms from:textarea[name='text'], submit" | |
| hx-sync="this:replace" | |
| > | |
| <div> | |
| <textarea | |
| id="text-input" | |
| name="text" | |
| rows="5" | |
| placeholder="Type or paste a tweet..." | |
| class="textarea textarea-bordered w-full resize-none rounded-2xl border border-slate-200 bg-slate-50/80 p-4 text-base text-slate-700 shadow-sm focus:border-sky-400 focus:outline-none focus:ring-2 focus:ring-sky-200" | |
| required>{{ default_preset }}</textarea> | |
| </div> | |
| <div class="flex flex-wrap items-center gap-3"> | |
| <button type="submit" class="btn btn-primary rounded-full border-none bg-sky-500 px-6 text-white shadow hover:bg-sky-600"> | |
| Analyze Sentiment | |
| </button> | |
| <span class="text-sm text-slate-500"> | |
| Auto-analyze on | |
| </span> | |
| </div> | |
| </form> | |
| </section> | |
| <aside class="rounded-2xl border border-slate-200 bg-slate-50/80 p-6 shadow-sm lg:self-start"> | |
| <h2 class="text-lg font-semibold text-slate-800"> | |
| Examples | |
| </h2> | |
| <ul class="mt-5 space-y-2"> | |
| {% for preset in presets %} | |
| <li> | |
| <button | |
| type="button" | |
| class="btn btn-sm w-full justify-start rounded-xl border border-transparent bg-white/70 text-left font-normal text-slate-600 shadow-sm transition hover:border-sky-200 hover:bg-sky-50" | |
| data-preset-index="{{ loop.index0 }}" | |
| > | |
| {{ preset }} | |
| </button> | |
| </li> | |
| {% endfor %} | |
| </ul> | |
| </aside> | |
| <section class="lg:col-span-3"> | |
| <div id="result" class="space-y-4">{{ initial_results|safe }}</div> | |
| </section> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <script> | |
| const presets = {{ presets | tojson }}; | |
| const textarea = document.querySelector('#text-input'); | |
| const presetButtons = document.querySelectorAll('[data-preset-index]'); | |
| function setPreset(text, activeButton, options = {}) { | |
| if (!textarea) { | |
| return; | |
| } | |
| const { trigger = true, focus = true } = options; | |
| textarea.value = text; | |
| if (focus) { | |
| textarea.focus({ preventScroll: true }); | |
| } | |
| presetButtons.forEach((button) => { | |
| button.classList.remove('btn-active', 'border-sky-300', 'bg-sky-100', 'text-sky-800'); | |
| button.setAttribute('aria-pressed', 'false'); | |
| }); | |
| if (activeButton) { | |
| activeButton.classList.add('btn-active', 'border-sky-300', 'bg-sky-100', 'text-sky-800'); | |
| activeButton.setAttribute('aria-pressed', 'true'); | |
| } | |
| if (trigger) { | |
| textarea.dispatchEvent(new Event('input', { bubbles: true })); | |
| textarea.dispatchEvent(new Event('change', { bubbles: true })); | |
| } | |
| } | |
| presetButtons.forEach((button) => { | |
| button.addEventListener('click', () => { | |
| const index = Number.parseInt(button.dataset.presetIndex, 10); | |
| const presetText = presets[index] ?? ''; | |
| setPreset(presetText, button); | |
| }); | |
| }); | |
| window.addEventListener('load', () => { | |
| if (!textarea) { | |
| return; | |
| } | |
| const firstButton = presetButtons[0] ?? null; | |
| const initialText = presets.length ? presets[0] : (textarea.value || ''); | |
| setPreset(initialText, firstButton, { trigger: false, focus: false }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |