chore: split cmake into multiple files and fix settings searching regex + add enabled option to llm
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Failing after 17s
JS/TS / lint (pull_request) Successful in 19s
Python / static (pull_request) Successful in 1m11s
Rust / fmt (pull_request) Successful in 1m3s
Rust / build (pull_request) Successful in 2m26s
Rust / clippy (pull_request) Failing after 16m40s
Python / verify (pull_request) Failing after 17m52s
C++ / clang-tidy (pull_request) Failing after 18m0s
C++ / build (pull_request) Failing after 18m2s

This commit is contained in:
2026-09-01 14:30:04 +02:00
parent 2150859847
commit ef2fb95fab
20 changed files with 855 additions and 641 deletions
+16 -7
View File
@@ -30,7 +30,7 @@ ColumnLayout {
function findAnchor(item: Item, anchor: string): Item {
if (!item)
return null;
if (item.settingAnchor !== undefined && item.settingAnchor === anchor)
if (item.settingAnchor !== undefined && item.settingAnchor === anchor) // qmllint disable missing-property
return item;
const kids = item.children;
for (let i = 0; i < kids.length; i++) {
@@ -43,8 +43,8 @@ ColumnLayout {
function highlightAnchor(anchor: string): void {
const row = findAnchor(contentChild, anchor);
if (row && row.flashHighlight !== undefined)
row.flashHighlight();
if (row && row.flashHighlight !== undefined) // qmllint disable missing-property
row.flashHighlight(); // qmllint disable missing-property
}
function scrollToAnchor(anchor: string): bool {
@@ -61,8 +61,8 @@ ColumnLayout {
root.animateScroll = true;
flickable.contentY = target;
Qt.callLater(() => root.animateScroll = false);
if (row.flashHighlight !== undefined)
row.flashHighlight();
if (row.flashHighlight !== undefined) // qmllint disable missing-property
row.flashHighlight(); // qmllint disable missing-property
return true;
}
@@ -154,11 +154,20 @@ ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: -topMargin
bottomMargin: Tokens.padding.extraLarge
topMargin: Tokens.padding.large
fadeAmount: 0.1
contentHeight: root.contentChild?.implicitHeight ?? 0
contentItem.children: [root.contentChild]
fadeAmount: 0.1
topMargin: Tokens.padding.large
rebound: Transition {
Anim {
properties: "x,y"
type: Anim.DefaultEffects
}
}
Behavior on contentY {
enabled: root.animateScroll
+12 -19
View File
@@ -81,14 +81,10 @@ VerticalFadeFlickable {
topLeftRadius: stateLayer.pressed ? Tokens.rounding.medium : isCurrentPage ? Tokens.rounding.extraLarge : isCategoryStart ? Tokens.rounding.largeIncreased : Tokens.rounding.extraSmall
topRightRadius: stateLayer.pressed ? Tokens.rounding.medium : isCurrentPage ? Tokens.rounding.extraLarge : isCategoryStart ? Tokens.rounding.largeIncreased : Tokens.rounding.extraSmall
RadiusBehavior on bottomLeftRadius {
}
RadiusBehavior on bottomRightRadius {
}
RadiusBehavior on topLeftRadius {
}
RadiusBehavior on topRightRadius {
}
RadiusBehavior on bottomLeftRadius {}
RadiusBehavior on bottomRightRadius {}
RadiusBehavior on topLeftRadius {}
RadiusBehavior on topRightRadius {}
StateLayer {
id: stateLayer
@@ -262,14 +258,10 @@ VerticalFadeFlickable {
topRightRadius: layer.pressed ? Tokens.rounding.largeIncreased : isFirst ? Tokens.rounding.largeIncreased : Tokens.rounding.extraSmall
width: cardList.width
RadiusBehavior on bottomLeftRadius {
}
RadiusBehavior on bottomRightRadius {
}
RadiusBehavior on topLeftRadius {
}
RadiusBehavior on topRightRadius {
}
RadiusBehavior on bottomLeftRadius {}
RadiusBehavior on bottomRightRadius {}
RadiusBehavior on topLeftRadius {}
RadiusBehavior on topRightRadius {}
ColumnLayout {
id: resultLayout
@@ -318,9 +310,10 @@ VerticalFadeFlickable {
z: 1
onClicked: {
root.sState.jumpToSetting(result.modelData.pageIdx, result.modelData.subPath, result.modelData.anchor);
}
onClicked: {
const target = result.modelData.targetSubPath;
root.sState.jumpToSetting(result.modelData.pageIdx, target.length > 0 ? target : result.modelData.subPath, target.length > 0 ? "" : result.modelData.anchor);
}
}
CustomSwitch {
@@ -67,6 +67,20 @@ PageBase {
SectionHeader {
first: true
text: qsTr("General")
}
ToggleRow {
settingAnchor: "panels-sidebar-llm-enabled"
checked: Config.llm.enabled
text: qsTr("Enabled")
first: true
last: true
onToggled: Config.llm.enabled = checked
}
SectionHeader {
text: qsTr("Appearance")
}
@@ -120,6 +120,7 @@ PageBase {
NavRow {
text: qsTr("AI chat")
settingAnchor: "panels-sidebar-llm"
icon: "robot_2"
first: true
last: true
+6
View File
@@ -38,6 +38,9 @@ PageBase {
spacing: Tokens.spacing.small
IconTextButton {
property string settingAnchor: "style-wallpapers"
enabled: Config.background.enabled
horizontalPadding: Tokens.padding.extraLarge
icon: "wallpaper"
@@ -51,6 +54,9 @@ PageBase {
}
IconTextButton {
property string settingAnchor: "style-colors-fonts"
enabled: Config.background.enabled
horizontalPadding: Tokens.padding.extraLarge
icon: "palette"
+84 -59
View File
@@ -1,66 +1,24 @@
pragma Singleton
import "../../scripts/fzf.js" as Fzf
import "../../scripts/settings-indexer.js" as SettingsIndexer
import QtQuick
import Quickshell
import ZShell
import ZShell.Config
import qs.Paths
Singleton {
id: root
property var fzfFinder: null
property var inverted: ({})
property var ranking: ({})
readonly property var highlightCache: ({
"search": "",
"pattern": null
})
property var inverted: ({})
property var ranking: ({})
function highlight(text: string, search: string, colour: color): string {
const escaped = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
if (search.length === 0)
return escaped;
const cache = root.highlightCache;
if (search !== cache.search) {
const tokens = tokenize(search);
cache.search = search;
if (tokens.length === 0)
cache.pattern = null;
else {
const escapedTokens = tokens.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
cache.pattern = new RegExp("\\b(" + escapedTokens.join("|") + ")", "gi");
}
}
const pattern = cache.pattern;
if (!pattern)
return escaped;
pattern.lastIndex = 0;
if (!pattern.test(escaped))
return escaped;
pattern.lastIndex = 0;
return escaped.replace(pattern, `<font color="${colour}">$1</font>`);
}
function lookup(token: string): var {
const result = ({});
const exact = root.inverted[token] !== undefined;
const keys = exact ? [token] : Object.keys(root.inverted).filter(k => k.startsWith(token));
for (const key of keys) {
const rank = root.ranking[key] ?? ({});
for (const id of root.inverted[key]) {
const w = rank[id] ?? 0.1;
if (result[id] === undefined || w > result[id])
result[id] = w;
}
}
return result;
}
property var fzfFinder: null
readonly property string cachePath: Paths.cache + "/settings-index.json"
function query(search: string): list<QtObject> {
const tokens = root.tokenize(search);
@@ -70,7 +28,7 @@ Singleton {
const scores = ({});
const hitCounts = ({});
for (const token of tokens) {
const matches = root.lookup(token);
const matches = root.lookup(token); // { id: weight }
for (const id in matches) {
scores[id] = (scores[id] ?? 0) + matches[id];
hitCounts[id] = (hitCounts[id] ?? 0) + 1;
@@ -103,13 +61,76 @@ Singleton {
return out;
}
function lookup(token: string): var {
const result = ({});
const exact = root.inverted[token] !== undefined;
const keys = exact ? [token] : Object.keys(root.inverted).filter(k => k.startsWith(token));
for (const key of keys) {
const rank = root.ranking[key] ?? ({});
for (const id of root.inverted[key]) {
const w = rank[id] ?? 0.1;
if (result[id] === undefined || w > result[id])
result[id] = w;
}
}
return result;
}
function tokenize(text: string): var {
return text.toLowerCase().split(/[^a-z0-9]+/).filter(t => t.length > 0);
}
function highlight(text: string, search: string, colour: color): string {
const escaped = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
if (search.length === 0)
return escaped;
const cache = root.highlightCache;
if (search !== cache.search) {
const tokens = root.tokenize(search);
cache.search = search;
if (tokens.length === 0) {
cache.pattern = null;
} else {
const escapedTokens = tokens.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
cache.pattern = new RegExp("\\b(" + escapedTokens.join("|") + ")", "gi");
}
}
const pattern = cache.pattern;
if (!pattern)
return escaped;
pattern.lastIndex = 0;
if (!pattern.test(escaped))
return escaped;
pattern.lastIndex = 0;
return escaped.replace(pattern, `<font color="${colour}">$1</font>`);
}
function loadIndex(): var {
const revision = ZUtils.gitRevision();
// const cached = ZUtils.readTextFile(cachePath);
// if (cached) {
// try {
// const parsed = JSON.parse(cached);
// if (parsed.version === 3 && revision && parsed.revision === revision && parsed.locale === Qt.locale().name)
// return parsed;
// } catch (e) {}
// }
const data = SettingsIndexer.buildIndex(`${Quickshell.shellDir}/Modules/Settings`, p => ZUtils.readTextFile(p), (d, s) => ZUtils.listFiles(d, s), (ctx, text) => qsTranslate(ctx, text));
data.revision = revision;
data.locale = Qt.locale().name;
ZUtils.writeTextFile(cachePath, JSON.stringify(data));
console.log(`SettingsSearcher: indexed ${data.entries.length} settings (revision ${revision || "unknown"})`);
return data;
}
Component.onCompleted: {
try {
const data = JSON.parse(ZUtils.settingsIndex());
const data = root.loadIndex();
entries.model = data.entries;
root.inverted = data.inverted ?? {};
root.ranking = data.ranking ?? {};
@@ -122,6 +143,7 @@ Singleton {
limit: 25
});
} catch (e) {
console.warn("SettingsSearcher: failed to build settings index:", e);
entries.model = [];
root.inverted = {};
root.ranking = {};
@@ -132,22 +154,25 @@ Singleton {
Variants {
id: entries
SettingEntry {
}
SettingEntry {}
}
component SettingEntry: QtObject {
readonly property string anchor: modelData.anchor ?? ""
required property var modelData
readonly property int pageIdx: modelData.pageIdx
readonly property var subPath: modelData.subPath
readonly property var targetSubPath: modelData.targetSubPath ?? []
readonly property var crumbIcons: modelData.crumbIcons
readonly property var crumbLabels: modelData.crumbLabels
readonly property bool isToggle: togglePath.length > 0
required property var modelData
readonly property int pageIdx: modelData.pageIdx
readonly property string section: modelData.section ?? ""
readonly property var subPath: modelData.subPath
readonly property string subtext: modelData.subtext ?? ""
readonly property string title: modelData.title
readonly property string section: modelData.section ?? ""
readonly property string subtext: modelData.subtext ?? ""
readonly property string anchor: modelData.anchor ?? ""
readonly property string icon: modelData.icon ?? ""
readonly property string togglePath: modelData.togglePath ?? ""
readonly property bool isToggle: togglePath.length > 0
readonly property bool toggleValue: {
if (!isToggle)
return false;