Skip to content

feat(web): реализовать рабочую область проверки решения (#125)#233

Merged
ArtVsMark merged 8 commits into
mainfrom
feat/web-workspace-125
Jul 9, 2026
Merged

feat(web): реализовать рабочую область проверки решения (#125)#233
ArtVsMark merged 8 commits into
mainfrom
feat/web-workspace-125

Conversation

@ArtVsMark

Copy link
Copy Markdown
Owner

Summary

Реализация issue #125 (эпик #123) — split-pane workspace раздела «Проверка решений» + раздел «Глоссарий» поверх готового store (#126).

  • web.pyweb/-подпакет (server.py/viewmodels.py/glossary_adapter.py/commands.py/static/) — чистый перенос, публичный API (grade_benchmark/grade_path/run_server) не изменился.
  • Расширенные ErrorCard-поля на /api/grade (case_n/severity/stdin/expected/actual/stderr/exit_code/timeout_s/suggestions/glossary_ids/actions) — аддитивно, старый контракт не ломается.
  • J7: неизвестные исключения из RE-кейсов встают в очередь пополнения глоссария.
  • Новые endpoint'ы: GET /api/glossary, /api/glossary/<id>, /api/glossary/missing, /api/commands.
  • Фронтенд: split-pane (sidebar/result/detail), 5 MVP action cards, command palette (Ctrl+K/⌘K), сценарные кнопки и раздел «Глоссарий» — все три поверхности (палитра/action cards/сценарные кнопки) питаются одним contextTags()/visibleCommands().
  • Два маленьких аддитивных изменения в core/: exit_code в run_single_test, core/glossary.all_entries().
  • Два новых поля GraderConfig: glossary_store, glossary_missing_queue.

Сознательно вне скоупа (design-only / другие issue): Downloader (#186), микро-бенчмарк в web (#187), create_test/compare_solutions, hash-роутинг, экспорт в Glossary-Python, полный a11y-аудит — подробности в docs/web-mvp.md и docs/claude-handoff.md.

Test plan

  • pytest tests/ -x -q --tb=short — зелёные (868 passed; 37 pre-existing subprocess-related failures на этой машине, идентичные на main)
  • ruff check . / ruff format --check . — чисто
  • mypy src/stepik_grader --ignore-missing-imports — чисто
  • Ручная проверка через запущенный сервер (browser preview): AC/WA/RE кейсы, детальная панель, command palette (поиск/стрелки/Enter/Escape+фокус), тумблер темы, поиск/карточка/backlog глоссария, deep-link open_glossary, схлопывание на узком экране
  • CHANGELOG.md — запись под [Unreleased]

🤖 Generated with Claude Code

ArtVsMark and others added 8 commits July 9, 2026 15:16
Pure move, no behavior change: server.py (HTTP handler), viewmodels.py
(grade_path/grade_benchmark/_case_view), static/{index.html,app.css,app.js}
(HTML/CSS/JS extracted from the inline string). Public API
(grade_benchmark/grade_path/run_server) unchanged; _Handler/_INDEX_HTML/
_case_view re-exported from web/__init__.py for test back-compat. JS moved
out of _INDEX_HTML into static/app.js, so the 3 esc()-hardening regression
tests (issue #214) now grep the new web._APP_JS re-export instead.

Lays the groundwork for the split-pane workspace, extended error cards,
and glossary section this issue adds on top.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ts (issue #125)

Prep for the web workspace's ErrorCard/glossary endpoints:
- GraderConfig.glossary_store / glossary_missing_queue (config.py) — where
  the web layer's future glossary adapter will look for a local card store
  and the missing-concept queue; both optional/defaulted, no behavior change
  for existing users. .grader_glossary_missing.json gitignored (local state,
  same posture as .grader_cache/).
- core/grader_core.run_single_test now returns "exit_code" (additive key,
  every branch) — needed for the web ErrorCard's exit_code field without
  re-implementing execution in the web layer.
- core/glossary.all_entries() — lists the compact curated glossary, used as
  zero-config fallback content for the web Glossary section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…sue #125)

_case_view() now adds case_n, severity, stdin, expected, actual, stderr,
exit_code, timeout_s, suggestions, glossary_ids, and actions on top of the
existing n/verdict/time/error/diff/glossary keys — additive only, so the
existing /api/grade contract and its tests are unchanged.

- stdin comes from a second (cheap) load_test_cases() call in grade_path(),
  zipped by position with run_tests()'s case results — no core signature
  change needed.
- glossary_ids/suggestions are RE-only (TLE has no curated content to link
  to; fabricating some would be inventing content, not adapting existing
  data — left out per the #125 plan).
- actions is a pure function over exactly the 5 MVP action-card ids
  (run_again/copy_input/copy_output/explain_error/open_glossary) — never
  create_test/compare_solutions, which stay out of scope for this issue.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…#125)

When an RE case's exception has no curated glossary card (lookup_from_error
misses), grade_path() now best-effort queues it via
MissingConceptDetector.detect_from_error() + append_missing_entries() into
CONFIG.glossary_missing_queue (or an explicit missing_queue_path, mainly for
tests). Wrapped in try/except (GlossaryError, OSError) — a bad/unwritable
queue path degrades gracefully instead of breaking grading, same posture as
the existing opt-in cache (#56).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
glossary_adapter.py is a thin pass-through over JsonGlossaryProvider (#126)
with a zero-config fallback onto core/glossary.py's compact ~28-entry set
when CONFIG.glossary_store is unset or the configured file is missing/bad —
so the future Glossary section UI has content to show on a fresh install.

New routes on _Handler.do_GET:
- GET /api/glossary?q=       -> GlossaryCard[] (empty q = list all)
- GET /api/glossary/<id>     -> GlossaryCard or 404 JSON
- GET /api/glossary/missing  -> GlossaryMissingEntry[] (J7 backlog)

Also fixes a test-isolation bug caught while adding these: a _case_view()
unit test for an unknown RE exception didn't pin missing_queue_path, so it
wrote a real entry into the repo's working directory via the J7 wiring
added in the previous commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
COMMANDS is a plain list[dict] of the 7 MVP commands (run_again, copy_input,
copy_output, explain_error, open_glossary, toggle_theme, switch_section) --
the single source of truth that will drive the command palette, action
cards, and scenario buttons on the frontend, so "which button when" isn't
reimplemented three times.

Deliberate simplification vs. the design doc's free-form predicate strings:
`when` is a fixed tag vocabulary (always/has_stdin/has_output/is_failure/
has_glossary/...) instead of an eval()'d expression -- unjustified complexity
for 7 commands. GET /api/commands?context=tag1,tag2 filters server-side;
omitted context returns the full registry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…issue #125)

Replaces the single-page tests/bench view with the full workspace design
from docs/web-mvp.md:

- Split-pane layout: topbar (section switch, theme toggle, Ctrl+K),
  resizable sidebar (run history, recent paths), result panel (command bar
  + table), detail panel (ErrorCard for the selected case).
- ErrorCard detail panel renders stdin/expected/actual/diff/stderr/exit_code
  /timeout_s (all through the existing esc() hardening) plus the glossary
  mini-card, with the 5 MVP action cards (run_again/copy_input/copy_output/
  explain_error/open_glossary) driven by the server-computed `actions` list.
- Command palette (Ctrl+K/⌘K): fuzzy-ish substring search over title/
  keywords, arrow-key nav, Enter to run, Escape to close with focus return,
  role="dialog" overlay.
- Scenario buttons (result panel, shown only when nothing is selected) and
  action cards (detail panel, shown only when a case is selected) both
  render from the same contextTags()/visibleCommands() filter over the
  /api/commands registry -- one source of truth, no duplicated "which
  button when" logic.
- Glossary section: search + card list + card detail + a read-only
  "Недостающее" backlog view (J7), with open_glossary doing an in-memory
  section switch + card selection (no URL-hash routing, per the #125 plan).
- Narrow-viewport (<=860px) collapses panels to a vertical stack and hides
  drag handles.

Also fixes two bugs found while manually verifying in a browser:
- `[hidden]` was losing the cascade to `.workspace`/`.palette-overlay`'s
  `display: flex` (same specificity, author style wins) -- the glossary
  section and command palette were both visible on load. Added an explicit
  `[hidden] { display: none !important; }` rule.
- Theme toggle changed `color-scheme` but body/html had no background tied
  to it, so light/dark had no visible effect -- added `background: Canvas;
  color: CanvasText` to html/body.
- Long sidebar entries (paths) overflowed and pushed a page-level horizontal
  scrollbar -- added `overflow-wrap: anywhere` on .side-list items and
  scoped overflow-x to individual .panel elements instead of the page.
- Topbar didn't wrap at narrow widths, so its controls visually overlapped
  and swallowed clicks meant for other buttons -- added flex-wrap for the
  <=860px breakpoint.

Manually verified end-to-end via a running server (browser preview tool):
AC/WA/RE case selection and detail panel, command palette open/search/
arrow-nav/Enter/Escape+focus-return, theme toggle, glossary search/card
detail/backlog, the open_glossary deep-link, and the narrow-viewport
collapse. No JS test runner in this repo (by design, see docs/web-mvp.md
non-goals) -- frontend logic has no automated coverage beyond this manual
pass; Python-side contracts (ErrorCard fields, endpoints, command registry)
remain covered by pytest.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ANGELOG entry

- docs/web-mvp.md: MVP/v1/later table rows for #125 (split-pane, ErrorCard
  WA/TLE, action cards, palette, scenario buttons, glossary section) flipped
  from "дизайн" to "реализовано"; endpoint table marks the 4 implemented
  routes; intro/closing notes point at src/stepik_grader/web/ instead of the
  old single-file web.py.
- docs/claude-handoff.md: #125 section rewritten to "закрыт" status (matches
  the existing convention for #126/#163/etc in this file) so a future Claude
  session doesn't redo it; dependency-order section updated to reflect only
  #186/#187/#129 remain open for epic #123.
- docs/glossary.md: documents the new GraderConfig.glossary_store/
  glossary_missing_queue fields the web adapter reads.
- CHANGELOG.md [Unreleased]: Added entry for the web workspace, Refactored
  entry for the web.py -> web/ package split.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ArtVsMark ArtVsMark merged commit e7ba9f1 into main Jul 9, 2026
9 checks passed
@ArtVsMark ArtVsMark deleted the feat/web-workspace-125 branch July 9, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant