Skip to content

Add Loop UI design system tokens and canonical state mapping (#194) - #525

Open
mberrys wants to merge 5 commits into
devfrom
cc/hopeful-galileo-k8b2cu
Open

Add Loop UI design system tokens and canonical state mapping (#194)#525
mberrys wants to merge 5 commits into
devfrom
cc/hopeful-galileo-k8b2cu

Conversation

@mberrys

@mberrys mberrys commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the load-bearing pieces of #194 ("Define and implement the Loupe UI design system"):

  • pdfquick::tokens (LoopLibQuick/sources/looptokens.h/.cpp) — semantic spacing tokens (4px grid) and a ColorRole enum resolved per LoopTheme (Dark/Light/HighContrast). No call site hardcodes a colour; every foreground/background pair is WCAG contrast-checked in docs/LOOP_DESIGN_SYSTEM.md.
  • resolveStateVisual() (LoopLibQuick/sources/loopstatevisual.h/.cpp) — the single canonical finding/check presentation mapping the issue calls for, consumed by (finding, status, decision). Implements the two invariants [0.2.1] Define and implement the Loupe UI design system #194 is actually about: an incomplete check never renders as a pass, and an actively-waived finding never renders as a pass.
  • docs/LOOP_DESIGN_SYSTEM.md — the token table with contrast figures, the canonical state table, and current adoption state.
  • UnitTests/tst_loopstatevisualtest.cpp (UnitTestsLoopStateVisual) — table-driven over severities, check statuses, waive/non-waive decisions, stale vs. active decisions, and out-of-schema inputs; explicitly asserts the two never-passed invariants.

Scope decisions (read before reviewing)

The issue text is stale. #194 (and #193/#195/#196/#127) were written against a pre-rebrand snapshot: Pdf4QtLibGui/loupe/… and Pdf4QtEditorPlugins/… no longer exist (Widgets GUI retired, Loupe → Loop rebrand). This PR implements against the repo's actual current layout — pdfquick::tokens in LoopLibQuick, docs/LOOP_DESIGN_SYSTEM.md — and explains the translation in the doc's "Naming note". docs/quick-design-tokens.json (issue #178) already defined a provisional dark-theme contract for the Quick canvas; this extends it to a full role set and a light theme rather than duplicating it.

Components are not included. Issue §3's finding card / inspector row / overlay / progress / empty-state / error-state / confirm implementations have no consuming surface yet — #193, #195, #196, and #127 are all still open. Building fixtures with nothing to host them would be speculative; docs/LOOP_DESIGN_SYSTEM.md records this and says each should land with its consuming surface, built on the tokens and mapping here.

CanvasPalette is left as-is. It already reuses one colour for both focus ring and warning severity, which the new ColorRole set intentionally separates (FocusRing vs. SeverityWarning). Re-pointing the canvas overlay at these tokens is canvas-specific work with its own visual-regression needs, tracked as adoption work rather than done here.

Build/test verification is incomplete. This environment has no configured Qt 6.11 CMake build (/home/user/loop/build does not exist) and provisioning one is outside this session's autonomous budget (configure_or_reconfigure and toolchain changes require approval). I ran everything that doesn't need a configured build:

  • clang-format --dry-run --Werror on all touched/added C++ files — clean.
  • scripts/ci/check_source_integrity.py — pass.
  • scripts/generate-architecture-catalogs.py --check — pass (regenerated to add UnitTestsLoopStateVisual).
  • scripts/agent/generate-adapters.py — pass, no drift beyond agent-policy.json itself.
  • Changelog fragment present and valid.

cmake --build / ctest / clang-tidy-18 for the new UnitTestsLoopStateVisual target could not be run here and need CI or a maintainer to confirm the build. I read every relevant existing source (PreflightFinding/PreflightCheckStatus/PreflightDecision in LoopLibCore, CanvasPalette, the LoopLibQuick/ProductQuickAccessibilitySmoke CMake linkage) to keep the new code consistent, but flagging this honestly rather than claiming a green build I couldn't produce.

One build-graph note: UnitTests/CMakeLists.txt currently has no target linking LoopLibQuick at all (pre-existing — tst_quickcanvastest.cpp etc. exist as source but aren't wired to any target, and linking the SHARED LoopLibQuick library as a plain add_executable needs qt_add_executable() + qt_import_qml_plugins() per ProductQuickAccessibilitySmoke/CMakeLists.txt, which no existing UnitTests target does). Since looptokens.cpp/loopstatevisual.cpp only need QColor (no Qml/Quick), the new test compiles those two translation units directly into UnitTestsLoopStateVisual instead of linking the shared library, avoiding that untested link edge. UnitTests/CMakeLists.txt is a protected path (flagged risk: high by check-change.py) for this reason — the change is additive and follows the existing sibling-target pattern otherwise.

Test plan

  • clang-format --dry-run --Werror on all new/changed files
  • scripts/ci/check_source_integrity.py
  • scripts/generate-architecture-catalogs.py --check
  • scripts/agent/generate-adapters.py
  • Changelog fragment (changes/cc-hopeful-galileo-k8b2cu.md)
  • cmake --build + ctest -R UnitTestsLoopStateVisual (needs a configured Qt 6.11 build — not available in this session)

🤖 Generated with Claude Code

https://claude.ai/code/session_01NxoABBuEB3mYqr8QyA6KuB


Generated by Claude Code

Adds pdfquick::tokens (LoopLibQuick/sources/looptokens.h/.cpp): semantic
spacing and colour-role tokens with dark/light/high-contrast values, each
foreground/background pair WCAG contrast-checked. Adds resolveStateVisual()
(loopstatevisual.h/.cpp), the canonical finding/check presentation mapping
issue #194 asks for, with a table-driven test (UnitTestsLoopStateVisual)
asserting an incomplete check and an actively-waived finding never resolve
to the passed treatment. Documents both in docs/LOOP_DESIGN_SYSTEM.md,
including why this uses the repo's current Loop/Quick naming rather than
the issue's stale pre-rebrand Pdf4QtLibGui/Loupe paths, and what remains
open (component implementations land with their consuming surfaces #193,
#195, #196, #127, which are not yet implemented).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxoABBuEB3mYqr8QyA6KuB
scripts/ci/check_loop_identity.py fails CI on any tracked text file
containing the pre-rebrand product name outside its historical-evidence
allowlist. The "Naming note" section quoted that name three times while
explaining why this doc uses current naming instead of issue #194's stale
paths; reword it to make the same point without the literal token.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxoABBuEB3mYqr8QyA6KuB

mberrys commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

source_integrity failed on the first push: scripts/ci/test_check_loop_identity.py found 8 occurrences of the legacy pre-rebrand product-name token.

3 were mine — docs/LOOP_DESIGN_SYSTEM.md's "Naming note" quoted the old product name while explaining why this doc uses current naming instead of issue #194's stale paths. Fixed and pushed (f31f749): reworded to make the same point without the literal token.

The other 5 are pre-existing on dev and not introduced by this PR: .github/workflows/reusable-linux.yml:79, .github/workflows/reusable-windows.yml:92, UnitTests/testdata/budget-exhaustion/manifest.json:190, UnitTests/tst_budgetexhaustiontest.cpp:133, and scripts/budget_exhaustion/generate_corpus.py:14 all already contain the token on origin/dev (verified via git show origin/dev:<path>) — this check is already red on the base branch. I don't have a fix for those to port (no existing PR or revert addresses them), and fixing them is outside this design-system PR's scope. Leaving them for a separate fix.


Generated by Claude Code

Adding UnitTestsLoopStateVisual to UnitTests/CMakeLists.txt bumped the
project-wide CMake target count the Phase 5 Widgets inventory tracks (its
generator enumerates every target to prove widgets-linkage status, not just
widgets-related ones). Regenerate docs/generated/phase5-widgets-inventory.json
via scripts/generate_phase5_widgets_evidence.py --write (new target's
widgets_linkage is "none", as expected) and update the test's hardcoded
target count (70 -> 71) to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxoABBuEB3mYqr8QyA6KuB
@mberrys
mberrys marked this pull request as ready for review September 3, 2026 05:34
@mberrys

mberrys commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up on the Loop identity findings: fixed the 5 remaining legacy product-token occurrences so this PR is green on its own (base dev is still red at those paths).\n\n- .github/workflows/reusable-linux.yml:79 and reusable-windows.yml:92: working-directory loupe -> loop. This was also the agent-fast failure (No such file or directory .../loupe).\n- scripts/budget_exhaustion/generate_corpus.py:14, UnitTests/tst_budgetexhaustiontest.cpp:133, UnitTests/testdata/budget-exhaustion/manifest.json:190: schema_kind loupe-processing-budget-exhaustion-corpus -> loop-processing-budget-exhaustion-corpus (manifest regenerated via generate_corpus.py, --check passes locally).\n\nVerified locally: check_loop_identity.contract_findings() == [], check_source_integrity.py passes, generate_corpus.py --check passes, check-change.py dry-run passes with head-branch cc/hopeful-galileo-k8b2cu.

agent-fast's clang-tidy step runs on every changed C++ file regardless of
module classification, but the cmake --build step that generates each
test's AUTOMOC .moc file only runs for targets selected by
classify()/selected_values() against agent-policy.json's module_boundaries
paths. UnitTestsBudgetExhaustion was already listed in core's `tests`, but
its source file, UnitTests/tst_budgetexhaustiontest.cpp, was missing from
core's `paths` glob list, so a change touching only that file never
classified as "core" and the target was never built before clang-tidy ran
on it standalone -- producing "tst_budgetexhaustiontest.moc file not
found". Surfaced by 0aa0d4c's one-line fix to that file. Add the missing
path entry so the target builds first, as it does for every other core
test file already listed there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxoABBuEB3mYqr8QyA6KuB

mberrys commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

agent-fast / build failed on 0aa0d4c: clang-tidy-18 on UnitTests/tst_budgetexhaustiontest.cpp errored with 'tst_budgetexhaustiontest.moc' file not found.

Root cause: agent-policy.json's core module already listed UnitTestsBudgetExhaustion under tests, but its source file wasn't in core's paths glob list. check-change.py runs clang-tidy on every changed C++ file regardless of module classification, but only cmake --builds the targets/tests selected by module classification — so when a change touches only tst_budgetexhaustiontest.cpp (as 0aa0d4c's schema_kind fix did), the file's own AUTOMOC target never gets built first, and clang-tidy can't find the generated .moc. (Everything else in 0aa0d4c's build was fine — worth noting UnitTestsLoopStateVisual itself built, linked, and passed cleanly in that same run.)

Fixed and pushed (ed178bd): added UnitTests/tst_budgetexhaustiontest.cpp to core.paths so its target builds before clang-tidy runs on it, same as every other core test file already listed there.


Generated by Claude Code

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.

2 participants