Web UI¶
UI — статический progressive enhancement слой без framework, bundler, CDN и внешних runtime-зависимостей. FastAPI отдаёт три packaged files.
Все рабочие состояния интерфейса на desktop и mobile собраны в галерее реального стенда: там есть Search, Ask с локальной Ollama, loading и ошибка валидации.
- Вход
- API key и текст пользователя
- Выход
- Search/Ask request, безопасно созданные DOM nodes
- Хранение
- API key только в sessionStorage текущей вкладки
- Файлы
- index.html · app.js · styles.css
DOM-структура¶
body
├── header.topbar
│ ├── a.brand
│ └── #health.status-pill
├── main.layout
│ ├── section.hero
│ ├── section.workspace
│ │ ├── .search-panel (key, query, mode, submit)
│ │ └── .side-panel (объяснение + rate state)
│ ├── #notice
│ ├── #answer-card
│ └── .results-section
│ └── #results
└── footer
HTML: web/index.html.
JavaScript flow¶
flowchart TD
CLICK["click / Ctrl+Enter"] --> VALIDATE["query≥3 + API key"]
VALIDATE --> FETCH["fetch /api/v1/{mode}"]
FETCH --> HEADERS["updateRate(headers)"]
FETCH -->|"!ok"| ERROR["parseError → notice"]
FETCH -->|"ok"| JSON["response.json"]
JSON --> DOM["createResult per item"]
JSON -->|"ask"| ANSWER["answerCard.textContent"]
createResult() использует createElement и textContent, а не innerHTML: текст внешнего документа не интерпретируется как markup. Source link получает target=_blank вместе с rel="noopener noreferrer".
submitQuery() управляет loading state через disabled/aria-busy, очищает старый результат, различает items/sources и всегда восстанавливает кнопку в finally.
API key¶
Кнопка «Сохранить в сессии» пишет tenderLensApiKey в sessionStorage. Это удобство, а не secret vault: любой JavaScript той же origin мог бы прочитать значение. Поэтому UI не подключает remote scripts, а production должен использовать строгий CSP и TLS.
Accessibility¶
- semantic
header/main/section/aside/footer; - связанные
labelи form controls; aria-liveдля health/notice;- hidden heading для workspace landmark;
- keyboard shortcut Ctrl+Enter;
- focus styles и responsive stacking;
- цвет не является единственным носителем статуса: рядом есть текст.
CSS¶
styles.css определяет tokens в :root, двухколоночный workspace, компактные result rows и breakpoints 850px/560px. В UI нет скрытого build step: изменение CSS/JS сразу попадает в image через package data.
Почему не React¶
Текущий экран имеет одно состояние запроса и небольшой объём DOM. Framework добавил бы Node toolchain, bundle и dependency surface без пропорциональной пользы. При появлении routing, сложных filters, pagination и shared components решение стоит пересмотреть.
Полный generated-список DOM anchors, JS functions и CSS selectors — Frontend API.