Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ jobs:
run: pip install -e '.[dev]'
- name: Install frontend browser dependencies
run: npm ci
- name: Install deterministic CJK screenshot font
run: sudo apt-get update && sudo apt-get install -y fonts-noto-cjk
- name: Install Playwright Firefox
run: npx playwright install --with-deps firefox
- name: Validate frontend script syntax
Expand All @@ -46,6 +48,16 @@ jobs:
run: python -m pytest tests/test_frontend_contract.py -q
- name: Multi-viewport browser regression test
run: npm run test:browser
- name: Upload visual baselines and failure diffs
if: always()
uses: actions/upload-artifact@v4
with:
name: theme-release-gate
if-no-files-found: ignore
path: |
tests/browser/*-snapshots/*.png
test-results/**/*.png
test-results/**/*.zip

test:
runs-on: ubuntu-latest
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## v4.6.29 - 2026-08-15

- Unified public chat, Support, status, settings, account, onboarding, and
operational state colors behind one light/dark semantic token contract.
- Split info/success/warning/error foreground, surface, and opaque border roles
so components cannot accidentally reuse a background token as text.
- Added a browser release gate for computed alpha-composited contrast across
desktop and 390x844, plus checked-in visual baselines and CI diff artifacts.
- Fixed previously untested low-contrast borders on recommendation chips and
settings cards, and added an explicit accessible name to account settings.

## Unreleased

## v4.6.28 - 2026-08-15
Expand Down
55 changes: 55 additions & 0 deletions docs/theme-system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Theme system and visual release gate

The public UI has one semantic color contract in the `:root` and
`html[data-theme="light"]` blocks of `web/styles.css`. Components must consume
those tokens; they must not encode a light or dark palette locally.

## Token roles

- `--bg`, `--bg-deep`: page canvas.
- `--surface`, `--surface-solid`, `--surface-soft`, `--surface-muted`,
`--surface-raised`, `--surface-inset`: elevation and containment.
- `--text-primary`, `--text-secondary`, `--muted`: text hierarchy.
- `--line`, `--line-strong`: decorative separators.
- `--control-border`, `--focus-ring`: interactive boundaries and keyboard focus.
- `--info-*`, `--success-*`, `--warning-*`, `--error-*`: separate
`surface`, `text`, and `border` roles. Never reuse a surface token as text.
- `--action-*`, `--send-*`: composer actions and their state changes.

SVG icons use `currentColor`. Selected, processing, disabled, error, and
success states require a non-color cue such as a border, ring, icon, or label.

## Release gate

`tests/browser/onboarding-responsive.spec.js` exercises both themes at desktop
and 390x844. It audits final computed colors after alpha composition for the
chat landing page, a completed answer with Support references, system status,
settings, account forms, and success/warning/error states.

The gate enforces:

- normal text contrast of at least 4.5:1;
- tested control boundaries of at least 3:1;
- no horizontal overflow at either viewport;
- checked-in Firefox screenshot baselines for the light/dark status route.

Run the gate with:

```bash
npm ci
sudo apt-get install fonts-noto-cjk
npx playwright install firefox
npx playwright test tests/browser/onboarding-responsive.spec.js
```

Intentional visual changes require local review before updating snapshots with
`--update-snapshots`. CI uploads the baselines and failure diffs for review.

## New component checklist

1. Use semantic tokens only; do not add component-local black/white RGBA colors.
2. Verify default, hover, focus-visible, selected, processing, disabled,
loading, empty, success, warning, and error states where applicable.
3. Add the component's rendered selectors to the computed-style audit.
4. Check light/dark at desktop and 390x844, including keyboard focus.
5. Update a screenshot baseline only after inspecting the rendered image.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "sage-mate"
version = "4.6.28"
version = "4.6.29"
description = "Sage Mate multi-profile local and hosted assistant built on SAGE and vllm-hust"
readme = "README.md"
license = {file = "LICENSE"}
Expand Down
2 changes: 1 addition & 1 deletion src/sage_faculty_twin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Sage Mate application package."""

__version__ = "4.6.28"
__version__ = "4.6.29"

__all__ = ["__version__"]
4 changes: 2 additions & 2 deletions src/sage_faculty_twin/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,7 @@ async def home() -> FileResponse:

@llm_app.api_route("/styles.css", methods=["GET", "HEAD"], include_in_schema=False)
@llm_app.api_route("/styles.4217.css", methods=["GET", "HEAD"], include_in_schema=False)
@llm_app.api_route("/styles.4220.css", methods=["GET", "HEAD"], include_in_schema=False)
@llm_app.api_route("/styles.4221.css", methods=["GET", "HEAD"], include_in_schema=False)
async def styles() -> FileResponse:
return frontend_asset("styles.css")

Expand All @@ -1284,7 +1284,7 @@ async def homepage_redirect() -> RedirectResponse:
@llm_app.api_route("/app.js", methods=["GET", "HEAD"], include_in_schema=False)
@llm_app.api_route("/app.4217.js", methods=["GET", "HEAD"], include_in_schema=False)
@llm_app.api_route("/app.4218.js", methods=["GET", "HEAD"], include_in_schema=False)
@llm_app.api_route("/app.4220.js", methods=["GET", "HEAD"], include_in_schema=False)
@llm_app.api_route("/app.4221.js", methods=["GET", "HEAD"], include_in_schema=False)
async def app_js() -> FileResponse:
return frontend_asset("app.js")

Expand Down
7 changes: 4 additions & 3 deletions src/sage_faculty_twin/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;700&display=swap"
rel="stylesheet" media="print" onload="this.media='all'" />
<link rel="stylesheet" href="./styles.4220.css" />
<link rel="stylesheet" href="./styles.4221.css" />
<link rel="stylesheet" href="./companion.css" />
<style>
html {
Expand Down Expand Up @@ -154,7 +154,8 @@ <h1 id="topbar-title" class="topbar-title">张书豪</h1>
</div>
</div>
<div class="rail-bottom">
<button type="button" id="sidebar-user-icon" class="rail-user rail-btn" title="账号设置">
<button type="button" id="sidebar-user-icon" class="rail-user rail-btn" title="账号设置"
aria-label="账号设置">
<div class="rail-user-avatar"><svg class="ui-icon" aria-hidden="true"><use href="#icon-user"></use></svg></div>
<span class="rail-label" id="sidebar-user-name">用户</span>
</button>
Expand Down Expand Up @@ -1442,7 +1443,7 @@ <h3 class="analytics-section-title">待补知识草稿</h3>
</section>

<script src="./companion.js" defer></script>
<script src="./app.4220.js" defer></script>
<script src="./app.4221.js" defer></script>
</body>

</html>
Loading
Loading