mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-13 14:36:32 -04:00
feat(Hyprland): Introduce Lua support for Hyprland configurations
- Note: We do not convert your existing conf configs to lua. This update only reflects DMS defaults state - Updated README.md to reflect changes - Updated Keyboard shortcut support
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
function shQuote(value) {
|
||||
return "'" + String(value ?? "").replace(/'/g, "'\\''") + "'";
|
||||
}
|
||||
|
||||
function dirname(path) {
|
||||
const idx = String(path ?? "").lastIndexOf("/");
|
||||
return idx > 0 ? path.substring(0, idx) : ".";
|
||||
}
|
||||
|
||||
function buildRepairScript(options) {
|
||||
const configFile = options.configFile;
|
||||
const backupFile = options.backupFile;
|
||||
const fragments = options.fragmentFiles || (options.fragmentFile ? [options.fragmentFile] : []);
|
||||
const includes = options.includes || [{
|
||||
grepPattern: options.grepPattern,
|
||||
includeLine: options.includeLine
|
||||
}];
|
||||
|
||||
const commands = [];
|
||||
if (backupFile)
|
||||
commands.push(`cp ${shQuote(configFile)} ${shQuote(backupFile)} 2>/dev/null || true`);
|
||||
|
||||
const dirs = {};
|
||||
for (const fragment of fragments)
|
||||
dirs[dirname(fragment)] = true;
|
||||
for (const dir in dirs)
|
||||
commands.push(`mkdir -p ${shQuote(dir)}`);
|
||||
if (fragments.length > 0)
|
||||
commands.push("touch " + fragments.map(shQuote).join(" "));
|
||||
|
||||
for (const include of includes) {
|
||||
if (!include.grepPattern || !include.includeLine)
|
||||
continue;
|
||||
commands.push(`if ! grep -v '^[[:space:]]*\\(//\\|#\\|--\\)' ${shQuote(configFile)} 2>/dev/null | grep -q ${shQuote(include.grepPattern)}; then echo '' >> ${shQuote(configFile)} && printf '%s\\n' ${shQuote(include.includeLine)} >> ${shQuote(configFile)}; fi`);
|
||||
}
|
||||
|
||||
return commands.join("; ");
|
||||
}
|
||||
@@ -178,7 +178,7 @@ sudo systemctl enable greetd
|
||||
#### Legacy installation (deprecated)
|
||||
|
||||
If you prefer the old method with separate shell scripts and config files:
|
||||
1. Copy `assets/dms-niri.kdl` or `assets/dms-hypr.conf` to `/etc/greetd`
|
||||
1. Copy `assets/dms-niri.kdl` or `assets/dms-hypr.lua` (legacy: `assets/dms-hypr.conf`) to `/etc/greetd`
|
||||
2. Copy `assets/greet-niri.sh` or `assets/greet-hyprland.sh` to `/usr/local/bin/start-dms-greetd.sh`
|
||||
3. Edit the config file and replace `_DMS_PATH_` with your DMS installation path
|
||||
4. Configure greetd to use `/usr/local/bin/start-dms-greetd.sh`
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Deprecated: greetd expects Hyprland 0.55+ Lua; use `/etc/greetd/dms-hypr.lua` instead.
|
||||
env = DMS_RUN_GREETER,1
|
||||
|
||||
exec = sh -c "qs -p _DMS_PATH_; hyprctl dispatch exit"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-- Minimal Hyprland (Lua) session for greetd — replace _DMS_PATH_ with your DMS checkout.
|
||||
-- Copy to `/etc/greetd/dms-hypr.lua` alongside `greet-hyprland.sh`.
|
||||
|
||||
hl.env("DMS_RUN_GREETER", "1")
|
||||
|
||||
hl.on("hyprland.start", function()
|
||||
hl.exec_cmd('sh -c "qs -p _DMS_PATH_; hyprctl dispatch exit"')
|
||||
end)
|
||||
@@ -5,7 +5,7 @@ export QT_QPA_PLATFORM=wayland
|
||||
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
|
||||
export EGL_PLATFORM=gbm
|
||||
if command -v start-hyprland >/dev/null 2>&1; then
|
||||
exec start-hyprland -- -c /etc/greetd/dms-hypr.conf
|
||||
exec start-hyprland -- -c /etc/greetd/dms-hypr.lua
|
||||
else
|
||||
exec Hyprland -c /etc/greetd/dms-hypr.conf
|
||||
exec Hyprland -c /etc/greetd/dms-hypr.lua
|
||||
fi
|
||||
|
||||
@@ -7,6 +7,7 @@ import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import "../../../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -1074,10 +1075,86 @@ Singleton {
|
||||
return result;
|
||||
}
|
||||
|
||||
function hyprLuaField(line, field) {
|
||||
const re = new RegExp("\\b" + field + "\\s*=\\s*(\\\"(?:\\\\\\\\.|[^\\\"])*\\\"|'(?:\\\\\\\\.|[^'])*'|\\[\\[.*?\\]\\]|[^,}\\s]+)");
|
||||
const match = line.match(re);
|
||||
if (!match)
|
||||
return undefined;
|
||||
const raw = match[1].trim();
|
||||
if (raw.startsWith("[[") && raw.endsWith("]]"))
|
||||
return raw.slice(2, -2);
|
||||
if (raw.startsWith("\"")) {
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
return raw.slice(1, -1);
|
||||
}
|
||||
}
|
||||
if (raw.startsWith("'") && raw.endsWith("'"))
|
||||
return raw.slice(1, -1).replace(/\\'/g, "'");
|
||||
if (raw === "true")
|
||||
return true;
|
||||
if (raw === "false")
|
||||
return false;
|
||||
const num = Number(raw);
|
||||
return isNaN(num) ? raw : num;
|
||||
}
|
||||
|
||||
function parseHyprlandLuaMonitorLine(line) {
|
||||
if (!line.match(/^\s*hl\.monitor\s*\(/))
|
||||
return null;
|
||||
const name = hyprLuaField(line, "output");
|
||||
if (name === undefined)
|
||||
return null;
|
||||
const disabled = hyprLuaField(line, "disabled") === true;
|
||||
const mode = hyprLuaField(line, "mode") || "preferred";
|
||||
const position = hyprLuaField(line, "position") || "0x0";
|
||||
const scaleValue = hyprLuaField(line, "scale");
|
||||
const transform = Number(hyprLuaField(line, "transform") ?? 0);
|
||||
const vrrMode = Number(hyprLuaField(line, "vrr") ?? 0);
|
||||
const posMatch = String(position).match(/^(-?\d+)x(-?\d+)$/);
|
||||
const modeMatch = String(mode).match(/^(\d+)x(\d+)@([\d.]+)/);
|
||||
const settings = {
|
||||
"disabled": disabled || undefined,
|
||||
"bitdepth": hyprLuaField(line, "bitdepth"),
|
||||
"colorManagement": hyprLuaField(line, "cm"),
|
||||
"sdrBrightness": hyprLuaField(line, "sdrbrightness"),
|
||||
"sdrSaturation": hyprLuaField(line, "sdrsaturation"),
|
||||
"supportsWideColor": hyprLuaField(line, "supports_wide_color"),
|
||||
"supportsHdr": hyprLuaField(line, "supports_hdr"),
|
||||
"vrrFullscreenOnly": vrrMode === 2 ? true : undefined
|
||||
};
|
||||
return {
|
||||
"name": String(name),
|
||||
"logical": {
|
||||
"x": posMatch ? parseInt(posMatch[1]) : 0,
|
||||
"y": posMatch ? parseInt(posMatch[2]) : 0,
|
||||
"scale": typeof scaleValue === "number" ? scaleValue : 1.0,
|
||||
"transform": hyprlandToTransform(transform)
|
||||
},
|
||||
"modes": modeMatch ? [{
|
||||
"width": parseInt(modeMatch[1]),
|
||||
"height": parseInt(modeMatch[2]),
|
||||
"refresh_rate": Math.round(parseFloat(modeMatch[3]) * 1000)
|
||||
}] : [],
|
||||
"current_mode": modeMatch ? 0 : -1,
|
||||
"vrr_enabled": vrrMode >= 1,
|
||||
"vrr_supported": vrrMode > 0,
|
||||
"hyprlandSettings": settings,
|
||||
"mirror": hyprLuaField(line, "mirror") || ""
|
||||
};
|
||||
}
|
||||
|
||||
function parseHyprlandOutputs(content) {
|
||||
const result = {};
|
||||
const lines = content.split("\n");
|
||||
for (const line of lines) {
|
||||
const luaMonitor = parseHyprlandLuaMonitorLine(line);
|
||||
if (luaMonitor) {
|
||||
result[luaMonitor.name] = luaMonitor;
|
||||
continue;
|
||||
}
|
||||
|
||||
const disableMatch = line.match(/^\s*monitor\s*=\s*([^,]+),\s*disable\s*$/);
|
||||
if (disableMatch) {
|
||||
const name = disableMatch[1].trim();
|
||||
@@ -1269,10 +1346,10 @@ Singleton {
|
||||
};
|
||||
case "hyprland":
|
||||
return {
|
||||
"configFile": configDir + "/hypr/hyprland.conf",
|
||||
"outputsFile": configDir + "/hypr/dms/outputs.conf",
|
||||
"grepPattern": 'source.*dms/outputs.conf',
|
||||
"includeLine": "source = ./dms/outputs.conf"
|
||||
"configFile": configDir + "/hypr/hyprland.lua",
|
||||
"outputsFile": configDir + "/hypr/dms/outputs.lua",
|
||||
"grepPattern": "dms.outputs",
|
||||
"includeLine": "require(\"dms.outputs\")"
|
||||
};
|
||||
case "dwl":
|
||||
return {
|
||||
@@ -1296,7 +1373,7 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
const filename = (compositor === "niri") ? "outputs.kdl" : "outputs.conf";
|
||||
const filename = (compositor === "niri") ? "outputs.kdl" : ((compositor === "hyprland") ? "outputs.lua" : "outputs.conf");
|
||||
const compositorArg = (compositor === "dwl") ? "mangowc" : compositor;
|
||||
|
||||
checkingInclude = true;
|
||||
@@ -1326,11 +1403,17 @@ Singleton {
|
||||
return;
|
||||
|
||||
fixingInclude = true;
|
||||
const outputsDir = paths.outputsFile.substring(0, paths.outputsFile.lastIndexOf("/"));
|
||||
const unixTime = Math.floor(Date.now() / 1000);
|
||||
const backupFile = paths.configFile + ".backup" + unixTime;
|
||||
const script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: paths.configFile,
|
||||
backupFile: backupFile,
|
||||
fragmentFile: paths.outputsFile,
|
||||
grepPattern: paths.grepPattern,
|
||||
includeLine: paths.includeLine
|
||||
});
|
||||
|
||||
Proc.runCommand("fix-outputs-include", ["sh", "-c", `cp "${paths.configFile}" "${backupFile}" 2>/dev/null; ` + `mkdir -p "${outputsDir}" && ` + `touch "${paths.outputsFile}" && ` + `if ! grep -v '^[[:space:]]*\\(//\\|#\\)' "${paths.configFile}" 2>/dev/null | grep -q '${paths.grepPattern}'; then ` + `echo '' >> "${paths.configFile}" && ` + `echo '${paths.includeLine}' >> "${paths.configFile}"; fi`], (output, exitCode) => {
|
||||
Proc.runCommand("fix-outputs-include", ["sh", "-c", script], (output, exitCode) => {
|
||||
fixingInclude = false;
|
||||
if (exitCode !== 0)
|
||||
return;
|
||||
|
||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Modals.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
@@ -96,6 +97,32 @@ Item {
|
||||
expandedKey = bindData.action;
|
||||
}
|
||||
|
||||
function confirmRemoveBind(key, remainingKey) {
|
||||
removeBindConfirm.showWithOptions({
|
||||
title: I18n.tr("Remove Shortcut?"),
|
||||
message: KeybindsService.currentProvider === "hyprland" ? I18n.tr("Remove the shortcut %1? An unbind entry will be saved to dms/binds-user.lua so it stays removed across DMS updates.").arg(key) : I18n.tr("Remove the shortcut %1?").arg(key),
|
||||
confirmText: I18n.tr("Remove"),
|
||||
confirmColor: Theme.primary,
|
||||
onConfirm: () => {
|
||||
KeybindsService.removeBind(key);
|
||||
keybindsTab._editingKey = remainingKey;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function confirmResetBind(key, remainingKey) {
|
||||
removeBindConfirm.showWithOptions({
|
||||
title: I18n.tr("Reset to Default?"),
|
||||
message: I18n.tr("Drop your override for %1 so the DMS default action re-applies?").arg(key),
|
||||
confirmText: I18n.tr("Reset"),
|
||||
confirmColor: Theme.primary,
|
||||
onConfirm: () => {
|
||||
KeybindsService.resetBind(key);
|
||||
keybindsTab._editingKey = remainingKey;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _onSaveSuccess() {
|
||||
if (showingNewBind) {
|
||||
showingNewBind = false;
|
||||
@@ -129,6 +156,10 @@ Item {
|
||||
onTriggered: keybindsTab._updateFiltered()
|
||||
}
|
||||
|
||||
ConfirmModal {
|
||||
id: removeBindConfirm
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: KeybindsService
|
||||
function onBindsLoaded() {
|
||||
@@ -238,7 +269,7 @@ Item {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
readonly property string bindsFile: KeybindsService.currentProvider === "niri" ? "dms/binds.kdl" : "dms/binds.conf"
|
||||
readonly property string bindsFile: KeybindsService.currentProvider === "niri" ? "dms/binds.kdl" : KeybindsService.currentProvider === "hyprland" ? "dms/binds-user.lua" : "dms/binds.conf"
|
||||
text: I18n.tr("Click any shortcut to edit. Changes save to %1").arg(bindsFile)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
@@ -336,7 +367,7 @@ Item {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
readonly property string bindsFile: KeybindsService.currentProvider === "niri" ? "dms/binds.kdl" : "dms/binds.conf"
|
||||
readonly property string bindsFile: KeybindsService.currentProvider === "niri" ? "dms/binds.kdl" : KeybindsService.currentProvider === "hyprland" ? "dms/binds-user.lua" : "dms/binds.conf"
|
||||
text: {
|
||||
if (warningBox.showSetup)
|
||||
return I18n.tr("Click 'Setup' to create %1 and add include to config.").arg(bindsFile);
|
||||
@@ -623,8 +654,11 @@ Item {
|
||||
}
|
||||
onRemoveBind: key => {
|
||||
const remainingKey = bindItem.keys.find(k => k.key !== key)?.key ?? "";
|
||||
KeybindsService.removeBind(key);
|
||||
keybindsTab._editingKey = remainingKey;
|
||||
keybindsTab.confirmRemoveBind(key, remainingKey);
|
||||
}
|
||||
onResetBind: key => {
|
||||
const remainingKey = bindItem.keys.find(k => k.key !== key)?.key ?? "";
|
||||
keybindsTab.confirmResetBind(key, remainingKey);
|
||||
}
|
||||
onIsExpandedChanged: {
|
||||
if (!isExpanded || !keybindsTab._editingKey)
|
||||
|
||||
@@ -7,6 +7,7 @@ import qs.Modals.FileBrowser
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings.Widgets
|
||||
import "../../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
|
||||
|
||||
Item {
|
||||
id: themeColorsTab
|
||||
@@ -39,10 +40,10 @@ Item {
|
||||
};
|
||||
case "hyprland":
|
||||
return {
|
||||
"configFile": configDir + "/hypr/hyprland.conf",
|
||||
"cursorFile": configDir + "/hypr/dms/cursor.conf",
|
||||
"grepPattern": 'source.*dms/cursor.conf',
|
||||
"includeLine": "source = ./dms/cursor.conf"
|
||||
"configFile": configDir + "/hypr/hyprland.lua",
|
||||
"cursorFile": configDir + "/hypr/dms/cursor.lua",
|
||||
"grepPattern": "dms.cursor",
|
||||
"includeLine": "require(\"dms.cursor\")"
|
||||
};
|
||||
case "dwl":
|
||||
return {
|
||||
@@ -66,7 +67,7 @@ Item {
|
||||
return;
|
||||
}
|
||||
|
||||
const filename = (compositor === "niri") ? "cursor.kdl" : "cursor.conf";
|
||||
const filename = (compositor === "niri") ? "cursor.kdl" : ((compositor === "hyprland") ? "cursor.lua" : "cursor.conf");
|
||||
const compositorArg = (compositor === "dwl") ? "mangowc" : compositor;
|
||||
|
||||
checkingCursorInclude = true;
|
||||
@@ -95,10 +96,16 @@ Item {
|
||||
if (!paths)
|
||||
return;
|
||||
fixingCursorInclude = true;
|
||||
const cursorDir = paths.cursorFile.substring(0, paths.cursorFile.lastIndexOf("/"));
|
||||
const unixTime = Math.floor(Date.now() / 1000);
|
||||
const backupFile = paths.configFile + ".backup" + unixTime;
|
||||
Proc.runCommand("fix-cursor-include", ["sh", "-c", `cp "${paths.configFile}" "${backupFile}" 2>/dev/null; ` + `mkdir -p "${cursorDir}" && ` + `touch "${paths.cursorFile}" && ` + `if ! grep -v '^[[:space:]]*\\(//\\|#\\)' "${paths.configFile}" 2>/dev/null | grep -q '${paths.grepPattern}'; then ` + `echo '' >> "${paths.configFile}" && ` + `echo '${paths.includeLine}' >> "${paths.configFile}"; fi`], (output, exitCode) => {
|
||||
const script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: paths.configFile,
|
||||
backupFile: backupFile,
|
||||
fragmentFile: paths.cursorFile,
|
||||
grepPattern: paths.grepPattern,
|
||||
includeLine: paths.includeLine
|
||||
});
|
||||
Proc.runCommand("fix-cursor-include", ["sh", "-c", script], (output, exitCode) => {
|
||||
fixingCursorInclude = false;
|
||||
if (exitCode !== 0)
|
||||
return;
|
||||
|
||||
@@ -8,6 +8,7 @@ import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import "../../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -54,10 +55,10 @@ Item {
|
||||
};
|
||||
case "hyprland":
|
||||
return {
|
||||
"configFile": configDir + "/hypr/hyprland.conf",
|
||||
"rulesFile": configDir + "/hypr/dms/windowrules.conf",
|
||||
"grepPattern": 'source.*dms/windowrules.conf',
|
||||
"includeLine": "source = ./dms/windowrules.conf"
|
||||
"configFile": configDir + "/hypr/hyprland.lua",
|
||||
"rulesFile": configDir + "/hypr/dms/windowrules.lua",
|
||||
"grepPattern": "dms.windowrules",
|
||||
"includeLine": "require(\"dms.windowrules\")"
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
@@ -135,7 +136,7 @@ Item {
|
||||
return;
|
||||
}
|
||||
|
||||
const filename = (compositor === "niri") ? "windowrules.kdl" : "windowrules.conf";
|
||||
const filename = (compositor === "niri") ? "windowrules.kdl" : "windowrules.lua";
|
||||
checkingInclude = true;
|
||||
Proc.runCommand("check-windowrules-include", ["dms", "config", "resolve-include", compositor, filename], (output, exitCode) => {
|
||||
checkingInclude = false;
|
||||
@@ -162,10 +163,16 @@ Item {
|
||||
if (!paths)
|
||||
return;
|
||||
fixingInclude = true;
|
||||
const rulesDir = paths.rulesFile.substring(0, paths.rulesFile.lastIndexOf("/"));
|
||||
const unixTime = Math.floor(Date.now() / 1000);
|
||||
const backupFile = paths.configFile + ".backup" + unixTime;
|
||||
Proc.runCommand("fix-windowrules-include", ["sh", "-c", `cp "${paths.configFile}" "${backupFile}" 2>/dev/null; ` + `mkdir -p "${rulesDir}" && ` + `touch "${paths.rulesFile}" && ` + `if ! grep -v '^[[:space:]]*\\(//\\|#\\)' "${paths.configFile}" 2>/dev/null | grep -q '${paths.grepPattern}'; then ` + `echo '' >> "${paths.configFile}" && ` + `echo '${paths.includeLine}' >> "${paths.configFile}"; fi`], (output, exitCode) => {
|
||||
const script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: paths.configFile,
|
||||
backupFile: backupFile,
|
||||
fragmentFile: paths.rulesFile,
|
||||
grepPattern: paths.grepPattern,
|
||||
includeLine: paths.includeLine
|
||||
});
|
||||
Proc.runCommand("fix-windowrules-include", ["sh", "-c", script], (output, exitCode) => {
|
||||
fixingInclude = false;
|
||||
if (exitCode !== 0)
|
||||
return;
|
||||
@@ -252,7 +259,7 @@ Item {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Define rules for window behavior. Saves to %1").arg(CompositorService.isNiri ? "dms/windowrules.kdl" : "dms/windowrules.conf")
|
||||
text: I18n.tr("Define rules for window behavior. Saves to %1").arg(CompositorService.isNiri ? "dms/windowrules.kdl" : "dms/windowrules.lua")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
@@ -351,7 +358,7 @@ Item {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
readonly property string rulesFile: CompositorService.isNiri ? "dms/windowrules.kdl" : "dms/windowrules.conf"
|
||||
readonly property string rulesFile: CompositorService.isNiri ? "dms/windowrules.kdl" : "dms/windowrules.lua"
|
||||
text: warningBox.showSetup ? I18n.tr("Click 'Setup' to create %1 and add include to your compositor config.").arg(rulesFile) : I18n.tr("%1 exists but is not included. Window rules won't apply.").arg(rulesFile)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
|
||||
@@ -14,10 +14,10 @@ Singleton {
|
||||
|
||||
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
|
||||
readonly property string hyprDmsDir: configDir + "/hypr/dms"
|
||||
readonly property string outputsPath: hyprDmsDir + "/outputs.conf"
|
||||
readonly property string layoutPath: hyprDmsDir + "/layout.conf"
|
||||
readonly property string cursorPath: hyprDmsDir + "/cursor.conf"
|
||||
readonly property string windowrulesPath: hyprDmsDir + "/windowrules.conf"
|
||||
readonly property string outputsPath: hyprDmsDir + "/outputs.lua"
|
||||
readonly property string layoutPath: hyprDmsDir + "/layout.lua"
|
||||
readonly property string cursorPath: hyprDmsDir + "/cursor.lua"
|
||||
readonly property string windowrulesPath: hyprDmsDir + "/windowrules.lua"
|
||||
|
||||
property int _lastGapValue: -1
|
||||
|
||||
@@ -31,7 +31,7 @@ Singleton {
|
||||
function ensureWindowrulesConfig() {
|
||||
Proc.runCommand("hypr-ensure-windowrules", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && [ ! -f "${windowrulesPath}" ] && touch "${windowrulesPath}" || true`], (output, exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
log.warn("Failed to ensure windowrules.conf:", output);
|
||||
log.warn("Failed to ensure windowrules.lua:", output);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,6 +62,18 @@ Singleton {
|
||||
return outputName;
|
||||
}
|
||||
|
||||
function luaQuoted(str) {
|
||||
return JSON.stringify(String(str ?? ""));
|
||||
}
|
||||
|
||||
function forceFlagValue(value) {
|
||||
if (value === true)
|
||||
return 1;
|
||||
if (value === false)
|
||||
return -1;
|
||||
return Number(value);
|
||||
}
|
||||
|
||||
function generateOutputsConfig(outputsData, hyprlandSettings, callback) {
|
||||
if (!outputsData || Object.keys(outputsData).length === 0) {
|
||||
if (callback)
|
||||
@@ -70,8 +82,7 @@ Singleton {
|
||||
}
|
||||
|
||||
const settings = hyprlandSettings || SettingsData.hyprlandOutputSettings;
|
||||
let lines = ["# Auto-generated by DMS - do not edit manually", ""];
|
||||
let monitorv2Blocks = [];
|
||||
let lines = ["-- Auto-generated by DMS — do not edit manually", ""];
|
||||
|
||||
for (const outputName in outputsData) {
|
||||
const output = outputsData[outputName];
|
||||
@@ -82,7 +93,7 @@ Singleton {
|
||||
const outputSettings = settings[identifier] || {};
|
||||
|
||||
if (outputSettings.disabled) {
|
||||
lines.push("monitor = " + identifier + ", disable");
|
||||
lines.push(`hl.monitor({ output = ${luaQuoted(identifier)}, disabled = true })`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -98,68 +109,42 @@ Singleton {
|
||||
const position = x + "x" + y;
|
||||
const scale = output.logical?.scale ?? 1.0;
|
||||
|
||||
let monitorLine = "monitor = " + identifier + ", " + resolution + ", " + position + ", " + scale;
|
||||
const parts = [`output = ${luaQuoted(identifier)}`, `mode = ${luaQuoted(resolution)}`, `position = ${luaQuoted(position)}`, `scale = ${Number(scale)}`];
|
||||
|
||||
const transform = transformToHyprland(output.logical?.transform ?? "Normal");
|
||||
if (transform !== 0)
|
||||
monitorLine += ", transform, " + transform;
|
||||
parts.push(`transform = ${transform}`);
|
||||
|
||||
if (output.vrr_supported) {
|
||||
const vrrMode = outputSettings.vrrFullscreenOnly ? 2 : (output.vrr_enabled ? 1 : 0);
|
||||
monitorLine += ", vrr, " + vrrMode;
|
||||
parts.push(`vrr = ${vrrMode}`);
|
||||
}
|
||||
|
||||
if (output.mirror && output.mirror.length > 0)
|
||||
monitorLine += ", mirror, " + output.mirror;
|
||||
parts.push(`mirror = ${luaQuoted(output.mirror)}`);
|
||||
|
||||
if (outputSettings.bitdepth && outputSettings.bitdepth !== 8)
|
||||
monitorLine += ", bitdepth, " + outputSettings.bitdepth;
|
||||
parts.push(`bitdepth = ${Number(outputSettings.bitdepth)}`);
|
||||
|
||||
if (outputSettings.colorManagement && outputSettings.colorManagement !== "auto")
|
||||
monitorLine += ", cm, " + outputSettings.colorManagement;
|
||||
parts.push(`cm = ${luaQuoted(outputSettings.colorManagement)}`);
|
||||
|
||||
if (outputSettings.sdrBrightness !== undefined && outputSettings.sdrBrightness !== 1.0)
|
||||
monitorLine += ", sdrbrightness, " + outputSettings.sdrBrightness;
|
||||
parts.push(`sdrbrightness = ${Number(outputSettings.sdrBrightness)}`);
|
||||
|
||||
if (outputSettings.sdrSaturation !== undefined && outputSettings.sdrSaturation !== 1.0)
|
||||
monitorLine += ", sdrsaturation, " + outputSettings.sdrSaturation;
|
||||
parts.push(`sdrsaturation = ${Number(outputSettings.sdrSaturation)}`);
|
||||
|
||||
lines.push(monitorLine);
|
||||
if (outputSettings.supportsWideColor !== undefined)
|
||||
parts.push(`supports_wide_color = ${forceFlagValue(outputSettings.supportsWideColor)}`);
|
||||
|
||||
const needsMonitorv2 = outputSettings.supportsHdr || outputSettings.supportsWideColor || outputSettings.sdrMinLuminance !== undefined || outputSettings.sdrMaxLuminance !== undefined || outputSettings.minLuminance !== undefined || outputSettings.maxLuminance !== undefined || outputSettings.maxAvgLuminance !== undefined;
|
||||
if (outputSettings.supportsHdr !== undefined)
|
||||
parts.push(`supports_hdr = ${forceFlagValue(outputSettings.supportsHdr)}`);
|
||||
|
||||
if (needsMonitorv2) {
|
||||
let block = "monitorv2 {\n";
|
||||
block += " output = " + identifier + "\n";
|
||||
|
||||
if (outputSettings.supportsWideColor)
|
||||
block += " supports_wide_color = true\n";
|
||||
if (outputSettings.supportsHdr)
|
||||
block += " supports_hdr = true\n";
|
||||
if (outputSettings.sdrMinLuminance !== undefined)
|
||||
block += " sdr_min_luminance = " + outputSettings.sdrMinLuminance + "\n";
|
||||
if (outputSettings.sdrMaxLuminance !== undefined)
|
||||
block += " sdr_max_luminance = " + outputSettings.sdrMaxLuminance + "\n";
|
||||
if (outputSettings.minLuminance !== undefined)
|
||||
block += " min_luminance = " + outputSettings.minLuminance + "\n";
|
||||
if (outputSettings.maxLuminance !== undefined)
|
||||
block += " max_luminance = " + outputSettings.maxLuminance + "\n";
|
||||
if (outputSettings.maxAvgLuminance !== undefined)
|
||||
block += " max_avg_luminance = " + outputSettings.maxAvgLuminance + "\n";
|
||||
|
||||
block += "}";
|
||||
monitorv2Blocks.push(block);
|
||||
}
|
||||
}
|
||||
|
||||
if (monitorv2Blocks.length > 0) {
|
||||
lines.push("");
|
||||
for (const block of monitorv2Blocks)
|
||||
lines.push(block);
|
||||
lines.push("hl.monitor({ " + parts.join(", ") + " })");
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
|
||||
const content = lines.join("\n");
|
||||
|
||||
Proc.runCommand("hypr-write-outputs", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
|
||||
@@ -196,17 +181,18 @@ Singleton {
|
||||
const gaps = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutGapsOverride >= 0) ? SettingsData.hyprlandLayoutGapsOverride : defaultGaps;
|
||||
const borderSize = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutBorderSize >= 0) ? SettingsData.hyprlandLayoutBorderSize : defaultBorderSize;
|
||||
|
||||
let content = `# Auto-generated by DMS - do not edit manually
|
||||
let content = `-- Auto-generated by DMS — do not edit manually
|
||||
|
||||
general {
|
||||
gaps_in = ${gaps}
|
||||
gaps_out = ${gaps}
|
||||
border_size = ${borderSize}
|
||||
}
|
||||
|
||||
decoration {
|
||||
rounding = ${cornerRadius}
|
||||
}
|
||||
hl.config({
|
||||
general = {
|
||||
gaps_in = ${gaps},
|
||||
gaps_out = ${gaps},
|
||||
border_size = ${borderSize},
|
||||
},
|
||||
decoration = {
|
||||
rounding = ${cornerRadius},
|
||||
},
|
||||
})
|
||||
`;
|
||||
|
||||
Proc.runCommand("hypr-write-layout", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
|
||||
@@ -271,7 +257,7 @@ decoration {
|
||||
|
||||
const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : null;
|
||||
if (!settings) {
|
||||
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
|
||||
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && printf '%s\\n' "-- Auto-generated by DMS — do not edit manually" "" > "${cursorPath}"`], (output, exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
log.warn("Failed to write cursor config:", output);
|
||||
});
|
||||
@@ -289,32 +275,34 @@ decoration {
|
||||
const hasCursorSettings = hideOnKeyPress || hideOnTouch || inactiveTimeout > 0;
|
||||
|
||||
if (!hasTheme && !hasNonDefaultSize && !hasCursorSettings) {
|
||||
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
|
||||
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && printf '%s\\n' "-- Auto-generated by DMS — do not edit manually" "" > "${cursorPath}"`], (output, exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
log.warn("Failed to write cursor config:", output);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let lines = ["# Auto-generated by DMS - do not edit manually", ""];
|
||||
let lines = ["-- Auto-generated by DMS — do not edit manually", ""];
|
||||
|
||||
if (hasTheme) {
|
||||
lines.push(`env = HYPRCURSOR_THEME,${themeName}`);
|
||||
lines.push(`env = XCURSOR_THEME,${themeName}`);
|
||||
lines.push(`hl.env("HYPRCURSOR_THEME", ${luaQuoted(themeName)})`);
|
||||
lines.push(`hl.env("XCURSOR_THEME", ${luaQuoted(themeName)})`);
|
||||
}
|
||||
lines.push(`env = HYPRCURSOR_SIZE,${size}`);
|
||||
lines.push(`env = XCURSOR_SIZE,${size}`);
|
||||
lines.push(`hl.env("HYPRCURSOR_SIZE", ${luaQuoted(String(size))})`);
|
||||
lines.push(`hl.env("XCURSOR_SIZE", ${luaQuoted(String(size))})`);
|
||||
|
||||
if (hasCursorSettings) {
|
||||
lines.push("");
|
||||
lines.push("cursor {");
|
||||
lines.push("hl.config({");
|
||||
lines.push("\tcursor = {");
|
||||
if (hideOnKeyPress)
|
||||
lines.push(" hide_on_key_press = true");
|
||||
lines.push("\t\thide_on_key_press = true,");
|
||||
if (hideOnTouch)
|
||||
lines.push(" hide_on_touch = true");
|
||||
lines.push("\t\thide_on_touch = true,");
|
||||
if (inactiveTimeout > 0)
|
||||
lines.push(` inactive_timeout = ${inactiveTimeout}`);
|
||||
lines.push("}");
|
||||
lines.push(`\t\tinactive_timeout = ${inactiveTimeout},`);
|
||||
lines.push("\t},");
|
||||
lines.push("})");
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
|
||||
@@ -7,6 +7,7 @@ import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import "../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
|
||||
import "../Common/KeybindActions.js" as Actions
|
||||
|
||||
Singleton {
|
||||
@@ -82,6 +83,7 @@ Singleton {
|
||||
case "niri":
|
||||
return compositorConfigDir + "/dms/binds.kdl";
|
||||
case "hyprland":
|
||||
return compositorConfigDir + "/dms/binds.lua";
|
||||
case "mangowc":
|
||||
return compositorConfigDir + "/dms/binds.conf";
|
||||
default:
|
||||
@@ -93,7 +95,7 @@ Singleton {
|
||||
case "niri":
|
||||
return compositorConfigDir + "/config.kdl";
|
||||
case "hyprland":
|
||||
return compositorConfigDir + "/hyprland.conf";
|
||||
return compositorConfigDir + "/hyprland.lua";
|
||||
case "mangowc":
|
||||
return compositorConfigDir + "/config.conf";
|
||||
default:
|
||||
@@ -247,8 +249,8 @@ Singleton {
|
||||
root.lastError = "";
|
||||
root.dmsBindsIncluded = true;
|
||||
root.dmsBindsFixed();
|
||||
const bindsFile = root.currentProvider === "niri" ? "dms/binds.kdl" : "dms/binds.conf";
|
||||
ToastService.showInfo(I18n.tr("Binds include added"), I18n.tr("%1 is now included in config").arg(bindsFile), "", "keybinds");
|
||||
const bindsRel = root.currentProvider === "niri" ? "dms/binds.kdl" : root.currentProvider === "hyprland" ? "dms/binds.lua" : "dms/binds.conf";
|
||||
ToastService.showInfo(I18n.tr("Binds include added"), I18n.tr("%1 is now included in config").arg(bindsRel), "", "keybinds");
|
||||
Qt.callLater(root.forceReload);
|
||||
}
|
||||
}
|
||||
@@ -262,13 +264,36 @@ Singleton {
|
||||
let script;
|
||||
switch (currentProvider) {
|
||||
case "niri":
|
||||
script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.kdl" && cp "${mainConfigPath}" "${backupPath}" && echo 'include "dms/binds.kdl"' >> "${mainConfigPath}"`;
|
||||
script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: mainConfigPath,
|
||||
backupFile: backupPath,
|
||||
fragmentFile: compositorConfigDir + "/dms/binds.kdl",
|
||||
grepPattern: 'include.*"dms/binds.kdl"',
|
||||
includeLine: 'include "dms/binds.kdl"'
|
||||
});
|
||||
break;
|
||||
case "hyprland":
|
||||
script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.conf" && cp "${mainConfigPath}" "${backupPath}" && echo 'source = ./dms/binds.conf' >> "${mainConfigPath}"`;
|
||||
script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: mainConfigPath,
|
||||
backupFile: backupPath,
|
||||
fragmentFiles: [compositorConfigDir + "/dms/binds.lua", compositorConfigDir + "/dms/binds-user.lua"],
|
||||
includes: [{
|
||||
grepPattern: "dms.binds",
|
||||
includeLine: "require(\"dms.binds\")"
|
||||
}, {
|
||||
grepPattern: "dms.binds-user",
|
||||
includeLine: "require(\"dms.binds-user\")"
|
||||
}]
|
||||
});
|
||||
break;
|
||||
case "mangowc":
|
||||
script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.conf" && cp "${mainConfigPath}" "${backupPath}" && echo 'source = ./dms/binds.conf' >> "${mainConfigPath}"`;
|
||||
script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: mainConfigPath,
|
||||
backupFile: backupPath,
|
||||
fragmentFile: compositorConfigDir + "/dms/binds.conf",
|
||||
grepPattern: "source.*dms/binds.conf",
|
||||
includeLine: "source = ./dms/binds.conf"
|
||||
});
|
||||
break;
|
||||
default:
|
||||
fixing = false;
|
||||
@@ -321,6 +346,7 @@ Singleton {
|
||||
"statusMessage": status.statusMessage ?? ""
|
||||
};
|
||||
}
|
||||
_maybeWarnHyprlandLegacyConf();
|
||||
|
||||
if (!_rawData?.binds) {
|
||||
_allBinds = {};
|
||||
@@ -365,10 +391,13 @@ Singleton {
|
||||
for (var i = 0; i < binds.length; i++) {
|
||||
const bind = binds[i];
|
||||
const action = bind.action || "";
|
||||
const sourceStr = bind.source || "config";
|
||||
const keyData = {
|
||||
"key": bind.key || "",
|
||||
"source": bind.source || "config",
|
||||
"isOverride": bind.source === "dms",
|
||||
"source": sourceStr,
|
||||
"isOverride": sourceStr === "dms",
|
||||
"isDMSManaged": sourceStr === "dms" || sourceStr === "dms-default",
|
||||
"hasDefault": bind.hasDefault === true,
|
||||
"cooldownMs": bind.cooldownMs || 0,
|
||||
"flags": bind.flags || "",
|
||||
"allowWhenLocked": bind.allowWhenLocked || false,
|
||||
@@ -456,6 +485,19 @@ Singleton {
|
||||
_pendingSavedKey = bindData.key;
|
||||
}
|
||||
|
||||
property bool _hyprlandLegacyWarnShown: false
|
||||
|
||||
function _maybeWarnHyprlandLegacyConf() {
|
||||
if (_hyprlandLegacyWarnShown)
|
||||
return;
|
||||
if (currentProvider !== "hyprland")
|
||||
return;
|
||||
if (!dmsStatus.exists || dmsStatus.included)
|
||||
return;
|
||||
_hyprlandLegacyWarnShown = true;
|
||||
ToastService.showWarning(I18n.tr("Hyprland config still uses hyprlang"), I18n.tr("DMS Settings now writes Lua. Edits won't apply until you migrate."), "dms setup", "hyprland-migration");
|
||||
}
|
||||
|
||||
function removeBind(key) {
|
||||
if (!key)
|
||||
return;
|
||||
@@ -464,6 +506,14 @@ Singleton {
|
||||
bindRemoved(key);
|
||||
}
|
||||
|
||||
function resetBind(key) {
|
||||
if (!key)
|
||||
return;
|
||||
removeProcess.command = ["dms", "keybinds", "reset", currentProvider, key];
|
||||
removeProcess.running = true;
|
||||
bindRemoved(key);
|
||||
}
|
||||
|
||||
function isDmsAction(action) {
|
||||
return Actions.isDmsAction(action);
|
||||
}
|
||||
|
||||
@@ -66,13 +66,12 @@ Item {
|
||||
signal toggleExpand
|
||||
signal saveBind(string originalKey, var newData)
|
||||
signal removeBind(string key)
|
||||
signal resetBind(string key)
|
||||
signal cancelEdit
|
||||
|
||||
implicitHeight: contentColumn.implicitHeight
|
||||
height: implicitHeight
|
||||
|
||||
Component.onDestruction: _destroyShortcutInhibitor()
|
||||
|
||||
Component.onCompleted: {
|
||||
if (isNew && isExpanded)
|
||||
resetEdits();
|
||||
@@ -831,9 +830,12 @@ Item {
|
||||
color: root._actionType === modelData.id ? Theme.surfaceContainerHighest : Theme.surfaceContainer
|
||||
border.color: root._actionType === modelData.id ? Theme.outline : (typeArea.containsMouse ? Theme.outlineVariant : "transparent")
|
||||
border.width: 1
|
||||
clip: true
|
||||
|
||||
RowLayout {
|
||||
anchors.centerIn: parent
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
@@ -843,10 +845,13 @@ Item {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
text: typeDelegate.modelData.label
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: root._actionType === typeDelegate.modelData.id ? Theme.surfaceText : Theme.surfaceVariantText
|
||||
visible: typeDelegate.width > 100
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1763,10 +1768,19 @@ Item {
|
||||
iconName: "delete"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.error
|
||||
visible: root.editingKeyIndex >= 0 && root.editingKeyIndex < root.keys.length && root.keys[root.editingKeyIndex].isOverride && !root.isNew
|
||||
visible: root.editingKeyIndex >= 0 && root.editingKeyIndex < root.keys.length && (root.keys[root.editingKeyIndex].isDMSManaged || root.keys[root.editingKeyIndex].isOverride) && !root.isNew
|
||||
onClicked: root.removeBind(root._originalKey)
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset to default")
|
||||
buttonHeight: root._buttonHeight
|
||||
backgroundColor: Theme.surfaceContainer
|
||||
textColor: Theme.primary
|
||||
visible: root.editingKeyIndex >= 0 && root.editingKeyIndex < root.keys.length && root.keys[root.editingKeyIndex].isOverride === true && root.keys[root.editingKeyIndex].hasDefault === true && !root.isNew
|
||||
onClicked: root.resetBind(root._originalKey)
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[templates.dmshyprland]
|
||||
input_path = 'SHELL_DIR/matugen/templates/hypr-colors.conf'
|
||||
output_path = 'CONFIG_DIR/hypr/dms/colors.conf'
|
||||
input_path = 'SHELL_DIR/matugen/templates/hypr-colors.lua'
|
||||
output_path = 'CONFIG_DIR/hypr/dms/colors.lua'
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# Auto-generated by DMS - do not edit manually
|
||||
# Remove source = ./dms/colors.conf from your config to override.
|
||||
|
||||
$primary = rgb({{colors.primary.default.hex_stripped}})
|
||||
$outline = rgb({{colors.outline.default.hex_stripped}})
|
||||
$error = rgb({{colors.error.default.hex_stripped}})
|
||||
|
||||
general {
|
||||
col.active_border = $primary
|
||||
col.inactive_border = $outline
|
||||
}
|
||||
|
||||
group {
|
||||
col.border_active = $primary
|
||||
col.border_inactive = $outline
|
||||
col.border_locked_active = $error
|
||||
col.border_locked_inactive = $outline
|
||||
|
||||
groupbar {
|
||||
col.active = $primary
|
||||
col.inactive = $outline
|
||||
col.locked_active = $error
|
||||
col.locked_inactive = $outline
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
-- Auto-generated by DMS Matugen hook — do not edit manually.
|
||||
-- Remove require("dms.colors") from hyprland.lua to override.
|
||||
|
||||
hl.config({
|
||||
general = {
|
||||
col = {
|
||||
active_border = "rgb({{colors.primary.default.hex_stripped}})",
|
||||
inactive_border = "rgb({{colors.outline.default.hex_stripped}})",
|
||||
},
|
||||
},
|
||||
group = {
|
||||
col = {
|
||||
border_active = "rgb({{colors.primary.default.hex_stripped}})",
|
||||
border_inactive = "rgb({{colors.outline.default.hex_stripped}})",
|
||||
border_locked_active = "rgb({{colors.error.default.hex_stripped}})",
|
||||
border_locked_inactive = "rgb({{colors.outline.default.hex_stripped}})",
|
||||
},
|
||||
groupbar = {
|
||||
col = {
|
||||
active = "rgb({{colors.primary.default.hex_stripped}})",
|
||||
inactive = "rgb({{colors.outline.default.hex_stripped}})",
|
||||
locked_active = "rgb({{colors.error.default.hex_stripped}})",
|
||||
locked_inactive = "rgb({{colors.outline.default.hex_stripped}})",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user