// Headless theme editor, for accent-controls-contract. // // The two halves of the editor that write colour: the four wells // (ThemeEditorWells + ColorWell), which are the fast path and own hex // validation and the eyedropper, and the six HSV rows (AccentEditor), which // are the fine-tune behind a disclosure. // // AccentEditor commits on a debounce rather than per move, so `adjust` returns // what is *pending*, not what is stored -- the caller waits and reads `status` // again. That is the behaviour under test as much as the colour itself: a // slider that wrote on every move spent a whole drag in apply-and-verify round // trips and left the desktop repainting behind the pointer. import Quickshell import Quickshell.Io import QtQuick import qs.config import qs.modules.settings import qs.services ShellRoot { Item { width: 680 height: wells.implicitHeight + editor.implicitHeight ThemeEditorWells { id: wells width: parent.width } AccentEditor { id: editor y: wells.implicitHeight width: parent.width } } IpcHandler { target: "accent-controls-test" function status(): string { return JSON.stringify({ id: ThemeProfiles.activeProfile.id, name: ThemeProfiles.activeProfile.name, scheme: ThemeProfiles.activeProfile.scheme, accent: String(ThemeProfiles.activeProfile.accent), secondary: String(ThemeProfiles.activeProfile.secondary), shipped: ThemeProfiles.activeProfile.shipped === true, accentName: String(DesktopPreferences.get("accentName")), bg: String(ThemeProfiles.activePalette.bg), fg: String(ThemeProfiles.activePalette.fg), fgDim: String(ThemeProfiles.activePalette.fgDim), pending: editor.pending !== null, wellError: wells.lastError }); } // Moves one HSV slider. The commit is debounced, so the answer is the // pair the sliders are showing, not the stored one. function adjust(target: string, channel: string, ratio: real): string { editor.changeChannel(target, channel, ratio); return JSON.stringify({ shown: { accent: editor.shownAccent, secondary: editor.shownSecondary }, pending: editor.pending !== null, stored: { accent: String(ThemeProfiles.activeProfile.accent), secondary: String(ThemeProfiles.activeProfile.secondary) } }); } // The one path every well takes -- typed hex, colour wheel and // eyedropper all end here, so validation cannot differ between them. function well(which: string, hex: string): string { wells.apply(which, hex); return status(); } } }