From 2ba23bacd238724a02ccf58b41f303783b3a78e8 Mon Sep 17 00:00:00 2001 From: meh Date: Sat, 29 Aug 2026 03:24:56 +0700 Subject: [PATCH 01/40] test(theme): guard color flower first paint --- apps/gui/Cargo.toml | 2 +- tests/ps-qa/13-theme.ron | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/gui/Cargo.toml b/apps/gui/Cargo.toml index edc0deea..0fe6217b 100644 --- a/apps/gui/Cargo.toml +++ b/apps/gui/Cargo.toml @@ -101,7 +101,7 @@ tauri = { version = "2", default-features = false, features = ["macos-private-ap # 0.1.0 the runtime's own feature forwards to `tauri` and `tauri-runtime`, so # naming it here agrees with that rather than being the only thing holding the # two sides together. -tauri-runtime-blitz = { version = "^0.1.11", optional = true, features = ["macos-private-api"] } +tauri-runtime-blitz = { version = "^0.1.13", optional = true, features = ["macos-private-api"] } # # The engine by version, not by branch. Same move `chuzz` made, for the same # reasons its manifest records. diff --git a/tests/ps-qa/13-theme.ron b/tests/ps-qa/13-theme.ron index 9ef779d5..380f077c 100644 --- a/tests/ps-qa/13-theme.ron +++ b/tests/ps-qa/13-theme.ron @@ -30,14 +30,28 @@ ( id: "theme-base-colour-first-paint", group: "theme-first-paint", - what: "revealing Appearance paints the theme-colour flower in its final distinct geometry without a long mount animation", + what: "revealing Appearance paints the same neutral colour flower before and after its first hover", open: Some("Settings"), hover: None, + after_prepare_hover: Some("radio:Theme color "), click: None, type_into: Some("Search settings"), text: Some("Appearance"), + subject: "@color-wheel-flower", + expect: PixelsHoldAfterHover, + ), + ( + id: "theme-base-colour-contained", + group: "theme-first-paint", + what: "revealing Appearance keeps every theme-colour petal inside the flower", + open: Some("Settings"), + hover: None, + click: None, + type_into: Some("Search settings"), + text: Some("Appearance"), + compare: Some("@color-wheel-flower"), subject: "radio:Theme color ", - expect: DistinctPositions, + expect: ContainedBy, ), ( id: "theme-light-mode", From 721d1c08f7294e5bd03894fc10f3e235f31f82da Mon Sep 17 00:00:00 2001 From: meh Date: Sat, 29 Aug 2026 03:47:50 +0700 Subject: [PATCH 02/40] fix(composer): keep animated ring out of body fill --- .../src/features/project/Composer.tsx | 1 + .../src/features/settings/ThemePicker.tsx | 2 +- .../gui/frontend/src/styles/glassTint.test.ts | 10 +++---- apps/gui/frontend/src/styles/theme.css | 27 ++++++------------- tests/ps-qa/11-composer.ron | 10 +++++++ tests/ps-qa/13-theme.ron | 2 +- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/gui/frontend/src/features/project/Composer.tsx b/apps/gui/frontend/src/features/project/Composer.tsx index 6701a837..a1b30e16 100644 --- a/apps/gui/frontend/src/features/project/Composer.tsx +++ b/apps/gui/frontend/src/features/project/Composer.tsx @@ -1215,6 +1215,7 @@ export function Composer(props: ComposerProps): JSX.Element { }`} >
+
{ * The composer's animated ring is a gradient painted on the border box, and * the fill inside it is a child. Two declarations keep it visible and both * have been lost before: without `background-clip: border-box` the gradient - * lands behind the fill instead of on the edge, and without `isolation` on - * the child the parent gradient shows *through* a translucent fill as a hard - * vertical seam. There is no assertion for a seam, so this asserts the two - * declarations that prevent it. + * lands behind the fill instead of on the edge, and a translucent child + * necessarily reveals it across the body. Native QA checks the resolved + * alpha; this source guard keeps the intended solid token declaration. */ - it("keeps the composer ring on its border box, with an isolated fill", () => { + it("keeps the composer ring on its border box, with a solid fill", () => { const ring = THEME_CSS.slice( THEME_CSS.indexOf(".az-ring-composer {"), THEME_CSS.indexOf(".az-ring-drift"), ); expect(ring).toContain("border-box"); + expect(ring).toContain("background-color: var(--color-az-inset)"); expect(ring).toContain("isolation: isolate"); }); diff --git a/apps/gui/frontend/src/styles/theme.css b/apps/gui/frontend/src/styles/theme.css index 68978f97..50e36658 100644 --- a/apps/gui/frontend/src/styles/theme.css +++ b/apps/gui/frontend/src/styles/theme.css @@ -1686,20 +1686,10 @@ } /* - * The composer's own fill, scoped to the composer. - * - * It is painted with `bg-az-inset`, and that utility is on 38 other things: - * chips, small buttons, count badges. Those have to stay solid to remain - * legible against whatever passes behind them, and a translucent button reads - * as a disabled one. So the alpha is applied through the composer's own ring - * class rather than to the utility, which is the only large surface using it. - * - * The ring's own gradient is cleared where the fill sits. `az-ring-composer` - * paints an animated primary-tinted gradient as the 1px border, and a - * translucent child let that gradient show *through* the fill: it landed as a - * hard vertical seam down the middle of the composer, brightest where the - * gradient peaks. The ring is a border, so it belongs on the padding box and - * nowhere else. + * The parent paints the gradient into its one-pixel padding; the child owns + * the composer's flat fill. These must remain separate paint layers. Making + * the child translucent reveals the parent gradient across the whole body — + * no stacking-context rule can prevent ordinary alpha compositing. */ .az-ring-composer { background-origin: border-box; @@ -1707,11 +1697,10 @@ } .az-ring-composer > * { - background-color: rgb( - from var(--color-az-inset) r g b / - calc(var(--glass-background-opacity, 55%) * 0.72) - ); - /* Its own stacking context, so the parent gradient cannot bleed into it. */ + /* Opaque by design. A translucent child necessarily shows the animated + parent gradient through its body; stacking isolation cannot change + alpha compositing. The gradient belongs to the one-pixel ring only. */ + background-color: var(--color-az-inset); isolation: isolate; } diff --git a/tests/ps-qa/11-composer.ron b/tests/ps-qa/11-composer.ron index c87ec7da..bce45b7c 100644 --- a/tests/ps-qa/11-composer.ron +++ b/tests/ps-qa/11-composer.ron @@ -1,6 +1,16 @@ // Composer controls are per-tab state. Menus must render their choices, while // toggles must change either their semantic value or their inverse label. [ + ( + id: "composer-body-is-flat", + group: "composer", + what: "the composer body owns an opaque flat fill while its border animates independently", + open: Some("theta theta north indi"), + hover: None, + click: None, + subject: "@composer-body", + expect: OpaqueBackground, + ), ( id: "composer-advanced", group: "composer", diff --git a/tests/ps-qa/13-theme.ron b/tests/ps-qa/13-theme.ron index 380f077c..fb0031c7 100644 --- a/tests/ps-qa/13-theme.ron +++ b/tests/ps-qa/13-theme.ron @@ -37,7 +37,7 @@ click: None, type_into: Some("Search settings"), text: Some("Appearance"), - subject: "@color-wheel-flower", + subject: "@theme-color-control", expect: PixelsHoldAfterHover, ), ( From 8782861a95a16b579e19881d72c534e5463c3fbe Mon Sep 17 00:00:00 2001 From: meh Date: Sat, 29 Aug 2026 03:54:05 +0700 Subject: [PATCH 03/40] test(qa): use app semantic names for visual hooks --- apps/gui/frontend/src/features/project/Composer.tsx | 6 +++--- apps/gui/frontend/src/features/settings/ThemePicker.tsx | 4 ++-- tests/ps-qa/11-composer.ron | 2 +- tests/ps-qa/13-theme.ron | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/gui/frontend/src/features/project/Composer.tsx b/apps/gui/frontend/src/features/project/Composer.tsx index a1b30e16..e4b61be5 100644 --- a/apps/gui/frontend/src/features/project/Composer.tsx +++ b/apps/gui/frontend/src/features/project/Composer.tsx @@ -1214,8 +1214,8 @@ export function Composer(props: ComposerProps): JSX.Element { props.size === "lg" ? "az-ring-strong rounded-[19px]" : "" }`} > -
-
+ ); diff --git a/apps/gui/frontend/src/features/settings/ThemePicker.tsx b/apps/gui/frontend/src/features/settings/ThemePicker.tsx index e7c6cf8f..9cf20606 100644 --- a/apps/gui/frontend/src/features/settings/ThemePicker.tsx +++ b/apps/gui/frontend/src/features/settings/ThemePicker.tsx @@ -204,7 +204,7 @@ export function ThemePicker(props: { ]); return ( -
+
- + ); } diff --git a/tests/ps-qa/11-composer.ron b/tests/ps-qa/11-composer.ron index bce45b7c..9ce39567 100644 --- a/tests/ps-qa/11-composer.ron +++ b/tests/ps-qa/11-composer.ron @@ -8,7 +8,7 @@ open: Some("theta theta north indi"), hover: None, click: None, - subject: "@composer-body", + subject: "Composer body", expect: OpaqueBackground, ), ( diff --git a/tests/ps-qa/13-theme.ron b/tests/ps-qa/13-theme.ron index fb0031c7..f1eb0fc8 100644 --- a/tests/ps-qa/13-theme.ron +++ b/tests/ps-qa/13-theme.ron @@ -37,7 +37,7 @@ click: None, type_into: Some("Search settings"), text: Some("Appearance"), - subject: "@theme-color-control", + subject: "Theme color control", expect: PixelsHoldAfterHover, ), ( From 249f2bb631d18e1024dd31c6b1a446d9a84389f2 Mon Sep 17 00:00:00 2001 From: meh Date: Sat, 29 Aug 2026 04:24:14 +0700 Subject: [PATCH 04/40] test(theme): reveal first-paint region deterministically --- apps/gui/Cargo.toml | 2 +- tests/ps-qa/13-theme.ron | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/gui/Cargo.toml b/apps/gui/Cargo.toml index 0fe6217b..0f7f6624 100644 --- a/apps/gui/Cargo.toml +++ b/apps/gui/Cargo.toml @@ -101,7 +101,7 @@ tauri = { version = "2", default-features = false, features = ["macos-private-ap # 0.1.0 the runtime's own feature forwards to `tauri` and `tauri-runtime`, so # naming it here agrees with that rather than being the only thing holding the # two sides together. -tauri-runtime-blitz = { version = "^0.1.13", optional = true, features = ["macos-private-api"] } +tauri-runtime-blitz = { version = "^0.1.14", optional = true, features = ["macos-private-api"] } # # The engine by version, not by branch. Same move `chuzz` made, for the same # reasons its manifest records. diff --git a/tests/ps-qa/13-theme.ron b/tests/ps-qa/13-theme.ron index f1eb0fc8..cef7141e 100644 --- a/tests/ps-qa/13-theme.ron +++ b/tests/ps-qa/13-theme.ron @@ -34,9 +34,8 @@ open: Some("Settings"), hover: None, after_prepare_hover: Some("radio:Theme color "), + reveal_before_capture: Some("heading:Appearance"), click: None, - type_into: Some("Search settings"), - text: Some("Appearance"), subject: "Theme color control", expect: PixelsHoldAfterHover, ), From 9cad448b1ffdfbe53e510f8228a102fe1ebe9bd1 Mon Sep 17 00:00:00 2001 From: meh Date: Sat, 29 Aug 2026 04:28:47 +0700 Subject: [PATCH 05/40] fix(qa): use localized surface labels --- apps/gui/frontend/src/features/project/Composer.tsx | 2 +- apps/gui/frontend/src/features/settings/ThemePicker.tsx | 2 +- tests/ps-qa/11-composer.ron | 2 +- tests/ps-qa/13-theme.ron | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/gui/frontend/src/features/project/Composer.tsx b/apps/gui/frontend/src/features/project/Composer.tsx index e4b61be5..dc0c5be3 100644 --- a/apps/gui/frontend/src/features/project/Composer.tsx +++ b/apps/gui/frontend/src/features/project/Composer.tsx @@ -1215,7 +1215,7 @@ export function Composer(props: ComposerProps): JSX.Element { }`} >
+
Date: Sat, 29 Aug 2026 06:40:55 +0700 Subject: [PATCH 06/40] qa: guard native UI interaction outcomes --- .github/workflows/qa-panel.yml | 2 +- apps/gui/Cargo.toml | 2 +- .../frontend/src/features/home/HomeTab.tsx | 6 ++ .../src/features/project/ProjectPanel.tsx | 15 ++- .../src/features/settings/SettingsTab.tsx | 14 ++- apps/gui/frontend/src/i18n/ui/en.ts | 2 + apps/gui/frontend/src/i18n/ui/zh.ts | 2 + apps/gui/src/qa_profile.rs | 88 +++++++++++++++++- ps-qa.ron | 2 + tests/data/qa-profile.tar.zst | Bin 3023735 -> 3011425 bytes tests/ps-qa/05-session.ron | 7 +- tests/ps-qa/05-tasklog.ron | 11 ++- tests/ps-qa/05z-session-reset.ron | 2 + tests/ps-qa/07-dialog.ron | 12 ++- tests/ps-qa/08-delete.ron | 6 ++ tests/ps-qa/09-settings.ron | 20 ---- tests/ps-qa/09z-settings-coverage.ron | 1 + tests/ps-qa/11-composer.ron | 14 ++- tests/ps-qa/12-home.ron | 21 ++++- tests/ps-qa/16-pillmenu.ron | 8 ++ tests/ps-qa/99-tasklog-destructive.ron | 1 + 21 files changed, 183 insertions(+), 53 deletions(-) diff --git a/.github/workflows/qa-panel.yml b/.github/workflows/qa-panel.yml index 8bc2a916..7fe88593 100644 --- a/.github/workflows/qa-panel.yml +++ b/.github/workflows/qa-panel.yml @@ -72,7 +72,7 @@ jobs: # harness release. - name: Install ps-qa # Compatible 0.4 releases preserve the reviewed QA profile contract. - run: cargo install ps-qa --version '^0.4.4' + run: cargo install ps-qa --version '^0.4.13' - name: Validate the QA profile and outcome manifest run: ps-qa --app ps-qa.ron list --checks tests/ps-qa diff --git a/apps/gui/Cargo.toml b/apps/gui/Cargo.toml index 0f7f6624..71c0fa0f 100644 --- a/apps/gui/Cargo.toml +++ b/apps/gui/Cargo.toml @@ -101,7 +101,7 @@ tauri = { version = "2", default-features = false, features = ["macos-private-ap # 0.1.0 the runtime's own feature forwards to `tauri` and `tauri-runtime`, so # naming it here agrees with that rather than being the only thing holding the # two sides together. -tauri-runtime-blitz = { version = "^0.1.14", optional = true, features = ["macos-private-api"] } +tauri-runtime-blitz = { version = "^0.1.16", optional = true, features = ["macos-private-api"] } # # The engine by version, not by branch. Same move `chuzz` made, for the same # reasons its manifest records. diff --git a/apps/gui/frontend/src/features/home/HomeTab.tsx b/apps/gui/frontend/src/features/home/HomeTab.tsx index ab576f1b..c26a9944 100644 --- a/apps/gui/frontend/src/features/home/HomeTab.tsx +++ b/apps/gui/frontend/src/features/home/HomeTab.tsx @@ -170,6 +170,9 @@ export function HomeTab(): JSX.Element { } > - @@ -1989,6 +1999,7 @@ function ItemList(props: { projectId: string; items: ProjectItem[] }): JSX.Eleme