diff options
Diffstat (limited to 'desktop/modules/companion')
| -rw-r--r-- | desktop/modules/companion/ArtistsModule.qml | 67 | ||||
| -rw-r--r-- | desktop/modules/companion/CompanionModule.qml | 131 | ||||
| -rw-r--r-- | desktop/modules/companion/PhrasesModule.qml | 64 | ||||
| -rw-r--r-- | desktop/modules/companion/PhrasesPage.qml | 195 |
4 files changed, 0 insertions, 457 deletions
diff --git a/desktop/modules/companion/ArtistsModule.qml b/desktop/modules/companion/ArtistsModule.qml deleted file mode 100644 index a0d23f4..0000000 --- a/desktop/modules/companion/ArtistsModule.qml +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2026 Danilo M. <danix@danix.xyz> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -import QtQuick -import "../.." -import "../../companion/Lines.js" as Lines - -// The lines the companion says when a track by an artist she knows starts, -// matched as a case-insensitive substring of the player's artist. Same -// draft, Save and Reset as PhrasesModule, on Prefs.companionArtists and -// Lines.artists; the page's cls field holds the artist. -Module { - id: am - name: "artists" - label: "Companion artists" - // nf-fa-music, present in Inconsolata Nerd Font. - icon: "\uf001" - settings: true - group: "Companion" - keywords: ["companion", "artist", "music", "song", "track", "line", "say", "2b"] - searchItems: am.shown.map((a, i) => ({ text: a.cls + ": " + a.lines.join(" / "), key: "app:" + i })) - - property var draft: null - property bool resetting: false - readonly property var shown: am.draft ?? Lines.appList(Prefs.companionArtists, Lines.artists) - readonly property bool valid: am.shown.every(a => String(a.cls).trim() !== "" - && (a.lines || []).some(l => String(l).trim() !== "")) - dirty: am.draft !== null - - function edit(list) { am.draft = list; am.resetting = false; } - function reset() { am.draft = Lines.appList(null, Lines.artists); am.resetting = true; } - function save() { - if (am.resetting) Prefs.resetCompanionArtists(); - else Prefs.setCompanionArtists(Lines.toMap(am.draft)); - am.discard(); - } - function discard() { am.draft = null; am.resetting = false; } - - page: Component { - Page { - title: "Companion artists" - actions: Component { - SaveBar { dirty: am.dirty; valid: am.valid; note: "every artist needs a name and a phrase" - onSave: am.save(); onDiscard: am.discard() } - } - PhrasesPage { - width: parent.width - owner: am - intro: "When a track by an artist she knows starts or changes, she says one of their lines at " - + "random, under her 30 s line cooldown. The name matches any part of the player's artist, " - + "ignoring case, so \"daft punk\" also catches \"Daft Punk, Pharrell Williams\". " - + "Save applies at once." - keyHint: "artist, e.g. linkin park" - addText: "Add artist" - saved: Prefs.companionArtists - } - } - } -} diff --git a/desktop/modules/companion/CompanionModule.qml b/desktop/modules/companion/CompanionModule.qml deleted file mode 100644 index 8d6a68d..0000000 --- a/desktop/modules/companion/CompanionModule.qml +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (C) 2026 Danilo M. <danix@danix.xyz> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -import QtQuick -import "../.." -import "../appearance" -import "../../companion" - -// The desktop companion's settings: whether she is shown, the same -// preference SUPER+\ toggles, so the switch follows the key; and where her -// wallpaper list is, marked with the heart in the wallpaper picker. -Module { - name: "companion" - label: "Companion" - // nf-md-account_heart, present in Inconsolata Nerd Font. - icon: "\u{F0899}" - settings: true - group: "Companion" - keywords: ["companion", "2b", "mascot", "character", "show", "hide", "wallpaper", "blush", "leotard", "timeout"] - - page: Component { - Page { - title: "Companion" - Column { - width: parent.width - spacing: 6 - - Item { - width: parent.width - height: 34 - Text { - id: lbl - anchors { left: parent.left; verticalCenter: parent.verticalCenter } - width: 160 - text: "Shown" - color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } - } - Switch { - id: sw - anchors { left: lbl.right; leftMargin: 8; verticalCenter: parent.verticalCenter } - checked: Prefs.companion - onToggled: on => Prefs.setCompanion(on) - } - Text { - anchors { left: sw.right; leftMargin: 10; right: parent.right; verticalCenter: parent.verticalCenter } - elide: Text.ElideRight - text: "Also SUPER+\\. Kept across restarts." - color: Theme.overlay - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 } - } - } - // After the self-destruct. Off keeps the leotard until she is - // hidden and shown again; turning it back on starts from the - // seconds in the field. - Item { - width: parent.width - height: 34 - Text { - id: lblSk - anchors { left: parent.left; verticalCenter: parent.verticalCenter } - width: 160 - text: "Leotard timeout" - color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } - } - Switch { - id: swSk - anchors { left: lblSk.right; leftMargin: 8; verticalCenter: parent.verticalCenter } - checked: Prefs.companionSkirtless > 0 - onToggled: on => Prefs.setCompanionSkirtless(on ? (parseInt(fSk.value) || 60) : 0) - } - Field { - id: fSk - anchors { left: swSk.right; leftMargin: 10; verticalCenter: parent.verticalCenter } - width: 60 - enabled: swSk.checked - opacity: enabled ? 1 : 0.4 - value: String(Prefs.companionSkirtless || 60) - placeholder: "60" - onEdited: { - const n = parseInt(value); - if (n >= 1 && String(n) === value.trim()) Prefs.setCompanionSkirtless(n); - else value = String(Prefs.companionSkirtless || 60); - } - } - Text { - anchors { left: fSk.right; leftMargin: 10; right: parent.right; verticalCenter: parent.verticalCenter } - elide: Text.ElideRight - text: swSk.checked ? "seconds" : "until she is hidden and shown again" - color: Theme.overlay - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 } - } - } - Item { - width: parent.width - height: 34 - Text { - id: lbl2 - anchors { left: parent.left; verticalCenter: parent.verticalCenter } - width: 160 - text: "Wallpaper list" - color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } - } - Field { - anchors { left: lbl2.right; leftMargin: 8; right: parent.right; verticalCenter: parent.verticalCenter } - value: Prefs.companionBlushFile - placeholder: Admired.defaultPath.replace(Admired.home, "~") - onEdited: Prefs.setCompanionBlushFile(value) - } - } - Text { - width: parent.width - wrapMode: Text.WordWrap - text: "One path per line. She blushes at these on an empty workspace; the heart in Wallpaper adds and removes them." - color: Theme.overlay - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 } - } - } - } - } -} diff --git a/desktop/modules/companion/PhrasesModule.qml b/desktop/modules/companion/PhrasesModule.qml deleted file mode 100644 index 7e4895a..0000000 --- a/desktop/modules/companion/PhrasesModule.qml +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2026 Danilo M. <danix@danix.xyz> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -import QtQuick -import "../.." -import "../../companion/Lines.js" as Lines - -// The lines the companion says when focus moves to a program she -// recognizes, by its lowercased window class. They live in Prefs and she -// reads them live, so a save needs no restart. The page edits a draft here; -// Save hands it to Prefs, Reset drops the saved set so Lines.js' defaults -// show again. -Module { - id: pm - name: "phrases" - label: "Companion phrases" - // nf-fa-comment, present in Inconsolata Nerd Font. - icon: "\uf075" - settings: true - group: "Companion" - keywords: ["companion", "phrase", "line", "say", "speak", "app", "program", - "window", "class", "firefox", "2b"] - searchItems: pm.shown.map((a, i) => ({ text: a.cls + ": " + a.lines.join(" / "), key: "app:" + i })) - - // null while there are no edits: the page then shows the saved set, or - // Lines.js' defaults when nothing is saved. - property var draft: null - // Reset was pressed: Save drops the saved set rather than saving a copy - // of the defaults, so later default changes still arrive. - property bool resetting: false - readonly property var shown: pm.draft ?? Lines.appList(Prefs.companionApps) - // Every app needs a class and at least one non-empty phrase; Save waits. - readonly property bool valid: pm.shown.every(a => String(a.cls).trim() !== "" - && (a.lines || []).some(l => String(l).trim() !== "")) - dirty: pm.draft !== null - - function edit(list) { pm.draft = list; pm.resetting = false; } - function reset() { pm.draft = Lines.appList(null); pm.resetting = true; } - function save() { - if (pm.resetting) Prefs.resetCompanionApps(); - else Prefs.setCompanionApps(Lines.toMap(pm.draft)); - pm.discard(); - } - function discard() { pm.draft = null; pm.resetting = false; } - - page: Component { - Page { - title: "Companion phrases" - actions: Component { - SaveBar { dirty: pm.dirty; valid: pm.valid; note: "every app needs a class and a phrase" - onSave: pm.save(); onDiscard: pm.discard() } - } - PhrasesPage { width: parent.width; owner: pm } - } - } -} diff --git a/desktop/modules/companion/PhrasesPage.qml b/desktop/modules/companion/PhrasesPage.qml deleted file mode 100644 index 1971171..0000000 --- a/desktop/modules/companion/PhrasesPage.qml +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (C) 2026 Danilo M. <danix@danix.xyz> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -import QtQuick -import "../.." - -// One card per recognized program: its window class and the lines she says -// when focus moves to it. Every change edits the module's draft; the -// header's Save hands the set to Prefs. Reset fills the draft with the -// built-in set from Lines.js. -Column { - id: page - spacing: 10 - - // The module, holding the draft. Not named pm, its id (it would bind to - // itself, AGENTS.md). - required property QtObject owner - // What the cards are keyed by; the defaults are the app set's, so - // PhrasesModule passes nothing. - property string intro: "When focus moves to a program she recognizes, she says one of its lines at random, a third " - + "of the time and under her 30 s line cooldown. The class is Hyprland's window class, " - + "lowercased (hyprctl activewindow shows it). Save applies at once." - property string keyHint: "window class, e.g. firefox" - property string addText: "Add app" - // The saved set, for Reset's enabled state; null means the defaults. - property var saved: Prefs.companionApps - readonly property var list: owner.shown - // The app card awaiting a second click on Remove. - property int confirming: -1 - - // Saves a copy of the list with app i replaced by fn(app), or removed - // when fn returns null. - function edit(i, fn) { - const out = []; - for (let j = 0; j < page.list.length; j++) { - const a = page.list[j]; - const c = { cls: a.cls, lines: a.lines.slice() }; - if (j !== i) { out.push(c); continue; } - const r = fn(c); - if (r) out.push(r); - } - page.confirming = -1; - page.owner.edit(out); - } - - function addApp() { page.owner.edit(page.list.concat([{ cls: "", lines: [""] }])); } - function removeApp(i) { page.edit(i, () => null); } - function setCls(i, t) { page.edit(i, a => Object.assign(a, { cls: t })); } - function addLine(i) { page.edit(i, a => Object.assign(a, { lines: a.lines.concat([""]) })); } - function setLine(i, j, t) { - page.edit(i, a => { - const ls = a.lines.slice(); - ls[j] = t; - return Object.assign(a, { lines: ls }); - }); - } - function removeLine(i, j) { page.edit(i, a => Object.assign(a, { lines: a.lines.filter((_, k) => k !== j) })); } - - component Small: Text { - color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } - } - - // Reports every keystroke into the draft. Not bound to value, which - // would reset the cursor; it follows the value only when the draft moves - // under it (Discard, Reset). Empty is outlined, since Save refuses it. - component Field: Rectangle { - id: field - property string value: "" - property string placeholder: "" - signal edited(string text) - height: 32 - radius: 8 - color: Qt.alpha(Theme.base, 0.6) - border.width: ti.text.trim() === "" ? 1 : 0 - border.color: Theme.red - onValueChanged: if (ti.text !== field.value) ti.text = field.value - TextInput { - activeFocusOnTab: true - id: ti - anchors { fill: parent; leftMargin: 10; rightMargin: 10 } - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - color: Theme.text - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } - Component.onCompleted: text = field.value - onTextEdited: field.edited(text) - Text { - anchors.verticalCenter: parent.verticalCenter - visible: parent.text === "" - text: field.placeholder - color: Theme.overlay - font: parent.font - } - } - } - - Small { - width: page.width - wrapMode: Text.Wrap - text: page.intro - } - - Repeater { - // A count, not the list: a new list on every keystroke would rebuild - // every card and drop the focus of the field being typed in. - model: page.list.length - - delegate: Rectangle { - id: card - required property int index - readonly property var modelData: page.list[index] ?? { cls: "", lines: [] } - objectName: "app:" + index - width: page.width - height: body.implicitHeight + 20 - radius: 10 - color: Qt.alpha(Theme.surface, 0.45) - - Column { - id: body - anchors { left: parent.left; right: parent.right; top: parent.top; margins: 10 } - spacing: 8 - - Item { - width: parent.width - height: 32 - Field { - anchors { left: parent.left; right: del.left; rightMargin: 8; top: parent.top; bottom: parent.bottom } - value: card.modelData.cls - placeholder: page.keyHint - onEdited: t => page.setCls(card.index, t) - } - Button { - id: del - anchors { right: parent.right; verticalCenter: parent.verticalCenter } - danger: true - text: page.confirming === card.index ? "Confirm" : "Remove" - onClicked: { - if (page.confirming === card.index) page.removeApp(card.index); - else page.confirming = card.index; - } - } - } - - Repeater { - model: card.modelData.lines.length - delegate: Row { - id: phraseRow - required property int index - width: body.width - spacing: 6 - Field { - width: phraseRow.width - removeBtn.width - phraseRow.spacing - value: card.modelData.lines[phraseRow.index] ?? "" - placeholder: "a line she says" - onEdited: t => page.setLine(card.index, phraseRow.index, t) - } - Button { - id: removeBtn - // nf-fa-xmark, present in Inconsolata Nerd Font. - icon: "\uf00d" - danger: true - onClicked: page.removeLine(card.index, phraseRow.index) - } - } - } - - Button { - text: "Add phrase" - onClicked: page.addLine(card.index) - } - } - } - } - - Row { - spacing: 8 - Button { text: page.addText; onClicked: page.addApp() } - Button { - text: "Reset to defaults" - danger: true - enabled: !page.owner.resetting && (page.saved !== null || page.owner.dirty) - onClicked: page.owner.reset() - } - } -} |
