The rebase itself is the previous five commits replayed onto main; this is what
they needed once they landed there, kept separate so the replay stays readable.
The shell did not start. A Column in this branch's notification settings menu
assigned its own implicitWidth and implicitHeight, which a Qt 6 positioner
computes and does not let you set. That took out every contract that launches a
shell -- six of the seven failures were this one line, and none of them said so
until the error was read to the bottom. A Column already measures itself from its
children, so the bindings are simply gone.
Three assertions pinned an implementation main has since replaced, and each is
updated to pin the intent rather than the mechanism:
- The display picker now reads primaryFirstMonitors, which is monitors sorted
with the primary first. Still populated from what is connected, which is what
the contract is for; the sorted list is the point, so the picker opens on the
display somebody is most likely to mean. This branch made that change and
broke its own contract without noticing.
- The accent swatches come from the accentName schema rather than
Object.keys(Theme.accents). Same swatches, same order, one source shared with
every other enum row.
- The OSD used to take no pointer input at all. It takes some now, because this
branch's own design calls for a secondary click on a visible OSD to open its
settings, and a Wayland input region cannot admit one button and refuse
another. The rule that survives is that the region stays bounded to the OSD's
own card: it floats over other windows for a couple of seconds, and a region
bigger than the card would swallow clicks meant for something underneath.
Theme's accent table moved to ThemeProfiles, which is this branch's point -- a
curated accent and a custom profile become the same kind of record. main had
meanwhile given each accent a `gnome` member, the nearest name in GNOME's fixed
accent-color enum, which is what makes libadwaita applications recolor instead of
staying in GNOME blue. That member moved into the curated table rather than being
dropped, and adwaita-accent-contract now reads it where it lives.
Where main had simply moved further along the same path, main won: the focused
border driven by the chosen accent rather than a hardcoded pair, the gradient
built through the shared serializer rather than a hand-rolled string, the
multi-edge dock geometry. This branch's context menu, keyboard focus and
accessibility work sit on top of those rather than beside them.
Three new contracts arrived carrying .sh and lost it, along with the references
in this branch's own plan.
124 contracts pass.
Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
141 lines
6.6 KiB
Bash
Executable File
141 lines
6.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
settings="$repo_dir/config/dot/quickshell/modules/settings"
|
|
theme="$repo_dir/config/dot/quickshell/config/Theme.qml"
|
|
scheme="$repo_dir/config/dot/quickshell/services/ColorScheme.qml"
|
|
search="$repo_dir/config/dot/quickshell/services/SettingsSearch.qml"
|
|
|
|
fail() {
|
|
printf 'accent controls contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
for component in AccentPicker AccentEditor ThemeProfilePicker; do
|
|
[[ -f "$settings/$component.qml" ]] || fail "$component is missing"
|
|
rg -Fq "$component 1.0 $component.qml" "$settings/qmldir" \
|
|
|| fail "$component is not registered in the settings module"
|
|
done
|
|
|
|
for label in \
|
|
'Primary hue' 'Primary saturation' 'Primary value' \
|
|
'Secondary hue' 'Secondary saturation' 'Secondary value'; do
|
|
rg -Fq "$label" "$settings/AccentEditor.qml" \
|
|
|| fail "HSV control is missing the visible label $label"
|
|
done
|
|
|
|
rg -Fq 'ValueSlider {' "$settings/AccentEditor.qml" \
|
|
|| fail 'advanced accents do not reuse ValueSlider'
|
|
rg -Fq 'Accessible.role: Accessible.Slider' "$settings/AccentEditor.qml" \
|
|
|| fail 'HSV controls do not expose slider semantics'
|
|
rg -Fq 'Accessible.name: root.label' "$settings/AccentEditor.qml" \
|
|
|| fail 'HSV controls are not programmatically labelled'
|
|
rg -Fq 'Accessible.description:' "$settings/AccentEditor.qml" \
|
|
|| fail 'HSV controls do not expose their numeric value as a non-hue signal'
|
|
rg -Fq 'activeFocusOnTab: true' "$settings/AccentEditor.qml" \
|
|
|| fail 'HSV controls cannot receive keyboard focus'
|
|
rg -Fq 'Keys.onPressed:' "$settings/AccentEditor.qml" \
|
|
|| fail 'HSV controls cannot be adjusted from the keyboard'
|
|
rg -Fq 'activeFocus ? Theme.accentSecondary' "$settings/AccentEditor.qml" \
|
|
|| fail 'HSV controls have no visible keyboard focus treatment'
|
|
|
|
rg -Fq '["hyprpicker", "--format=hex", "--lowercase-hex", "--quiet", "--no-fancy"]' \
|
|
"$settings/AccentEditor.qml" \
|
|
|| fail 'screen picking does not use the validated hyprpicker hex invocation'
|
|
for target in 'Pick primary from screen' 'Pick secondary from screen'; do
|
|
rg -Fq "$target" "$settings/AccentEditor.qml" \
|
|
|| fail "screen picker action is missing $target"
|
|
done
|
|
|
|
# The curated swatches come from the accentName schema rather than from
|
|
# Object.keys(Theme.accents): the schema's option order is the palette's order,
|
|
# and it is the same source every other enum row reads. What matters here is
|
|
# unchanged -- the curated accents are still one click away, ahead of the
|
|
# editor.
|
|
rg -Fq 'PreferenceSchema.spec("accentName")' "$settings/AccentPicker.qml" \
|
|
|| fail 'curated swatches are no longer sourced from the accent schema'
|
|
rg -Fq 'model: root.options' "$settings/AccentPicker.qml" \
|
|
|| fail 'curated swatches are no longer the fast path'
|
|
rg -Fq 'ThemeProfiles.useCuratedAccent(entry.modelData)' "$settings/AccentPicker.qml" \
|
|
|| fail 'curated swatches do not select a profile-backed accent'
|
|
rg -Fq 'readonly property string current: ThemeProfiles.activeAccentName' "$settings/AccentPicker.qml" \
|
|
|| fail 'swatch selection does not follow the active profile'
|
|
rg -Fq 'Accessible.name: entry.pair.label + " accent"' "$settings/AccentPicker.qml" \
|
|
|| fail 'swatches rely on hue without a programmatic name'
|
|
rg -Fq 'activeFocusOnTab: true' "$settings/AccentPicker.qml" \
|
|
|| fail 'swatches cannot receive keyboard focus'
|
|
rg -Fq 'Keys.onReturnPressed:' "$settings/AccentPicker.qml" \
|
|
|| fail 'swatches cannot be selected from the keyboard'
|
|
|
|
rg -Fq 'model: ThemeProfiles.profiles' "$settings/ThemeProfilePicker.qml" \
|
|
|| fail 'profile picker does not show shipped and saved profiles'
|
|
rg -Fq 'ThemeProfiles.selectProfile(' "$settings/ThemeProfilePicker.qml" \
|
|
|| fail 'profile switching is not wired'
|
|
rg -Fq 'ThemeProfiles.saveProfile(' "$settings/ThemeProfilePicker.qml" \
|
|
|| fail 'profile saving is not wired'
|
|
rg -Fq 'maximumLength: 40' "$settings/ThemeProfilePicker.qml" \
|
|
|| fail 'profile names are not visibly bounded to the model limit'
|
|
rg -Fq 'ThemeProfiles.deleteProfile(' "$settings/ThemeProfilePicker.qml" \
|
|
|| fail 'custom profile deletion is not wired'
|
|
rg -Fq 'activeFocusOnTab:' "$settings/ThemeProfilePicker.qml" \
|
|
|| fail 'profile actions cannot receive keyboard focus'
|
|
rg -Fq 'Keys.onReturnPressed:' "$settings/ThemeProfilePicker.qml" \
|
|
|| fail 'profile actions cannot be triggered from the keyboard'
|
|
|
|
for component in ThemeProfilePicker AccentPicker AccentEditor; do
|
|
rg -Fq "$component {" "$settings/AppearancePage.qml" \
|
|
|| fail "Appearance does not include $component"
|
|
done
|
|
|
|
rg -Fq 'ThemeProfiles.activeProfile' "$theme" \
|
|
|| fail 'Theme roles do not react to the selected profile'
|
|
rg -Fq 'root.hyprColor(Theme.accent)' "$scheme" \
|
|
|| fail 'the focused border start does not follow Theme.accent'
|
|
rg -Fq 'root.hyprColor(Theme.accentSecondary)' "$scheme" \
|
|
|| fail 'the focused border end does not follow Theme.accentSecondary'
|
|
|
|
for term in 'Theme profiles' 'Advanced accent' 'Pick colour from screen'; do
|
|
rg -Fq "$term" "$search" || fail "Settings search is missing $term"
|
|
done
|
|
|
|
if rg -n 'NumberAnimation|ColorAnimation|SequentialAnimation|ParallelAnimation|loops:[[:space:]]*Animation\.Infinite' \
|
|
"$settings/AccentEditor.qml" "$settings/AccentPicker.qml" "$settings/ThemeProfilePicker.qml"; then
|
|
fail 'theme controls introduce continuously repainting or decorative animation'
|
|
fi
|
|
|
|
state_home="$(mktemp -d /tmp/panama-accent-controls.XXXXXX)"
|
|
harness="$repo_dir/config/dot/quickshell/accent-controls-harness.qml"
|
|
|
|
qs_for_test() {
|
|
XDG_CONFIG_HOME="$state_home/config" XDG_STATE_HOME="$state_home/state" \
|
|
QS_DISABLE_CRASH_HANDLER=1 qs -p "$harness" "$@"
|
|
}
|
|
|
|
cleanup() {
|
|
qs_for_test kill >/dev/null 2>&1 || true
|
|
rm -rf "$state_home"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
qs_for_test --daemonize >/dev/null
|
|
for _ in $(seq 1 40); do
|
|
qs_for_test ipc show 2>/dev/null | rg -q '^target accent-controls-test$' && break
|
|
sleep 0.1
|
|
done
|
|
qs_for_test ipc show 2>/dev/null | rg -q '^target accent-controls-test$' \
|
|
|| fail 'headless AccentEditor harness did not start'
|
|
|
|
before="$(qs_for_test ipc call accent-controls-test status)"
|
|
jq -e '.id == "moon" and .shipped == true' <<<"$before" >/dev/null \
|
|
|| fail 'headless editor did not begin on Moon'
|
|
after="$(qs_for_test ipc call accent-controls-test adjust primary h 0)"
|
|
jq -e '.shipped == false and .accent != "#82aaff" and .secondary == "#b172b0"' \
|
|
<<<"$after" >/dev/null \
|
|
|| fail 'an HSV adjustment did not create a custom profile with the unchanged secondary colour'
|
|
|
|
trap - EXIT
|
|
cleanup
|
|
printf 'accent controls contract: PASS\n'
|