diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 757038cc..8b1ad10a 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -15,7 +15,10 @@ Mark the relevant option with an "x".
## Testing
Describe how you tested your changes and mark the relevant items.
-- [ ] Tested on compositor: (e.g., Hyprland, Sway, Niri)
+
+- [ ] Tested on niri
+- [ ] Tested on Hyprland
+- [ ] Tested on sway
- [ ] Tested with different bar positions and density settings
- [ ] Tested with multiple monitors (if applicable)
diff --git a/Assets/ColorScheme/Ayu/Ayu.json b/Assets/ColorScheme/Ayu/Ayu.json
index a8e0d3ea..a1954f17 100644
--- a/Assets/ColorScheme/Ayu/Ayu.json
+++ b/Assets/ColorScheme/Ayu/Ayu.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#0B0E14",
"mOnSurfaceVariant": "#636A72",
"mOutline": "#565B66",
- "mShadow": "#000000"
+ "mShadow": "#000000",
+ "mHover": "#39BAE6",
+ "mOnHover": "#0B0E14"
},
"light": {
"mPrimary": "#FF8F40",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#F8F9FA",
"mOnSurfaceVariant": "#ABADB1",
"mOutline": "#8A9199",
- "mShadow": "#F8F9FA"
+ "mShadow": "#F8F9FA",
+ "mHover": "#55B4D4",
+ "mOnHover": "#F8F9FA"
}
}
diff --git a/Assets/ColorScheme/Ayu/terminal/wezterm/Ayu-dark.toml b/Assets/ColorScheme/Ayu/terminal/wezterm/Ayu-dark.toml
new file mode 100644
index 00000000..e104224b
--- /dev/null
+++ b/Assets/ColorScheme/Ayu/terminal/wezterm/Ayu-dark.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#0a0e14",
+ "#ff3333",
+ "#c2d94c",
+ "#ff8f40",
+ "#59c2ff",
+ "#ffee99",
+ "#95e6cb",
+ "#b3b1ad",
+]
+background = "#0a0e14"
+brights = [
+ "#4d5566",
+ "#ff3333",
+ "#c2d94c",
+ "#ff8f40",
+ "#59c2ff",
+ "#ffee99",
+ "#95e6cb",
+ "#b3b1ad",
+]
+cursor_bg = "#e6b450"
+cursor_border = "#e6b450"
+cursor_fg = "#0a0e14"
+foreground = "#b3b1ad"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Ayu/terminal/wezterm/Ayu-light.toml b/Assets/ColorScheme/Ayu/terminal/wezterm/Ayu-light.toml
new file mode 100644
index 00000000..bc2dad20
--- /dev/null
+++ b/Assets/ColorScheme/Ayu/terminal/wezterm/Ayu-light.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#575f66",
+ "#f51818",
+ "#86b300",
+ "#f2ae49",
+ "#399ee6",
+ "#a37acc",
+ "#4cbf99",
+ "#fafafa",
+]
+background = "#fafafa"
+brights = [
+ "#8a9199",
+ "#f51818",
+ "#86b300",
+ "#f2ae49",
+ "#399ee6",
+ "#a37acc",
+ "#4cbf99",
+ "#fafafa",
+]
+cursor_bg = "#ff9940"
+cursor_border = "#ff9940"
+cursor_fg = "#fafafa"
+foreground = "#575f66"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Catppuccin/Catppuccin.json b/Assets/ColorScheme/Catppuccin/Catppuccin.json
index 725f1830..c02d8fd1 100644
--- a/Assets/ColorScheme/Catppuccin/Catppuccin.json
+++ b/Assets/ColorScheme/Catppuccin/Catppuccin.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#313244",
"mOnSurfaceVariant": "#a3b4eb",
"mOutline": "#4c4f69",
- "mShadow": "#11111b"
+ "mShadow": "#11111b",
+ "mHover": "#94e2d5",
+ "mOnHover": "#11111b"
},
"light": {
"mPrimary": "#8839ef",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#ccd0da",
"mOnSurfaceVariant": "#6c6f85",
"mOutline": "#a5adcb",
- "mShadow": "#dce0e8"
+ "mShadow": "#dce0e8",
+ "mHover": "#40a02b",
+ "mOnHover": "#eff1f5"
}
}
diff --git a/Assets/ColorScheme/Catppuccin/terminal/wezterm/Catppuccin-dark.toml b/Assets/ColorScheme/Catppuccin/terminal/wezterm/Catppuccin-dark.toml
new file mode 100644
index 00000000..5d4eb50e
--- /dev/null
+++ b/Assets/ColorScheme/Catppuccin/terminal/wezterm/Catppuccin-dark.toml
@@ -0,0 +1,84 @@
+[colors]
+ansi = [
+ "#45475a",
+ "#f38ba8",
+ "#a6e3a1",
+ "#f9e2af",
+ "#89b4fa",
+ "#f5c2e7",
+ "#94e2d5",
+ "#bac2de",
+]
+background = "#1e1e2e"
+brights = [
+ "#585b70",
+ "#f38ba8",
+ "#a6e3a1",
+ "#f9e2af",
+ "#89b4fa",
+ "#f5c2e7",
+ "#94e2d5",
+ "#a6adc8",
+]
+compose_cursor = "#f2cdcd"
+cursor_bg = "#f5e0dc"
+cursor_border = "#f5e0dc"
+cursor_fg = "#11111b"
+foreground = "#cdd6f4"
+scrollbar_thumb = "#585b70"
+selection_bg = "#585b70"
+selection_fg = "#cdd6f4"
+split = "#6c7086"
+visual_bell = "#313244"
+
+[colors.indexed]
+16 = "#fab387"
+17 = "#f5e0dc"
+
+[colors.tab_bar]
+background = "#11111b"
+inactive_tab_edge = "#313244"
+
+[colors.tab_bar.active_tab]
+bg_color = "#cba6f7"
+fg_color = "#11111b"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab]
+bg_color = "#181825"
+fg_color = "#cdd6f4"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab_hover]
+bg_color = "#1e1e2e"
+fg_color = "#cdd6f4"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab]
+bg_color = "#313244"
+fg_color = "#cdd6f4"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab_hover]
+bg_color = "#45475a"
+fg_color = "#cdd6f4"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[metadata]
+author = "Catppuccin Org"
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Catppuccin/terminal/wezterm/Catppuccin-light.toml b/Assets/ColorScheme/Catppuccin/terminal/wezterm/Catppuccin-light.toml
new file mode 100644
index 00000000..9feddaa3
--- /dev/null
+++ b/Assets/ColorScheme/Catppuccin/terminal/wezterm/Catppuccin-light.toml
@@ -0,0 +1,84 @@
+[colors]
+ansi = [
+ "#bcc0cc",
+ "#d20f39",
+ "#40a02b",
+ "#df8e1d",
+ "#1e66f5",
+ "#ea76cb",
+ "#179299",
+ "#5c5f77",
+]
+background = "#eff1f5"
+brights = [
+ "#acb0be",
+ "#d20f39",
+ "#40a02b",
+ "#df8e1d",
+ "#1e66f5",
+ "#ea76cb",
+ "#179299",
+ "#6c6f85",
+]
+compose_cursor = "#dd7878"
+cursor_bg = "#dc8a78"
+cursor_border = "#dc8a78"
+cursor_fg = "#dce0e8"
+foreground = "#4c4f69"
+scrollbar_thumb = "#acb0be"
+selection_bg = "#acb0be"
+selection_fg = "#4c4f69"
+split = "#9ca0b0"
+visual_bell = "#ccd0da"
+
+[colors.indexed]
+16 = "#fe640b"
+17 = "#dc8a78"
+
+[colors.tab_bar]
+background = "#dce0e8"
+inactive_tab_edge = "#ccd0da"
+
+[colors.tab_bar.active_tab]
+bg_color = "#8839ef"
+fg_color = "#dce0e8"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab]
+bg_color = "#e6e9ef"
+fg_color = "#4c4f69"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab_hover]
+bg_color = "#eff1f5"
+fg_color = "#4c4f69"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab]
+bg_color = "#ccd0da"
+fg_color = "#4c4f69"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab_hover]
+bg_color = "#bcc0cc"
+fg_color = "#4c4f69"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[metadata]
+author = "Catppuccin Org"
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Cyberpunk/Cyberpunk.json b/Assets/ColorScheme/Cyberpunk/Cyberpunk.json
index de30e528..27b76a9d 100644
--- a/Assets/ColorScheme/Cyberpunk/Cyberpunk.json
+++ b/Assets/ColorScheme/Cyberpunk/Cyberpunk.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#11151D",
"mOnSurfaceVariant": "#9B6BC1",
"mOutline": "#45A0D6",
- "mShadow": "#090D13"
+ "mShadow": "#090D13",
+ "mHover": "#00A66C",
+ "mOnHover": "#0E1015"
},
"light": {
"mPrimary": "#00B8B3",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#C8DEE6",
"mOnSurfaceVariant": "#1A1914",
"mOutline": "#7B52AB",
- "mShadow": "#B8D4E6"
+ "mShadow": "#B8D4E6",
+ "mHover": "#45D395",
+ "mOnHover": "#1A1914"
}
}
\ No newline at end of file
diff --git a/Assets/ColorScheme/Cyberpunk/terminal/wezterm/Cyberpunk-dark.toml b/Assets/ColorScheme/Cyberpunk/terminal/wezterm/Cyberpunk-dark.toml
new file mode 100644
index 00000000..99ef8ab5
--- /dev/null
+++ b/Assets/ColorScheme/Cyberpunk/terminal/wezterm/Cyberpunk-dark.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#0c0e14",
+ "#e64572",
+ "#89d36a",
+ "#d7a23a",
+ "#4f8fff",
+ "#9d6dff",
+ "#43c9ff",
+ "#b7c4f2",
+]
+background = "#0a0d14"
+brights = [
+ "#2b314a",
+ "#e64572",
+ "#89d36a",
+ "#d7a23a",
+ "#4f8fff",
+ "#9d6dff",
+ "#43c9ff",
+ "#d8e0ff",
+]
+cursor_bg = "#d8e0ff"
+cursor_border = "#d8e0ff"
+cursor_fg = "#0a0d14"
+foreground = "#d8e0ff"
+selection_bg = "#d8e0ff"
+selection_fg = "#0a0d14"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Cyberpunk/terminal/wezterm/Cyberpunk-light.toml b/Assets/ColorScheme/Cyberpunk/terminal/wezterm/Cyberpunk-light.toml
new file mode 100644
index 00000000..e47261a7
--- /dev/null
+++ b/Assets/ColorScheme/Cyberpunk/terminal/wezterm/Cyberpunk-light.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#e9e9ed",
+ "#f52a65",
+ "#587539",
+ "#8c6c3e",
+ "#2e7de9",
+ "#9854f1",
+ "#007197",
+ "#6172b0",
+]
+background = "#e1e2e7"
+brights = [
+ "#a1a6c5",
+ "#f52a65",
+ "#587539",
+ "#8c6c3e",
+ "#2e7de9",
+ "#9854f1",
+ "#007197",
+ "#3760bf",
+]
+cursor_bg = "#3760bf"
+cursor_border = "#3760bf"
+cursor_fg = "#e1e2e7"
+foreground = "#3760bf"
+selection_bg = "#3760bf"
+selection_fg = "#e1e2e7"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Dracula/Dracula.json b/Assets/ColorScheme/Dracula/Dracula.json
index a8bdcd01..5314e9f1 100644
--- a/Assets/ColorScheme/Dracula/Dracula.json
+++ b/Assets/ColorScheme/Dracula/Dracula.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#44475A",
"mOnSurfaceVariant": "#d6d8e0",
"mOutline": "#5a5e77",
- "mShadow": "#282A36"
+ "mShadow": "#282A36",
+ "mHover": "#8be9fd",
+ "mOnHover": "#003543"
},
"light": {
"mPrimary": "#8332f4",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#e6e6ea",
"mOnSurfaceVariant": "#44475a",
"mOutline": "#cacad3",
- "mShadow": "#d6d8e0"
+ "mShadow": "#d6d8e0",
+ "mHover": "#0398b9",
+ "mOnHover": "#ffffff"
}
}
diff --git a/Assets/ColorScheme/Dracula/terminal/wezterm/Dracula-dark.toml b/Assets/ColorScheme/Dracula/terminal/wezterm/Dracula-dark.toml
new file mode 100644
index 00000000..4b9e7651
--- /dev/null
+++ b/Assets/ColorScheme/Dracula/terminal/wezterm/Dracula-dark.toml
@@ -0,0 +1,82 @@
+[colors]
+ansi = [
+ "#21222c",
+ "#ff5555",
+ "#50fa7b",
+ "#f1fa8c",
+ "#bd93f9",
+ "#ff79c6",
+ "#8be9fd",
+ "#f8f8f2",
+]
+background = "#282a36"
+brights = [
+ "#6272a4",
+ "#ff6e6e",
+ "#69ff94",
+ "#ffffa5",
+ "#d6acff",
+ "#ff92df",
+ "#a4ffff",
+ "#ffffff",
+]
+compose_cursor = "#ffb86c"
+cursor_bg = "#f8f8f2"
+cursor_border = "#f8f8f2"
+cursor_fg = "#282a36"
+foreground = "#f8f8f2"
+scrollbar_thumb = "#44475a"
+selection_bg = "rgba(26.666668% 27.843138% 35.294117% 50%)"
+selection_fg = "rgba(0% 0% 0% 0%)"
+split = "#6272a4"
+
+[colors.indexed]
+
+[colors.tab_bar]
+background = "#282a36"
+
+[colors.tab_bar.active_tab]
+bg_color = "#bd93f9"
+fg_color = "#282a36"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab]
+bg_color = "#282a36"
+fg_color = "#f8f8f2"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab_hover]
+bg_color = "#6272a4"
+fg_color = "#f8f8f2"
+intensity = "Normal"
+italic = true
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab]
+bg_color = "#282a36"
+fg_color = "#f8f8f2"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab_hover]
+bg_color = "#ff79c6"
+fg_color = "#f8f8f2"
+intensity = "Normal"
+italic = true
+strikethrough = false
+underline = "None"
+
+[metadata]
+author = "timescam"
+name = "Noctalia"
+
+
diff --git a/Assets/ColorScheme/Dracula/terminal/wezterm/Dracula-light.toml b/Assets/ColorScheme/Dracula/terminal/wezterm/Dracula-light.toml
new file mode 100644
index 00000000..244ef96c
--- /dev/null
+++ b/Assets/ColorScheme/Dracula/terminal/wezterm/Dracula-light.toml
@@ -0,0 +1,82 @@
+[colors]
+ansi = [
+ "#282a36",
+ "#ff5555",
+ "#3D995F",
+ "#b49100",
+ "#8332f4",
+ "#ff1399",
+ "#0398b9",
+ "#e6e6ea",
+]
+background = "#f8f8f2"
+brights = [
+ "#44475a",
+ "#ff6e6e",
+ "#48B372",
+ "#d6ac00",
+ "#bd93f9",
+ "#ff79c6",
+ "#8be9fd",
+ "#f8f8f2",
+]
+compose_cursor = "#ffb86c"
+cursor_bg = "#282a36"
+cursor_border = "#282a36"
+cursor_fg = "#f8f8f2"
+foreground = "#282a36"
+scrollbar_thumb = "#44475a"
+selection_bg = "rgba(26.666668% 27.843138% 35.294117% 50%)"
+selection_fg = "rgba(0% 0% 0% 0%)"
+split = "#44475a"
+
+[colors.indexed]
+
+[colors.tab_bar]
+background = "#f8f8f2"
+
+[colors.tab_bar.active_tab]
+bg_color = "#8332f4"
+fg_color = "#f8f8f2"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab]
+bg_color = "#f8f8f2"
+fg_color = "#282a36"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab_hover]
+bg_color = "#e6e6ea"
+fg_color = "#44475a"
+intensity = "Normal"
+italic = true
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab]
+bg_color = "#f8f8f2"
+fg_color = "#282a36"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab_hover]
+bg_color = "#ff1399"
+fg_color = "#f8f8f2"
+intensity = "Normal"
+italic = true
+strikethrough = false
+underline = "None"
+
+[metadata]
+author = "timescam"
+name = "Noctalia"
+
+
diff --git a/Assets/ColorScheme/Eldritch/Eldritch.json b/Assets/ColorScheme/Eldritch/Eldritch.json
index 3fedfeef..f253e81b 100644
--- a/Assets/ColorScheme/Eldritch/Eldritch.json
+++ b/Assets/ColorScheme/Eldritch/Eldritch.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#292e42",
"mOnSurfaceVariant": "#ABB4DA",
"mOutline": "#3b4261",
- "mShadow": "#414868"
+ "mShadow": "#414868",
+ "mHover": "#a48cf2",
+ "mOnHover": "#171928"
},
"light": {
"mPrimary": "#37f499",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#f2f4f8",
"mOnSurfaceVariant": "#3b4261",
"mOutline": "#b0b6c3",
- "mShadow": "#e0e3e8"
+ "mShadow": "#e0e3e8",
+ "mHover": "#a48cf2",
+ "mOnHover": "#171928"
}
}
diff --git a/Assets/ColorScheme/Eldritch/terminal/wezterm/Eldritch-dark.toml b/Assets/ColorScheme/Eldritch/terminal/wezterm/Eldritch-dark.toml
new file mode 100644
index 00000000..f3e499db
--- /dev/null
+++ b/Assets/ColorScheme/Eldritch/terminal/wezterm/Eldritch-dark.toml
@@ -0,0 +1,80 @@
+[colors]
+ansi = [
+ "#212337",
+ "#f16c75",
+ "#37f499",
+ "#f7c67f",
+ "#a48cf2",
+ "#f265b5",
+ "#04d1f9",
+ "#ebfafa",
+]
+background = "#212337"
+brights = [
+ "#323449",
+ "#f9515d",
+ "#37f499",
+ "#e9f941",
+ "#9071f4",
+ "#f265b5",
+ "#66e4fd",
+ "#ffffff",
+]
+compose_cursor = "#f7c67f"
+cursor_bg = "#37f499"
+cursor_border = "#04d1f9"
+cursor_fg = "#212337"
+foreground = "#ebfafa"
+scrollbar_thumb = "#37f499"
+selection_bg = "rgba(26.666668% 27.843138% 35.294117% 50%)"
+selection_fg = "rgba(0% 0% 0% 0%)"
+split = "#a48cf2"
+
+[colors.indexed]
+
+[colors.tab_bar]
+background = "#212337"
+
+[colors.tab_bar.active_tab]
+bg_color = "#37f499"
+fg_color = "#212337"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab]
+bg_color = "#212337"
+fg_color = "#04d1f9"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab_hover]
+bg_color = "#37f499"
+fg_color = "#212337"
+intensity = "Normal"
+italic = true
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab]
+bg_color = "#212337"
+fg_color = "#ebfafa"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab_hover]
+bg_color = "#37f499"
+fg_color = "#ebfafa"
+intensity = "Normal"
+italic = true
+strikethrough = false
+underline = "None"
+
+[metadata]
+author = "jacobrreed"
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Eldritch/terminal/wezterm/Eldritch-light.toml b/Assets/ColorScheme/Eldritch/terminal/wezterm/Eldritch-light.toml
new file mode 100644
index 00000000..602d31ae
--- /dev/null
+++ b/Assets/ColorScheme/Eldritch/terminal/wezterm/Eldritch-light.toml
@@ -0,0 +1,79 @@
+[colors]
+ansi = [
+ "#171928",
+ "#D9525E",
+ "#2FC280",
+ "#B88F4F",
+ "#8A71D0",
+ "#D94DA0",
+ "#039EBE",
+ "#b0b6c3",
+]
+background = "#f2f4f8"
+brights = [
+ "#3b4261",
+ "#F16C75",
+ "#2FC280",
+ "#D9A42E",
+ "#9A7DF4",
+ "#F27ABF",
+ "#4AD9FD",
+ "#f2f4f8",
+]
+compose_cursor = "#B88F4F"
+cursor_bg = "#171928"
+cursor_border = "#039EBE"
+cursor_fg = "#f2f4f8"
+foreground = "#171928"
+scrollbar_thumb = "#2FC280"
+selection_bg = "rgba(26.666668% 27.843138% 35.294117% 50%)"
+selection_fg = "rgba(0% 0% 0% 0%)"
+split = "#8A71D0"
+
+[colors.indexed]
+
+[colors.tab_bar]
+background = "#f2f4f8"
+
+[colors.tab_bar.active_tab]
+bg_color = "#2FC280"
+fg_color = "#f2f4f8"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab]
+bg_color = "#f2f4f8"
+fg_color = "#039EBE"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab_hover]
+bg_color = "#2FC280"
+fg_color = "#f2f4f8"
+intensity = "Normal"
+italic = true
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab]
+bg_color = "#f2f4f8"
+fg_color = "#171928"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab_hover]
+bg_color = "#2FC280"
+fg_color = "#171928"
+intensity = "Normal"
+italic = true
+strikethrough = false
+underline = "None"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Everforest/Everforest.json b/Assets/ColorScheme/Everforest/Everforest.json
index a912daaa..6e6bb3ea 100644
--- a/Assets/ColorScheme/Everforest/Everforest.json
+++ b/Assets/ColorScheme/Everforest/Everforest.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#2D353B",
"mOnSurfaceVariant": "#D3C6AA",
"mOutline": "#D3C6AA",
- "mShadow": "#475258"
+ "mShadow": "#475258",
+ "mHover": "#9DA9A0",
+ "mOnHover": "#232A2E"
},
"light": {
"mPrimary": "#434F55",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#BEC5B2",
"mOnSurfaceVariant": "#333C43",
"mOutline": "#232A2E",
- "mShadow": "#ECF5ED"
+ "mShadow": "#ECF5ED",
+ "mHover": "#333c43",
+ "mOnHover": "#9DA9A0"
}
}
diff --git a/Assets/ColorScheme/Everforest/terminal/wezterm/Everforest-dark.toml b/Assets/ColorScheme/Everforest/terminal/wezterm/Everforest-dark.toml
new file mode 100644
index 00000000..54c4d9c9
--- /dev/null
+++ b/Assets/ColorScheme/Everforest/terminal/wezterm/Everforest-dark.toml
@@ -0,0 +1,33 @@
+[colors]
+ansi = [
+ "#4b565c",
+ "#e67e80",
+ "#a7c080",
+ "#dbbc7f",
+ "#7fbbb3",
+ "#d699b6",
+ "#83c092",
+ "#d3c6aa",
+]
+background = "#2d353b"
+brights = [
+ "#5c6a72",
+ "#f85552",
+ "#8da101",
+ "#dfa000",
+ "#3a94c5",
+ "#df69ba",
+ "#35a77c",
+ "#dfddc8",
+]
+cursor_bg = "#d3c6aa"
+cursor_border = "#d3c6aa"
+cursor_fg = "#2d353b"
+foreground = "#d3c6aa"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
+
+
diff --git a/Assets/ColorScheme/Everforest/terminal/wezterm/Everforest-light.toml b/Assets/ColorScheme/Everforest/terminal/wezterm/Everforest-light.toml
new file mode 100644
index 00000000..eed23ff5
--- /dev/null
+++ b/Assets/ColorScheme/Everforest/terminal/wezterm/Everforest-light.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#5c6a72",
+ "#f85552",
+ "#8da101",
+ "#dfa000",
+ "#3a94c5",
+ "#df69ba",
+ "#35a77c",
+ "#dfddc8",
+]
+background = "#fdf6e3"
+brights = [
+ "#4b565c",
+ "#e67e80",
+ "#a7c080",
+ "#dbbc7f",
+ "#7fbbb3",
+ "#d699b6",
+ "#83c092",
+ "#d3c6aa",
+]
+cursor_bg = "#5c6a72"
+cursor_border = "#5c6a72"
+cursor_fg = "#fdf6e3"
+foreground = "#5c6a72"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Gruvbox/Gruvbox.json b/Assets/ColorScheme/Gruvbox/Gruvbox.json
index 81269a7c..33ba2470 100644
--- a/Assets/ColorScheme/Gruvbox/Gruvbox.json
+++ b/Assets/ColorScheme/Gruvbox/Gruvbox.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#3c3836",
"mOnSurfaceVariant": "#ebdbb2",
"mOutline": "#57514e",
- "mShadow": "#282828"
+ "mShadow": "#282828",
+ "mHover": "#83a598",
+ "mOnHover": "#282828"
},
"light": {
"mPrimary": "#98971a",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#ebdbb2",
"mOnSurfaceVariant": "#7c6f64",
"mOutline": "#bdae93",
- "mShadow": "#d5c4a1"
+ "mShadow": "#d5c4a1",
+ "mHover": "#458588",
+ "mOnHover": "#fbf1c7"
}
}
diff --git a/Assets/ColorScheme/Gruvbox/terminal/wezterm/Gruvbox-dark.toml b/Assets/ColorScheme/Gruvbox/terminal/wezterm/Gruvbox-dark.toml
new file mode 100644
index 00000000..628a7bc1
--- /dev/null
+++ b/Assets/ColorScheme/Gruvbox/terminal/wezterm/Gruvbox-dark.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#282828",
+ "#cc241d",
+ "#98971a",
+ "#d79921",
+ "#458588",
+ "#b16286",
+ "#689d6a",
+ "#a89984",
+]
+background = "#282828"
+brights = [
+ "#928374",
+ "#fb4934",
+ "#b8bb26",
+ "#fabd2f",
+ "#83a598",
+ "#d3869b",
+ "#8ec07c",
+ "#ebdbb2",
+]
+cursor_bg = "#ebdbb2"
+cursor_border = "#ebdbb2"
+cursor_fg = "#282828"
+foreground = "#ebdbb2"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Gruvbox/terminal/wezterm/Gruvbox-light.toml b/Assets/ColorScheme/Gruvbox/terminal/wezterm/Gruvbox-light.toml
new file mode 100644
index 00000000..b1bfc7a7
--- /dev/null
+++ b/Assets/ColorScheme/Gruvbox/terminal/wezterm/Gruvbox-light.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#fbf1c7",
+ "#cc241d",
+ "#98971a",
+ "#d79921",
+ "#458588",
+ "#b16286",
+ "#689d6a",
+ "#7c6f64",
+]
+background = "#fbf1c7"
+brights = [
+ "#928374",
+ "#9d0006",
+ "#79740e",
+ "#b57614",
+ "#076678",
+ "#8f3f71",
+ "#427b58",
+ "#3c3836",
+]
+cursor_bg = "#3c3836"
+cursor_border = "#3c3836"
+cursor_fg = "#fbf1c7"
+foreground = "#3c3836"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Kanagawa/Kanagawa.json b/Assets/ColorScheme/Kanagawa/Kanagawa.json
index dfb82a8c..20f0a273 100644
--- a/Assets/ColorScheme/Kanagawa/Kanagawa.json
+++ b/Assets/ColorScheme/Kanagawa/Kanagawa.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#2a2a37",
"mOnSurfaceVariant": "#717c7c",
"mOutline": "#363646",
- "mShadow": "#1f1f28"
+ "mShadow": "#1f1f28",
+ "mHover": "#7e9cd8",
+ "mOnHover": "#1f1f28"
},
"light": {
"mPrimary": "#6f894e",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#e5ddb0",
"mOnSurfaceVariant": "#8a8980",
"mOutline": "#cfc49c",
- "mShadow": "#f2ecbc"
+ "mShadow": "#f2ecbc",
+ "mHover": "#4d699b",
+ "mOnHover": "#f2ecbc"
}
}
diff --git a/Assets/ColorScheme/Kanagawa/terminal/wezterm/Kanagawa-dark.toml b/Assets/ColorScheme/Kanagawa/terminal/wezterm/Kanagawa-dark.toml
new file mode 100644
index 00000000..691d5c26
--- /dev/null
+++ b/Assets/ColorScheme/Kanagawa/terminal/wezterm/Kanagawa-dark.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#090618",
+ "#c34043",
+ "#76946a",
+ "#c0a36e",
+ "#7e9cd8",
+ "#957fb8",
+ "#6a9589",
+ "#dcd7ba",
+]
+background = "#1f1f28"
+brights = [
+ "#727169",
+ "#e82424",
+ "#98bb6c",
+ "#e6c384",
+ "#7fb4ca",
+ "#938aa9",
+ "#7aa89f",
+ "#c8c093",
+]
+cursor_bg = "#dcd7ba"
+cursor_border = "#dcd7ba"
+cursor_fg = "#1f1f28"
+foreground = "#dcd7ba"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Kanagawa/terminal/wezterm/Kanagawa-light.toml b/Assets/ColorScheme/Kanagawa/terminal/wezterm/Kanagawa-light.toml
new file mode 100644
index 00000000..e7e293de
--- /dev/null
+++ b/Assets/ColorScheme/Kanagawa/terminal/wezterm/Kanagawa-light.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#1f1f28",
+ "#c84053",
+ "#6f894e",
+ "#77713f",
+ "#4d699b",
+ "#b35b79",
+ "#597b75",
+ "#545464",
+]
+background = "#f2ecbc"
+brights = [
+ "#8a8980",
+ "#d7474b",
+ "#6e915f",
+ "#836f4a",
+ "#6693bf",
+ "#624c83",
+ "#5e857a",
+ "#43436c",
+]
+cursor_bg = "#545464"
+cursor_border = "#545464"
+cursor_fg = "#f2ecbc"
+foreground = "#545464"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Monochrome/Monochrome.json b/Assets/ColorScheme/Monochrome/Monochrome.json
index 1bca771b..31e45479 100644
--- a/Assets/ColorScheme/Monochrome/Monochrome.json
+++ b/Assets/ColorScheme/Monochrome/Monochrome.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#191919",
"mOnSurfaceVariant": "#5d5d5d",
"mOutline": "#3c3c3c",
- "mShadow": "#000000"
+ "mShadow": "#000000",
+ "mHover": "#cccccc",
+ "mOnHover": "#111111"
},
"light": {
"mPrimary": "#555555",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#e8e8e8",
"mOnSurfaceVariant": "#9e9e9e",
"mOutline": "#c3c3c3",
- "mShadow": "#fafafa"
+ "mShadow": "#fafafa",
+ "mHover": "#333333",
+ "mOnHover": "#eeeeee"
}
}
diff --git a/Assets/ColorScheme/Monochrome/terminal/wezterm/Monochrome-dark.toml b/Assets/ColorScheme/Monochrome/terminal/wezterm/Monochrome-dark.toml
new file mode 100644
index 00000000..4023b911
--- /dev/null
+++ b/Assets/ColorScheme/Monochrome/terminal/wezterm/Monochrome-dark.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#191919",
+ "#dddddd",
+ "#cccccc",
+ "#aaaaaa",
+ "#a7a7a7",
+ "#dddddd",
+ "#cccccc",
+ "#828282",
+]
+background = "#111111"
+brights = [
+ "#3c3c3c",
+ "#dddddd",
+ "#cccccc",
+ "#aaaaaa",
+ "#a7a7a7",
+ "#dddddd",
+ "#cccccc",
+ "#ffffff",
+]
+cursor_bg = "#aaaaaa"
+cursor_border = "#aaaaaa"
+cursor_fg = "#111111"
+foreground = "#828282"
+selection_bg = "#828282"
+selection_fg = "#111111"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Monochrome/terminal/wezterm/Monochrome-light.toml b/Assets/ColorScheme/Monochrome/terminal/wezterm/Monochrome-light.toml
new file mode 100644
index 00000000..51ac8812
--- /dev/null
+++ b/Assets/ColorScheme/Monochrome/terminal/wezterm/Monochrome-light.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#e8e8e8",
+ "#222222",
+ "#333333",
+ "#555555",
+ "#505058",
+ "#222222",
+ "#333333",
+ "#696969",
+]
+background = "#d4d4d4"
+brights = [
+ "#c3c3c3",
+ "#222222",
+ "#333333",
+ "#555555",
+ "#505058",
+ "#222222",
+ "#333333",
+ "#000000",
+]
+cursor_bg = "#555555"
+cursor_border = "#555555"
+cursor_fg = "#d4d4d4"
+foreground = "#696969"
+selection_bg = "#696969"
+selection_fg = "#d4d4d4"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Noctalia-default/Noctalia-default.json b/Assets/ColorScheme/Noctalia-default/Noctalia-default.json
index a95b273e..27f8c637 100644
--- a/Assets/ColorScheme/Noctalia-default/Noctalia-default.json
+++ b/Assets/ColorScheme/Noctalia-default/Noctalia-default.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#11112d",
"mOnSurfaceVariant": "#7c80b4",
"mOutline": "#21215F",
- "mShadow": "#070722"
+ "mShadow": "#070722",
+ "mHover": "#9BFECE",
+ "mOnHover": "#0e0e43"
},
"light": {
"mPrimary": "#5d65f5",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#eff0ff",
"mOnSurfaceVariant": "#4b55c8",
"mOutline": "#8288fc",
- "mShadow": "#f3edf7"
+ "mShadow": "#f3edf7",
+ "mHover": "#0e0e43",
+ "mOnHover": "#fef29a"
}
}
diff --git a/Assets/ColorScheme/Noctalia-default/terminal/wezterm/Noctalia-default-dark.toml b/Assets/ColorScheme/Noctalia-default/terminal/wezterm/Noctalia-default-dark.toml
new file mode 100644
index 00000000..48a41f4a
--- /dev/null
+++ b/Assets/ColorScheme/Noctalia-default/terminal/wezterm/Noctalia-default-dark.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#11112d",
+ "#FD4663",
+ "#9BFECE",
+ "#fff59b",
+ "#a9aefe",
+ "#FD4663",
+ "#9BFECE",
+ "#f3edf7",
+]
+background = "#070722"
+brights = [
+ "#21215F",
+ "#FD4663",
+ "#9BFECE",
+ "#fff59b",
+ "#a9aefe",
+ "#FD4663",
+ "#9BFECE",
+ "#ffffff",
+]
+cursor_bg = "#fff59b"
+cursor_border = "#fff59b"
+cursor_fg = "#070722"
+foreground = "#f3edf7"
+selection_bg = "#f3edf7"
+selection_fg = "#070722"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Noctalia-default/terminal/wezterm/Noctalia-default-light.toml b/Assets/ColorScheme/Noctalia-default/terminal/wezterm/Noctalia-default-light.toml
new file mode 100644
index 00000000..bd75b1f3
--- /dev/null
+++ b/Assets/ColorScheme/Noctalia-default/terminal/wezterm/Noctalia-default-light.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#eff0ff",
+ "#FD4663",
+ "#0e0e43",
+ "#5d65f5",
+ "#8E93D8",
+ "#FD4663",
+ "#0e0e43",
+ "#4b55c8",
+]
+background = "#e6e8fa"
+brights = [
+ "#8288fc",
+ "#FD4663",
+ "#0e0e43",
+ "#5d65f5",
+ "#8E93D8",
+ "#FD4663",
+ "#0e0e43",
+ "#0e0e43",
+]
+cursor_bg = "#5d65f5"
+cursor_border = "#5d65f5"
+cursor_fg = "#e6e8fa"
+foreground = "#4b55c8"
+selection_bg = "#4b55c8"
+selection_fg = "#e6e8fa"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Noctalia-legacy/Noctalia-legacy.json b/Assets/ColorScheme/Noctalia-legacy/Noctalia-legacy.json
index fe7704f8..3829f295 100644
--- a/Assets/ColorScheme/Noctalia-legacy/Noctalia-legacy.json
+++ b/Assets/ColorScheme/Noctalia-legacy/Noctalia-legacy.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#262130",
"mOnSurfaceVariant": "#a79ab0",
"mOutline": "#3e364e",
- "mShadow": "#120f18"
+ "mShadow": "#120f18",
+ "mHover": "#e0b7c9",
+ "mOnHover": "#20161f"
},
"light": {
"mPrimary": "#9b59ba",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#e7dfee",
"mOnSurfaceVariant": "#4a3d59",
"mOutline": "#cebedc",
- "mShadow": "#ffffff"
+ "mShadow": "#ffffff",
+ "mHover": "#c17093",
+ "mOnHover": "#ffffff"
}
}
diff --git a/Assets/ColorScheme/Noctalia-legacy/terminal/wezterm/Noctalia-legacy-dark.toml b/Assets/ColorScheme/Noctalia-legacy/terminal/wezterm/Noctalia-legacy-dark.toml
new file mode 100644
index 00000000..71b2e3db
--- /dev/null
+++ b/Assets/ColorScheme/Noctalia-legacy/terminal/wezterm/Noctalia-legacy-dark.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#262130",
+ "#e9899d",
+ "#e0b7c9",
+ "#c7a1d8",
+ "#a984c4",
+ "#e9899d",
+ "#e0b7c9",
+ "#e9e4f0",
+]
+background = "#1c1822"
+brights = [
+ "#3e364e",
+ "#e9899d",
+ "#e0b7c9",
+ "#c7a1d8",
+ "#a984c4",
+ "#e9899d",
+ "#e0b7c9",
+ "#ffffff",
+]
+cursor_bg = "#c7a1d8"
+cursor_border = "#c7a1d8"
+cursor_fg = "#1c1822"
+foreground = "#e9e4f0"
+selection_bg = "#e9e4f0"
+selection_fg = "#1c1822"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Noctalia-legacy/terminal/wezterm/Noctalia-legacy-light.toml b/Assets/ColorScheme/Noctalia-legacy/terminal/wezterm/Noctalia-legacy-light.toml
new file mode 100644
index 00000000..4933afe9
--- /dev/null
+++ b/Assets/ColorScheme/Noctalia-legacy/terminal/wezterm/Noctalia-legacy-light.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#e7dfee",
+ "#e9899d",
+ "#c17093",
+ "#9b59ba",
+ "#784999",
+ "#e9899d",
+ "#c17093",
+ "#1c1822",
+]
+background = "#f5f1fa"
+brights = [
+ "#cebedc",
+ "#e9899d",
+ "#c17093",
+ "#9b59ba",
+ "#784999",
+ "#e9899d",
+ "#c17093",
+ "#1c1822",
+]
+cursor_bg = "#9b59ba"
+cursor_border = "#9b59ba"
+cursor_fg = "#f5f1fa"
+foreground = "#1c1822"
+selection_bg = "#1c1822"
+selection_fg = "#f5f1fa"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Nord/Nord.json b/Assets/ColorScheme/Nord/Nord.json
index 154410d6..58bb595a 100644
--- a/Assets/ColorScheme/Nord/Nord.json
+++ b/Assets/ColorScheme/Nord/Nord.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#3b4252",
"mOnSurfaceVariant": "#d8dee9",
"mOutline": "#505a70",
- "mShadow": "#2e3440"
+ "mShadow": "#2e3440",
+ "mHover": "#5e81ac",
+ "mOnHover": "#2e3440"
},
"light": {
"mPrimary": "#5e81ac",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#e5e9f0",
"mOnSurfaceVariant": "#4c566a",
"mOutline": "#c5cedd",
- "mShadow": "#d8dee9"
+ "mShadow": "#d8dee9",
+ "mHover": "#6fa9a8",
+ "mOnHover": "#eceff4"
}
}
diff --git a/Assets/ColorScheme/Nord/terminal/wezterm/Nord-dark.toml b/Assets/ColorScheme/Nord/terminal/wezterm/Nord-dark.toml
new file mode 100644
index 00000000..5c300f11
--- /dev/null
+++ b/Assets/ColorScheme/Nord/terminal/wezterm/Nord-dark.toml
@@ -0,0 +1,33 @@
+[colors]
+ansi = [
+ "#3b4252",
+ "#bf616a",
+ "#a3be8c",
+ "#ebcb8b",
+ "#81a1c1",
+ "#b48ead",
+ "#88c0d0",
+ "#e5e9f0",
+]
+background = "#2e3440"
+brights = [
+ "#4c566a",
+ "#bf616a",
+ "#a3be8c",
+ "#ebcb8b",
+ "#81a1c1",
+ "#b48ead",
+ "#8fbcbb",
+ "#eceff4",
+]
+cursor_bg = "#eceff4"
+cursor_border = "#eceff4"
+cursor_fg = "#282828"
+foreground = "#d8dee9"
+selection_bg = "#eceff4"
+selection_fg = "#4c566a"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Nord/terminal/wezterm/Nord-light.toml b/Assets/ColorScheme/Nord/terminal/wezterm/Nord-light.toml
new file mode 100644
index 00000000..c204b311
--- /dev/null
+++ b/Assets/ColorScheme/Nord/terminal/wezterm/Nord-light.toml
@@ -0,0 +1,33 @@
+[colors]
+ansi = [
+ "#3b4252",
+ "#bf616a",
+ "#a3be8c",
+ "#ebcb8b",
+ "#81a1c1",
+ "#b48ead",
+ "#88c0d0",
+ "#d8dee9",
+]
+background = "#e5e9f0"
+brights = [
+ "#4c566a",
+ "#bf616a",
+ "#a3be8c",
+ "#ebcb8b",
+ "#81a1c1",
+ "#b48ead",
+ "#8fbcbb",
+ "#eceff4",
+]
+cursor_bg = "#88c0d0"
+cursor_border = "#88c0d0"
+cursor_fg = "#3b4252"
+foreground = "#414858"
+selection_bg = "#d8dee9"
+selection_fg = "#4c556a"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Osaka-jade/Osaka-jade.json b/Assets/ColorScheme/Osaka-jade/Osaka-jade.json
index 5cbefd98..433871f1 100644
--- a/Assets/ColorScheme/Osaka-jade/Osaka-jade.json
+++ b/Assets/ColorScheme/Osaka-jade/Osaka-jade.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#0F251F",
"mOnSurfaceVariant": "#99A8A4",
"mOutline": "#1B6352",
- "mShadow": "#040A09"
+ "mShadow": "#040A09",
+ "mHover": "#26A589",
+ "mOnHover": "#B8C8C4"
},
"light": {
"mPrimary": "#3B7561",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#95AD9C",
"mOnSurfaceVariant": "#263731",
"mOutline": "#5C7A6A",
- "mShadow": "#8A9E90"
+ "mShadow": "#8A9E90",
+ "mHover": "#4A8069",
+ "mOnHover": "#D8E5DB"
}
}
diff --git a/Assets/ColorScheme/Osaka-jade/terminal/wezterm/Osaka-jade-dark.toml b/Assets/ColorScheme/Osaka-jade/terminal/wezterm/Osaka-jade-dark.toml
new file mode 100644
index 00000000..c10554be
--- /dev/null
+++ b/Assets/ColorScheme/Osaka-jade/terminal/wezterm/Osaka-jade-dark.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "#232a2d",
+ "#e57474",
+ "#8ccf7e",
+ "#e5c76b",
+ "#67b0e8",
+ "#c47fd5",
+ "#6cbfbf",
+ "#b3b9b8",
+]
+background = "#141b1e"
+brights = [
+ "#464e50",
+ "#ef7e7e",
+ "#96d988",
+ "#f4d67a",
+ "#71baf2",
+ "#ce89df",
+ "#67cbe7",
+ "#bdc3c2",
+]
+cursor_bg = "#dadada"
+cursor_border = "#dadada"
+cursor_fg = "#141b1e"
+foreground = "#dadada"
+selection_bg = "#dadada"
+selection_fg = "#141b1e"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Osaka-jade/terminal/wezterm/Osaka-jade-light.toml b/Assets/ColorScheme/Osaka-jade/terminal/wezterm/Osaka-jade-light.toml
new file mode 100644
index 00000000..0ee63936
--- /dev/null
+++ b/Assets/ColorScheme/Osaka-jade/terminal/wezterm/Osaka-jade-light.toml
@@ -0,0 +1,32 @@
+[colors]
+ansi = [
+ "#7a8478",
+ "#e67e80",
+ "#9ab373",
+ "#c1a266",
+ "#7fbbb3",
+ "#d699b6",
+ "#83c092",
+ "#b2af9f",
+]
+background = "#efebd4"
+brights = [
+ "#a6b0a0",
+ "#f85552",
+ "#8da101",
+ "#dfa000",
+ "#3a94c5",
+ "#df69ba",
+ "#35a77c",
+ "#fffbef",
+]
+cursor_bg = "#f57d26"
+cursor_border = "#f57d26"
+cursor_fg = "#eaedc8"
+foreground = "#5c6a72"
+selection_bg = "#5c6a72"
+selection_fg = "#efebd4"
+
+[metadata]
+name = "Noctalia"
+
diff --git a/Assets/ColorScheme/Rosepine/Rosepine.json b/Assets/ColorScheme/Rosepine/Rosepine.json
index c6fc5c8f..0e07dba1 100644
--- a/Assets/ColorScheme/Rosepine/Rosepine.json
+++ b/Assets/ColorScheme/Rosepine/Rosepine.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#26233a",
"mOnSurfaceVariant": "#908caa",
"mOutline": "#403d52",
- "mShadow": "#191724"
+ "mShadow": "#191724",
+ "mHover": "#524f67",
+ "mOnHover": "#e0def4"
},
"light": {
"mPrimary": "#d7827e",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#f2e9e1",
"mOnSurfaceVariant": "#797593",
"mOutline": "#dfdad9",
- "mShadow": "#faf4ed"
+ "mShadow": "#faf4ed",
+ "mHover": "#cecacd",
+ "mOnHover": "#575279"
}
}
\ No newline at end of file
diff --git a/Assets/ColorScheme/Rosepine/terminal/wezterm/Rosepine-dark.toml b/Assets/ColorScheme/Rosepine/terminal/wezterm/Rosepine-dark.toml
new file mode 100644
index 00000000..37d602e8
--- /dev/null
+++ b/Assets/ColorScheme/Rosepine/terminal/wezterm/Rosepine-dark.toml
@@ -0,0 +1,34 @@
+[colors]
+ansi = [
+ "#26233a",
+ "#eb6f92",
+ "#9ccfd8",
+ "#f6c177",
+ "#31748f",
+ "#c4a7e7",
+ "#ebbcba",
+ "#e0def4",
+]
+background = "#191724"
+brights = [
+ "#6e6a86",
+ "#eb6f92",
+ "#9ccfd8",
+ "#f6c177",
+ "#31748f",
+ "#c4a7e7",
+ "#ebbcba",
+ "#e0def4",
+]
+cursor_bg = "#e0def4"
+cursor_border = "#e0def4"
+cursor_fg = "#191724"
+foreground = "#e0def4"
+selection_bg = "#191724"
+selection_fg = "#e0def4"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
+
diff --git a/Assets/ColorScheme/Rosepine/terminal/wezterm/Rosepine-light.toml b/Assets/ColorScheme/Rosepine/terminal/wezterm/Rosepine-light.toml
new file mode 100644
index 00000000..53a8cbdc
--- /dev/null
+++ b/Assets/ColorScheme/Rosepine/terminal/wezterm/Rosepine-light.toml
@@ -0,0 +1,33 @@
+[colors]
+ansi = [
+ "#f2e9e1",
+ "#b4637a",
+ "#56949f",
+ "#ea9d34",
+ "#286983",
+ "#907aa9",
+ "#d7827e",
+ "#575279",
+]
+background = "#faf4ed"
+brights = [
+ "#9893a5",
+ "#b4637a",
+ "#56949f",
+ "#ea9d34",
+ "#286983",
+ "#907aa9",
+ "#d7827e",
+ "#575279",
+]
+cursor_bg = "#575279"
+cursor_border = "#575279"
+cursor_fg = "#faf4ed"
+foreground = "#575279"
+selection_bg = "#faf4ed"
+selection_fg = "#575279"
+
+[colors.indexed]
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Solarized/Solarized.json b/Assets/ColorScheme/Solarized/Solarized.json
index 9d6327ae..a870df06 100644
--- a/Assets/ColorScheme/Solarized/Solarized.json
+++ b/Assets/ColorScheme/Solarized/Solarized.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#073642",
"mOnSurfaceVariant": "#657b83",
"mOutline": "#0c5c70",
- "mShadow": "#002b36"
+ "mShadow": "#002b36",
+ "mHover": "#cb4b16",
+ "mOnHover": "#002b36"
},
"light": {
"mPrimary": "#b58900",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#eee8d5",
"mOnSurfaceVariant": "#839496",
"mOutline": "#dfd4b1",
- "mShadow": "#eee8d5"
+ "mShadow": "#eee8d5",
+ "mHover": "#cb4b16",
+ "mOnHover": "#fdf6e3"
}
}
diff --git a/Assets/ColorScheme/Solarized/terminal/wezterm/Solarized-dark.toml b/Assets/ColorScheme/Solarized/terminal/wezterm/Solarized-dark.toml
new file mode 100644
index 00000000..673d582f
--- /dev/null
+++ b/Assets/ColorScheme/Solarized/terminal/wezterm/Solarized-dark.toml
@@ -0,0 +1,29 @@
+[colors]
+ansi = [
+ "#002b36",
+ "#dc322f",
+ "#859900",
+ "#b58900",
+ "#268bd2",
+ "#6c71c4",
+ "#2aa198",
+ "#93a1a1",
+]
+background = "#002b36"
+brights = [
+ "#657b83",
+ "#dc322f",
+ "#859900",
+ "#b58900",
+ "#268bd2",
+ "#6c71c4",
+ "#2aa198",
+ "#fdf6e3",
+]
+foreground = "#93a1a1"
+
+[colors.indexed]
+
+[metadata]
+author = "Chris Kempson"
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Solarized/terminal/wezterm/Solarized-light.toml b/Assets/ColorScheme/Solarized/terminal/wezterm/Solarized-light.toml
new file mode 100644
index 00000000..8d88a775
--- /dev/null
+++ b/Assets/ColorScheme/Solarized/terminal/wezterm/Solarized-light.toml
@@ -0,0 +1,30 @@
+[colors]
+ansi = [
+ "#002b36",
+ "#dc322f",
+ "#859900",
+ "#b58900",
+ "#268bd2",
+ "#6c71c4",
+ "#2aa198",
+ "#93a1a1",
+]
+background = "#fdf6e3"
+brights = [
+ "#657b83",
+ "#dc322f",
+ "#859900",
+ "#b58900",
+ "#268bd2",
+ "#6c71c4",
+ "#2aa198",
+ "#fdf6e3",
+]
+foreground = "#586e75"
+
+[colors.indexed]
+
+[metadata]
+author = "Chris Kempson"
+name = "Noctalia"
+
diff --git a/Assets/ColorScheme/Tokyo-Night/Tokyo-Night.json b/Assets/ColorScheme/Tokyo-Night/Tokyo-Night.json
index 520f3b13..2aeea6b2 100644
--- a/Assets/ColorScheme/Tokyo-Night/Tokyo-Night.json
+++ b/Assets/ColorScheme/Tokyo-Night/Tokyo-Night.json
@@ -13,7 +13,9 @@
"mSurfaceVariant": "#24283b",
"mOnSurfaceVariant": "#9aa5ce",
"mOutline": "#565f89",
- "mShadow": "#15161e"
+ "mShadow": "#15161e",
+ "mHover": "#9ece6a",
+ "mOnHover": "#16161e"
},
"light": {
"mPrimary": "#2e7de9",
@@ -29,6 +31,8 @@
"mSurfaceVariant": "#d0d5e3",
"mOnSurfaceVariant": "#6172b0",
"mOutline": "#b4b5b9",
- "mShadow": "#a8aecb"
+ "mShadow": "#a8aecb",
+ "mHover": "#587539",
+ "mOnHover": "#e1e2e7"
}
}
diff --git a/Assets/ColorScheme/Tokyo-Night/terminal/wezterm/Tokyo-Night-dark.toml b/Assets/ColorScheme/Tokyo-Night/terminal/wezterm/Tokyo-Night-dark.toml
new file mode 100644
index 00000000..b5276681
--- /dev/null
+++ b/Assets/ColorScheme/Tokyo-Night/terminal/wezterm/Tokyo-Night-dark.toml
@@ -0,0 +1,78 @@
+[colors]
+ansi = [
+ "#15161e",
+ "#f7768e",
+ "#9ece6a",
+ "#e0af68",
+ "#7aa2f7",
+ "#bb9af7",
+ "#7dcfff",
+ "#a9b1d6",
+]
+background = "#1a1b26"
+brights = [
+ "#414868",
+ "#f7768e",
+ "#9ece6a",
+ "#e0af68",
+ "#7aa2f7",
+ "#bb9af7",
+ "#7dcfff",
+ "#c0caf5",
+]
+cursor_bg = "#c0caf5"
+cursor_border = "#c0caf5"
+cursor_fg = "#1a1b26"
+foreground = "#c0caf5"
+selection_bg = "#283457"
+selection_fg = "#c0caf5"
+
+[colors.indexed]
+
+[colors.tab_bar]
+background = "#191b28"
+inactive_tab_edge = "#16161e"
+
+[colors.tab_bar.active_tab]
+bg_color = "#1a1b26"
+fg_color = "#7aa2f7"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab]
+bg_color = "#16161e"
+fg_color = "#545c7e"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab_hover]
+bg_color = "#16161e"
+fg_color = "#7aa2f7"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab]
+bg_color = "#191b28"
+fg_color = "#7aa2f7"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab_hover]
+bg_color = "#7aa2f7"
+fg_color = "#16161e"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[metadata]
+author = "folke"
+name = "Noctalia"
diff --git a/Assets/ColorScheme/Tokyo-Night/terminal/wezterm/Tokyo-Night-light.toml b/Assets/ColorScheme/Tokyo-Night/terminal/wezterm/Tokyo-Night-light.toml
new file mode 100644
index 00000000..e95c3ac5
--- /dev/null
+++ b/Assets/ColorScheme/Tokyo-Night/terminal/wezterm/Tokyo-Night-light.toml
@@ -0,0 +1,78 @@
+[colors]
+ansi = [
+ "#e9e9ed",
+ "#f52a65",
+ "#587539",
+ "#8c6c3e",
+ "#2e7de9",
+ "#9854f1",
+ "#007197",
+ "#6172b0",
+]
+background = "#e1e2e7"
+brights = [
+ "#a1a6c5",
+ "#f52a65",
+ "#587539",
+ "#8c6c3e",
+ "#2e7de9",
+ "#9854f1",
+ "#007197",
+ "#3760bf",
+]
+cursor_bg = "#3760bf"
+cursor_border = "#3760bf"
+cursor_fg = "#e1e2e7"
+foreground = "#3760bf"
+selection_bg = "#b6bfe2"
+selection_fg = "#3760bf"
+
+[colors.indexed]
+
+[colors.tab_bar]
+background = "#191b28"
+inactive_tab_edge = "#e9e9ec"
+
+[colors.tab_bar.active_tab]
+bg_color = "#e1e2e7"
+fg_color = "#2e7de9"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab]
+bg_color = "#e9e9ec"
+fg_color = "#8990b3"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.inactive_tab_hover]
+bg_color = "#e9e9ec"
+fg_color = "#2e7de9"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab]
+bg_color = "#191b28"
+fg_color = "#2e7de9"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[colors.tab_bar.new_tab_hover]
+bg_color = "#2e7de9"
+fg_color = "#e9e9ec"
+intensity = "Normal"
+italic = false
+strikethrough = false
+underline = "None"
+
+[metadata]
+author = "folke"
+name = "Noctalia"
diff --git a/Assets/MatugenTemplates/Terminal/wezterm.toml b/Assets/MatugenTemplates/Terminal/wezterm.toml
new file mode 100644
index 00000000..4c73b0e6
--- /dev/null
+++ b/Assets/MatugenTemplates/Terminal/wezterm.toml
@@ -0,0 +1,31 @@
+[colors]
+ansi = [
+ "{{colors.surface.default.hex}}",
+ "{{colors.error.default.hex}}",
+ "{{colors.tertiary.default.hex}}",
+ "{{colors.secondary.default.hex}}",
+ "{{colors.primary.default.hex}}",
+ "{{colors.tertiary_fixed_dim.default.hex}}",
+ "{{colors.secondary_fixed_dim.default.hex}}",
+ "{{colors.on_surface.default.hex}}",
+]
+background = "{{colors.surface.default.hex}}"
+brights = [
+ "{{colors.outline.default.hex}}",
+ "{{colors.error.default.hex}}",
+ "{{colors.tertiary.default.hex}}",
+ "{{colors.secondary.default.hex}}",
+ "{{colors.primary.default.hex}}",
+ "{{colors.tertiary_fixed_dim.default.hex}}",
+ "{{colors.secondary_fixed_dim.default.hex}}",
+ "{{colors.on_surface.default.hex}}",
+]
+cursor_bg = "{{colors.on_surface.default.hex}}"
+cursor_border = "{{colors.on_surface.default.hex}}"
+cursor_fg = "{{colors.surface.default.hex}}"
+foreground = "{{colors.on_surface.default.hex}}"
+selection_bg = "{{colors.surface_variant.default.hex}}"
+selection_fg = "{{colors.on_surface_variant.default.hex}}"
+
+[metadata]
+name = "Noctalia"
diff --git a/Assets/MatugenTemplates/noctalia.json b/Assets/MatugenTemplates/noctalia.json
index db4735e2..1fe2097b 100644
--- a/Assets/MatugenTemplates/noctalia.json
+++ b/Assets/MatugenTemplates/noctalia.json
@@ -18,5 +18,8 @@
"mOnSurfaceVariant": "{{colors.on_surface_variant.default.hex}}",
"mOutline": "{{colors.outline_variant.default.hex}}",
- "mShadow": "{{colors.shadow.default.hex}}"
+ "mShadow": "{{colors.shadow.default.hex}}",
+
+ "mHover": "{{colors.tertiary.default.hex}}",
+ "mOnHover": "{{colors.on_tertiary.default.hex}}"
}
\ No newline at end of file
diff --git a/Assets/MatugenTemplates/spicetify.ini b/Assets/MatugenTemplates/spicetify.ini
new file mode 100644
index 00000000..55c68652
--- /dev/null
+++ b/Assets/MatugenTemplates/spicetify.ini
@@ -0,0 +1,27 @@
+[Comfy]
+text = {{colors.on_surface.default.hex_stripped}}
+subtext = {{colors.on_surface_variant.default.hex_stripped}}
+main = {{colors.surface.default.hex_stripped}}
+main-elevated = {{colors.surface.default.hex_stripped}}
+main-transition = {{colors.surface_container_lowest.default.hex_stripped}}
+highlight = {{colors.surface_container_low.default.hex_stripped}}
+highlight-elevated = {{colors.surface_container_highest.default.hex_stripped}}
+sidebar = {{colors.surface.default.hex_stripped}}
+player = {{colors.surface.default.hex_stripped}}
+card = {{colors.surface.default.hex_stripped}}
+shadow = {{colors.surface.default.hex_stripped}}
+selected-row = {{colors.on_surface.default.hex_stripped}}
+button = {{colors.primary.default.hex_stripped}}
+button-active = {{colors.primary.default.hex_stripped}}
+button-disabled = {{colors.primary.default.hex_stripped}}
+tab-active = {{colors.surface.default.hex_stripped}}
+notification = {{colors.tertiary.default.hex_stripped}}
+notification-error = {{colors.error.default.hex_stripped}}
+misc = {{colors.surface.default.hex_stripped}}
+play-button = {{colors.secondary.default.hex_stripped}}
+play-button-active = {{colors.secondary.default.hex_stripped}}
+progress-fg = {{colors.primary.default.hex_stripped}}
+progress-bg = {{colors.surface.default.hex_stripped}}
+heart = {{colors.error.default.hex_stripped}}
+pagelink-active = {{colors.on_tertiary_container.default.hex_stripped}}
+radio-btn-active = {{colors.on_tertiary_container.default.hex_stripped}}
diff --git a/Assets/MatugenTemplates/vesktop.css b/Assets/MatugenTemplates/vesktop.css
index 17472dd3..7208535a 100644
--- a/Assets/MatugenTemplates/vesktop.css
+++ b/Assets/MatugenTemplates/vesktop.css
@@ -102,4 +102,155 @@
--purple-3: {{colors.on_secondary.default.hex}};
--purple-4: {{colors.on_secondary_container.default.hex}};
--purple-5: {{colors.secondary.default.hex}};
+}
+
+/* Improve timestamp/clock readability */
+/* Target timestamps with various selector patterns to work across Discord versions */
+[class*="timestamp"] time,
+[class*="timestamp"] time[id*="message-timestamp"],
+[id*="message-timestamp"] time,
+span[class*="timestamp"] time {
+ color: var(--text-3) !important; /* Use normal text color for better contrast */
+ opacity: 0.85 !important; /* Slightly muted but still readable */
+}
+
+/* Hover state for timestamps - make them fully visible */
+[class*="timestamp"]:hover time,
+[id*="message-timestamp"]:hover time,
+span[class*="timestamp"]:hover time {
+ color: var(--text-2) !important; /* Use heading color on hover */
+ opacity: 1 !important;
+}
+
+/* Improve slowmode cooldown and typing indicator readability */
+[class*="cooldownText"],
+[class*="cooldownWrapper"],
+[class*="cooldownText"] svg,
+.cooldownText_b21699,
+.cooldownWrapper_b21699 {
+ color: var(--text-3) !important; /* Use normal text color for better contrast */
+ opacity: 0.85 !important; /* Slightly muted but still readable */
+}
+
+/* Slowmode icon color */
+[class*="slowModeIcon"],
+.slowModeIcon_b21699 {
+ color: var(--text-3) !important;
+ opacity: 0.85 !important;
+}
+
+/* Typing indicator text */
+[class*="typing"] [class*="text"],
+[class*="typingDots"] [class*="text"],
+.typing_b88801 .text_b88801 {
+ color: var(--text-3) !important;
+ opacity: 0.85 !important;
+}
+
+[class*="postTitleText"],
+[class*="postTitleText"] span,
+h3[class*="postTitleText"],
+[class*="heading-lg"][class*="postTitleText"],
+[data-text-variant*="heading"] [class*="postTitleText"] {
+ color: var(--text-2) !important;
+ opacity: 0.9 !important;
+}
+
+[class*="postTitleText"]:hover,
+[class*="postTitleText"]:hover span,
+h3[class*="postTitleText"]:hover {
+ color: var(--text-2) !important;
+ opacity: 1 !important;
+}
+
+[class*="messageContent"],
+[class*="markup"] {
+ line-height: 1.35 !important;
+ color: var(--text-3) !important;
+ opacity: 0.85 !important;
+}
+
+/* Improve message input/editor text readability */
+[class*="editor"] [class*="markup"],
+[class*="slateTextArea"] [class*="markup"],
+[class*="slateTextArea"],
+[role="textbox"][class*="markup"],
+[role="textbox"][class*="editor"],
+[class*="editor"] [class*="slateTextArea"],
+[data-slate-editor="true"] {
+ color: var(--text-3) !important;
+ opacity: 0.9 !important;
+ line-height: 1.4 !important;
+}
+
+/* Empty/placeholder text in message input */
+[class*="emptyText"],
+[class*="emptyText"] span,
+[data-slate-zero-width] {
+ color: var(--text-4) !important;
+ opacity: 0.7 !important;
+}
+
+[class*="messageContent"][class*="deleted"],
+[class*="text-sm"][class*="messageContent"][class*="deleted"],
+[data-text-variant*="text-sm"][class*="messageContent"][class*="deleted"] {
+ color: var(--text-3) !important;
+ opacity: 0.85 !important;
+}
+
+[class*="messageContent"][class*="deleted"]:hover,
+[class*="messageContent"][class*="deleted"]:hover span {
+ color: var(--text-2) !important;
+ opacity: 1 !important;
+}
+
+/* Improve tooltip readability */
+[class*="tooltip"],
+[class*="tooltip"] *,
+[role="tooltip"],
+[role="tooltip"] * {
+ color: var(--text-2) !important;
+}
+
+[class*="tooltipText"],
+[class*="tooltipContent"],
+[class*="tooltip"] span,
+[class*="tooltip"] div {
+ color: var(--text-2) !important;
+ opacity: 1 !important;
+}
+
+/* Improve link readability - ONLY target link callout containers (not channels or other UI) */
+/* Override CSS variables only within link callout containers */
+div[class*="linkCalloutContainer"] {
+ --text-muted: var(--text-3) !important;
+ --text-default: var(--accent-1) !important;
+}
+
+/* Make muted parts (protocol, path) more readable - only direct child spans in link containers */
+div[class*="linkCalloutContainer"] > span[class*="normal"][data-text-variant*="normal"][style*="--text-muted"],
+div[class*="linkCalloutContainer"] > span[class*="normal"][style*="color: var(--text-muted)"] {
+ color: var(--text-3) !important;
+ opacity: 0.9 !important;
+}
+
+/* Make the domain (semibold part) stand out with accent color - only direct child spans in link containers */
+div[class*="linkCalloutContainer"] > span[class*="semibold"],
+div[class*="linkCalloutContainer"] > span[data-text-variant*="semibold"],
+div[class*="linkCalloutContainer"] > span[style*="color: var(--text-default)"] {
+ color: var(--accent-1) !important;
+ font-weight: 600 !important;
+ opacity: 1 !important;
+}
+
+/* Hover state for link containers - only affects spans inside link containers */
+div[class*="linkCalloutContainer"]:hover > span[class*="normal"][data-text-variant*="normal"] {
+ color: var(--text-2) !important;
+ opacity: 1 !important;
+}
+
+div[class*="linkCalloutContainer"]:hover > span[class*="semibold"],
+div[class*="linkCalloutContainer"]:hover > span[data-text-variant*="semibold"] {
+ color: var(--accent-1) !important;
+ opacity: 1 !important;
}
\ No newline at end of file
diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json
index c1f0b709..e1cfbd42 100644
--- a/Assets/Translations/de.json
+++ b/Assets/Translations/de.json
@@ -1,1261 +1,285 @@
{
- "settings": {
- "general": {
- "title": "Allgemein",
- "profile": {
- "section": {
- "label": "Profil",
- "description": "Ändern Sie Ihren Avatar."
- },
- "picture": {
- "label": "{user}s Avatar",
- "description": "Der Avatar, der in der gesamten Benutzeroberfläche angezeigt wird."
- },
- "select-avatar": "Avatar auswählen"
- },
- "screen-corners": {
- "section": {
- "label": "Bildschirmecken",
- "description": "Bildschirmecken-Rundung und visuelle Effekte anpassen."
- },
- "show-corners": {
- "label": "Bildschirmecken anzeigen",
- "description": "Gerundete Ecken am Rand des Bildschirms anzeigen."
- },
- "solid-black": {
- "label": "Schwarze Ecken",
- "description": "Schwarze Ecken anstelle der Statusleisten-Hintergrundfarbe verwenden."
- },
- "radius": {
- "label": "Bildschirmecken-Radius",
- "description": "Rundung der Bildschirmecken anpassen.",
- "reset": "Eckenradius des Bildschirms zurücksetzen"
- }
- },
- "fonts": {
- "section": {
- "label": "Schriftarten",
- "description": "Wählen Sie die Schriftart der Benutzeroberfläche."
- },
- "default": {
- "label": "Standard-Schriftart",
- "description": "Hauptschriftart für die gesamte Benutzeroberfläche.",
- "placeholder": "Standard-Schriftart auswählen...",
- "search-placeholder": "Schriftarten suchen...",
- "scale": {
- "description": "Ändern Sie die Größe des Standardtextes.",
- "label": "Standardmäßige Schriftgröße"
- }
- },
- "monospace": {
- "label": "Monospace-Schriftart",
- "description": "Monospace-Schriftart für Zahlen und Statistik-Anzeigen.",
- "placeholder": "Monospace-Schriftart auswählen...",
- "search-placeholder": "Monospace-Schriftarten suchen...",
- "scale": {
- "description": "Ändern Sie die Größe des Monotextes.",
- "label": "Schriftgröße mit fester Breite"
- }
- },
- "reset-scaling": "Skalierung zurücksetzen"
- },
- "language": {
- "section": {
- "label": "Sprache",
- "description": "Ändern Sie die Sprache von Noctalia."
- },
- "select": {
- "label": "Anwendungssprache",
- "description": "Wählen Sie die Sprache der Anwendungsoberfläche.",
- "auto-detect": "Automatisch"
- }
- },
- "launch-setup-wizard": "Starten Sie den Setup-Assistenten"
- },
- "audio": {
- "title": "Audio",
- "volumes": {
- "section": {
- "label": "Lautstärken",
- "description": "Lautstärkeregler und Audiopegel anpassen."
- },
- "output-volume": {
- "label": "Ausgabe-Lautstärke",
- "description": "Systemweite Lautstärke."
- },
- "mute-output": {
- "label": "Audio-Ausgabe stummschalten",
- "description": "Die Haupt-Audio-Ausgabe des Systems stummschalten."
- },
- "input-volume": {
- "label": "Eingabe-Lautstärke",
- "description": "Mikrofon-Eingabe-Lautstärke."
- },
- "mute-input": {
- "label": "Audio-Eingabe stummschalten",
- "description": "Die Standard-Audio-Eingabe (Mikrofon) stummschalten."
- },
- "step-size": {
- "label": "Lautstärke-Schrittgröße",
- "description": "Schrittgröße für Lautstärkeänderungen anpassen (Mausrad, Tastenkombinationen)."
- },
- "volume-overdrive": {
- "label": "Lautstärke-Übersteuerung erlauben",
- "description": "Erlaubt das Anheben der Lautstärke über 100%. Wird möglicherweise nicht von aller Hardware unterstützt."
- }
- },
- "devices": {
- "section": {
- "label": "Audio-Geräte",
- "description": "Verfügbare Audio-Ein- und Ausgabegeräte konfigurieren."
- },
- "output-device": {
- "label": "Ausgabegerät",
- "description": "Gewünschtes Audio-Ausgabegerät auswählen."
- },
- "input-device": {
- "label": "Eingabegerät",
- "description": "Gewünschtes Audio-Eingabegerät auswählen."
- }
- },
- "media": {
- "section": {
- "label": "Medienplayer",
- "description": "Bevorzugte und ignorierte Medienanwendungen festlegen."
- },
- "primary-player": {
- "label": "Hauptplayer",
- "description": "Stichwort eingeben, um den Hauptplayer zu identifizieren.",
- "placeholder": "z.B. spotify, vlc, mpv"
- },
- "excluded-player": {
- "label": "Ausgeschlossener Player",
- "description": "Stichwörter für Player hinzufügen, die das System ignorieren soll. Jedes Stichwort sollte in einer neuen Zeile stehen.",
- "placeholder": "Teilstring eingeben und + drücken"
- },
- "visualizer-type": {
- "label": "Visualisierungstyp",
- "description": "Visualisierungstyp für Medienwiedergabe wählen."
- },
- "frame-rate": {
- "label": "Bildrate",
- "description": "Höhere Raten sind flüssiger, verbrauchen aber mehr Ressourcen."
- },
- "scrolling-title": {
- "label": "Scrollender Titel",
- "description": "Kontinuierliches Scrollen für lange Medientitel aktivieren."
- },
- "scrolling-speed": {
- "label": "Scrollgeschwindigkeit",
- "description": "Zeit in Sekunden, die der Titel zum Scrollen vom Anfang bis zum Ende benötigt."
- }
- }
- },
- "display": {
- "title": "Anzeige",
- "monitors": {
- "section": {
- "label": "Monitor-spezifische Einstellungen",
- "description": "Helligkeitseinstellungen für jedes Display anpassen."
- },
- "brightness": "Helligkeit",
- "brightness-step": {
- "label": "Helligkeits-Schrittgröße",
- "description": "Schrittgröße für Helligkeitsänderungen anpassen (Mausrad und Tastenkombinationen)."
- },
- "enforce-minimum": {
- "label": "Mindesthelligkeit erzwingen (1%)",
- "description": "Löst das Problem, dass die Hintergrundbeleuchtung auf einigen Displays bei 0% Helligkeit vollständig ausgeschaltet wird."
- }
- },
- "night-light": {
- "section": {
- "label": "Nachtlicht",
- "description": "Blaulicht-Emission reduzieren für besseren Schlaf und weniger Augenbelastung."
- },
- "enable": {
- "label": "Nachtlicht aktivieren",
- "description": "Warmen Farbfilter anwenden, um Blaulicht-Emission zu reduzieren."
- },
- "temperature": {
- "label": "Farbtemperatur",
- "description": "Farbwärme für Nacht- und Tageszeit einstellen.",
- "night": "Nacht",
- "day": "Tag"
- },
- "auto-schedule": {
- "label": "Automatische Planung",
- "description": "Basierend auf Sonnenuntergang und Sonnenaufgang in {location} - empfohlen."
- },
- "manual-schedule": {
- "label": "Manuelle Planung",
- "description": "Benutzerdefinierte Zeiten für Sonnenaufgang und Sonnenuntergang festlegen.",
- "sunrise": "Sonnenaufgang",
- "sunset": "Sonnenuntergang",
- "select-start": "Startzeit auswählen",
- "select-stop": "Endzeit auswählen"
- },
- "force-activation": {
- "label": "Aktivierung erzwingen",
- "description": "Ignoriert die Planung und wendet den Nachtfilter sofort an."
- }
- }
- },
- "bar": {
- "title": "Statusleiste",
- "appearance": {
- "section": {
- "label": "Erscheinungsbild",
- "description": "Erscheinungsbild und Position der Statusleiste anpassen."
- },
- "position": {
- "label": "Statusleistenposition",
- "description": "Wählen Sie, wo die Statusleiste auf dem Bildschirm platziert werden soll."
- },
- "density": {
- "label": "Statusleistendichte",
- "description": "Innenabstand der Statusleiste für kompaktes oder geräumiges Aussehen anpassen."
- },
- "background-opacity": {
- "label": "Hintergrund-Transparenz",
- "description": "Hintergrund-Transparenz der Statusleiste anpassen."
- },
- "show-capsule": {
- "label": "Kapsel anzeigen",
- "description": "Widget-Hintergründe anzeigen."
- },
- "floating": {
- "label": "Schwebende Statusleiste",
- "description": "Statusleiste als schwebende 'Pille' anzeigen. Hinweis: Dies verschiebt die Bildschirmecken an die Ränder."
- },
- "margins": {
- "label": "Ränder",
- "description": "Ränder um die schwebende Statusleiste anpassen.",
- "vertical": "Vertikal",
- "horizontal": "Horizontal"
- }
- },
- "widgets": {
- "section": {
- "label": "Widget-Positionierung",
- "description": "Widgets per Drag & Drop neu anordnen. Symoble zeigen die Verwendung an: [L]inks, [M]itte, [R]echts."
- }
- },
- "monitors": {
- "section": {
- "label": "Monitor-Anzeige",
- "description": "Statusleiste auf bestimmten Monitoren anzeigen. Standardmäßig auf allen."
- }
- },
- "tray": {
- "blacklist": {
- "label": "Ausschlussliste",
- "description": "Ausschlussregeln für die Tray-Symbolleiste hinzufügen, unterstützt Platzhalter (*).",
- "placeholder": "z.B., nm-applet, Fcitx*"
- }
- }
- },
- "dock": {
- "title": "Dock",
- "enabled": {
- "label": "Dock aktivieren",
- "description": "Dock vollständig anzeigen oder ausblenden"
- },
- "appearance": {
- "section": {
- "label": "Erscheinungsbild",
- "description": "Verhalten und Erscheinungsbild des Docks anpassen."
- },
- "display": {
- "label": "Anzeige",
- "description": "Wählen Sie, wie sich das Dock verhält.",
- "always-visible": "Immer sichtbar",
- "auto-hide": "Automatisch ausblenden",
- "exclusive": "Exklusiv"
- },
- "background-opacity": {
- "label": "Hintergrund-Transparenz",
- "description": "Hintergrund-Transparenz des Docks anpassen."
- },
- "floating-distance": {
- "label": "Dock-Schwebeabstand",
- "description": "Schwebeabstand vom Bildschirmrand anpassen."
- },
- "icon-size": {
- "label": "Dock-Größe",
- "description": "Gesamtgröße des Docks anpassen."
- },
- "colorize-icons": {
- "label": "Symbole einfärben",
- "description": "Theme-Farben auf Dock-App-Symbole anwenden (nur nicht fokussierte Apps)."
- }
- },
- "monitors": {
- "section": {
- "label": "Monitor-Anzeige",
- "description": "Dock auf bestimmten Monitoren anzeigen. Standardmäßig werden alle angezeigt, wenn keine ausgewählt wurden."
- },
- "only-same-output": {
- "description": "Zeige nur Apps von dem Bildschirm, auf dem sich das Dock befindet.",
- "label": "Nur Apps vom gleichen Bildschirm"
- }
- }
- },
- "launcher": {
- "title": "Starter",
- "settings": {
- "section": {
- "label": "Erscheinungsbild",
- "description": "Verhalten und Erscheinungsbild des Starters anpassen."
- },
- "position": {
- "label": "Position",
- "description": "Wählen Sie, wo das Starter-Panel erscheint."
- },
- "background-opacity": {
- "label": "Hintergrund-Transparenz",
- "description": "Hintergrund-Transparenz des Starters anpassen."
- },
- "clipboard-history": {
- "label": "Zwischenablageverlauf aktivieren",
- "description": "Zugriff auf zuvor kopierte Elemente über den Launcher."
- },
- "sort-by-usage": {
- "label": "Nach Häufigkeit sortieren",
- "description": "Wenn aktiviert, erscheinen häufig gestartete Apps zuerst in der Liste."
- },
- "use-app2unit": {
- "label": "App2Unit zum Starten von Anwendungen verwenden",
- "description": "Verwendet eine alternative Startmethode zur besseren Verwaltung von App-Prozessen und Problemvermeidung."
- },
- "terminal-command": {
- "label": "Terminalbefehl",
- "description": "Befehl zum Starten eines Terminals. Z.B. 'kitty -e' oder 'gnome-terminal --'."
- },
- "custom-launch-prefix": {
- "label": "Benutzerdefiniertes Start-Präfix",
- "description": "Befehle mit einem benutzerdefinierten Launcher präfixieren (z.B. 'runapp' für systemd-Integration)."
- },
- "custom-launch-prefix-enabled": {
- "label": "Benutzerdefiniertes Start-Präfix aktivieren",
- "description": "Verwenden Sie ein benutzerdefiniertes Präfix zum Starten von Anwendungen anstelle der Standardmethode."
- }
- }
- },
- "notifications": {
- "title": "Benachrichtigungen",
- "settings": {
- "section": {
- "label": "Erscheinungsbild",
- "description": "Erscheinungsbild und Verhalten von Benachrichtigungen konfigurieren."
- },
- "do-not-disturb": {
- "label": "Nicht stören",
- "description": "Alle Benachrichtigungs-Popups deaktivieren."
- },
- "enable-osd": {
- "label": "Bildschirmanzeige aktivieren",
- "description": "Lautstärke- und Helligkeitsänderungen in Echtzeit anzeigen."
- },
- "location": {
- "label": "Position",
- "description": "Wo Benachrichtigungen auf dem Bildschirm erscheinen."
- },
- "always-on-top": {
- "label": "Immer im Vordergrund",
- "description": "Benachrichtigungen über Vollbildfenstern und anderen Ebenen anzeigen."
- },
- "background-opacity": {
- "label": "Hintergrund-Transparenz",
- "description": "Transparenz der Benachrichtigungshintergründe anpassen."
- }
- },
- "duration": {
- "section": {
- "label": "Benachrichtigungsdauer",
- "description": "Konfigurieren, wie lange Benachrichtigungen je nach Dringlichkeitsstufe sichtbar bleiben."
- },
- "respect-expire": {
- "label": "Ablaufzeit respektieren",
- "description": "Die in der Benachrichtigung gesetzte Ablaufzeit verwenden."
- },
- "low-urgency": {
- "label": "Niedrige Dringlichkeit",
- "description": "Wie lange Benachrichtigungen niedriger Priorität sichtbar bleiben."
- },
- "normal-urgency": {
- "label": "Normale Dringlichkeit",
- "description": "Wie lange Benachrichtigungen normaler Priorität sichtbar bleiben."
- },
- "critical-urgency": {
- "label": "Kritische Dringlichkeit",
- "description": "Wie lange kritische Benachrichtigungen sichtbar bleiben."
- }
- },
- "monitors": {
- "section": {
- "label": "Monitor-Anzeige",
- "description": "Benachrichtigungen auf bestimmten Monitoren anzeigen. Standardmäßig werden sie auf allen Monitoren angezeigt."
- }
- }
- },
- "osd": {
- "title": "On-Screen Display",
- "description": "Bildschirm-Overlays wie Lautstärke- und Helligkeitsanzeigen konfigurieren.",
- "section": {
- "general": {
- "label": "Allgemein",
- "description": "Sichtbarkeit und Verhalten vom On-Screen Display konfigurieren."
- }
- },
- "enabled": {
- "label": "On-Screen Display aktivieren",
- "description": "Lautstärke- und Helligkeitsänderungen in Echtzeit anzeigen."
- },
- "always-on-top": {
- "label": "Immer im Vordergrund",
- "description": "On-Screen Display über Vollbildfenstern und anderen Ebenen anzeigen."
- },
- "location": {
- "label": "Position",
- "description": "Wo On-Screen Displays erscheinen."
- },
- "duration": {
- "section": {
- "label": "Automatisches Ausblenden",
- "description": "Wie lange das On-Screen Display sichtbar bleibt, bevor es automatisch ausgeblendet wird."
- },
- "auto-hide": {
- "label": "Ausblenden nach",
- "description": "Zeitspanne bis zum automatischen Ausblenden anpassen."
- }
- },
- "monitors": {
- "section": {
- "label": "Monitor-Anzeige",
- "description": "On-Screen Display auf bestimmten Monitoren anzeigen. Standardmäßig auf allen angezeigt."
- }
- }
- },
- "wallpaper": {
- "title": "Hintergrundbild",
- "settings": {
- "section": {
- "label": "Hintergrundbild-Einstellungen",
- "description": "Steuern Sie, wie Hintergrundbilder verwaltet und angezeigt werden."
- },
- "enable-management": {
- "label": "Hintergrundbild-Verwaltung aktivieren",
- "description": "Hintergrundbilder mit Noctalia verwalten. Deaktivieren, wenn Sie eine andere Anwendung bevorzugen."
- },
- "folder": {
- "label": "Hintergrundbild-Ordner",
- "description": "Pfad zu Ihrem Haupt-Hintergrundbild-Ordner.",
- "tooltip": "Nach Hintergrundbild-Ordner suchen"
- },
- "recursive-search": {
- "label": "Unterordner durchsuchen",
- "description": "Auch nach Hintergrundbildern in Unterordnern des Hintergrundbild-Verzeichnisses suchen."
- },
- "monitor-specific": {
- "label": "Monitor-spezifische Verzeichnisse",
- "description": "Unterschiedlichen Hintergrundbild-Ordner für jeden Monitor festlegen.",
- "tooltip": "Nach Hintergrundbild-Ordner suchen"
- },
- "select-folder": "Hintergrundbild-Ordner auswählen",
- "select-monitor-folder": "Monitor-Hintergrundbild-Ordner auswählen"
- },
- "look-feel": {
- "section": {
- "label": "Aussehen & Verhalten"
- },
- "fill-mode": {
- "label": "Füllmodus",
- "description": "Wählen Sie, wie das Bild skaliert werden soll, um zur Auflösung Ihres Monitors zu passen."
- },
- "fill-color": {
- "label": "Füllfarbe",
- "description": "Füllfarbe wählen, die hinter dem Hintergrundbild erscheinen kann."
- },
- "transition-type": {
- "label": "Übergangstyp",
- "description": "Animationstyp beim Wechsel zwischen Hintergrundbildern."
- },
- "transition-duration": {
- "label": "Übergangsdauer",
- "description": "Dauer der Übergangsanimationen in Sekunden."
- },
- "edge-smoothness": {
- "label": "Übergangseffekt weichzeichnen",
- "description": "Wendet einen weichen, gefiederten Effekt auf die Kante von Übergängen an."
- }
- },
- "automation": {
- "section": {
- "label": "Automatisierung"
- },
- "random-wallpaper": {
- "label": "Zufälliges Hintergrundbild",
- "description": "Zufällige Hintergrundbild-Wechsel in regelmäßigen Abständen planen."
- },
- "interval": {
- "label": "Hintergrundbild-Intervall",
- "description": "Wie oft Hintergrundbilder automatisch gewechselt werden sollen."
- },
- "custom-interval": {
- "label": "Benutzerdefiniertes Intervall",
- "description": "Zeit als HH:MM eingeben (z.B. 01:30)."
- }
- }
- },
- "color-scheme": {
- "title": "Farbschema",
- "color-source": {
- "section": {
- "label": "Farbquelle",
- "description": "Haupteinstellungen für Noctalias Farben."
- },
- "use-wallpaper-colors": {
- "label": "Hintergrundbild-Farben verwenden",
- "description": "Farbschemata aus Ihrem Hintergrundbild mit Matugen generieren. Extrahiert automatisch Farben für ein kohärentes Design."
- },
- "matugen-scheme-type": {
- "label": "Matugen-Schema-Typ",
- "description": {
- "scheme-content": "Leitet Farben ab, die eng mit dem zugrunde liegenden Bild übereinstimmen.",
- "scheme-expressive": "Lebendige Farbpalette mit verspielter Sättigung",
- "scheme-fidelity": "Farbtreue Palette, die die Originalfarbtöne beibehält",
- "scheme-fruit-salad": "Farbenfroher Mix aus leuchtenden, kontrastreichen Akzenten",
- "scheme-monochrome": "Minimale Farbpalette, die auf einem einzigen Farbton basiert.",
- "scheme-neutral": "Gedämpfte Farbpalette mit dezenten und beruhigenden Tönen.",
- "scheme-rainbow": "Vielfältige Palette, die das gesamte Spektrum abdeckt",
- "scheme-tonal-spot": "Ausgewogene Farbpalette mit gezielten Akzenten"
- }
- }
- },
- "dark-mode": {
- "switch": {
- "label": "Dunkler Modus",
- "description": "Wechselt zu einem dunkleren Theme für einfachere Betrachtung bei Nacht."
- },
- "mode": {
- "label": "Automatischer dunkler Modus",
- "description": "Ermöglicht einen automatischen Wechsel zwischen dem hellen und dunklen Modus.",
- "off": "Aus",
- "manual": "Manuell",
- "location": "Standort"
- }
- },
- "predefined": {
- "section": {
- "label": "Vordefinierte Farbschemata",
- "description": "Wählen Sie aus einer Sammlung vordefinierter Farbschemata."
- },
- "generate-templates": {
- "label": "Templates für vordefinierte Schemata generieren",
- "description": "Generiert Matugen-Templates (GTK, Terminal-Themes, etc.) bei der Verwendung vordefinierter Farbschemata."
- }
- },
- "templates": {
- "ui": {
- "label": "UI",
- "description": "Desktop-Umgebung und UI-Toolkit-Theming.",
- "gtk": {
- "description": "Schreibt {filepath}"
- },
- "qt": {
- "description": "Schreibt {filepath}"
- },
- "kcolorscheme": {
- "description": "Schreibt {filepath}"
- }
- },
- "terminal": {
- "label": "Terminal",
- "description": "Terminal-Emulator-Theming.",
- "kitty": {
- "description": "Schreibt {filepath} und lädt neu",
- "description-missing": "Erfordert {app} Terminal"
- },
- "ghostty": {
- "description": "Schreibt {filepath} und lädt neu",
- "description-missing": "Erfordert {app} Terminal"
- },
- "foot": {
- "description": "Schreibt {filepath} und lädt neu",
- "description-missing": "Erfordert {app} Terminal"
- }
- },
- "programs": {
- "label": "Programme",
- "description": "Anwendungsspezifisches Theming.",
- "fuzzel": {
- "description": "Schreibt {filepath} und lädt neu",
- "description-missing": "Erfordert die Installation von {app}"
- },
- "vicinae": {
- "description": "Schreibt {filepath} und lädt neu",
- "description-missing": "Erfordert die Installation von {app}"
- },
- "walker": {
- "description": "Schreibt {filepath} und setzt das Theme auf noctalia",
- "description-missing": "Erfordert die Installation von {app}"
- },
- "discord": {
- "description": "Schreibt {filepath} für {client}",
- "description-missing": "Kein Discord-Client erkannt. Installieren Sie vencord, vesktop, webcord, armcord, equibop, lightcord oder dorion"
- },
- "pywalfox": {
- "description": "Schreibt {filepath} und führt pywalfox update aus",
- "description-missing": "Erfordert die Installation von {app}"
- },
- "code": {
- "description": "Schreibe {Dateipfad}. Das Hyprluna-Theme muss manuell installiert und aktiviert werden.",
- "description-missing": "Benötigt die Installation von {app}"
- }
- },
- "misc": {
- "label": "Verschiedenes",
- "description": "Zusätzliche Konfigurationsoptionen.",
- "user-templates": {
- "label": "Benutzer-Vorlagen",
- "description": "Benutzerdefinierte Matugen-Konfiguration aktivieren. Eine Vorlagendatei wird beim ersten Aktivieren unter ~/.config/noctalia/user-templates.toml erstellt"
- }
- },
- "section": {
- "description": "Farben auf externe Anwendungen anwenden.",
- "label": "Vorlagen"
- }
- }
- },
- "location": {
- "title": "Standort",
- "location": {
- "section": {
- "label": "Ihr Standort",
- "description": "Genaues Wetter und Nachtlicht-Planung durch Festlegung Ihres Standorts erhalten."
- },
- "search": {
- "label": "Nach einem Standort suchen",
- "description": "z.B. Dortmund, Deutschland",
- "placeholder": "Standortnamen eingeben"
- }
- },
- "weather": {
- "section": {
- "label": "Wetter",
- "description": "Bevorzugte Temperatureinheit wählen."
- },
- "enabled": {
- "label": "Wetter aktivieren",
- "description": "Wetterinformationen in der gesamten Benutzeroberfläche anzeigen und Wetterdaten abrufen. Wenn deaktiviert, werden alle Wetterelemente ausgeblendet und es werden keine Netzwerkanfragen gestellt."
- },
- "fahrenheit": {
- "label": "Temperatur in Fahrenheit (°F) anzeigen",
- "description": "Temperatur in Fahrenheit statt Celsius anzeigen."
- },
- "show-in-calendar": {
- "description": "Zeige die tägliche Wettervorhersage direkt in deiner Kalenderansicht an.",
- "label": "Wetter im Kalender anzeigen"
- }
- },
- "date-time": {
- "section": {
- "label": "Datum & Zeit",
- "description": "Anpassen, wie Datum und Zeit erscheinen."
- },
- "12hour-format": {
- "label": "12-Stunden-Zeitformat benutzen",
- "description": "Zeigt die Uhrzeit im 12-Stunden-Format auf dem Sperrbildschirm und im Kalender an. Die Uhr in der Statusleiste hat eigene Einstellungen."
- },
- "week-numbers": {
- "label": "Wochennummern anzeigen",
- "description": "Zeigt die Kalenderwochen an (z.B. Woche 38)."
- },
- "show-events": {
- "label": "Kalenderereignisse anzeigen",
- "description": "Ereignisse im Kalender-Panel anzeigen."
- },
- "use-analog": {
- "description": "Eine Analoguhr auf dem Kalenderbildschirm anzeigen.",
- "label": "Analoge Uhr verwenden"
- },
- "first-day-of-week": {
- "automatic": "Automatisch (Systemgebietsschema verwenden)",
- "description": "Wähle, welcher Tag die Woche im Kalender beginnen soll.",
- "label": "Erster Tag der Woche"
- }
- }
- },
- "network": {
- "title": "Netzwerk",
- "section": {
- "description": "WLAN- und Bluetooth-Verbindungen verwalten."
- },
- "wifi": {
- "label": "WLAN aktivieren"
- },
- "bluetooth": {
- "label": "Bluetooth aktivieren"
- }
- },
- "screen-recorder": {
- "title": "Bildschirmrekorder",
- "general": {
- "section": {
- "label": "Allgemeine Einstellungen",
- "description": "Bildschirmaufnahme-Ausgabe und -Inhalt verwalten."
- },
- "output-folder": {
- "label": "Ausgabe-Ordner",
- "description": "Ordner, in dem Bildschirmaufnahmen gespeichert werden.",
- "tooltip": "Nach Ausgabe-Ordner suchen"
- },
- "show-cursor": {
- "label": "Cursor anzeigen",
- "description": "Mauszeiger im Video aufnehmen."
- },
- "select-output-folder": "Ausgabe-Ordner auswählen"
- },
- "video": {
- "section": {
- "label": "Video-Einstellungen",
- "description": "Video-Aufnahme-Optionen konfigurieren."
- },
- "video-source": {
- "label": "Video-Quelle",
- "description": "Portal wird empfohlen, bei Artefakten versuchen Sie Bildschirm."
- },
- "frame-rate": {
- "label": "Bildrate",
- "description": "Ziel-Bildrate für Bildschirmaufnahmen."
- },
- "video-quality": {
- "label": "Video-Qualität",
- "description": "Höhere Qualität führt zu größeren Dateigrößen."
- },
- "video-codec": {
- "label": "Video-Codec",
- "description": "h264 ist der gebräuchlichste Codec."
- },
- "color-range": {
- "label": "Farbbereich",
- "description": "Begrenzt wird für bessere Kompatibilität empfohlen."
- }
- },
- "audio": {
- "section": {
- "label": "Audio-Einstellungen",
- "description": "Audio-Aufnahme-Optionen konfigurieren."
- },
- "audio-source": {
- "label": "Audio-Quelle",
- "description": "Audio-Quelle für die Aufnahme."
- },
- "audio-codec": {
- "label": "Audio-Codec",
- "description": "Opus wird für beste Leistung und kleinste Audio-Größe empfohlen."
- }
- }
- },
- "about": {
- "title": "Über",
- "noctalia": {
- "section": {
- "label": "Noctalia Shell",
- "description": "Eine elegante und minimalistische Desktop-Shell, sorgfältig für Wayland entwickelt, gebaut mit Quickshell."
- },
- "latest-version": "Neueste Version:",
- "installed-version": "Installierte Version:",
- "download-latest": "Neueste Version herunterladen"
- },
- "contributors": {
- "section": {
- "label": "Mitwirkende",
- "description": "Ein Dankeschön an unseren {count} großartigen Mitwirkenden!",
- "description_plural": "Ein Dankeschön an unsere {count} großartigen Mitwirkenden!"
- }
- },
- "support": "Unterstütze uns"
- },
- "hooks": {
- "title": "Hooks",
- "system-hooks": {
- "section": {
- "label": "System-Hooks",
- "description": "Befehle konfigurieren, die bei Systemereignissen ausgeführt werden."
- },
- "enable": {
- "label": "Hooks aktivieren",
- "description": "Alle Hook-Befehle aktivieren oder deaktivieren."
- }
- },
- "wallpaper-changed": {
- "label": "Hintergrundbild geändert",
- "description": "Befehl, der ausgeführt wird, wenn sich das Hintergrundbild ändert.",
- "placeholder": "z.B. notify-send \"Hintergrundbild\" \"Geändert\""
- },
- "theme-changed": {
- "label": "Theme geändert",
- "description": "Befehl, der ausgeführt wird, wenn das Theme zwischen dunklem und hellem Modus wechselt.",
- "placeholder": "z.B. notify-send \"Theme\" \"Gewechselt\""
- },
- "info": {
- "command-info": {
- "label": "Hook-Befehl-Informationen",
- "description": "• Befehle werden über Shell ausgeführt (sh -c)\n• Befehle laufen im Hintergrund (getrennt)\n• Test-Buttons führen mit aktuellen Werten aus"
- },
- "parameters": {
- "label": "Verfügbare Parameter",
- "description": "• Hintergrundbild-Hook: $1 = Hintergrundbild-Pfad, $2 = Bildschirmname\n• Theme-Wechsel-Hook: $1 = true/false (Dunkelmodus-Status)"
- }
- }
- },
- "control-center": {
- "position": {
- "description": "Wählen Sie aus, wo das Kontrollzentrum-Panel angezeigt wird, wenn es geöffnet wird.",
- "label": "Position"
- },
- "section": {
- "description": "Konfigurieren Sie die Positionierung und das Verhalten des Kontrollzentrum-Panels.",
- "label": "Aussehen"
- },
- "title": "Kontrollzentrum",
- "cards": {
- "section": {
- "description": "Passen Sie an, welche Steuerelemente im Kontrollzentrum angezeigt werden und in welcher Reihenfolge.",
- "label": "Karten"
- }
- },
- "shortcuts": {
- "section": {
- "description": "Konfigurieren und verwalten Sie die Verknüpfungs-Widgets.",
- "label": "Widgets für Kurzbefehle"
- },
- "sectionLeft": "Links",
- "sectionRight": "Rechts"
- }
- },
- "user-interface": {
- "animation-disable": {
- "description": "Deaktivieren Sie alle Animationen für eine schnellere und reaktionsfreudigere Erfahrung.",
- "label": "UI-Animationen deaktivieren"
- },
- "panels-overlay": {
- "label": "Panels in Overlay-Ebene öffnen",
- "description": "Panels werden über Vollbildfenstern angezeigt."
- },
- "animation-speed": {
- "description": "Globale Animationsgeschwindigkeit anpassen.",
- "label": "Animationsgeschwindigkeit",
- "reset": "Animationsgeschwindigkeit zurücksetzen"
- },
- "border-radius": {
- "description": "Steuert die Eckenrundung von Fenstern, Schaltflächen und anderen Elementen.",
- "label": "Eckenradius",
- "reset": "Rahmenradius zurücksetzen"
- },
- "scaling": {
- "description": "Ändert die Größe der allgemeinen Benutzeroberfläche, mit Ausnahme der Leiste.",
- "label": "Oberflächenskalierung",
- "reset-scaling": "Schnittstellenskalierung zurücksetzen"
- },
- "section": {
- "description": "Passen Sie das Aussehen, die Haptik und das Verhalten der Benutzeroberfläche an.",
- "label": "Aussehen"
- },
- "title": "Benutzeroberfläche",
- "tooltips": {
- "description": "Tooltips in der gesamten Benutzeroberfläche aktivieren oder deaktivieren.",
- "label": "Tooltips anzeigen"
- },
- "panels-attached-to-bar": {
- "description": "Wenn aktiviert, werden die Panels mit einem schönen, umgekehrten Eckdesign an der Leiste befestigt.",
- "label": "Paneele an Stange befestigen"
- }
- },
- "lock-screen": {
- "compact-lockscreen": {
- "description": "Zeige nur die Login-Eingabe und Systemsteuerung, blende Wetter- und Medien-Widgets aus.",
- "label": "Kompakter Sperrbildschirm"
- },
- "lock-on-suspend": {
- "description": "Den Bildschirm beim Suspendieren des Systems automatisch sperren.",
- "label": "Sperren beim Ruhezustand"
- },
- "title": "Sperrbildschirm"
- }
- },
- "general": {
- "no-results": "Keine Ergebnisse",
- "no-summary": "Keine Zusammenfassung",
- "unknown": "Unbekannt"
- },
- "weather": {
- "clear-sky": "Klarer Himmel",
- "mainly-clear": "Überwiegend klar",
- "partly-cloudy": "Teilweise bewölkt",
- "overcast": "Bedeckt",
- "fog": "Nebelig",
- "drizzle": "Nieselregen",
- "snow": "Schnee",
- "rain-showers": "Regenschauer",
- "thunderstorm": "Gewitter",
- "unknown": "Unbekannt"
- },
- "widgets": {
- "tooltip": {
- "placeholder": "Platzhalter"
- },
- "file-picker": {
- "select-folder": "Ordner auswählen",
- "select-file": "Datei auswählen",
- "cancel": "Abbrechen",
- "search-placeholder": "Dateien und Ordner suchen...",
- "select-current": "Aktuelles Objekt auswählen",
- "title": "Dateiauswahl"
- },
- "datetime-tokens": {
- "common": {
- "12hour-time-minutes": "12-Stunden-Zeit mit Minuten",
- "24hour-time-minutes": "24-Stunden-Zeit mit Minuten",
- "24hour-time-seconds": "24-Stunden-Zeit mit Sekunden",
- "weekday-month-day": "Wochentag, Monat und Tag",
- "iso-date": "ISO-Datumsformat",
- "us-date": "US-Datumsformat",
- "european-date": "Europäisches Datumsformat",
- "weekday-date": "Wochentag mit Datum"
- },
- "hour": {
- "no-leading-zero": "Stunde ohne führende Null (0-23) - 24-Stunden-Format",
- "leading-zero": "Stunde mit führender Null (00-23) - 24-Stunden-Format"
- },
- "minute": {
- "no-leading-zero": "Minute ohne führende Null (0-59)",
- "leading-zero": "Minute mit führender Null (00-59)"
- },
- "second": {
- "no-leading-zero": "Sekunde ohne führende Null (0-59)",
- "leading-zero": "Sekunde mit führender Null (00-59)"
- },
- "ampm": {
- "uppercase": "AM/PM in Großbuchstaben",
- "lowercase": "am/pm in Kleinbuchstaben"
- },
- "timezone": {
- "abbreviation": "Zeitzonenabkürzung"
- },
- "year": {
- "two-digit": "Jahr als zweistellige Zahl (00-99)",
- "four-digit": "Jahr als vierstellige Zahl"
- },
- "month": {
- "number-no-zero": "Monat als Zahl ohne führende Null (1-12)",
- "number-leading-zero": "Monat als Zahl mit führender Null (01-12)",
- "abbreviated": "Abgekürzter Monatsname",
- "full": "Vollständiger Monatsname"
- },
- "day": {
- "no-leading-zero": "Tag ohne führende Null (1-31)",
- "leading-zero": "Tag mit führender Null (01-31)",
- "abbreviated": "Abgekürzter Tagesname",
- "full": "Vollständiger Tagesname"
- }
- },
- "icon-picker": {
- "title": "Symbol-Auswahl",
- "search": {
- "label": "Suchen"
- },
- "cancel": "Abbrechen",
- "apply": "Anwenden"
- },
- "color-picker": {
- "title": "Farbauswahl",
- "hex": {
- "label": "Hex-Farbe",
- "description": "Geben Sie einen hexadezimalen Farbcode ein."
- },
- "rgb": {
- "label": "RGB-Werte",
- "description": "Passen Sie Rot-, Grün-, Blau- und Helligkeitswerte an."
- },
- "brightness": "Helligkeit",
- "theme-colors": {
- "label": "Theme-Farben",
- "description": "Schnellzugriff auf die Farbpalette Ihres Themes."
- },
- "palette": {
- "label": "Palette",
- "description": "Wählen Sie aus einer großen Auswahl vordefinierter Farben."
- },
- "cancel": "Abbrechen",
- "apply": "Anwenden"
- },
- "text-input": {
- "clear": "Löschen"
- }
+ "authentication": {
+ "error": "Authentifizierungsfehler",
+ "failed": "Authentifizierung fehlgeschlagen"
},
"bar": {
"widget-settings": {
- "dialog": {
- "cancel": "Abbrechen",
- "apply": "Anwenden"
- },
- "section-editor": {
- "placeholder": "Widget auswählen...",
- "search-placeholder": "Widgets suchen..."
- },
"active-window": {
+ "colorize-icons": {
+ "description": "Theme-Farben auf das aktive Fenster-Symbol anwenden.",
+ "label": "Symbole einfärben"
+ },
"hide-mode": {
- "label": "Ausblendmodus",
- "description": "Steuert das Verhalten des Widgets, wenn kein Fenster aktiv ist."
- },
- "show-app-icon": {
- "label": "App-Symbol anzeigen",
- "description": "Anwendungssymbol neben dem Fenstertitel anzeigen."
- },
- "scrolling-mode": {
- "label": "Scrollmodus",
- "description": "Steuern, wann Textscrolling für lange Fenstertitel aktiviert ist."
+ "description": "Steuert das Verhalten des Widgets, wenn kein Fenster aktiv ist.",
+ "label": "Ausblendmodus"
},
"max-width": {
- "label": "Maximale Breite",
- "description": "Stellt die maximale Horizontalgröße des Widgets ein. Das Widget wird sich an kürzere Inhalte anpassen."
+ "description": "Stellt die maximale Horizontalgröße des Widgets ein. Das Widget wird sich an kürzere Inhalte anpassen.",
+ "label": "Maximale Breite"
+ },
+ "scrolling-mode": {
+ "description": "Steuern, wann Textscrolling für lange Fenstertitel aktiviert ist.",
+ "label": "Scrollmodus"
+ },
+ "show-app-icon": {
+ "description": "Anwendungssymbol neben dem Fenstertitel anzeigen.",
+ "label": "App-Symbol anzeigen"
},
"use-fixed-width": {
- "label": "Feste Breite verwenden",
- "description": "Wenn aktiviert, verwendet das Widget immer die maximale Breite, anstatt sich dynamisch an den Inhalt anzupassen."
- },
- "colorize-icons": {
- "label": "Symbole einfärben",
- "description": "Theme-Farben auf das aktive Fenster-Symbol anwenden."
+ "description": "Wenn aktiviert, verwendet das Widget immer die maximale Breite, anstatt sich dynamisch an den Inhalt anzupassen.",
+ "label": "Feste Breite verwenden"
}
},
- "system-monitor": {
- "cpu-usage": {
- "label": "CPU-Auslastung",
- "description": "Aktuelle CPU-Auslastung in Prozent anzeigen."
+ "audio-visualizer": {
+ "color-name": {
+ "description": "Wähle die Farbe für den Visualisierer aus.",
+ "label": "Füllfarbe"
},
- "cpu-temperature": {
- "label": "CPU-Temperatur",
- "description": "CPU-Temperaturwerte anzeigen, falls verfügbar."
+ "hide-when-idle": {
+ "description": "Wenn aktiviert, wird der Visualizer ausgeblendet, sofern keine Wiedergabe läuft.",
+ "label": "Ausblenden, wenn keine Medien wiedergegeben werden"
},
- "memory-usage": {
- "label": "Speicherverbrauch",
- "description": "Aktuelle RAM-Nutzungsinformationen anzeigen."
- },
- "memory-percentage": {
- "label": "Speicher als Prozentsatz",
- "description": "Speicherverbrauch als Prozentsatz statt absolute Werte anzeigen."
- },
- "network-traffic": {
- "label": "Netzwerkverkehr",
- "description": "Upload- und Download-Geschwindigkeiten anzeigen."
- },
- "storage-usage": {
- "label": "Speichernutzung",
- "description": "Festplattenspeicher-Nutzungsinformationen anzeigen."
- }
- },
- "notification-history": {
- "show-unread-badge": {
- "label": "Badge für ungelesene Nachrichten anzeigen",
- "description": "Badge mit der Anzahl ungelesener Benachrichtigungen anzeigen."
- },
- "hide-badge-when-zero": {
- "label": "Badge ausblenden, wenn null",
- "description": "Benachrichtigungs-Badge ausblenden, wenn keine ungelesenen Benachrichtigungen vorhanden sind."
+ "width": {
+ "description": "Benutzerdefinierte Komponentenbreite.",
+ "label": "Breite"
}
},
"battery": {
"display-mode": {
- "label": "Anzeigemodus",
- "description": "Wählen Sie, wie dieser Wert angezeigt werden soll."
+ "description": "Wählen Sie, wie dieser Wert angezeigt werden soll.",
+ "label": "Anzeigemodus"
},
"low-battery-threshold": {
- "label": "Schwellenwert für niedrigen Batteriestand",
- "description": "Warnung anzeigen, wenn Batterie unter diesen Prozentsatz fällt."
- }
- },
- "control-center": {
- "use-distro-logo": {
- "label": "Distro-Logo anstelle von Symbol verwenden",
- "description": "Logo Ihrer Distribution anstelle eines benutzerdefinierten Symbols verwenden."
- },
- "icon": {
- "label": "Symbol",
- "description": "Symbol aus der Bibliothek oder eine benutzerdefinierte Datei auswählen."
- },
- "browse-library": "Bibliothek durchsuchen",
- "browse-file": "Datei durchsuchen",
- "select-custom-icon": "Benutzerdefiniertes Symbol auswählen"
- },
- "keyboard-layout": {
- "display-mode": {
- "label": "Anzeigemodus",
- "description": "Wählen Sie, wie dieser Wert angezeigt werden soll."
- }
- },
- "volume": {
- "display-mode": {
- "label": "Anzeigemodus",
- "description": "Wählen Sie, wie dieser Wert angezeigt werden soll."
- }
- },
- "workspace": {
- "label-mode": {
- "label": "Beschriftungsmodus",
- "description": "Wählen Sie, wie Arbeitsbereichs-Beschriftungen angezeigt werden."
- },
- "hide-unoccupied": {
- "label": "Unbesetzte ausblenden",
- "description": "Arbeitsbereiche ohne Fenster nicht anzeigen."
- },
- "character-count": {
- "label": "Zeichenanzahl",
- "description": "Anzahl der Zeichen, die von Arbeitsbereichsnamen angezeigt werden (1-10)."
- }
- },
- "microphone": {
- "display-mode": {
- "label": "Anzeigemodus",
- "description": "Wählen Sie, wie dieser Wert angezeigt werden soll."
+ "description": "Warnung anzeigen, wenn Batterie unter diesen Prozentsatz fällt.",
+ "label": "Schwellenwert für niedrigen Batteriestand"
}
},
"brightness": {
"display-mode": {
- "label": "Anzeigemodus",
- "description": "Wählen Sie, wie dieser Wert angezeigt werden soll."
+ "description": "Wählen Sie, wie dieser Wert angezeigt werden soll.",
+ "label": "Anzeigemodus"
}
},
- "spacer": {
- "width": {
- "label": "Breite",
- "description": "Abstandsbreite in Pixeln."
- }
- },
- "custom-button": {
- "icon": {
- "label": "Symbol",
- "description": "Symbol aus der Bibliothek auswählen."
- },
- "browse": "Durchsuchen",
- "left-click": {
- "label": "Linksklick",
- "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche links angeklickt wird."
- },
- "right-click": {
- "label": "Rechtsklick",
- "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche rechts angeklickt wird."
- },
- "middle-click": {
- "label": "Mittelklick",
- "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche mit der mittleren Maustaste angeklickt wird."
- },
- "dynamic-text": "Dynamischer Text",
- "display-command-output": {
- "label": "Befehlsausgabe anzeigen",
- "description": "Befehl eingeben, der in regelmäßigen Abständen ausgeführt wird. Die erste Zeile seiner Ausgabe wird als Text angezeigt.",
- "stream-description": "Geben Sie einen Befehl ein, der kontinuierlich ausgeführt werden soll."
- },
- "refresh-interval": {
- "label": "Aktualisierungsintervall",
- "description": "Intervall in Millisekunden."
- },
- "text-stream": {
- "label": "Strom",
- "description": "Gestreamte Zeilen aus dem Befehl werden als Text auf der Schaltfläche angezeigt."
- },
- "collapse-condition": {
- "label": "Collapse condition",
- "description": "If the output text matches this value, the button will collapse."
- },
- "parse-json": {
- "label": "Ausgabe als JSON parsen",
- "description": "Die Befehlsausgabe als JSON-Objekt parsen, um Text und Symbol dynamisch festzulegen."
- }
- },
- "media-mini": {
- "hide-mode": {
- "label": "Ausblendmodus",
- "description": "Steuert das Verhalten des Widgets, wenn keine Medien abgespielt werden."
- },
- "show-album-art": {
- "label": "Albumcover anzeigen",
- "description": "Albumcover des aktuell abgespielten Tracks anzeigen."
- },
- "show-visualizer": {
- "label": "Visualizer anzeigen",
- "description": "Audio-Visualizer anzeigen, wenn Musik abgespielt wird."
- },
- "visualizer-type": {
- "label": "Visualizer-Typ",
- "description": "Stil des Audio-Visualizers auswählen."
- },
- "max-width": {
- "label": "Maximale Breite",
- "description": "Stellt die maximale Horizontalgröße des Widgets ein. Das Widget wird sich an kürzere Inhalte anpassen."
- },
- "use-fixed-width": {
- "label": "Feste Breite verwenden",
- "description": "Wenn aktiviert, verwendet das Widget immer die maximale Breite, anstatt sich dynamisch an den Inhalt anzupassen."
- },
- "scrolling-mode": {
- "label": "Scrollmodus",
- "description": "Steuern, wann Textscrolling für lange Track-Titel aktiviert ist."
- },
- "no-active-player": "Kein aktiver Player"
- },
"clock": {
- "use-primary-color": {
- "label": "Primärfarbe verwenden",
- "description": "Wenn aktiviert, wird die Primärfarbe zur Hervorhebung angewendet."
- },
- "use-monospaced-font": {
- "label": "Monospace-Schriftart verwenden",
- "description": "Wenn aktiviert, verwendet die Uhr die Monospace-Schriftart."
- },
"clock-display": {
- "label": "Uhrenanzeige",
- "description": "Passen Sie die Anzeige Ihrer Uhr an, indem Sie Token aus der Liste unten hinzufügen. Um das 12-Stunden-Format zu verwenden, müssen Sie das 'AP'-Token einschließen."
+ "description": "Passen Sie die Anzeige Ihrer Uhr an, indem Sie Token aus der Liste unten hinzufügen. Um das 12-Stunden-Format zu verwenden, müssen Sie das 'AP'-Token einschließen.",
+ "label": "Uhrenanzeige"
},
- "horizontal-bar": {
- "label": "Horizontale Statusleiste",
- "description": "Tipp: Verwenden Sie \\n, um einen Zeilenumbruch zu erstellen."
- },
- "vertical-bar": {
- "label": "Vertikale Statusleiste",
- "description": "Verwenden Sie ein Leerzeichen, um jeden Teil in eine neue Zeile zu trennen."
- },
- "preview": "Vorschau",
"custom-font": {
"description": "Wählen Sie eine benutzerdefinierte Schriftart für die Uhrenanzeige aus.",
"label": "Benutzerdefinierte Schriftart",
"placeholder": "Benutzerdefinierte Schriftart auswählen...",
"search-placeholder": "Schriftarten suchen..."
},
+ "horizontal-bar": {
+ "description": "Tipp: Verwenden Sie \\n, um einen Zeilenumbruch zu erstellen.",
+ "label": "Horizontale Statusleiste"
+ },
+ "preview": "Vorschau",
"use-custom-font": {
"description": "Überschreibt die Standard-Schriftart mit einer benutzerdefinierten Schriftart für die Uhr.",
"label": "Benutzerdefinierte Schriftart verwenden"
+ },
+ "use-monospaced-font": {
+ "description": "Wenn aktiviert, verwendet die Uhr die Monospace-Schriftart.",
+ "label": "Monospace-Schriftart verwenden"
+ },
+ "use-primary-color": {
+ "description": "Wenn aktiviert, wird die Primärfarbe zur Hervorhebung angewendet.",
+ "label": "Primärfarbe verwenden"
+ },
+ "vertical-bar": {
+ "description": "Verwenden Sie ein Leerzeichen, um jeden Teil in eine neue Zeile zu trennen.",
+ "label": "Vertikale Statusleiste"
+ }
+ },
+ "control-center": {
+ "browse-file": "Datei durchsuchen",
+ "browse-library": "Bibliothek durchsuchen",
+ "icon": {
+ "description": "Symbol aus der Bibliothek oder eine benutzerdefinierte Datei auswählen.",
+ "label": "Symbol"
+ },
+ "select-custom-icon": "Benutzerdefiniertes Symbol auswählen",
+ "use-distro-logo": {
+ "description": "Logo Ihrer Distribution anstelle eines benutzerdefinierten Symbols verwenden.",
+ "label": "Distro-Logo anstelle von Symbol verwenden"
+ },
+ "colorize-distro-logo": {
+ "description": "Wende die Designfarben auf das Logo deiner Distribution an.",
+ "label": "Distro-Logo einfärben"
+ }
+ },
+ "custom-button": {
+ "browse": "Durchsuchen",
+ "collapse-condition": {
+ "description": "Wenn der Ausgabetext diesem Wert entspricht, wird die Schaltfläche eingeklappt.",
+ "label": "Einklappbedingung"
+ },
+ "display-command-output": {
+ "description": "Befehl eingeben, der in regelmäßigen Abständen ausgeführt wird. Die erste Zeile seiner Ausgabe wird als Text angezeigt.",
+ "label": "Befehlsausgabe anzeigen",
+ "stream-description": "Geben Sie einen Befehl ein, der kontinuierlich ausgeführt werden soll."
+ },
+ "dynamic-text": "Dynamischer Text",
+ "hide-vertical": {
+ "description": "Wenn aktiviert, wird der Text aus der Befehlsausgabe nicht angezeigt, wenn sich die Leiste in einem vertikalen Layout befindet (links oder rechts).",
+ "label": "Text in vertikaler Leiste ausblenden"
+ },
+ "icon": {
+ "description": "Symbol aus der Bibliothek auswählen.",
+ "label": "Symbol"
+ },
+ "left-click": {
+ "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche links angeklickt wird.",
+ "label": "Linksklick"
+ },
+ "middle-click": {
+ "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche mit der mittleren Maustaste angeklickt wird.",
+ "label": "Mittelklick"
+ },
+ "parse-json": {
+ "description": "Die Befehlsausgabe als JSON-Objekt parsen, um Text und Symbol dynamisch festzulegen.",
+ "label": "Ausgabe als JSON parsen"
+ },
+ "refresh-interval": {
+ "description": "Intervall in Millisekunden.",
+ "label": "Aktualisierungsintervall"
+ },
+ "right-click": {
+ "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche rechts angeklickt wird.",
+ "label": "Rechtsklick"
+ },
+ "text-stream": {
+ "description": "Gestreamte Zeilen aus dem Befehl werden als Text auf der Schaltfläche angezeigt.",
+ "label": "Stream"
+ }
+ },
+ "dialog": {
+ "apply": "Anwenden",
+ "cancel": "Abbrechen"
+ },
+ "keyboard-layout": {
+ "display-mode": {
+ "description": "Wählen Sie, wie dieser Wert angezeigt werden soll.",
+ "label": "Anzeigemodus"
+ }
+ },
+ "lock-keys": {
+ "browse": "Durchsuchen",
+ "show-caps-lock": {
+ "description": "Caps-Lock-Status anzeigen.",
+ "label": "Feststelltaste"
+ },
+ "show-num-lock": {
+ "description": "Num-Lock-Status anzeigen.",
+ "label": "Num-Taste"
+ },
+ "show-scroll-lock": {
+ "description": "Scroll-Lock-Status anzeigen.",
+ "label": "Rollenfeststelltaste"
+ }
+ },
+ "media-mini": {
+ "hide-mode": {
+ "description": "Steuert das Verhalten des Widgets, wenn keine Medien abgespielt werden.",
+ "label": "Ausblendmodus"
+ },
+ "max-width": {
+ "description": "Stellt die maximale Horizontalgröße des Widgets ein. Das Widget wird sich an kürzere Inhalte anpassen.",
+ "label": "Maximale Breite"
+ },
+ "no-active-player": "Kein aktiver Player",
+ "scrolling-mode": {
+ "description": "Steuern, wann Textscrolling für lange Track-Titel aktiviert ist.",
+ "label": "Scrollmodus"
+ },
+ "show-album-art": {
+ "description": "Albumcover des aktuell abgespielten Tracks anzeigen.",
+ "label": "Albumcover anzeigen"
+ },
+ "show-visualizer": {
+ "description": "Audio-Visualizer anzeigen, wenn Musik abgespielt wird.",
+ "label": "Visualizer anzeigen"
+ },
+ "use-fixed-width": {
+ "description": "Wenn aktiviert, verwendet das Widget immer die maximale Breite, anstatt sich dynamisch an den Inhalt anzupassen.",
+ "label": "Feste Breite verwenden"
+ },
+ "visualizer-type": {
+ "description": "Stil des Audio-Visualizers auswählen.",
+ "label": "Visualizer-Typ"
+ }
+ },
+ "microphone": {
+ "display-mode": {
+ "description": "Wählen Sie, wie dieser Wert angezeigt werden soll.",
+ "label": "Anzeigemodus"
+ }
+ },
+ "notification-history": {
+ "hide-badge-when-zero": {
+ "description": "Benachrichtigungs-Badge ausblenden, wenn keine ungelesenen Benachrichtigungen vorhanden sind.",
+ "label": "Badge ausblenden, wenn null"
+ },
+ "show-unread-badge": {
+ "description": "Badge mit der Anzahl ungelesener Benachrichtigungen anzeigen.",
+ "label": "Badge für ungelesene Nachrichten anzeigen"
+ }
+ },
+ "section-editor": {
+ "placeholder": "Widget auswählen...",
+ "search-placeholder": "Widgets suchen..."
+ },
+ "spacer": {
+ "width": {
+ "description": "Abstandsbreite in Pixeln.",
+ "label": "Breite"
+ }
+ },
+ "system-monitor": {
+ "cpu-temperature": {
+ "description": "CPU-Temperaturwerte anzeigen, falls verfügbar.",
+ "label": "CPU-Temperatur"
+ },
+ "cpu-usage": {
+ "description": "Aktuelle CPU-Auslastung in Prozent anzeigen.",
+ "label": "CPU-Auslastung"
+ },
+ "memory-percentage": {
+ "description": "Speicherverbrauch als Prozentsatz statt absolute Werte anzeigen.",
+ "label": "Speicher als Prozentsatz"
+ },
+ "memory-usage": {
+ "description": "Aktuelle RAM-Nutzungsinformationen anzeigen.",
+ "label": "Speicherverbrauch"
+ },
+ "network-traffic": {
+ "description": "Upload- und Download-Geschwindigkeiten anzeigen.",
+ "label": "Netzwerkverkehr"
+ },
+ "storage-usage": {
+ "description": "Festplattenspeicher-Nutzungsinformationen anzeigen.",
+ "label": "Speichernutzung"
}
},
"taskbar": {
+ "colorize-icons": {
+ "description": "Theme-Farben auf Taskbar-Symbole anwenden.",
+ "label": "Symbole einfärben"
+ },
"hide-mode": {
- "label": "Ausblendmodus",
- "description": "Steuert das Verhalten des Widgets, wenn keine übereinstimmenden Fenster vorhanden sind."
+ "description": "Steuert das Verhalten des Widgets, wenn keine übereinstimmenden Fenster vorhanden sind.",
+ "label": "Ausblendmodus"
},
"only-active-workspaces": {
- "label": "Nur von aktiven Arbeitsbereichen",
- "description": "Zeige nur Apps von aktiven Arbeitsbereichen."
+ "description": "Zeige nur Apps von aktiven Arbeitsbereichen.",
+ "label": "Nur von aktiven Arbeitsbereichen"
},
"only-same-output": {
- "label": "Nur vom gleichen Bildschirm",
- "description": "Zeige nur Apps von dem Bildschirm, auf dem sich die Taskbar befindet."
- },
- "colorize-icons": {
- "label": "Symbole einfärben",
- "description": "Theme-Farben auf Taskbar-Symbole anwenden."
+ "description": "Zeige nur Apps von dem Bildschirm, auf dem sich die Taskbar befindet.",
+ "label": "Nur vom gleichen Bildschirm"
}
},
"taskbar-grouped": {
@@ -1270,118 +294,69 @@
},
"tray": {
"colorize-icons": {
- "label": "Symbole einfärben",
- "description": "Theme-Farben auf Tray-Symbole anwenden."
+ "description": "Theme-Farben auf Tray-Symbole anwenden.",
+ "label": "Symbole einfärben"
+ },
+ "drawer-enabled": {
+ "description": "Wenn aktiviert, werden nicht angeheftete Tray-Elemente in einem Schubladen-Panel angezeigt. Wenn deaktiviert, werden alle Tray-Elemente inline angezeigt.",
+ "label": "Schublade aktivieren"
}
},
- "audio-visualizer": {
- "width": {
- "label": "Breite",
- "description": "Benutzerdefinierte Komponentenbreite."
- },
- "hide-when-idle": {
- "label": "Ausblenden, wenn keine Medien wiedergegeben werden",
- "description": "Wenn aktiviert, wird der Visualizer ausgeblendet, sofern keine Wiedergabe läuft."
- },
- "color-name": {
- "description": "Wähle die Farbe für den Visualisierer aus.",
- "label": "Füllfarbe"
+ "volume": {
+ "display-mode": {
+ "description": "Wählen Sie, wie dieser Wert angezeigt werden soll.",
+ "label": "Anzeigemodus"
}
},
- "lock-keys": {
- "show-caps-lock": {
- "description": "Caps Lock Status anzeigen.",
- "label": "Feststelltaste"
+ "workspace": {
+ "character-count": {
+ "description": "Anzahl der Zeichen, die von Arbeitsbereichsnamen angezeigt werden (1-10).",
+ "label": "Zeichenanzahl"
},
- "show-num-lock": {
- "description": "Num-Lock-Status anzeigen.",
- "label": "Num-Taste"
+ "hide-unoccupied": {
+ "description": "Arbeitsbereiche ohne Fenster nicht anzeigen.",
+ "label": "Unbesetzte ausblenden"
},
- "show-scroll-lock": {
- "description": "Scroll-Lock-Status anzeigen.",
- "label": "Rollenfeststelltaste"
- },
- "indicator-style": {
- "circle": "Kreisbuchstaben",
- "circle-dash": "Gestrichelte Kreisbuchstaben",
- "circle-dot": "Punktierte Kreisbuchstaben",
- "description": "Symbolstil für die Feststelltastenanzeigen",
- "hex": "Sechseck-Buchstaben",
- "label": "Indikatorstil",
- "large": "Große Buchstaben",
- "small": "Kleinbuchstaben",
- "square": "Quadratische Buchstaben",
- "square-round": "Abgerundete quadratische Buchstaben"
+ "label-mode": {
+ "description": "Wählen Sie, wie Arbeitsbereichs-Beschriftungen angezeigt werden.",
+ "label": "Beschriftungsmodus"
}
}
}
},
- "notifications": {
+ "battery": {
+ "charging": "Wird geladen.",
+ "charging-rate": "Laderate: {rate} W.",
+ "discharging": "Wird entladen.",
+ "discharging-rate": "Entladerate: {rate} W.",
+ "health": "Zustand: {percent}%",
+ "idle": "Leerlauf.",
+ "no-battery-detected": "Keine Batterie erkannt.",
"panel": {
- "title": "Benachrichtigungen",
- "no-notifications": "Keine Benachrichtigungen",
- "description": "Ihre Benachrichtigungen werden hier angezeigt, sobald sie eintreffen.",
- "click-to-expand": "Zum Erweitern klicken"
- }
- },
- "wallpaper": {
- "panel": {
- "title": "Hintergrundbild-Auswahl",
- "apply-all-monitors": {
- "label": "Auf alle Monitore anwenden",
- "description": "Ausgewähltes Hintergrundbild auf alle Monitore gleichzeitig anwenden."
- },
- "search": "Suchen:"
+ "balanced": "Ausgeglichen ({percent}%)",
+ "disabled": "Batteriemanager deaktiviert",
+ "full": "Volle Kapazität ({percent}%)",
+ "lifespan": "Verlängerte Lebensdauer ({percent}%)",
+ "title": "Ladeschwelle"
},
- "transitions": {
- "none": "Keine",
- "random": "Zufällig",
- "fade": "Überblenden",
- "disc": "Scheibe",
- "stripes": "Streifen",
- "wipe": "Wischen"
- },
- "fill-modes": {
- "center": "Zentrieren",
- "crop": "Zuschneiden (Füllen)",
- "fit": "Einpassen (Beibehalten)",
- "stretch": "Strecken"
- },
- "no-match": "Keine Übereinstimmung gefunden.",
- "no-wallpaper": "Kein Hintergrundbild gefunden.",
- "try-different-search": "Versuchen Sie eine andere Suchanfrage.",
- "configure-directory": "Konfigurieren Sie Ihr Hintergrundbild-Verzeichnis mit Bildern."
+ "plugged-in": "Angeschlossen.",
+ "time-left": "Verbleibende Zeit: {time}.",
+ "time-until-full": "Zeit bis vollständig geladen: {time}."
},
"bluetooth": {
"panel": {
- "title": "Bluetooth",
- "disabled": "Bluetooth ist deaktiviert",
- "enable-message": "Aktivieren Sie Bluetooth, um verfügbare Geräte zu sehen.",
- "connected-devices": "Verbundene Geräte",
- "known-devices": "Bekannte Geräte",
"available-devices": "Verfügbare Geräte",
- "scanning": "Scannen nach Geräten...",
- "pairing-mode": "Stellen Sie sicher, dass sich Ihr Gerät im Kopplungsmodus befindet."
- }
- },
- "wifi": {
- "panel": {
- "title": "WLAN",
- "disabled": "WLAN ist deaktiviert",
- "enable-message": "Aktivieren Sie WLAN, um verfügbare Netzwerke zu sehen.",
- "searching": "Suche nach nahegelegenen Netzwerken...",
- "connected": "Verbunden",
- "disconnecting": "Verbindung wird getrennt...",
- "forgetting": "Wird vergessen...",
- "saved": "Gespeichert",
- "disconnect": "Trennen",
- "enter-password": "Passwort eingeben...",
+ "blocked": "Blockiert",
"connect": "Verbinden",
- "password": "Passwort",
- "forget-network": "Dieses Netzwerk vergessen?",
- "forget": "Vergessen",
- "no-networks": "Keine Netzwerke gefunden",
- "scan-again": "Erneut scannen"
+ "connected-devices": "Verbundene Geräte",
+ "disabled": "Bluetooth ist deaktiviert",
+ "disconnect": "Trennen",
+ "enable-message": "Aktivieren Sie Bluetooth, um verfügbare Geräte zu sehen.",
+ "known-devices": "Bekannte Geräte",
+ "pairing": "Koppeln...",
+ "pairing-mode": "Stellen Sie sicher, dass sich Ihr Gerät im Kopplungsmodus befindet.",
+ "scanning": "Scannen nach Geräten...",
+ "title": "Bluetooth"
}
},
"calendar": {
@@ -1392,304 +367,246 @@
"loading": "Wetter wird geladen…"
}
},
- "tooltips": {
- "refresh": "Aktualisieren",
- "close": "Schließen",
- "up": "Nach oben",
- "home": "Home",
- "refresh-wallpaper-list": "Hintergrundbild-Liste aktualisieren",
- "refresh-devices": "Geräte aktualisieren",
- "forget-network": "Netzwerk vergessen",
- "clear-history": "Verlauf löschen",
- "delete-notification": "Benachrichtigung löschen",
- "previous-month": "Vorheriger Monat",
- "next-month": "Nächster Monat",
- "add-widget": "Widget hinzufügen",
- "widget-settings": "Widget-Einstellungen",
- "remove-widget": "Widget entfernen",
- "move-to-left-section": "Zur linken Sektion verschieben",
- "move-to-center-section": "Zur mittleren Sektion verschieben",
- "move-to-right-section": "Zur rechten Sektion verschieben",
- "open-settings": "Einstellungen öffnen",
- "session-menu": "Sitzungsmenü",
- "cancel-timer": "Timer abbrechen",
- "start-screen-recording": "Bildschirmaufnahme starten",
- "stop-screen-recording": "Bildschirmaufnahme stoppen",
- "screen-recorder-not-installed": "Bildschirmrekorder ist nicht installiert",
- "keep-awake": "Wach halten",
- "enable-keep-awake": "Wach halten aktivieren",
- "disable-keep-awake": "Wach halten deaktivieren",
- "wallpaper-selector": "Linksklick: Hintergrundbild-Auswahl öffnen.\nRechtsklick: Zufälliges Hintergrundbild setzen.",
- "do-not-disturb-enabled": "'Nicht stören' aktiviert",
- "do-not-disturb-disabled": "'Nicht stören' deaktiviert",
- "connect-disconnect-devices": "Linksklick zum Verbinden. Rechtsklick zum Vergessen.",
- "set-power-profile": "'{profile}' Energieprofil setzen",
- "switch-to-light-mode": "Zum hellen Modus wechseln",
- "switch-to-dark-mode": "Zum dunklen Modus wechseln",
- "night-light-disabled": "Nachtlicht ist deaktiviert.\nLinksklick zum Wechseln des Modus.\nRechtsklick für Einstellungen.",
- "night-light-enabled": "Nachtlicht ist aktiviert.\nLinksklick zum Wechseln des Modus.\nRechtsklick für Einstellungen.",
- "night-light-forced": "Nachtlicht ist erzwungen.\nLinksklick zum Wechseln des Modus.\nRechtsklick für Einstellungen.",
- "night-light-not-installed": "Nachtlicht ist nicht verfügbar.\nwlsunset ist nicht installiert.",
- "click-to-start-recording": "Klicken zum Starten einer Bildschirmaufnahme",
- "click-to-stop-recording": "Klicken zum Stoppen einer Bildschirmaufnahme",
- "open-control-center": "Kontrollzentrum öffnen",
- "brightness-at": "Helligkeit: {brightness}%\nRechtsklick für Einstellungen.\nScrollen zum Ändern der Helligkeit.",
- "manage-wifi": "WLAN verwalten",
- "bluetooth-devices": "Bluetooth-Geräte",
- "open-notification-history-enable-dnd": "Benachrichtigungsverlauf öffnen\nRechtsklick um 'Nicht stören' zu aktivieren.",
- "open-notification-history-disable-dnd": "Benachrichtigungsverlauf öffnen\nRechtsklick um 'Nicht stören' zu deaktivieren.",
- "open-wallpaper-selector": "Hintergrundbild-Auswahl öffnen",
- "previous-media": "Vorheriges Medium",
- "pause": "Pausieren",
- "play": "Wiedergeben",
- "next-media": "Nächstes Medium",
- "power-profile": "'{profile}' Energieprofil",
- "keyboard-layout": "{layout} Tastaturlayout",
- "output-muted": "Audio-Ausgabe stummschalten",
- "input-muted": "Audio-Eingabe stummschalten",
- "microphone-volume-at": "Mikrofonlautstärke bei {volume}%.\nLinksklick für Einstellungen. Rechtsklick zum Stummschalten.\nScrollen zum Ändern der Lautstärke.",
- "volume-at": "Lautstärke bei {volume}%.\nLinksklick für Einstellungen. Rechtsklick zum Stummschalten.\nScrollen zum Ändern der Lautstärke."
- },
"clock": {
"tooltip": "Kalender öffnen"
},
"dock": {
"menu": {
+ "close": "Schließen",
"focus": "Fokussieren",
"pin": "Anheften",
- "unpin": "Lösen",
- "close": "Schließen"
+ "unpin": "Lösen"
}
},
+ "general": {
+ "no-results": "Keine Ergebnisse",
+ "no-summary": "Keine Zusammenfassung",
+ "unknown": "Unbekannt"
+ },
"launcher": {
"pin": "An das Dock anheften",
"unpin": "Vom Dock lösen"
},
- "placeholders": {
- "search-icons": "z.B. noctalia, niri, battery, cloud",
- "profile-picture-path": "/home/benutzer/.face",
- "enter-width-pixels": "Breite in Pixeln eingeben",
- "enter-command": "Befehl eingeben (App oder benutzerdefiniertes Skript)",
- "command-example": "echo \"Hallo Welt\"",
- "enter-text-to-collapse": "z.B. 'nothing is playing'. Verwenden Sie /regex/ für Muster.",
- "search-wallpapers": "Zum Filtern von Hintergrundbildern eingeben...",
- "search-launcher": "Einträge suchen... oder > für Befehle verwenden",
- "search": "Suchen...",
- "select": "Auswählen",
- "cancel": "Abbrechen",
- "test": "Test"
+ "lock-screen": {
+ "authenticating": "Authentifizierung...",
+ "authentication-failed": "Authentifizierung fehlgeschlagen",
+ "password": "Passwort eingeben...",
+ "restart": "Neu starten",
+ "shut-down": "Herunterfahren",
+ "suspend": "Ruhezustand",
+ "welcome-back": "Willkommen zurück,"
+ },
+ "notifications": {
+ "panel": {
+ "click-to-expand": "Zum Erweitern klicken",
+ "description": "Ihre Benachrichtigungen werden hier angezeigt, sobald sie eintreffen.",
+ "no-notifications": "Keine Benachrichtigungen",
+ "title": "Benachrichtigungen"
+ }
},
"options": {
"bar": {
- "position": {
- "top": "Oben",
- "bottom": "Unten",
- "left": "Links",
- "right": "Rechts"
- },
"density": {
+ "comfortable": "Bequem",
"compact": "Kompakt",
"default": "Standard",
- "comfortable": "Bequem",
"mini": "Mini"
+ },
+ "position": {
+ "bottom": "Unten",
+ "left": "Links",
+ "right": "Rechts",
+ "top": "Oben"
}
},
- "launcher": {
- "position": {
- "center": "Mitte (Standard)",
- "top_left": "Oben links",
- "top_right": "Oben rechts",
- "bottom_left": "Unten links",
- "bottom_right": "Unten rechts",
- "bottom_center": "Unten mittig",
- "top_center": "Oben mittig",
- "center_left": "Links mittig",
- "center_right": "Rechts mittig"
- }
+ "colors": {
+ "error": "Fehler",
+ "onSurface": "An der Oberfläche",
+ "primary": "Primär",
+ "secondary": "Sekundär",
+ "tertiary": "Tertiär"
},
"control-center": {
"position": {
- "close_to_bar_button": "Nah zur Leisten-Schaltfläche",
- "top_left": "Oben links",
- "top_right": "Oben rechts",
+ "bottom_center": "Unten mittig",
"bottom_left": "Unten links",
"bottom_right": "Unten rechts",
- "bottom_center": "Unten mittig",
+ "center": "Mitte",
+ "close_to_bar_button": "Nah zur Leisten-Schaltfläche",
"top_center": "Oben mittig",
- "center": "Mitte"
+ "top_left": "Oben links",
+ "top_right": "Oben rechts"
},
"quickSettingsStyle": {
- "modern": "Modern",
"classic": "Klassisch",
- "compact": "Kompakt"
+ "compact": "Kompakt",
+ "modern": "Modern"
+ }
+ },
+ "display-mode": {
+ "always-hide": "Immer ausblenden",
+ "always-show": "Immer anzeigen",
+ "force-open": "Erzwingen",
+ "on-hover": "Beim Darüberfahren"
+ },
+ "frame-rates": {
+ "fps": "{fps} FPS"
+ },
+ "hide-modes": {
+ "hidden": "Ausblenden, wenn leer",
+ "idle": "Ausblenden bei Inaktivität",
+ "transparent": "Transparent, wenn leer",
+ "visible": "Immer sichtbar"
+ },
+ "launcher": {
+ "position": {
+ "bottom_center": "Unten mittig",
+ "bottom_left": "Unten links",
+ "bottom_right": "Unten rechts",
+ "center": "Mitte (Standard)",
+ "center_left": "Links mittig",
+ "center_right": "Rechts mittig",
+ "follow_bar": "Folge dem Balken (Standard)",
+ "top_center": "Oben mittig",
+ "top_left": "Oben links",
+ "top_right": "Oben rechts"
}
},
"osd": {
"position": {
- "top_left": "Oben links",
- "top_right": "Oben rechts",
- "top_center": "Oben mittig",
+ "bottom_center": "Unten mittig",
"bottom_left": "Unten links",
"bottom_right": "Unten rechts",
- "bottom_center": "Unten mittig",
"center_left": "Links mittig",
- "center_right": "Rechts mittig"
+ "center_right": "Rechts mittig",
+ "top_center": "Oben mittig",
+ "top_left": "Oben links",
+ "top_right": "Oben rechts"
}
},
- "display-mode": {
- "on-hover": "Beim Darüberfahren",
- "always-show": "Immer anzeigen",
- "always-hide": "Immer ausblenden",
- "force-open": "Erzwingen"
- },
- "workspace-labels": {
- "none": "Keine",
- "index": "Index",
- "name": "Name"
- },
- "visualizer-types": {
- "none": "Keine",
- "linear": "Linear",
- "mirrored": "Gespiegelt",
- "wave": "Welle"
+ "screen-recording": {
+ "audio-sources": {
+ "both": "System-Ausgabe + Mikrofon-Eingabe",
+ "microphone-input": "Mikrofon-Eingabe",
+ "system-output": "System-Ausgabe"
+ },
+ "color-range": {
+ "full": "Vollständig",
+ "limited": "Begrenzt"
+ },
+ "quality": {
+ "high": "Hoch",
+ "medium": "Mittel",
+ "ultra": "Ultra",
+ "very-high": "Sehr hoch"
+ },
+ "sources": {
+ "portal": "Portal",
+ "screen": "Bildschirm"
+ }
},
"scrolling-modes": {
"always": "Immer Scrollen",
"hover": "Beim Hover Scrollen",
"never": "Nie Scrollen"
},
- "frame-rates": {
- "fps": "{fps} FPS"
+ "shadow-direction": {
+ "bottom": "Unten",
+ "bottom_left": "Unten links",
+ "bottom_right": "Unten rechts",
+ "center": "Zentriert",
+ "left": "Links",
+ "right": "Rechts",
+ "top": "Oben",
+ "top_left": "Oben links",
+ "top_right": "Oben rechts"
},
- "screen-recording": {
- "sources": {
- "portal": "Portal",
- "screen": "Bildschirm"
- },
- "quality": {
- "medium": "Mittel",
- "high": "Hoch",
- "very-high": "Sehr hoch",
- "ultra": "Ultra"
- },
- "color-range": {
- "limited": "Begrenzt",
- "full": "Vollständig"
- },
- "audio-sources": {
- "system-output": "System-Ausgabe",
- "microphone-input": "Mikrofon-Eingabe",
- "both": "System-Ausgabe + Mikrofon-Eingabe"
- }
+ "visualizer-quality": {
+ "high": "Hoch",
+ "low": "Niedrig"
},
- "hide-modes": {
- "hidden": "Ausblenden, wenn leer",
- "transparent": "Transparent, wenn leer",
- "visible": "Immer sichtbar"
+ "visualizer-types": {
+ "linear": "Linear",
+ "mirrored": "Gespiegelt",
+ "none": "Keine",
+ "wave": "Welle"
},
- "colors": {
- "error": "Fehler",
- "primary": "Primär",
- "secondary": "Sekundär",
- "tertiary": "Tertiär",
- "onSurface": "An der Oberfläche"
+ "workspace-labels": {
+ "index": "Index",
+ "name": "Name",
+ "none": "Keine"
}
},
- "session-menu": {
- "title": "Sitzungsmenü",
- "action-in-seconds": "{action} in {seconds} Sekunden...",
- "lock": "Sperren",
- "lock-and-suspend": "Sperren und Ruhezustand",
- "suspend": "Ruhezustand",
- "reboot": "Neu starten",
- "logout": "Abmelden",
- "shutdown": "Herunterfahren"
+ "placeholders": {
+ "cancel": "Abbrechen",
+ "command-example": "echo \"Hallo Welt\"",
+ "enter-command": "Befehl eingeben (App oder benutzerdefiniertes Skript)",
+ "enter-text-to-collapse": "z.B. 'nothing is playing'. Verwenden Sie /regex/ für Muster.",
+ "enter-tooltip": "Tooltip eingeben",
+ "enter-width-pixels": "Breite in Pixeln eingeben",
+ "profile-picture-path": "/home/benutzer/.face",
+ "search": "Suchen...",
+ "search-icons": "z.B. noctalia, niri, battery, cloud",
+ "search-launcher": "Einträge suchen... oder > für Befehle verwenden",
+ "search-wallpapers": "Zum Filtern von Hintergrundbildern eingeben...",
+ "select": "Auswählen",
+ "test": "Test"
},
"plugins": {
"applications": "Anwendungen",
- "clipboard": "Zwischenablageverlauf",
"calculator": "Rechner",
- "clipboard-search-description": "Zwischenablageverlauf durchsuchen",
+ "calculator-description": "Rechner - mathematische Ausdrücke auswerten",
+ "calculator-enter-expression": "Mathematischen Ausdruck eingeben",
+ "calculator-error": "Fehler",
+ "calculator-name": "Rechner",
+ "clipboard": "Zwischenablageverlauf",
"clipboard-clear-description": "Gesamten Zwischenablageverlauf löschen",
+ "clipboard-clear-description-full": "Alle Elemente aus dem Zwischenablageverlauf entfernen",
+ "clipboard-clear-history": "Zwischenablageverlauf löschen",
"clipboard-history-disabled": "Zwischenablageverlauf deaktiviert",
"clipboard-history-disabled-description": "Zwischenablageverlauf in den Einstellungen aktivieren oder cliphist installieren",
- "clipboard-clear-history": "Zwischenablageverlauf löschen",
- "clipboard-clear-description-full": "Alle Elemente aus dem Zwischenablageverlauf entfernen",
"clipboard-loading": "Lade Zwischenablageverlauf...",
"clipboard-loading-description": "Bitte warten",
- "calculator-description": "Rechner - mathematische Ausdrücke auswerten",
- "calculator-name": "Rechner",
- "calculator-enter-expression": "Mathematischen Ausdruck eingeben",
- "calculator-error": "Fehler"
- },
- "system": {
- "uptime": "Laufzeit: {uptime}",
- "welcome-back": "Willkommen zurück,",
- "monitor-description": "{model} ({width}x{height} @ {scale}x)",
- "scaling-percentage": "{percentage}%",
- "location-display": "{name} ({coordinates})",
- "signal-strength": "{signal}%",
- "cpu-temperature": "{temp}°C",
- "disk-usage": "{percent}%",
- "widget-settings-title": "{widget} Einstellungen",
- "unknown-app": "Unbekannte App",
- "no-media-player-detected": "Kein Mediaplayer erkannt",
- "user-requested": "Vom Benutzer angefordert",
- "unknown": "Unbekannt",
- "unknown-version": "Unbekannt",
- "unknown-layout": "Unbekannt"
- },
- "battery": {
- "no-battery-detected": "Keine Batterie erkannt.",
- "charging-rate": "Laderate: {rate} W.",
- "discharging-rate": "Entladerate: {rate} W.",
- "charging": "Wird geladen.",
- "discharging": "Wird entladen.",
- "plugged-in": "Angeschlossen.",
- "idle": "Leerlauf.",
- "time-left": "Verbleibende Zeit: {time}.",
- "time-until-full": "Zeit bis vollständig geladen: {time}.",
- "health": "Zustand: {percent}%",
- "panel": {
- "title": "Ladeschwelle",
- "full": "Volle Kapazität ({percent}%)",
- "balanced": "Ausgeglichen ({percent}%)",
- "lifespan": "Verlängerte Lebensdauer ({percent}%)",
- "disabled": "Batteriemanager deaktiviert"
- }
- },
- "authentication": {
- "failed": "Authentifizierung fehlgeschlagen",
- "error": "Authentifizierungsfehler"
- },
- "lock-screen": {
- "password": "Passwort eingeben...",
- "welcome-back": "Willkommen zurück,",
- "authenticating": "Authentifizierung...",
- "authentication-failed": "Authentifizierung fehlgeschlagen",
- "shut-down": "Herunterfahren",
- "restart": "Neu starten",
- "suspend": "Ruhezustand"
+ "clipboard-search-description": "Zwischenablageverlauf durchsuchen"
},
"quickSettings": {
+ "bluetooth": {
+ "label": {
+ "disabled": "Bluetooth",
+ "enabled": "Bluetooth"
+ },
+ "tooltip": {
+ "action": "Klicken zum Verwalten der Bluetooth-Geräte"
+ }
+ },
+ "keepAwake": {
+ "label": {
+ "disabled": "Wach halten",
+ "enabled": "Wach halten"
+ },
+ "tooltip": {
+ "action": "Klicken zum Umschalten des Wachmodus"
+ }
+ },
+ "nightLight": {
+ "label": {
+ "disabled": "Nachtlicht",
+ "enabled": "Nachtlicht",
+ "forced": "Nachtlicht"
+ },
+ "tooltip": {
+ "action": "Klicken zum Wechseln des Nachtlicht-Modus\nRechtsklick: Einstellungen öffnen"
+ }
+ },
"notifications": {
"label": {
- "enabled": "Benachrichtigungen",
- "disabled": "Nicht stören"
+ "disabled": "Nicht stören",
+ "enabled": "Benachrichtigungen"
},
"tooltip": {
"action": "Linksklick: Benachrichtigungsverlauf öffnen\nRechtsklick: 'Nicht stören' umschalten"
}
},
- "screenRecorder": {
- "label": {
- "recording": "Stopp",
- "stopped": "Aufnehmen"
- },
- "tooltip": {
- "action": "Klicken zum Starten/Stoppen einer Bildschirmaufnahme"
- }
- },
"powerProfile": {
"label": {
"unavailable": "Energieprofil"
@@ -1699,33 +616,13 @@
"disabled": "Installiere power-profiles-daemon, um Energieprofile zu verwenden"
}
},
- "wifi": {
+ "screenRecorder": {
"label": {
- "ethernet": "Ethernet",
- "wifi": "WLAN",
- "disconnected": "WLAN getrennt"
+ "recording": "Stopp",
+ "stopped": "Aufnehmen"
},
"tooltip": {
- "action": "Klicken zum Verwalten der WLAN-Verbindungen"
- }
- },
- "bluetooth": {
- "label": {
- "enabled": "Bluetooth",
- "disabled": "Bluetooth"
- },
- "tooltip": {
- "action": "Klicken zum Verwalten der Bluetooth-Geräte"
- }
- },
- "nightLight": {
- "label": {
- "enabled": "Nachtlicht",
- "forced": "Nachtlicht",
- "disabled": "Nachtlicht"
- },
- "tooltip": {
- "action": "Klicken zum Wechseln des Nachtlicht-Modus\nRechtsklick: Einstellungen öffnen"
+ "action": "Klicken zum Starten/Stoppen einer Bildschirmaufnahme"
}
},
"wallpaperSelector": {
@@ -1734,137 +631,1386 @@
"action": "Linksklick: Hintergrundbildauswahl öffnen\nRechtsklick: Zufälliges Hintergrundbild setzen"
}
},
- "keepAwake": {
+ "wifi": {
"label": {
- "enabled": "Wach halten",
- "disabled": "Wach halten"
+ "disconnected": "WLAN getrennt",
+ "ethernet": "Ethernet",
+ "wifi": "WLAN"
},
"tooltip": {
- "action": "Klicken zum Umschalten des Wachmodus"
+ "action": "Klicken zum Verwalten der WLAN-Verbindungen"
}
}
},
+ "session-menu": {
+ "action-in-seconds": "{action} in {seconds} Sekunden...",
+ "lock": "Sperren",
+ "lock-and-suspend": "Sperren und Ruhezustand",
+ "logout": "Abmelden",
+ "reboot": "Neu starten",
+ "shutdown": "Herunterfahren",
+ "suspend": "Ruhezustand",
+ "title": "Sitzungsmenü"
+ },
+ "settings": {
+ "about": {
+ "contributors": {
+ "section": {
+ "description": "Ein Dankeschön an unseren {count} großartigen Mitwirkenden!",
+ "description_plural": "Ein Dankeschön an unsere {count} großartigen Mitwirkenden!",
+ "label": "Mitwirkende"
+ }
+ },
+ "noctalia": {
+ "download-latest": "Neueste Version herunterladen",
+ "installed-version": "Installierte Version:",
+ "latest-version": "Neueste Version:",
+ "section": {
+ "description": "Eine elegante und minimalistische Desktop-Shell, sorgfältig für Wayland entwickelt, gebaut mit Quickshell.",
+ "label": "Noctalia Shell"
+ }
+ },
+ "support": "Unterstütze uns",
+ "title": "Über"
+ },
+ "audio": {
+ "devices": {
+ "input-device": {
+ "description": "Gewünschtes Audio-Eingabegerät auswählen.",
+ "label": "Eingabegerät"
+ },
+ "output-device": {
+ "description": "Gewünschtes Audio-Ausgabegerät auswählen.",
+ "label": "Ausgabegerät"
+ },
+ "section": {
+ "description": "Verfügbare Audio-Ein- und Ausgabegeräte konfigurieren.",
+ "label": "Audio-Geräte"
+ }
+ },
+ "media": {
+ "excluded-player": {
+ "description": "Stichwörter für Player hinzufügen, die das System ignorieren soll. Jedes Stichwort sollte in einer neuen Zeile stehen.",
+ "label": "Ausgeschlossener Player",
+ "placeholder": "Teilstring eingeben und + drücken"
+ },
+ "frame-rate": {
+ "description": "Höhere Raten sind flüssiger, verbrauchen aber mehr Ressourcen.",
+ "label": "Bildrate"
+ },
+ "primary-player": {
+ "description": "Stichwort eingeben, um den Hauptplayer zu identifizieren.",
+ "label": "Hauptplayer",
+ "placeholder": "z.B. spotify, vlc, mpv"
+ },
+ "scrolling-speed": {
+ "description": "Zeit in Sekunden, die der Titel zum Scrollen vom Anfang bis zum Ende benötigt.",
+ "label": "Scrollgeschwindigkeit"
+ },
+ "scrolling-title": {
+ "description": "Kontinuierliches Scrollen für lange Medientitel aktivieren.",
+ "label": "Scrollender Titel"
+ },
+ "section": {
+ "description": "Bevorzugte und ignorierte Medienanwendungen festlegen.",
+ "label": "Medienplayer"
+ },
+ "visualizer-quality": {
+ "description": "Hohe Qualität verbraucht mehr GPU-Ressourcen.",
+ "label": "Visualisierungsqualität"
+ },
+ "visualizer-type": {
+ "description": "Visualisierungstyp für Medienwiedergabe wählen.",
+ "label": "Visualisierungstyp"
+ }
+ },
+ "title": "Audio",
+ "volumes": {
+ "input-volume": {
+ "description": "Mikrofon-Eingabe-Lautstärke.",
+ "label": "Eingabe-Lautstärke"
+ },
+ "mute-input": {
+ "description": "Die Standard-Audio-Eingabe (Mikrofon) stummschalten.",
+ "label": "Audio-Eingabe stummschalten"
+ },
+ "mute-output": {
+ "description": "Die Haupt-Audio-Ausgabe des Systems stummschalten.",
+ "label": "Audio-Ausgabe stummschalten"
+ },
+ "output-volume": {
+ "description": "Systemweite Lautstärke.",
+ "label": "Ausgabe-Lautstärke"
+ },
+ "section": {
+ "description": "Lautstärkeregler und Audiopegel anpassen.",
+ "label": "Lautstärken"
+ },
+ "step-size": {
+ "description": "Schrittgröße für Lautstärkeänderungen anpassen (Mausrad, Tastenkombinationen).",
+ "label": "Lautstärke-Schrittgröße"
+ },
+ "volume-overdrive": {
+ "description": "Erlaubt das Anheben der Lautstärke über 100%. Wird möglicherweise nicht von aller Hardware unterstützt.",
+ "label": "Lautstärke-Übersteuerung erlauben"
+ }
+ }
+ },
+ "bar": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Hintergrund-Transparenz der Statusleiste anpassen.",
+ "label": "Hintergrund-Transparenz"
+ },
+ "density": {
+ "description": "Innenabstand der Statusleiste für kompaktes oder geräumiges Aussehen anpassen.",
+ "label": "Statusleistendichte"
+ },
+ "floating": {
+ "description": "Statusleiste als schwebende 'Pille' anzeigen.",
+ "label": "Schwebende Statusleiste"
+ },
+ "margins": {
+ "description": "Ränder um die schwebende Statusleiste anpassen.",
+ "horizontal": "Horizontal",
+ "label": "Ränder",
+ "vertical": "Vertikal"
+ },
+ "outer-corners": {
+ "description": "Zeigt nach außen gewölbte Ecken auf der Leiste an.",
+ "label": "Äußere Ecken"
+ },
+ "position": {
+ "description": "Wählen Sie, wo die Statusleiste auf dem Bildschirm platziert werden soll.",
+ "label": "Statusleistenposition"
+ },
+ "section": {
+ "description": "Erscheinungsbild und Position der Statusleiste anpassen.",
+ "label": "Erscheinungsbild"
+ },
+ "show-capsule": {
+ "description": "Widget-Hintergründe anzeigen.",
+ "label": "Kapsel anzeigen"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Statusleiste auf bestimmten Monitoren anzeigen. Standardmäßig auf allen.",
+ "label": "Monitor-Anzeige"
+ }
+ },
+ "title": "Statusleiste",
+ "tray": {
+ "back": "Zurück",
+ "blacklist": {
+ "description": "Ausschlussregeln für die Tray-Symbolleiste hinzufügen, unterstützt Platzhalter (*).",
+ "label": "Ausschlussliste",
+ "placeholder": "z.B., nm-applet, Fcitx*"
+ },
+ "pin-application": "Anwendung anheften",
+ "unpin-application": "Anheftung aufheben"
+ },
+ "widgets": {
+ "section": {
+ "description": "Widgets per Drag & Drop neu anordnen. Symoble zeigen die Verwendung an: [L]inks, [M]itte, [R]echts.",
+ "label": "Widget-Positionierung"
+ }
+ }
+ },
+ "color-scheme": {
+ "color-source": {
+ "matugen-scheme-type": {
+ "description": {
+ "scheme-content": "Leitet Farben ab, die eng mit dem zugrunde liegenden Bild übereinstimmen.",
+ "scheme-expressive": "Lebendige Farbpalette mit verspielter Sättigung.",
+ "scheme-fidelity": "Farbtreue Palette, die die Originalfarbtöne beibehält.",
+ "scheme-fruit-salad": "Farbenfroher Mix aus leuchtenden, kontrastreichen Akzenten.",
+ "scheme-monochrome": "Minimale Farbpalette, die auf einem einzigen Farbton basiert.",
+ "scheme-neutral": "Gedämpfte Farbpalette mit dezenten und beruhigenden Tönen.",
+ "scheme-rainbow": "Vielfältige Palette, die das gesamte Spektrum abdeckt.",
+ "scheme-tonal-spot": "Ausgewogene Farbpalette mit gezielten Akzenten."
+ },
+ "label": "Matugen-Schema-Typ"
+ },
+ "section": {
+ "description": "Haupteinstellungen für Noctalias Farben.",
+ "label": "Farbquelle"
+ },
+ "use-wallpaper-colors": {
+ "description": "Farbschemata aus Ihrem Hintergrundbild mit Matugen generieren. Extrahiert automatisch Farben für ein kohärentes Design.",
+ "label": "Hintergrundbild-Farben verwenden"
+ }
+ },
+ "dark-mode": {
+ "mode": {
+ "description": "Ermöglicht einen automatischen Wechsel zwischen dem hellen und dunklen Modus.",
+ "label": "Automatischer dunkler Modus",
+ "location": "Standort",
+ "manual": "Manuell",
+ "off": "Aus"
+ },
+ "switch": {
+ "description": "Wechselt zu einem dunkleren Theme für einfachere Betrachtung bei Nacht.",
+ "label": "Dunkler Modus"
+ }
+ },
+ "predefined": {
+ "generate-templates": {
+ "description": "Generiert Matugen-Templates (GTK, Terminal-Themes, etc.) bei der Verwendung vordefinierter Farbschemata.",
+ "label": "Templates für vordefinierte Schemata generieren"
+ },
+ "section": {
+ "description": "Wählen Sie aus einer Sammlung vordefinierter Farbschemata.",
+ "label": "Vordefinierte Farbschemata"
+ }
+ },
+ "templates": {
+ "misc": {
+ "description": "Zusätzliche Konfigurationsoptionen.",
+ "label": "Verschiedenes",
+ "user-templates": {
+ "description": "Benutzerdefinierte Matugen-Konfiguration aktivieren. Eine Vorlagendatei wird beim ersten Aktivieren unter ~/.config/noctalia/user-templates.toml erstellt",
+ "label": "Benutzer-Vorlagen"
+ }
+ },
+ "programs": {
+ "code": {
+ "description": "Schreibe {Dateipfad}. Das Hyprluna-Theme muss manuell installiert und aktiviert werden",
+ "description-missing": "Benötigt die Installation von {app}"
+ },
+ "spicetify": {
+ "description": "Schreibe {Dateipfad}. Das Comfy-Theme muss manuell installiert und aktiviert werden",
+ "description-missing": "Benötigt die Installation von {app}"
+ },
+ "description": "Anwendungsspezifisches Theming.",
+ "discord": {
+ "description": "Schreibt {filepath} für {client}",
+ "description-missing": "Kein Discord-Client erkannt. Installieren Sie vencord, vesktop, webcord, armcord, equibop, lightcord oder dorion"
+ },
+ "fuzzel": {
+ "description": "Schreibt {filepath} und lädt neu",
+ "description-missing": "Erfordert die Installation von {app}"
+ },
+ "label": "Programme",
+ "pywalfox": {
+ "description": "Schreibt {filepath} und führt pywalfox update aus",
+ "description-missing": "Erfordert die Installation von {app}"
+ },
+ "vicinae": {
+ "description": "Schreibt {filepath} und lädt neu",
+ "description-missing": "Erfordert die Installation von {app}"
+ },
+ "walker": {
+ "description": "Schreibt {filepath} und setzt das Theme auf Noctalia",
+ "description-missing": "Erfordert die Installation von {app}"
+ }
+ },
+ "section": {
+ "description": "Farben auf externe Anwendungen anwenden.",
+ "label": "Vorlagen"
+ },
+ "terminal": {
+ "alacritty": {
+ "description": "Schreibe {Dateipfad} und lade neu",
+ "description-missing": "Benötigt die Installation von {app}"
+ },
+ "description": "Terminal-Emulator-Theming.",
+ "foot": {
+ "description": "Schreibt {filepath} und lädt neu",
+ "description-missing": "Erfordert {app} Terminal"
+ },
+ "ghostty": {
+ "description": "Schreibt {filepath} und lädt neu",
+ "description-missing": "Erfordert {app} Terminal"
+ },
+ "kitty": {
+ "description": "Schreibt {filepath} und lädt neu",
+ "description-missing": "Erfordert {app} Terminal"
+ },
+ "label": "Terminal",
+ "wezterm": {
+ "description": "Schreibe {Dateipfad} und lade neu",
+ "description-missing": "Erfordert die Installation von {app}"
+ }
+ },
+ "ui": {
+ "description": "Desktop-Umgebung und UI-Toolkit-Theming.",
+ "gtk": {
+ "description": "Schreibt {filepath}"
+ },
+ "kcolorscheme": {
+ "description": "Schreibt {filepath}"
+ },
+ "label": "UI",
+ "qt": {
+ "description": "Schreibt {filepath}"
+ }
+ }
+ },
+ "title": "Farbschema"
+ },
+ "control-center": {
+ "cards": {
+ "section": {
+ "description": "Passen Sie an, welche Steuerelemente im Kontrollzentrum angezeigt werden und in welcher Reihenfolge.",
+ "label": "Karten"
+ }
+ },
+ "position": {
+ "description": "Wählen Sie aus, wo das Kontrollzentrum-Panel angezeigt wird, wenn es geöffnet wird.",
+ "label": "Position"
+ },
+ "section": {
+ "description": "Konfigurieren Sie die Positionierung und das Verhalten des Kontrollzentrum-Panels.",
+ "label": "Aussehen"
+ },
+ "shortcuts": {
+ "custom-button": {
+ "browse": "Durchsuchen",
+ "command": {
+ "description": "Der Befehl, der ausgeführt werden soll, wenn die Schaltfläche geklickt wird.",
+ "label": "Befehl"
+ },
+ "enable-on-state-logic": {
+ "description": "Aktiviert ein zweites Symbol und 'Aktiv'-Zustand basierend auf einem Prüfbefehl.",
+ "label": "Aktiv-Zustand-Logik aktivieren"
+ },
+ "general-tooltip-text": {
+ "description": "Allgemeine Beschreibung für den Tooltip der Schaltfläche.",
+ "label": "Allgemeiner Tooltip-Text"
+ },
+ "icon": {
+ "description": "Wählen Sie ein Symbol aus der Bibliothek.",
+ "label": "Symbol"
+ },
+ "on-clicked": {
+ "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche links geklickt wird.",
+ "label": "Linksklick-Befehl"
+ },
+ "on-middle-clicked": {
+ "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche mit der mittleren Maustaste geklickt wird.",
+ "label": "Mittelklick-Befehl"
+ },
+ "on-right-clicked": {
+ "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche rechts geklickt wird.",
+ "label": "Rechtsklick-Befehl"
+ },
+ "on-state-command": {
+ "description": "Befehl, der ausgeführt wird, um zu prüfen, ob sich die Schaltfläche im 'Aktiv'-Zustand befinden soll. Gibt 0 für aktiv, ungleich 0 für inaktiv zurück.",
+ "label": "Befehl für Aktiv-Zustand-Prüfung"
+ },
+ "on-state-icon": {
+ "description": "Das Symbol für die Schaltfläche, wenn sie sich im 'Aktiv'-Zustand befindet.",
+ "label": "Symbol im Aktiv-Zustand"
+ },
+ "state-checks": {
+ "add": "Zustandsprüfung hinzufügen",
+ "browse-icon": "Durchsuchen",
+ "command": "Befehl, der für diese Zustandsprüfung ausgeführt werden soll",
+ "label": "Zustandsprüfungen",
+ "remove": "Entfernen"
+ },
+ "tooltip": {
+ "description": "Der Tooltip, der angezeigt wird, wenn Sie mit der Maus über die Schaltfläche fahren.",
+ "label": "Tooltip"
+ }
+ },
+ "dialog": {
+ "apply": "Anwenden",
+ "cancel": "Abbrechen"
+ },
+ "section": {
+ "description": "Konfigurieren und verwalten Sie die Verknüpfungs-Widgets.",
+ "label": "Widgets für Kurzbefehle"
+ },
+ "sectionLeft": "Links",
+ "sectionRight": "Rechts"
+ },
+ "title": "Kontrollzentrum"
+ },
+ "display": {
+ "monitors": {
+ "brightness": "Helligkeit",
+ "brightness-step": {
+ "description": "Schrittgröße für Helligkeitsänderungen anpassen (Mausrad und Tastenkombinationen).",
+ "label": "Helligkeits-Schrittgröße"
+ },
+ "brightness-unavailable": {
+ "ddc-disabled": "Helligkeitssteuerung nicht verfügbar. Aktivieren Sie \"Externe Helligkeitsunterstützung\", um die Helligkeit dieses Displays zu steuern.",
+ "generic": "Die Helligkeitssteuerung ist für dieses Display nicht verfügbar."
+ },
+ "enforce-minimum": {
+ "description": "Löst das Problem, dass die Hintergrundbeleuchtung auf einigen Displays bei 0% Helligkeit vollständig ausgeschaltet wird.",
+ "label": "Mindesthelligkeit erzwingen (1%)"
+ },
+ "external-brightness": {
+ "description": "DDCUtil-Unterstützung zum Steuern der Helligkeit externer Displays über das DDC/CI-Protokoll aktivieren.",
+ "label": "Externe Helligkeitsunterstützung"
+ },
+ "section": {
+ "description": "Helligkeitseinstellungen für jedes Display anpassen.",
+ "label": "Monitor-spezifische Einstellungen"
+ }
+ },
+ "night-light": {
+ "auto-schedule": {
+ "description": "Basierend auf Sonnenuntergang und Sonnenaufgang in {location} - empfohlen.",
+ "label": "Automatische Planung"
+ },
+ "enable": {
+ "description": "Warmen Farbfilter anwenden, um Blaulicht-Emission zu reduzieren.",
+ "label": "Nachtlicht aktivieren"
+ },
+ "force-activation": {
+ "description": "Ignoriert die Planung und wendet den Nachtfilter sofort an.",
+ "label": "Aktivierung erzwingen"
+ },
+ "manual-schedule": {
+ "description": "Benutzerdefinierte Zeiten für Sonnenaufgang und Sonnenuntergang festlegen.",
+ "label": "Manuelle Planung",
+ "select-start": "Startzeit auswählen",
+ "select-stop": "Endzeit auswählen",
+ "sunrise": "Sonnenaufgang",
+ "sunset": "Sonnenuntergang"
+ },
+ "section": {
+ "description": "Blaulicht-Emission reduzieren für besseren Schlaf und weniger Augenbelastung.",
+ "label": "Nachtlicht"
+ },
+ "temperature": {
+ "day": "Tag",
+ "description": "Farbwärme für Nacht- und Tageszeit einstellen.",
+ "label": "Farbtemperatur",
+ "night": "Nacht"
+ }
+ },
+ "title": "Anzeige"
+ },
+ "dock": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Hintergrund-Transparenz des Docks anpassen.",
+ "label": "Hintergrund-Transparenz"
+ },
+ "colorize-icons": {
+ "description": "Theme-Farben auf Dock-App-Symbole anwenden (nur nicht fokussierte Apps).",
+ "label": "Symbole einfärben"
+ },
+ "display": {
+ "always-visible": "Immer sichtbar",
+ "auto-hide": "Automatisch ausblenden",
+ "description": "Wählen Sie, wie sich das Dock verhält.",
+ "exclusive": "Exklusiv",
+ "label": "Anzeige"
+ },
+ "floating-distance": {
+ "description": "Schwebeabstand vom Bildschirmrand anpassen.",
+ "label": "Dock-Schwebeabstand"
+ },
+ "icon-size": {
+ "description": "Gesamtgröße des Docks anpassen.",
+ "label": "Dock-Größe"
+ },
+ "section": {
+ "description": "Verhalten und Erscheinungsbild des Docks anpassen.",
+ "label": "Erscheinungsbild"
+ }
+ },
+ "enabled": {
+ "description": "Dock vollständig anzeigen oder ausblenden.",
+ "label": "Dock aktivieren"
+ },
+ "monitors": {
+ "only-same-output": {
+ "description": "Zeige nur Apps von dem Bildschirm, auf dem sich das Dock befindet.",
+ "label": "Nur Apps vom gleichen Bildschirm"
+ },
+ "section": {
+ "description": "Dock auf bestimmten Monitoren anzeigen. Standardmäßig werden alle angezeigt, wenn keine ausgewählt wurden.",
+ "label": "Monitor-Anzeige"
+ }
+ },
+ "title": "Dock"
+ },
+ "general": {
+ "fonts": {
+ "default": {
+ "description": "Hauptschriftart für die gesamte Benutzeroberfläche.",
+ "label": "Standard-Schriftart",
+ "placeholder": "Standard-Schriftart auswählen...",
+ "scale": {
+ "description": "Ändern Sie die Größe des Standardtextes.",
+ "label": "Standardmäßige Schriftgröße"
+ },
+ "search-placeholder": "Schriftarten suchen..."
+ },
+ "monospace": {
+ "description": "Monospace-Schriftart für Zahlen und Statistik-Anzeigen.",
+ "label": "Monospace-Schriftart",
+ "placeholder": "Monospace-Schriftart auswählen...",
+ "scale": {
+ "description": "Ändern Sie die Größe des Monotextes.",
+ "label": "Schriftgröße mit fester Breite"
+ },
+ "search-placeholder": "Monospace-Schriftarten suchen..."
+ },
+ "reset-scaling": "Skalierung zurücksetzen",
+ "section": {
+ "description": "Wählen Sie die Schriftart der Benutzeroberfläche.",
+ "label": "Schriftarten"
+ }
+ },
+ "language": {
+ "section": {
+ "description": "Ändern Sie die Sprache von Noctalia.",
+ "label": "Sprache"
+ },
+ "select": {
+ "auto-detect": "Automatisch",
+ "description": "Wählen Sie die Sprache der Anwendungsoberfläche.",
+ "label": "Anwendungssprache"
+ }
+ },
+ "launch-setup-wizard": "Starten Sie den Setup-Assistenten",
+ "profile": {
+ "picture": {
+ "description": "Der Avatar, der in der gesamten Benutzeroberfläche angezeigt wird.",
+ "label": "{user}s Avatar"
+ },
+ "section": {
+ "description": "Ändern Sie Ihren Avatar.",
+ "label": "Profil"
+ },
+ "select-avatar": "Avatar auswählen"
+ },
+ "screen-corners": {
+ "radius": {
+ "description": "Rundung der Bildschirmecken anpassen.",
+ "label": "Bildschirmecken-Radius",
+ "reset": "Eckenradius des Bildschirms zurücksetzen"
+ },
+ "section": {
+ "description": "Bildschirmecken-Rundung und visuelle Effekte anpassen.",
+ "label": "Bildschirmecken"
+ },
+ "show-corners": {
+ "description": "Gerundete Ecken am Rand des Bildschirms anzeigen.",
+ "label": "Bildschirmecken anzeigen"
+ },
+ "solid-black": {
+ "description": "Schwarze Ecken anstelle der Statusleisten-Hintergrundfarbe verwenden.",
+ "label": "Schwarze Ecken"
+ }
+ },
+ "title": "Allgemein"
+ },
+ "hooks": {
+ "info": {
+ "command-info": {
+ "description": "• Befehle werden über Shell ausgeführt (sh -c)\n• Befehle laufen im Hintergrund (getrennt)\n• Test-Buttons führen mit aktuellen Werten aus",
+ "label": "Hook-Befehl-Informationen"
+ },
+ "parameters": {
+ "description": "• Hintergrundbild-Hook: $1 = Hintergrundbild-Pfad, $2 = Bildschirmname\n• Theme-Wechsel-Hook: $1 = true/false (Dunkelmodus-Status)",
+ "label": "Verfügbare Parameter"
+ }
+ },
+ "system-hooks": {
+ "enable": {
+ "description": "Alle Hook-Befehle aktivieren oder deaktivieren.",
+ "label": "Hooks aktivieren"
+ },
+ "section": {
+ "description": "Befehle konfigurieren, die bei Systemereignissen ausgeführt werden.",
+ "label": "System-Hooks"
+ }
+ },
+ "theme-changed": {
+ "description": "Befehl, der ausgeführt wird, wenn das Theme zwischen dunklem und hellem Modus wechselt.",
+ "label": "Theme geändert",
+ "placeholder": "z.B. notify-send \"Theme\" \"Gewechselt\""
+ },
+ "title": "Hooks",
+ "wallpaper-changed": {
+ "description": "Befehl, der ausgeführt wird, wenn sich das Hintergrundbild ändert.",
+ "label": "Hintergrundbild geändert",
+ "placeholder": "z.B. notify-send \"Hintergrundbild\" \"Geändert\""
+ }
+ },
+ "launcher": {
+ "settings": {
+ "background-opacity": {
+ "description": "Hintergrund-Transparenz des Starters anpassen.",
+ "label": "Hintergrund-Transparenz"
+ },
+ "clipboard-history": {
+ "description": "Zugriff auf zuvor kopierte Elemente über den Launcher.",
+ "label": "Zwischenablageverlauf aktivieren"
+ },
+ "custom-launch-prefix": {
+ "description": "Befehle mit einem benutzerdefinierten Launcher präfixieren (z.B. 'runapp' für systemd-Integration).",
+ "label": "Benutzerdefiniertes Start-Präfix"
+ },
+ "custom-launch-prefix-enabled": {
+ "description": "Verwenden Sie ein benutzerdefiniertes Präfix zum Starten von Anwendungen anstelle der Standardmethode.",
+ "label": "Benutzerdefiniertes Start-Präfix aktivieren"
+ },
+ "position": {
+ "description": "Wählen Sie, wo das Starter-Panel erscheint.",
+ "label": "Position"
+ },
+ "section": {
+ "description": "Verhalten und Erscheinungsbild des Starters anpassen.",
+ "label": "Erscheinungsbild"
+ },
+ "sort-by-usage": {
+ "description": "Wenn aktiviert, erscheinen häufig gestartete Apps zuerst in der Liste.",
+ "label": "Nach Häufigkeit sortieren"
+ },
+ "terminal-command": {
+ "description": "Befehl zum Starten eines Terminals. Z.B. 'kitty -e' oder 'gnome-terminal --'.",
+ "label": "Terminalbefehl"
+ },
+ "use-app2unit": {
+ "description": "Verwendet eine alternative Startmethode zur besseren Verwaltung von App-Prozessen und Problemvermeidung.",
+ "label": "App2Unit zum Starten von Anwendungen verwenden"
+ }
+ },
+ "title": "Starter"
+ },
+ "location": {
+ "date-time": {
+ "12hour-format": {
+ "description": "Zeigt die Uhrzeit im 12-Stunden-Format auf dem Sperrbildschirm und im Kalender an. Die Uhr in der Statusleiste hat eigene Einstellungen.",
+ "label": "12-Stunden-Zeitformat benutzen"
+ },
+ "first-day-of-week": {
+ "automatic": "Automatisch (Systemgebietsschema verwenden)",
+ "description": "Wähle, welcher Tag die Woche im Kalender beginnen soll.",
+ "label": "Erster Tag der Woche"
+ },
+ "section": {
+ "description": "Anpassen, wie Datum und Zeit erscheinen.",
+ "label": "Datum & Zeit"
+ },
+ "show-events": {
+ "description": "Ereignisse im Kalender-Panel anzeigen.",
+ "label": "Kalenderereignisse anzeigen"
+ },
+ "use-analog": {
+ "description": "Eine Analoguhr im Kalenderfenster und auf dem Sperrbildschirm anzeigen.",
+ "label": "Analoge Uhr verwenden"
+ },
+ "week-numbers": {
+ "description": "Zeigt die Kalenderwochen an (z.B. Woche 38).",
+ "label": "Wochennummern anzeigen"
+ }
+ },
+ "location": {
+ "search": {
+ "description": "z.B. Dortmund, Deutschland",
+ "label": "Nach einem Standort suchen",
+ "placeholder": "Standortnamen eingeben"
+ },
+ "section": {
+ "description": "Genaues Wetter und Nachtlicht-Planung durch Festlegung Ihres Standorts erhalten.",
+ "label": "Ihr Standort"
+ }
+ },
+ "title": "Standort",
+ "weather": {
+ "enabled": {
+ "description": "Wetterinformationen in der gesamten Benutzeroberfläche anzeigen und Wetterdaten abrufen. Wenn deaktiviert, werden alle Wetterelemente ausgeblendet und es werden keine Netzwerkanfragen gestellt.",
+ "label": "Wetter aktivieren"
+ },
+ "fahrenheit": {
+ "description": "Temperatur in Fahrenheit statt Celsius anzeigen.",
+ "label": "Temperatur in Fahrenheit (°F) anzeigen"
+ },
+ "section": {
+ "description": "Bevorzugte Temperatureinheit wählen.",
+ "label": "Wetter"
+ },
+ "show-in-calendar": {
+ "description": "Zeige die tägliche Wettervorhersage direkt in deiner Kalenderansicht an.",
+ "label": "Wetter im Kalender anzeigen"
+ }
+ }
+ },
+ "lock-screen": {
+ "compact-lockscreen": {
+ "description": "Zeige nur die Login-Eingabe und Systemsteuerung, blende Wetter- und Medien-Widgets aus.",
+ "label": "Kompakter Sperrbildschirm"
+ },
+ "lock-on-suspend": {
+ "description": "Den Bildschirm beim Suspendieren des Systems automatisch sperren.",
+ "label": "Sperren beim Ruhezustand"
+ },
+ "title": "Sperrbildschirm"
+ },
+ "network": {
+ "bluetooth": {
+ "label": "Bluetooth aktivieren"
+ },
+ "section": {
+ "description": "WLAN- und Bluetooth-Verbindungen verwalten."
+ },
+ "title": "Netzwerk",
+ "wifi": {
+ "label": "WLAN aktivieren"
+ }
+ },
+ "notifications": {
+ "duration": {
+ "critical-urgency": {
+ "description": "Wie lange kritische Benachrichtigungen sichtbar bleiben.",
+ "label": "Kritische Dringlichkeit"
+ },
+ "low-urgency": {
+ "description": "Wie lange Benachrichtigungen niedriger Priorität sichtbar bleiben.",
+ "label": "Niedrige Dringlichkeit"
+ },
+ "normal-urgency": {
+ "description": "Wie lange Benachrichtigungen normaler Priorität sichtbar bleiben.",
+ "label": "Normale Dringlichkeit"
+ },
+ "respect-expire": {
+ "description": "Die in der Benachrichtigung gesetzte Ablaufzeit verwenden.",
+ "label": "Ablaufzeit respektieren"
+ },
+ "section": {
+ "description": "Konfigurieren, wie lange Benachrichtigungen je nach Dringlichkeitsstufe sichtbar bleiben.",
+ "label": "Benachrichtigungsdauer"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Benachrichtigungen auf bestimmten Monitoren anzeigen. Standardmäßig werden sie auf allen Monitoren angezeigt.",
+ "label": "Monitor-Anzeige"
+ }
+ },
+ "settings": {
+ "always-on-top": {
+ "description": "Benachrichtigungen über Vollbildfenstern und anderen Ebenen anzeigen.",
+ "label": "Immer im Vordergrund"
+ },
+ "background-opacity": {
+ "description": "Transparenz der Benachrichtigungshintergründe anpassen.",
+ "label": "Hintergrund-Transparenz"
+ },
+ "do-not-disturb": {
+ "description": "Alle Benachrichtigungs-Popups deaktivieren.",
+ "label": "Nicht stören"
+ },
+ "enable-osd": {
+ "description": "Lautstärke- und Helligkeitsänderungen in Echtzeit anzeigen.",
+ "label": "Bildschirmanzeige aktivieren"
+ },
+ "enabled": {
+ "description": "Benachrichtigungs-Daemon aktivieren oder deaktivieren, erfordert einen Neustart von Noctalia Shell.",
+ "label": "Benachrichtigungen aktivieren"
+ },
+ "location": {
+ "description": "Wo Benachrichtigungen auf dem Bildschirm erscheinen.",
+ "label": "Position"
+ },
+ "section": {
+ "description": "Erscheinungsbild und Verhalten von Benachrichtigungen konfigurieren.",
+ "label": "Erscheinungsbild"
+ }
+ },
+ "title": "Benachrichtigungen"
+ },
+ "osd": {
+ "always-on-top": {
+ "description": "On-Screen Display über Vollbildfenstern und anderen Ebenen anzeigen.",
+ "label": "Immer im Vordergrund"
+ },
+ "description": "Bildschirm-Overlays wie Lautstärke- und Helligkeitsanzeigen konfigurieren.",
+ "duration": {
+ "auto-hide": {
+ "description": "Zeitspanne bis zum automatischen Ausblenden anpassen.",
+ "label": "Ausblenden nach"
+ },
+ "section": {
+ "description": "Wie lange das On-Screen Display sichtbar bleibt, bevor es automatisch ausgeblendet wird.",
+ "label": "Automatisches Ausblenden"
+ }
+ },
+ "enabled": {
+ "description": "Lautstärke- und Helligkeitsänderungen in Echtzeit anzeigen.",
+ "label": "On-Screen Display aktivieren"
+ },
+ "location": {
+ "description": "Wo On-Screen Displays erscheinen.",
+ "label": "Position"
+ },
+ "monitors": {
+ "section": {
+ "description": "On-Screen Display auf bestimmten Monitoren anzeigen. Standardmäßig auf allen angezeigt.",
+ "label": "Monitor-Anzeige"
+ }
+ },
+ "section": {
+ "general": {
+ "description": "Sichtbarkeit und Verhalten vom On-Screen Display konfigurieren.",
+ "label": "Allgemein"
+ }
+ },
+ "title": "On-Screen Display"
+ },
+ "screen-recorder": {
+ "audio": {
+ "audio-codec": {
+ "description": "Opus wird für beste Leistung und kleinste Audio-Größe empfohlen.",
+ "label": "Audio-Codec"
+ },
+ "audio-source": {
+ "description": "Audio-Quelle für die Aufnahme.",
+ "label": "Audio-Quelle"
+ },
+ "section": {
+ "description": "Audio-Aufnahme-Optionen konfigurieren.",
+ "label": "Audio-Einstellungen"
+ }
+ },
+ "general": {
+ "output-folder": {
+ "description": "Ordner, in dem Bildschirmaufnahmen gespeichert werden.",
+ "label": "Ausgabe-Ordner",
+ "tooltip": "Nach Ausgabe-Ordner suchen"
+ },
+ "section": {
+ "description": "Bildschirmaufnahme-Ausgabe und -Inhalt verwalten.",
+ "label": "Allgemeine Einstellungen"
+ },
+ "select-output-folder": "Ausgabe-Ordner auswählen",
+ "show-cursor": {
+ "description": "Mauszeiger im Video aufnehmen.",
+ "label": "Cursor anzeigen"
+ }
+ },
+ "title": "Bildschirmrekorder",
+ "video": {
+ "color-range": {
+ "description": "Begrenzt wird für bessere Kompatibilität empfohlen.",
+ "label": "Farbbereich"
+ },
+ "frame-rate": {
+ "description": "Ziel-Bildrate für Bildschirmaufnahmen.",
+ "label": "Bildrate"
+ },
+ "section": {
+ "description": "Video-Aufnahme-Optionen konfigurieren.",
+ "label": "Video-Einstellungen"
+ },
+ "video-codec": {
+ "description": "h264 ist der gebräuchlichste Codec.",
+ "label": "Video-Codec"
+ },
+ "video-quality": {
+ "description": "Höhere Qualität führt zu größeren Dateigrößen.",
+ "label": "Video-Qualität"
+ },
+ "video-source": {
+ "description": "Portal wird empfohlen, bei Artefakten versuchen Sie Bildschirm.",
+ "label": "Video-Quelle"
+ }
+ }
+ },
+ "user-interface": {
+ "animation-disable": {
+ "description": "Deaktivieren Sie alle Animationen für eine schnellere und reaktionsfreudigere Erfahrung.",
+ "label": "UI-Animationen deaktivieren"
+ },
+ "animation-speed": {
+ "description": "Globale Animationsgeschwindigkeit anpassen.",
+ "label": "Animationsgeschwindigkeit",
+ "reset": "Animationsgeschwindigkeit zurücksetzen"
+ },
+ "border-radius": {
+ "description": "Steuert die Eckenrundung von Fenstern, Schaltflächen und anderen Elementen.",
+ "label": "Eckenradius",
+ "reset": "Rahmenradius zurücksetzen"
+ },
+ "dim-desktop": {
+ "description": "Den Desktop abdunkeln, wenn Fenster oder Menüs geöffnet sind.",
+ "label": "Desktop abdunkeln"
+ },
+ "panels-attached-to-bar": {
+ "description": "Die Paneele rasten an der Stange und den Bildschirmrändern ein und sorgen so für ein nahtloses Erscheinungsbild mit stilvollen, umgekehrten Ecken.",
+ "label": "Paneele an Kanten ausrichten"
+ },
+ "panels-overlay": {
+ "description": "Stellt sicher, dass Panels und die Leiste sichtbar bleiben, auch über Vollbildanwendungen.",
+ "label": "Panels & Leiste oben behalten"
+ },
+ "scaling": {
+ "description": "Ändert die Größe der allgemeinen Benutzeroberfläche, mit Ausnahme der Leiste.",
+ "label": "Oberflächenskalierung",
+ "reset-scaling": "Schnittstellenskalierung zurücksetzen"
+ },
+ "section": {
+ "description": "Passen Sie das Aussehen, die Haptik und das Verhalten der Benutzeroberfläche an.",
+ "label": "Aussehen"
+ },
+ "settings-panel-attached-to-bar": {
+ "description": "Richten Sie das Einstellungsfenster an der Leiste aus, um ein einheitliches Erscheinungsbild zu erhalten.",
+ "label": "Einstellungsfenster an Leiste ausrichten"
+ },
+ "shadows": {
+ "description": "Aktiviert Schlagschatten unter Balken und Panels.",
+ "direction": {
+ "description": "Wählen, aus welcher Richtung der Schatten fällt.",
+ "label": "Schattenrichtung"
+ },
+ "label": "Schlagschatten"
+ },
+ "title": "Benutzeroberfläche",
+ "tooltips": {
+ "description": "Tooltips in der gesamten Benutzeroberfläche aktivieren oder deaktivieren.",
+ "label": "Tooltips anzeigen"
+ }
+ },
+ "wallpaper": {
+ "automation": {
+ "custom-interval": {
+ "description": "Zeit als HH:MM eingeben (z.B. 01:30).",
+ "label": "Benutzerdefiniertes Intervall"
+ },
+ "interval": {
+ "description": "Wie oft Hintergrundbilder automatisch gewechselt werden sollen.",
+ "label": "Hintergrundbild-Intervall"
+ },
+ "random-wallpaper": {
+ "description": "Zufällige Hintergrundbild-Wechsel in regelmäßigen Abständen planen.",
+ "label": "Zufälliges Hintergrundbild"
+ },
+ "section": {
+ "label": "Automatisierung"
+ }
+ },
+ "look-feel": {
+ "edge-smoothness": {
+ "description": "Wendet einen weichen, gefiederten Effekt auf die Kante von Übergängen an.",
+ "label": "Übergangseffekt weichzeichnen"
+ },
+ "fill-color": {
+ "description": "Füllfarbe wählen, die hinter dem Hintergrundbild erscheinen kann.",
+ "label": "Füllfarbe"
+ },
+ "fill-mode": {
+ "description": "Wählen Sie, wie das Bild skaliert werden soll, um zur Auflösung Ihres Monitors zu passen.",
+ "label": "Füllmodus"
+ },
+ "section": {
+ "label": "Aussehen & Verhalten"
+ },
+ "transition-duration": {
+ "description": "Dauer der Übergangsanimationen in Sekunden.",
+ "label": "Übergangsdauer"
+ },
+ "transition-type": {
+ "description": "Animationstyp beim Wechsel zwischen Hintergrundbildern.",
+ "label": "Übergangstyp"
+ }
+ },
+ "settings": {
+ "enable-management": {
+ "description": "Hintergrundbilder mit Noctalia verwalten. Deaktivieren, wenn Sie eine andere Anwendung bevorzugen.",
+ "label": "Hintergrundbild-Verwaltung aktivieren"
+ },
+ "enable-overview": {
+ "description": "Wendet ein verschwommenes und abgedunkeltes Hintergrundbild auf den Übersichts-Bildschirm an.",
+ "label": "Übersichtshintergrund aktivieren"
+ },
+ "folder": {
+ "description": "Pfad zu Ihrem Haupt-Hintergrundbild-Ordner.",
+ "label": "Hintergrundbild-Ordner",
+ "tooltip": "Nach Hintergrundbild-Ordner suchen"
+ },
+ "monitor-specific": {
+ "description": "Unterschiedlichen Hintergrundbild-Ordner für jeden Monitor festlegen.",
+ "label": "Monitor-spezifische Verzeichnisse",
+ "tooltip": "Nach Hintergrundbild-Ordner suchen"
+ },
+ "recursive-search": {
+ "description": "Auch nach Hintergrundbildern in Unterordnern des Hintergrundbild-Verzeichnisses suchen.",
+ "label": "Unterordner durchsuchen"
+ },
+ "section": {
+ "description": "Steuern Sie, wie Hintergrundbilder verwaltet und angezeigt werden.",
+ "label": "Hintergrundbild-Einstellungen"
+ },
+ "select-folder": "Hintergrundbild-Ordner auswählen",
+ "select-monitor-folder": "Monitor-Hintergrundbild-Ordner auswählen",
+ "selector-position": {
+ "description": "Wählen Sie aus, wo das Hintergrundbildauswahlfeld angezeigt wird.",
+ "label": "Position"
+ }
+ },
+ "title": "Hintergrundbild"
+ }
+ },
+ "setup": {
+ "appearance": {
+ "header": "Erscheinungsbild",
+ "subheader": "Dunkelmodus und Farbquellen wählen (Matugen oder vordefiniert)."
+ },
+ "customize": {
+ "header": "Erlebnis anpassen",
+ "subheader": "Leistenposition, Dichte, Skalierung und mehr einstellen."
+ },
+ "wallpaper": {
+ "choose-dir": "Wählen Sie ein Verzeichnis mit Ihren Hintergrundbildern",
+ "dir": {
+ "browse": "Ordner auswählen",
+ "description": "Wählen Sie den Ordner mit Ihren Hintergrundbildern",
+ "label": "Hintergrundbild-Verzeichnis",
+ "select-title": "Hintergrundbild-Ordner wählen"
+ },
+ "header": "Wählen Sie Ihr Hintergrundbild",
+ "no-dir": "Kein Hintergrundbild-Verzeichnis ausgewählt",
+ "no-valid": "Keine gültigen Bilddateien gefunden in: {dir}",
+ "none-in-dir": "Keine Hintergrundbilder im Verzeichnis gefunden",
+ "preview-error": "Bild konnte nicht geladen werden",
+ "select-prompt": "Wählen Sie unten ein Hintergrundbild",
+ "subheader": "Bestimmen Sie die Stimmung mit einem schönen Hintergrund."
+ },
+ "welcome": {
+ "note": "Nur ein paar Grundeinstellungen – alle Optionen befinden sich in den Einstellungen"
+ }
+ },
+ "system": {
+ "cpu-temperature": "{temp}°C",
+ "disk-usage": "{percent}%",
+ "location-display": "{name} ({coordinates})",
+ "monitor-description": "{model} ({width}x{height} @ {scale}x)",
+ "no-media-player-detected": "Kein Mediaplayer erkannt",
+ "scaling-percentage": "{percentage}%",
+ "signal-strength": "{signal}%",
+ "unknown": "Unbekannt",
+ "unknown-app": "Unbekannte App",
+ "unknown-layout": "Unbekannt",
+ "unknown-version": "Unbekannt",
+ "uptime": "Laufzeit: {uptime}",
+ "user-requested": "Vom Benutzer angefordert",
+ "welcome-back": "Willkommen zurück,",
+ "widget-settings-title": "{widget} Einstellungen"
+ },
"toast": {
- "night-light": {
- "enabled": "Aktiviert",
- "disabled": "Deaktiviert",
- "not-installed": "wlsunset nicht installiert",
- "forced": "Aktivierung erzwungen",
- "normal": "Normaler Modus"
- },
- "keep-awake": {
- "enabled": "Aktiviert",
- "disabled": "Deaktiviert"
- },
- "wallpaper-colors": {
- "enabled": "Hintergrundbild-Farben aktiviert",
- "disabled": "Hintergrundbild-Farben deaktiviert",
- "not-installed": "Matugen nicht installiert - erforderlich für Hintergrundbild-Farbextraktion"
- },
- "matugen": {
- "failed": "Matugen-Verarbeitung fehlgeschlagen",
- "failed-general": "Matugen ist beim Verarbeiten der Vorlagen auf einen Fehler gestoßen"
- },
- "recording": {
- "stopping": "Aufnahme wird gestoppt…",
- "started": "Aufnahme gestartet",
- "saved": "Aufnahme gespeichert",
- "failed-start": "Aufnahme konnte nicht gestartet werden",
- "failed-gpu": "gpu-screen-recorder wurde unerwartet beendet",
- "failed-general": "Rekorder wurde mit einem Fehler beendet",
- "no-portals": "Desktop-Portale laufen nicht",
- "no-portals-desc": "Starten Sie xdg-desktop-portal und ein Compositor-Portal (wlr/hyprland/gnome/kde).",
- "not-installed": "gpu-screen-recorder nicht installiert",
- "not-installed-desc": "Bitte installieren Sie gpu-screen-recorder, um Bildschirmaufnahme-Funktionen zu nutzen."
- },
- "clipboard": {
- "unavailable": "Zwischenablageverlauf nicht verfügbar",
- "unavailable-desc": "Die 'cliphist' Anwendung ist nicht installiert. Bitte installieren Sie sie, um Zwischenablageverlauf-Features zu nutzen."
- },
- "ipc": {
- "powerpanel-deprecated": "PowerPanel wurde in SessionMenu umbenannt, dieser IPC-Aufruf wird bald veraltet sein. Bitte verwenden Sie stattdessen \"ipc call sessionMenu toggle\".",
- "sidepanel-deprecated": "SidePanel wurde in ControlCenter umbenannt, dieser IPC-Aufruf wird bald veraltet sein. Bitte verwenden Sie stattdessen \"ipc call controlCenter toggle\"."
- },
- "wifi": {
- "enabled": "Aktiviert",
- "disabled": "Deaktiviert",
- "connected": "Verbunden mit '{ssid}'",
- "disconnected": "Getrennt von '{ssid}'"
- },
- "bluetooth": {
- "enabled": "Aktiviert",
- "disabled": "Deaktiviert"
- },
"airplane-mode": {
- "title": "Flugmodus",
+ "disabled": "Deaktiviert",
"enabled": "Aktiviert",
- "disabled": "Deaktiviert"
- },
- "internet": {
- "limited": "Verbunden ohne Internet"
- },
- "kofi": {
- "opened": "Ko-fi-Seite in Ihrem Browser geöffnet"
- },
- "do-not-disturb": {
- "enabled": "'Nicht stören' aktiviert",
- "disabled": "'Nicht stören' deaktiviert",
- "enabled-desc": "Sie finden diese Benachrichtigungen in Ihrem Verlauf.",
- "disabled-desc": "Alle Benachrichtigungen werden angezeigt."
- },
- "power-profile": {
- "changed": "Energieprofil geändert",
- "profile-name": "\"{profile}\""
+ "title": "Flugmodus"
},
"battery": {
"low": "Niedriger Batteriestand",
"low-desc": "Batterie ist bei {percent}%. Bitte schließen Sie das Ladegerät an."
},
"battery-manager": {
- "title": "Batterieschwelle",
- "set-success-desc": "Batterieschwelle auf {percent}% gesetzt",
"initial-setup": "Ersteinrichtung erforderlich",
- "set-failed": "Fehler beim Setzen der Batterieschwelle",
- "install-success": "Erfolgreich installiert",
- "install-missing": "Erforderliche Dateien fehlen",
- "install-unsupported": "System wird nicht unterstützt",
"install-failed": "Installation fehlgeschlagen",
+ "install-missing": "Erforderliche Dateien fehlen",
+ "install-success": "Erfolgreich installiert",
+ "install-unsupported": "System wird nicht unterstützt",
+ "set-failed": "Fehler beim Setzen der Batterieschwelle",
+ "set-success-desc": "Batterieschwelle auf {percent}% gesetzt",
+ "title": "Batterieschwelle",
+ "uninstall-failed": "Deinstallation fehlgeschlagen",
"uninstall-setup": "Deinstallation, Authentifizierung erforderlich",
- "uninstall-success": "Erfolgreich deinstalliert",
- "uninstall-failed": "Deinstallation fehlgeschlagen"
+ "uninstall-success": "Erfolgreich deinstalliert"
+ },
+ "bluetooth": {
+ "disabled": "Deaktiviert",
+ "enabled": "Aktiviert"
+ },
+ "clipboard": {
+ "unavailable": "Zwischenablageverlauf nicht verfügbar",
+ "unavailable-desc": "Die 'cliphist' Anwendung ist nicht installiert. Bitte installieren Sie sie, um Zwischenablageverlauf-Features zu nutzen."
+ },
+ "do-not-disturb": {
+ "disabled": "'Nicht stören' deaktiviert",
+ "disabled-desc": "Alle Benachrichtigungen werden angezeigt.",
+ "enabled": "'Nicht stören' aktiviert",
+ "enabled-desc": "Sie finden diese Benachrichtigungen in Ihrem Verlauf."
+ },
+ "internet": {
+ "limited": "Verbunden ohne Internet"
+ },
+ "ipc": {
+ "powerpanel-deprecated": "PowerPanel wurde in SessionMenu umbenannt, dieser IPC-Aufruf wird bald veraltet sein. Bitte verwenden Sie stattdessen \"ipc call sessionMenu toggle\".",
+ "sidepanel-deprecated": "SidePanel wurde in ControlCenter umbenannt, dieser IPC-Aufruf wird bald veraltet sein. Bitte verwenden Sie stattdessen \"ipc call controlCenter toggle\"."
+ },
+ "keep-awake": {
+ "disabled": "Deaktiviert",
+ "enabled": "Aktiviert"
+ },
+ "kofi": {
+ "opened": "Ko-fi-Seite in Ihrem Browser geöffnet"
},
"missing-control-center": {
- "label": "Letztes Kontrollzentrum-Widget entfernt",
- "description": "Das Kontrollzentrum-Widget wurde aus der Leiste entfernt. Um es erneut über die Leiste zu öffnen, fügen Sie das Widget wieder hinzu. Sie können es auch durch Rechtsklick auf die Leiste öffnen."
+ "description": "Das Kontrollzentrum-Widget wurde aus der Leiste entfernt. Um es erneut über die Leiste zu öffnen, fügen Sie das Widget wieder hinzu. Sie können es auch durch Rechtsklick auf die Leiste öffnen.",
+ "label": "Letztes Kontrollzentrum-Widget entfernt"
+ },
+ "night-light": {
+ "disabled": "Deaktiviert",
+ "enabled": "Aktiviert",
+ "forced": "Aktivierung erzwungen",
+ "normal": "Normaler Modus",
+ "not-installed": "wlsunset nicht installiert"
+ },
+ "noctalia-performance": {
+ "disabled": "Leistungsmodus deaktiviert",
+ "enabled": "Leistungsmodus aktiviert",
+ "label": "Noctalia-Aufführung"
+ },
+ "power-profile": {
+ "changed": "Energieprofil geändert",
+ "profile-name": "\"{profile}\""
+ },
+ "recording": {
+ "failed-general": "Rekorder wurde mit einem Fehler beendet",
+ "failed-gpu": "gpu-screen-recorder wurde unerwartet beendet",
+ "failed-start": "Aufnahme konnte nicht gestartet werden",
+ "no-portals": "Desktop-Portale laufen nicht",
+ "no-portals-desc": "Starten Sie xdg-desktop-portal und ein Compositor-Portal (wlr/hyprland/gnome/kde).",
+ "not-installed": "gpu-screen-recorder nicht installiert",
+ "not-installed-desc": "Bitte installieren Sie gpu-screen-recorder, um Bildschirmaufnahme-Funktionen zu nutzen.",
+ "saved": "Aufnahme gespeichert",
+ "started": "Aufnahme gestartet",
+ "stopping": "Aufnahme wird gestoppt…"
+ },
+ "theming-processor-failed": {
+ "desc-generic": "Beim Verarbeiten von Vorlagen ist ein Fehler aufgetreten.",
+ "title-matugen": "Matugen-Templating-Verarbeitung fehlgeschlagen",
+ "title-predefined": "Die Verarbeitung des vordefinierten Farbschemas ist fehlgeschlagen."
+ },
+ "wallpaper-colors": {
+ "disabled": "Hintergrundbild-Farben deaktiviert",
+ "enabled": "Hintergrundbild-Farben aktiviert",
+ "label": "Hintergrundfarbengenerierung",
+ "not-installed": "Matugen nicht installiert - erforderlich für Hintergrundbild-Farbextraktion"
+ },
+ "wifi": {
+ "connected": "Verbunden mit '{ssid}'",
+ "disabled": "Deaktiviert",
+ "disconnected": "Getrennt von '{ssid}'",
+ "enabled": "Aktiviert"
}
},
- "setup": {
- "customize": {
- "header": "Erlebnis anpassen",
- "subheader": "Leistenposition, Dichte, Skalierung und mehr einstellen."
+ "tooltips": {
+ "add-widget": "Widget hinzufügen",
+ "bluetooth-devices": "Bluetooth-Geräte",
+ "brightness-at": "Helligkeit: {brightness}%\nRechtsklick für Einstellungen.\nScrollen zum Ändern der Helligkeit.",
+ "cancel-timer": "Timer abbrechen",
+ "clear-history": "Verlauf löschen",
+ "click-to-start-recording": "Klicken zum Starten einer Bildschirmaufnahme",
+ "click-to-stop-recording": "Klicken zum Stoppen einer Bildschirmaufnahme",
+ "close": "Schließen",
+ "connect-disconnect-devices": "Linksklick zum Verbinden. Rechtsklick zum Vergessen.",
+ "delete-notification": "Benachrichtigung löschen",
+ "disable-keep-awake": "Wach halten deaktivieren",
+ "do-not-disturb-disabled": "'Nicht stören' deaktiviert",
+ "do-not-disturb-enabled": "'Nicht stören' aktiviert",
+ "enable-keep-awake": "Wach halten aktivieren",
+ "forget-network": "Netzwerk vergessen",
+ "home": "Home",
+ "input-muted": "Audio-Eingabe stummschalten",
+ "keep-awake": "Wach halten",
+ "keyboard-layout": "{layout} Tastaturlayout",
+ "manage-wifi": "WLAN verwalten",
+ "microphone-volume-at": "Mikrofonlautstärke bei {volume}%.\nLinksklick für Einstellungen. Rechtsklick zum Stummschalten.\nScrollen zum Ändern der Lautstärke.",
+ "move-to-center-section": "Zur mittleren Sektion verschieben",
+ "move-to-left-section": "Zur linken Sektion verschieben",
+ "move-to-right-section": "Zur rechten Sektion verschieben",
+ "next-media": "Nächstes Medium",
+ "next-month": "Nächster Monat",
+ "night-light-disabled": "Nachtlicht ist deaktiviert.\nLinksklick zum Wechseln des Modus.\nRechtsklick für Einstellungen.",
+ "night-light-enabled": "Nachtlicht ist aktiviert.\nLinksklick zum Wechseln des Modus.\nRechtsklick für Einstellungen.",
+ "night-light-forced": "Nachtlicht ist erzwungen.\nLinksklick zum Wechseln des Modus.\nRechtsklick für Einstellungen.",
+ "night-light-not-installed": "Nachtlicht ist nicht verfügbar.\nwlsunset ist nicht installiert.",
+ "noctalia-performance-disabled": "Noctalia-Leistungsmodus ist deaktiviert.\nLinksklick zum Aktivieren.",
+ "noctalia-performance-enabled": "Noctalia-Leistungsmodus ist aktiviert.\nLinksklick zum Deaktivieren.",
+ "open-control-center": "Kontrollzentrum öffnen",
+ "open-notification-history-disable-dnd": "Benachrichtigungsverlauf öffnen\nRechtsklick um 'Nicht stören' zu deaktivieren.",
+ "open-notification-history-enable-dnd": "Benachrichtigungsverlauf öffnen\nRechtsklick um 'Nicht stören' zu aktivieren.",
+ "open-settings": "Einstellungen öffnen",
+ "open-tray-dropdown": "Tray-Dropdown öffnen",
+ "open-wallpaper-selector": "Hintergrundbild-Auswahl öffnen",
+ "output-muted": "Audio-Ausgabe stummschalten",
+ "pause": "Pausieren",
+ "play": "Wiedergeben",
+ "power-profile": "'{profile}' Energieprofil",
+ "previous-media": "Vorheriges Medium",
+ "previous-month": "Vorheriger Monat",
+ "refresh": "Aktualisieren",
+ "refresh-devices": "Geräte aktualisieren",
+ "refresh-wallpaper-list": "Hintergrundbild-Liste aktualisieren",
+ "remove-widget": "Widget entfernen",
+ "screen-recorder-not-installed": "Bildschirmrekorder ist nicht installiert",
+ "session-menu": "Sitzungsmenü",
+ "set-power-profile": "'{profile}' Energieprofil setzen",
+ "start-screen-recording": "Bildschirmaufnahme starten",
+ "stop-screen-recording": "Bildschirmaufnahme stoppen",
+ "switch-to-dark-mode": "Zum dunklen Modus wechseln",
+ "switch-to-light-mode": "Zum hellen Modus wechseln",
+ "up": "Nach oben",
+ "volume-at": "Lautstärke bei {volume}%.\nLinksklick für Einstellungen. Rechtsklick zum Stummschalten.\nScrollen zum Ändern der Lautstärke.",
+ "wallpaper-selector": "Linksklick: Hintergrundbild-Auswahl öffnen.\nRechtsklick: Zufälliges Hintergrundbild setzen.",
+ "widget-settings": "Widget-Einstellungen"
+ },
+ "wallpaper": {
+ "configure-directory": "Konfigurieren Sie Ihr Hintergrundbild-Verzeichnis mit Bildern.",
+ "fill-modes": {
+ "center": "Zentrieren",
+ "crop": "Zuschneiden (Füllen)",
+ "fit": "Einpassen (Beibehalten)",
+ "stretch": "Strecken"
},
- "appearance": {
- "header": "Erscheinungsbild",
- "subheader": "Dunkelmodus und Farbquellen wählen (Matugen oder vordefiniert)."
+ "no-match": "Keine Übereinstimmung gefunden.",
+ "no-wallpaper": "Kein Hintergrundbild gefunden.",
+ "panel": {
+ "apply-all-monitors": {
+ "description": "Ausgewähltes Hintergrundbild auf alle Monitore gleichzeitig anwenden.",
+ "label": "Auf alle Monitore anwenden"
+ },
+ "search": "Suchen:",
+ "title": "Hintergrundbild-Auswahl"
},
- "wallpaper": {
- "header": "Wählen Sie Ihr Hintergrundbild",
- "subheader": "Bestimmen Sie die Stimmung mit einem schönen Hintergrund.",
- "select-prompt": "Wählen Sie unten ein Hintergrundbild",
- "preview-error": "Bild konnte nicht geladen werden",
- "none-in-dir": "Keine Hintergrundbilder im Verzeichnis gefunden",
- "no-dir": "Kein Hintergrundbild-Verzeichnis ausgewählt",
- "no-valid": "Keine gültigen Bilddateien gefunden in: {dir}",
- "choose-dir": "Wählen Sie ein Verzeichnis mit Ihren Hintergrundbildern",
- "dir": {
- "label": "Hintergrundbild-Verzeichnis",
- "description": "Wählen Sie den Ordner mit Ihren Hintergrundbildern",
- "browse": "Ordner auswählen",
- "select-title": "Hintergrundbild-Ordner wählen"
+ "transitions": {
+ "disc": "Scheibe",
+ "fade": "Überblenden",
+ "none": "Keine",
+ "random": "Zufällig",
+ "stripes": "Streifen",
+ "wipe": "Wischen"
+ },
+ "try-different-search": "Versuchen Sie eine andere Suchanfrage."
+ },
+ "weather": {
+ "clear-sky": "Klarer Himmel",
+ "drizzle": "Nieselregen",
+ "fog": "Nebelig",
+ "mainly-clear": "Überwiegend klar",
+ "overcast": "Bedeckt",
+ "partly-cloudy": "Teilweise bewölkt",
+ "rain-showers": "Regenschauer",
+ "snow": "Schnee",
+ "thunderstorm": "Gewitter",
+ "unknown": "Unbekannt"
+ },
+ "widgets": {
+ "color-picker": {
+ "apply": "Anwenden",
+ "brightness": "Helligkeit",
+ "cancel": "Abbrechen",
+ "hex": {
+ "description": "Geben Sie einen hexadezimalen Farbcode ein.",
+ "label": "Hex-Farbe"
+ },
+ "palette": {
+ "description": "Wählen Sie aus einer großen Auswahl vordefinierter Farben.",
+ "label": "Palette"
+ },
+ "rgb": {
+ "description": "Passen Sie Rot-, Grün-, Blau- und Helligkeitswerte an.",
+ "label": "RGB-Werte"
+ },
+ "theme-colors": {
+ "description": "Schnellzugriff auf die Farbpalette Ihres Themes.",
+ "label": "Theme-Farben"
+ },
+ "title": "Farbauswahl"
+ },
+ "datetime-tokens": {
+ "ampm": {
+ "lowercase": "am/pm in Kleinbuchstaben",
+ "uppercase": "AM/PM in Großbuchstaben"
+ },
+ "common": {
+ "12hour-time-minutes": "12-Stunden-Zeit mit Minuten",
+ "24hour-time-minutes": "24-Stunden-Zeit mit Minuten",
+ "24hour-time-seconds": "24-Stunden-Zeit mit Sekunden",
+ "european-date": "Europäisches Datumsformat",
+ "iso-date": "ISO-Datumsformat",
+ "us-date": "US-Datumsformat",
+ "weekday-date": "Wochentag mit Datum",
+ "weekday-month-day": "Wochentag, Monat und Tag"
+ },
+ "day": {
+ "abbreviated": "Abgekürzter Tagesname",
+ "full": "Vollständiger Tagesname",
+ "leading-zero": "Tag mit führender Null (01-31)",
+ "no-leading-zero": "Tag ohne führende Null (1-31)"
+ },
+ "hour": {
+ "leading-zero": "Stunde mit führender Null (00-23) - 24-Stunden-Format",
+ "no-leading-zero": "Stunde ohne führende Null (0-23) - 24-Stunden-Format"
+ },
+ "minute": {
+ "leading-zero": "Minute mit führender Null (00-59)",
+ "no-leading-zero": "Minute ohne führende Null (0-59)"
+ },
+ "month": {
+ "abbreviated": "Abgekürzter Monatsname",
+ "full": "Vollständiger Monatsname",
+ "number-leading-zero": "Monat als Zahl mit führender Null (01-12)",
+ "number-no-zero": "Monat als Zahl ohne führende Null (1-12)"
+ },
+ "second": {
+ "leading-zero": "Sekunde mit führender Null (00-59)",
+ "no-leading-zero": "Sekunde ohne führende Null (0-59)"
+ },
+ "timezone": {
+ "abbreviation": "Zeitzonenabkürzung"
+ },
+ "year": {
+ "four-digit": "Jahr als vierstellige Zahl",
+ "two-digit": "Jahr als zweistellige Zahl (00-99)"
}
},
- "welcome": {
- "note": "Nur ein paar Grundeinstellungen – alle Optionen befinden sich in den Einstellungen"
+ "file-picker": {
+ "cancel": "Abbrechen",
+ "search-placeholder": "Dateien und Ordner suchen...",
+ "select-current": "Aktuelles Objekt auswählen",
+ "select-file": "Datei auswählen",
+ "select-folder": "Ordner auswählen",
+ "title": "Dateiauswahl"
+ },
+ "icon-picker": {
+ "apply": "Anwenden",
+ "cancel": "Abbrechen",
+ "search": {
+ "label": "Suchen"
+ },
+ "title": "Symbol-Auswahl"
+ },
+ "text-input": {
+ "clear": "Löschen"
+ },
+ "tooltip": {
+ "placeholder": "Platzhalter"
+ }
+ },
+ "wifi": {
+ "panel": {
+ "connect": "Verbinden",
+ "connected": "Verbunden",
+ "disabled": "WLAN ist deaktiviert",
+ "disconnect": "Trennen",
+ "disconnecting": "Verbindung wird getrennt...",
+ "enable-message": "Aktivieren Sie WLAN, um verfügbare Netzwerke zu sehen.",
+ "enter-password": "Passwort eingeben...",
+ "forget": "Vergessen",
+ "forget-network": "Dieses Netzwerk vergessen?",
+ "forgetting": "Wird vergessen...",
+ "no-networks": "Keine Netzwerke gefunden",
+ "password": "Passwort",
+ "saved": "Gespeichert",
+ "scan-again": "Erneut scannen",
+ "searching": "Suche nach nahegelegenen Netzwerken...",
+ "title": "WLAN"
}
}
}
diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json
index 6738e256..8f84f7ff 100644
--- a/Assets/Translations/en.json
+++ b/Assets/Translations/en.json
@@ -1,1244 +1,285 @@
{
- "settings": {
- "general": {
- "title": "General",
- "launch-setup-wizard": "Launch the setup wizard",
- "profile": {
- "section": {
- "label": "Profile",
- "description": "Edit your user details and avatar."
- },
- "picture": {
- "label": "{user}'s Profile picture",
- "description": "Your profile picture that appears throughout the interface."
- },
- "select-avatar": "Select avatar image"
- },
- "screen-corners": {
- "section": {
- "label": "Screen corners",
- "description": "Customize screen corner rounding and visual effects."
- },
- "show-corners": {
- "label": "Show screen corners",
- "description": "Display rounded corners on the edge of the screen."
- },
- "solid-black": {
- "label": "Solid black corners",
- "description": "Use solid black instead of the bar background color."
- },
- "radius": {
- "label": "Screen corners radius",
- "description": "Adjust the rounded corners of the screen.",
- "reset": "Reset screen corners radius"
- }
- },
- "fonts": {
- "reset-scaling": "Reset scaling",
- "section": {
- "label": "Fonts",
- "description": "Choose the fonts used throughout the interface."
- },
- "default": {
- "label": "Default font",
- "description": "Main font used throughout the interface.",
- "placeholder": "Select default font...",
- "search-placeholder": "Search font...",
- "scale": {
- "label": "Default font size",
- "description": "Increase or decrease the size of the standard text."
- }
- },
- "monospace": {
- "label": "Monospaced font",
- "description": "Monospaced font used for numbers and stats display.",
- "placeholder": "Select monospace font...",
- "search-placeholder": "Search monospace font...",
- "scale": {
- "label": "Monospaced font size",
- "description": "Increase or decrease the size of the monospaced text."
- }
- }
- },
- "language": {
- "section": {
- "label": "Language",
- "description": "Choose your preferred language for the application."
- },
- "select": {
- "label": "Application Language",
- "description": "Select the language used in the application's interface.",
- "auto-detect": "Automatic"
- }
- }
- },
- "audio": {
- "title": "Audio",
- "volumes": {
- "section": {
- "label": "Volumes",
- "description": "Adjust volume controls and audio levels."
- },
- "output-volume": {
- "label": "Output volume",
- "description": "System-wide volume level."
- },
- "mute-output": {
- "label": "Mute audio output",
- "description": "Mute the system's main audio output."
- },
- "input-volume": {
- "label": "Input volume",
- "description": "Microphone input volume level."
- },
- "mute-input": {
- "label": "Mute audio input",
- "description": "Mute the default audio input (microphone)."
- },
- "step-size": {
- "label": "Volume step size",
- "description": "Adjust the step size for volume changes (scroll wheel, keyboard shortcuts)."
- },
- "volume-overdrive": {
- "label": "Allow volume overdrive",
- "description": "Allow raising volume above 100%. May not be supported by all hardware."
- }
- },
- "devices": {
- "section": {
- "label": "Audio devices",
- "description": "Configure available audio input and output devices."
- },
- "output-device": {
- "label": "Output device",
- "description": "Select the desired audio output device."
- },
- "input-device": {
- "label": "Input device",
- "description": "Select the desired audio input device."
- }
- },
- "media": {
- "section": {
- "label": "Media players",
- "description": "Set your preferred and ignored media applications."
- },
- "primary-player": {
- "label": "Primary player",
- "description": "Enter a keyword to identify your main player.",
- "placeholder": "e.g. spotify, vlc, mpv"
- },
- "excluded-player": {
- "label": "Excluded player",
- "description": "Add keywords for players you want the system to ignore. Each keyword should be on a new line.",
- "placeholder": "type substring and press +"
- },
- "visualizer-type": {
- "label": "Visualization type",
- "description": "Choose a visualization type for media playback"
- },
- "frame-rate": {
- "label": "Frame rate",
- "description": "Higher rates are smoother but use more resources."
- },
- "scrolling-title": {
- "label": "Scrolling title",
- "description": "Enable continuous scrolling for long media titles"
- },
- "scrolling-speed": {
- "label": "Scrolling speed",
- "description": "Time in seconds for the title to scroll from start to end"
- }
- }
- },
- "display": {
- "title": "Display",
- "monitors": {
- "section": {
- "label": "Per-monitor settings",
- "description": "Adjust settings brightness for each display."
- },
- "brightness": "Brightness",
- "brightness-step": {
- "label": "Brightness step size",
- "description": "Adjust the step size for brightness changes (scroll wheel and keyboard shortcuts)."
- },
- "enforce-minimum": {
- "label": "Enforce minimum brightness (1%)",
- "description": "Solves the problem of backlight completely turning off on some displays at 0% brightness."
- }
- },
- "night-light": {
- "section": {
- "label": "Night light",
- "description": "Reduce blue light emission to help you sleep better and reduce eye strain."
- },
- "enable": {
- "label": "Enable night light",
- "description": "Apply a warm color filter to reduce blue light emission."
- },
- "temperature": {
- "label": "Color temperature",
- "description": "Set the color warmth for nighttime and daytime.",
- "night": "Night",
- "day": "Day"
- },
- "auto-schedule": {
- "label": "Automatic scheduling",
- "description": "Based on the sunset and sunrise time in {location} - recommended."
- },
- "manual-schedule": {
- "label": "Manual scheduling",
- "description": "Set custom times for sunrise and sunset.",
- "sunrise": "Sunrise time",
- "sunset": "Sunset time",
- "select-start": "Select start time",
- "select-stop": "Select stop time"
- },
- "force-activation": {
- "label": "Force activation",
- "description": "Ignores the schedule and applies the night filter immediately."
- }
- }
- },
- "bar": {
- "title": "Bar",
- "appearance": {
- "section": {
- "label": "Appearance",
- "description": "Customize the bar's appearance and position."
- },
- "position": {
- "label": "Bar position",
- "description": "Choose where to place the bar on the screen."
- },
- "density": {
- "label": "Bar density",
- "description": "Adjust the bar's padding for a compact or spacious look."
- },
- "background-opacity": {
- "label": "Background opacity",
- "description": "Adjust the background opacity of the bar."
- },
- "show-capsule": {
- "label": "Show capsule",
- "description": "Show widget backgrounds."
- },
- "floating": {
- "label": "Floating bar",
- "description": "Displays the bar as a floating 'pill'. Note: This will move the screen corners to the edges."
- },
- "margins": {
- "label": "Margins",
- "description": "Adjust the margins around the floating bar.",
- "vertical": "Vertical",
- "horizontal": "Horizontal"
- }
- },
- "widgets": {
- "section": {
- "label": "Widgets positioning",
- "description": "Drag and drop widgets to reorder them. Badges indicate usage: [L]eft, [C]enter, [R]ight."
- }
- },
- "monitors": {
- "section": {
- "label": "Monitors display",
- "description": "Show bar on specific monitors. Defaults to all if none are chosen."
- }
- },
- "tray": {
- "blacklist": {
- "label": "Blacklist",
- "description": "Add tray exclusion rules, supports wildcards (*).",
- "placeholder": "e.g., nm-applet, Fcitx*"
- }
- }
- },
- "dock": {
- "title": "Dock",
- "enabled": {
- "label": "Enable dock",
- "description": "Show or hide the dock entirely"
- },
- "appearance": {
- "section": {
- "label": "Appearance",
- "description": "Customize the dock's behavior and appearance."
- },
- "display": {
- "label": "Display",
- "description": "Choose how the dock behaves.",
- "always-visible": "Always visible",
- "auto-hide": "Auto hide",
- "exclusive": "Exclusive"
- },
- "background-opacity": {
- "label": "Background opacity",
- "description": "Adjust the dock's background opacity."
- },
- "floating-distance": {
- "label": "Dock floating distance",
- "description": "Set the distance between the dock and the edge of the screen."
- },
- "icon-size": {
- "label": "Dock size",
- "description": "Adjust the overall size of the dock."
- },
- "colorize-icons": {
- "label": "Colorize Icons",
- "description": "Apply theme colors to dock app icons (non-focused apps only)."
- }
- },
- "monitors": {
- "section": {
- "label": "Monitor display",
- "description": "Show dock on specific monitors. Defaults to all if none are chosen."
- },
- "only-same-output": {
- "label": "Only apps from same output",
- "description": "Show only apps from the output where the dock is located."
- }
- }
- },
- "launcher": {
- "title": "Launcher",
- "settings": {
- "section": {
- "label": "Appearance",
- "description": "Customize the launcher's behavior and appearance."
- },
- "position": {
- "label": "Position",
- "description": "Choose where the launcher panel appears."
- },
- "background-opacity": {
- "label": "Background opacity",
- "description": "Adjust the background opacity of the launcher."
- },
- "clipboard-history": {
- "label": "Enable clipboard history",
- "description": "Access previously copied items from the launcher."
- },
- "sort-by-usage": {
- "label": "Sort by most used",
- "description": "When enabled, frequently launched apps appear first in the list."
- },
- "use-app2unit": {
- "label": "Use App2Unit to launch applications",
- "description": "Uses an alternative launch method to better manage app processes and prevent issues."
- },
- "terminal-command": {
- "label": "Terminal command",
- "description": "Command to launch a terminal. E.g., 'kitty -e' or 'gnome-terminal --'."
- },
- "custom-launch-prefix": {
- "label": "Custom launch prefix",
- "description": "Prefix commands with a custom launcher (e.g., 'runapp' for systemd integration)."
- },
- "custom-launch-prefix-enabled": {
- "label": "Enable custom launch prefix",
- "description": "Use a custom prefix for launching applications instead of the default method."
- }
- }
- },
- "notifications": {
- "title": "Notifications",
- "settings": {
- "section": {
- "label": "Appearance",
- "description": "Configure notifications appearance and behavior."
- },
- "do-not-disturb": {
- "label": "Do not disturb",
- "description": "Disable all notification popups when enabled."
- },
- "enable-osd": {
- "label": "Enable on screen display",
- "description": "Show volume and brightness changes in real-time."
- },
- "location": {
- "label": "Location",
- "description": "Where notifications appear on screen."
- },
- "always-on-top": {
- "label": "Always on top",
- "description": "Display notifications above fullscreen windows and other layers."
- },
- "background-opacity": {
- "label": "Background opacity",
- "description": "Adjust the opacity of notification backgrounds."
- }
- },
- "duration": {
- "section": {
- "label": "Notification duration",
- "description": "Configure how long notifications stay visible based on their urgency level."
- },
- "respect-expire": {
- "label": "Respect expire timeout",
- "description": "Use the expire timeout set in the notification."
- },
- "low-urgency": {
- "label": "Low urgency",
- "description": "How long low priority notifications stay visible."
- },
- "normal-urgency": {
- "label": "Normal urgency",
- "description": "How long normal priority notifications stay visible."
- },
- "critical-urgency": {
- "label": "Critical urgency",
- "description": "How long critical priority notifications stay visible."
- }
- },
- "monitors": {
- "section": {
- "label": "Monitors display",
- "description": "Show notification on specific monitors. Defaults to all if none are chosen."
- }
- }
- },
- "osd": {
- "title": "On-Screen Display",
- "description": "Configure on-screen indicators such as volume and brightness overlays.",
- "section": {
- "general": {
- "label": "General",
- "description": "Configure visibility and behavior of OSD."
- }
- },
- "enabled": {
- "label": "Enable on screen display",
- "description": "Show volume and brightness changes in real-time."
- },
- "always-on-top": {
- "label": "Always on top",
- "description": "Display OSD above fullscreen windows and other layers."
- },
- "location": {
- "label": "Location",
- "description": "Where on-screen displays appear."
- },
- "duration": {
- "section": {
- "label": "Auto-hide timeout",
- "description": "How long OSD remains visible before hiding automatically."
- },
- "auto-hide": {
- "label": "Auto-hide after",
- "description": "Adjust the time before OSD disappears."
- }
- },
- "monitors": {
- "section": {
- "label": "Monitors display",
- "description": "Show OSD on specific monitors. Defaults to all if none are chosen."
- }
- }
- },
- "wallpaper": {
- "title": "Wallpaper",
- "settings": {
- "section": {
- "label": "Wallpaper settings",
- "description": "Control how wallpapers are managed and displayed."
- },
- "enable-management": {
- "label": "Enable wallpaper management",
- "description": "Manage wallpapers with Noctalia. Uncheck if you prefer using another application."
- },
- "folder": {
- "label": "Wallpaper folder",
- "description": "Path to your main wallpaper folder.",
- "tooltip": "Browse for wallpaper folder"
- },
- "recursive-search": {
- "label": "Search subfolders",
- "description": "Also search for wallpapers in subfolders of the wallpaper directory."
- },
- "monitor-specific": {
- "label": "Monitor-specific directories",
- "description": "Set a different wallpaper folder for each monitor.",
- "tooltip": "Browse for wallpaper folder"
- },
- "select-folder": "Select wallpaper folder",
- "select-monitor-folder": "Select monitor wallpaper folder"
- },
- "look-feel": {
- "section": {
- "label": "Look & feel"
- },
- "fill-mode": {
- "label": "Fill mode",
- "description": "Select how the image should scale to match your monitor's resolution."
- },
- "fill-color": {
- "label": "Fill color",
- "description": "Choose a fill color that may appear behind the wallpaper."
- },
- "transition-type": {
- "label": "Transition type",
- "description": "Animation type when switching between wallpapers."
- },
- "transition-duration": {
- "label": "Transition duration",
- "description": "Duration of transition animations in seconds."
- },
- "edge-smoothness": {
- "label": "Soften transition edge",
- "description": "Applies a soft, feathered effect to the edge of transitions."
- }
- },
- "automation": {
- "section": {
- "label": "Automation"
- },
- "random-wallpaper": {
- "label": "Random wallpaper",
- "description": "Schedule random wallpaper changes at regular intervals."
- },
- "interval": {
- "label": "Wallpaper interval",
- "description": "How often to change wallpapers automatically."
- },
- "custom-interval": {
- "label": "Custom interval",
- "description": "Enter time as HH:MM (e.g., 01:30)."
- }
- }
- },
- "color-scheme": {
- "title": "Color scheme",
- "color-source": {
- "section": {
- "label": "Color source",
- "description": "Main settings for Noctalia's colors."
- },
- "use-wallpaper-colors": {
- "label": "Use wallpaper colors",
- "description": "Generate color schemes from your wallpaper using Matugen. Automatically extracts colors to create a cohesive theme."
- },
- "matugen-scheme-type": {
- "label": "Matugen scheme type",
- "description": {
- "scheme-content": "Derives colors that closely match the underlying image",
- "scheme-expressive": "Vibrant palette with playful saturation",
- "scheme-fidelity": "High-fidelity palette that preserves source hues",
- "scheme-fruit-salad": "Colorful mix of bright contrasting accents",
- "scheme-monochrome": "Minimal palette built around a single hue",
- "scheme-neutral": "Muted palette with subdued and calming tones",
- "scheme-rainbow": "Diverse palette spanning the full spectrum",
- "scheme-tonal-spot": "Balanced palette with focused accents"
- }
- }
- },
- "dark-mode": {
- "switch": {
- "label": "Dark mode",
- "description": "Switches to a darker theme for easier viewing at night."
- },
- "mode": {
- "label": "Dark mode schedule",
- "description": "Enables automatic switching between light and dark mode.",
- "off": "Off",
- "manual": "Manual",
- "location": "Location"
- }
- },
- "predefined": {
- "section": {
- "label": "Predefined color schemes",
- "description": "Choose from a collection of predefined color schemes."
- },
- "generate-templates": {
- "label": "Generate templates for predefined schemes",
- "description": "Generate Matugen templates (GTK, terminal themes, etc.) when using predefined color schemes."
- }
- },
- "templates": {
- "section": {
- "label": "Templates",
- "description": "Apply colors to external applications."
- },
- "ui": {
- "label": "UI",
- "description": "Desktop environment and UI toolkit theming.",
- "gtk": {
- "description": "Write {filepath}"
- },
- "qt": {
- "description": "Write {filepath}"
- },
- "kcolorscheme": {
- "description": "Write {filepath}"
- }
- },
- "terminal": {
- "label": "Terminal",
- "description": "Terminal emulator theming.",
- "kitty": {
- "description": "Write {filepath} and reload",
- "description-missing": "Requires {app} to be installed"
- },
- "ghostty": {
- "description": "Write {filepath} and reload",
- "description-missing": "Requires {app} to be installed"
- },
- "foot": {
- "description": "Write {filepath} and reload",
- "description-missing": "Requires {app} to be installed"
- }
- },
- "programs": {
- "label": "Programs",
- "description": "Application-specific theming.",
- "fuzzel": {
- "description": "Write {filepath} and reload",
- "description-missing": "Requires {app} to be installed"
- },
- "vicinae": {
- "description": "Write {filepath} and reload",
- "description-missing": "Requires {app} to be installed"
- },
- "walker": {
- "description": "Write {filepath} and set theme to noctalia",
- "description-missing": "Requires {app} to be installed"
- },
- "discord": {
- "description": "Write {filepath} for {client}. Hyprluna theme needs to be activated manually.",
- "description-missing": "No Discord client detected. Install vencord, vesktop, webcord, armcord, equibop, lightcord, or dorion."
- },
- "pywalfox": {
- "description": "Write {filepath} and run pywalfox update",
- "description-missing": "Requires {app} to be installed"
- },
- "code": {
- "description": "Write {filepath}. Hyprluna theme needs to be installed and activated manually.",
- "description-missing": "Requires {app} to be installed"
- }
- },
- "misc": {
- "label": "Misc",
- "description": "Additional configuration options.",
- "user-templates": {
- "label": "User templates",
- "description": "Enable user-defined Matugen config. A template file will be created at ~/.config/noctalia/user-templates.toml on first enable"
- }
- }
- }
- },
- "location": {
- "title": "Location",
- "location": {
- "section": {
- "label": "Your location",
- "description": "Get accurate weather and night light scheduling by setting your location."
- },
- "search": {
- "label": "Search for a location",
- "description": "e.g., Toronto, ON",
- "placeholder": "Enter the location name"
- }
- },
- "weather": {
- "section": {
- "label": "Weather",
- "description": "Choose your preferred temperature unit."
- },
- "enabled": {
- "label": "Enable weather",
- "description": "Show weather information throughout the interface and fetch weather data. When disabled, all weather elements will be hidden and no network requests will be made."
- },
- "fahrenheit": {
- "label": "Display temperature in Fahrenheit (°F)",
- "description": "Display temperature in Fahrenheit instead of Celsius."
- },
- "show-in-calendar": {
- "label": "Display weather in calendar",
- "description": "Show the daily weather forecast directly in your calendar view."
- }
- },
- "date-time": {
- "section": {
- "label": "Date & time",
- "description": "Customize how date and time appear."
- },
- "12hour-format": {
- "label": "Use 12-hour time format",
- "description": "Displays time in 12-hour format on the lock screen and calendar. The bar clock has its own settings."
- },
- "week-numbers": {
- "label": "Show week numbers",
- "description": "Displays the week of the year (e.g., Week 38) in the calendar."
- },
- "first-day-of-week": {
- "label": "First day of week",
- "description": "Choose which day starts the week in the calendar.",
- "automatic": "Automatic (use system locale)"
- },
- "show-events": {
- "label": "Show calendar events",
- "description": "Display events in the calendar panel."
- },
- "use-analog": {
- "label": "Use analog style clock",
- "description": "Show an analog style clock on the calendar screen."
- }
- }
- },
- "network": {
- "title": "Network",
- "section": {
- "description": "Manage Wi-Fi and Bluetooth connections."
- },
- "wifi": {
- "label": "Enable Wi-Fi"
- },
- "bluetooth": {
- "label": "Enable Bluetooth"
- }
- },
- "screen-recorder": {
- "title": "Screen recorder",
- "general": {
- "section": {
- "label": "General settings",
- "description": "Manage screen recording output and content."
- },
- "output-folder": {
- "label": "Output folder",
- "description": "Folder where screen recordings will be saved.",
- "tooltip": "Browse for output folder"
- },
- "show-cursor": {
- "label": "Show cursor",
- "description": "Record mouse cursor in the video."
- },
- "select-output-folder": "Select output folder"
- },
- "video": {
- "section": {
- "label": "Video settings",
- "description": "Configure video recording options."
- },
- "video-source": {
- "label": "Video source",
- "description": "Portal is recommended, if you get artifacts try Screen."
- },
- "frame-rate": {
- "label": "Frame rate",
- "description": "Target frame rate for screen recordings."
- },
- "video-quality": {
- "label": "Video quality",
- "description": "Higher quality results in larger file sizes."
- },
- "video-codec": {
- "label": "Video codec",
- "description": "h264 is the most common codec."
- },
- "color-range": {
- "label": "Color range",
- "description": "Limited is recommended for better compatibility."
- }
- },
- "audio": {
- "section": {
- "label": "Audio settings",
- "description": "Configure audio recording options."
- },
- "audio-source": {
- "label": "Audio source",
- "description": "Audio source to capture during recording."
- },
- "audio-codec": {
- "label": "Audio codec",
- "description": "Opus is recommended for best performance and smallest audio size."
- }
- }
- },
- "about": {
- "title": "About",
- "noctalia": {
- "section": {
- "label": "Noctalia shell",
- "description": "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell."
- },
- "latest-version": "Latest version:",
- "installed-version": "Installed version:",
- "download-latest": "Download latest release"
- },
- "contributors": {
- "section": {
- "label": "Contributors",
- "description": "Shout-out to our {count} awesome contributor!",
- "description_plural": "Shout-out to our {count} awesome contributors!"
- }
- },
- "support": "Support us"
- },
- "hooks": {
- "title": "Hooks",
- "system-hooks": {
- "section": {
- "label": "System hooks",
- "description": "Configure commands to be executed when system events occur."
- },
- "enable": {
- "label": "Enable hooks",
- "description": "Enable or disable all hook commands."
- }
- },
- "wallpaper-changed": {
- "label": "Wallpaper changed",
- "description": "Command to be executed when wallpaper changes.",
- "placeholder": "e.g., notify-send \"Wallpaper\" \"Changed\""
- },
- "theme-changed": {
- "label": "Theme changed",
- "description": "Command to be executed when theme toggles between dark and light mode.",
- "placeholder": "e.g., notify-send \"Theme\" \"Toggled\""
- },
- "info": {
- "command-info": {
- "label": "Hook Command Information",
- "description": "• Commands are executed via shell (sh -c)\n• Commands run in background (detached)\n• Test buttons execute with current values"
- },
- "parameters": {
- "label": "Available Parameters",
- "description": "• Wallpaper Hook: $1 = wallpaper path, $2 = screen name\n• Theme Toggle Hook: $1 = true/false (dark mode state)"
- }
- }
- },
- "control-center": {
- "title": "Control Center",
- "section": {
- "label": "Appearance",
- "description": "Configure the Control Center panel positioning and behavior."
- },
- "position": {
- "label": "Position",
- "description": "Choose where the Control Center panel appears when opened."
- },
- "cards": {
- "section": {
- "label": "Cards",
- "description": "Customize which controls appear in the Control Center and in what order."
- }
- },
- "shortcuts": {
- "section": {
- "label": "Shortcuts widgets",
- "description": "Configure and manage the shortcuts widgets."
- },
- "sectionLeft": "Left",
- "sectionRight": "Right"
- }
- },
- "user-interface": {
- "title": "User interface",
- "section": {
- "label": "Appearance",
- "description": "Customize the look, feel, and behavior of the interface."
- },
- "tooltips": {
- "label": "Show tooltips",
- "description": "Enable or disable tooltips throughout the interface."
- },
- "scaling": {
- "label": "Interface scaling",
- "description": "Changes the size of the general user interface, excluding the bar.",
- "reset-scaling": "Reset interface scaling"
- },
- "border-radius": {
- "label": "Border radius",
- "description": "Controls the corner roundness of windows, buttons, and other elements.",
- "reset": "Reset border radius"
- },
- "animation-speed": {
- "label": "Animation speed",
- "description": "Adjust global animation speed.",
- "reset": "Reset animation speed"
- },
- "animation-disable": {
- "label": "Disable UI Animations",
- "description": "Disable all animations for a faster, more responsive experience."
- },
- "panels-attached-to-bar": {
- "label": "Attach panels to bar",
- "description": "When enabled, panels will be attached to the bar with beautiful inverted corner design"
- },
- "panels-overlay": {
- "label": "Open panels in overlay layer",
- "description": "Panels will appear above fullscreen windows"
- }
- },
- "lock-screen": {
- "title": "Lock screen",
- "compact-lockscreen": {
- "label": "Compact lock screen",
- "description": "Show only the login input and system controls, hiding weather and media widgets."
- },
- "lock-on-suspend": {
- "label": "Lock on suspend",
- "description": "Automatically lock the screen when suspending the system."
- }
- }
- },
- "widgets": {
- "tooltip": {
- "placeholder": "Placeholder"
- },
- "file-picker": {
- "title": "File Picker",
- "select-folder": "Select Folder",
- "select-file": "Select File",
- "search-placeholder": "Search files and folders...",
- "select-current": "Select Current",
- "cancel": "Cancel"
- },
- "datetime-tokens": {
- "common": {
- "12hour-time-minutes": "12-hour time with minutes",
- "24hour-time-minutes": "24-hour time with minutes",
- "24hour-time-seconds": "24-hour time with seconds",
- "weekday-month-day": "Weekday, month and day",
- "iso-date": "ISO date format",
- "us-date": "US date format",
- "european-date": "European date format",
- "weekday-date": "Weekday with date"
- },
- "hour": {
- "no-leading-zero": "Hour without leading zero (0-23) - 24-hour format",
- "leading-zero": "Hour with leading zero (00-23) - 24-hour format"
- },
- "minute": {
- "no-leading-zero": "Minute without leading zero (0-59)",
- "leading-zero": "Minute with leading zero (00-59)"
- },
- "second": {
- "no-leading-zero": "Second without leading zero (0-59)",
- "leading-zero": "Second with leading zero (00-59)"
- },
- "ampm": {
- "uppercase": "AM/PM in uppercase",
- "lowercase": "am/pm in lowercase"
- },
- "timezone": {
- "abbreviation": "Timezone abbreviation"
- },
- "year": {
- "two-digit": "Year as two-digit number (00-99)",
- "four-digit": "Year as four-digit number"
- },
- "month": {
- "number-no-zero": "Month as number without leading zero (1-12)",
- "number-leading-zero": "Month as number with leading zero (01-12)",
- "abbreviated": "Abbreviated month name",
- "full": "Full month name"
- },
- "day": {
- "no-leading-zero": "Day without leading zero (1-31)",
- "leading-zero": "Day with leading zero (01-31)",
- "abbreviated": "Abbreviated day name",
- "full": "Full day name"
- }
- },
- "icon-picker": {
- "title": "Icon picker",
- "search": {
- "label": "Search"
- },
- "cancel": "Cancel",
- "apply": "Apply"
- },
- "color-picker": {
- "title": "Color picker",
- "hex": {
- "label": "Hex color",
- "description": "Enter a hexadecimal color code."
- },
- "rgb": {
- "label": "RGB values",
- "description": "Adjust red, green, blue, and brightness values."
- },
- "brightness": "Brightness",
- "theme-colors": {
- "label": "Theme colors",
- "description": "Quick access to your theme's color palette."
- },
- "palette": {
- "label": "Palette",
- "description": "Choose from a wide range of predefined colors."
- },
- "cancel": "Cancel",
- "apply": "Apply"
- },
- "text-input": {
- "clear": "Clear"
- }
+ "authentication": {
+ "error": "Authentication error",
+ "failed": "Authentication failed"
},
"bar": {
"widget-settings": {
+ "active-window": {
+ "colorize-icons": {
+ "description": "Apply theme colors to active window icon.",
+ "label": "Colorize Icons"
+ },
+ "hide-mode": {
+ "description": "Controls how the widget behaves when no window is active.",
+ "label": "Hiding mode"
+ },
+ "max-width": {
+ "description": "Sets the maximum horizontal size of the widget. The widget will shrink to fit shorter content.",
+ "label": "Maximum Width"
+ },
+ "scrolling-mode": {
+ "description": "Control when text scrolling is enabled for long window titles.",
+ "label": "Scrolling mode"
+ },
+ "show-app-icon": {
+ "description": "Display the application icon next to the window title.",
+ "label": "Show app icon"
+ },
+ "use-fixed-width": {
+ "description": "When enabled, the widget will always use the maximum width instead of dynamically adjusting to content.",
+ "label": "Use Fixed Width"
+ }
+ },
+ "audio-visualizer": {
+ "color-name": {
+ "description": "Select the color for the visualizer.",
+ "label": "Fill Color"
+ },
+ "hide-when-idle": {
+ "description": "When enabled, the visualizer is hidden unless a player is actively playing.",
+ "label": "Hide when no media is playing"
+ },
+ "width": {
+ "description": "Custom component width.",
+ "label": "Width"
+ }
+ },
+ "battery": {
+ "display-mode": {
+ "description": "Choose how you'd like this value to appear.",
+ "label": "Display mode"
+ },
+ "low-battery-threshold": {
+ "description": "Show a warning when battery falls below this percentage.",
+ "label": "Low battery warning threshold"
+ }
+ },
+ "brightness": {
+ "display-mode": {
+ "description": "Choose how you'd like this value to appear.",
+ "label": "Display mode"
+ }
+ },
+ "clock": {
+ "clock-display": {
+ "description": "Customize your clock's display by adding tokens from the list below. To use the 12-hour format, you must include the 'AP' token.",
+ "label": "Clock display"
+ },
+ "custom-font": {
+ "description": "Select a custom font for the clock display.",
+ "label": "Custom font",
+ "placeholder": "Select custom font...",
+ "search-placeholder": "Search fonts..."
+ },
+ "horizontal-bar": {
+ "description": "Tip: Use \\n to create a line break.",
+ "label": "Horizontal bar"
+ },
+ "preview": "Preview",
+ "use-custom-font": {
+ "description": "Override the default font selection with a custom font for the clock.",
+ "label": "Use custom font"
+ },
+ "use-monospaced-font": {
+ "description": "When enabled, the clock will use the monospaced font.",
+ "label": "Use monospaced font"
+ },
+ "use-primary-color": {
+ "description": "When enabled, this applies the primary color for emphasis.",
+ "label": "Use primary color"
+ },
+ "vertical-bar": {
+ "description": "Use a space to separate each part onto a new line.",
+ "label": "Vertical bar"
+ }
+ },
+ "control-center": {
+ "browse-file": "Browse File",
+ "browse-library": "Browse Library",
+ "icon": {
+ "description": "Select an icon from the library or a custom file.",
+ "label": "Icon"
+ },
+ "select-custom-icon": "Select a custom icon",
+ "use-distro-logo": {
+ "description": "Use your distribution's logo instead of a custom icon.",
+ "label": "Use distro logo instead of icon"
+ },
+ "colorize-distro-logo": {
+ "description": "Apply theme colors to your distribution logo.",
+ "label": "Colorize distro logo"
+ }
+ },
+ "custom-button": {
+ "browse": "Browse",
+ "collapse-condition": {
+ "description": "If the output text matches this value, the button will collapse.",
+ "label": "Collapse condition"
+ },
+ "display-command-output": {
+ "description": "Enter a command to run at a regular interval. The first line of its output will be displayed as text.",
+ "label": "Display Command Output",
+ "stream-description": "Enter a command to run continuously."
+ },
+ "dynamic-text": "Dynamic text",
+ "hide-vertical": {
+ "description": "If enabled, the text from the command output will not be shown when the bar is in a vertical layout (left or right).",
+ "label": "Hide text in vertical bar"
+ },
+ "icon": {
+ "description": "Select an icon from the library.",
+ "label": "Icon"
+ },
+ "left-click": {
+ "description": "Command to execute when the button is left-clicked.",
+ "label": "Left click"
+ },
+ "middle-click": {
+ "description": "Command to execute when the button is middle-clicked.",
+ "label": "Middle click"
+ },
+ "parse-json": {
+ "description": "Parse the command output as a JSON object to dynamically set text and icon.",
+ "label": "Parse output as JSON"
+ },
+ "refresh-interval": {
+ "description": "Interval in milliseconds.",
+ "label": "Refresh interval"
+ },
+ "right-click": {
+ "description": "Command to execute when the button is right-clicked.",
+ "label": "Right click"
+ },
+ "text-stream": {
+ "description": "Streamed lines from the command will be displayed as text on the button.",
+ "label": "Stream"
+ }
+ },
"dialog": {
- "cancel": "Cancel",
- "apply": "Apply"
+ "apply": "Apply",
+ "cancel": "Cancel"
+ },
+ "keyboard-layout": {
+ "display-mode": {
+ "description": "Choose how you'd like this value to appear.",
+ "label": "Display mode"
+ }
+ },
+ "lock-keys": {
+ "browse": "Browse",
+ "show-caps-lock": {
+ "description": "Display caps lock status.",
+ "label": "Caps Lock"
+ },
+ "show-num-lock": {
+ "description": "Display num lock status.",
+ "label": "Num Lock"
+ },
+ "show-scroll-lock": {
+ "description": "Display scroll lock status.",
+ "label": "Scroll Lock"
+ }
+ },
+ "media-mini": {
+ "hide-mode": {
+ "description": "Controls how the widget behaves when no media is playing.",
+ "label": "Hiding mode"
+ },
+ "max-width": {
+ "description": "Sets the maximum horizontal size of the widget. The widget will shrink to fit shorter content.",
+ "label": "Maximum Width"
+ },
+ "no-active-player": "No active player",
+ "scrolling-mode": {
+ "description": "Control when text scrolling is enabled for long track titles.",
+ "label": "Scrolling mode"
+ },
+ "show-album-art": {
+ "description": "Display the album artwork for the currently playing track.",
+ "label": "Show album art"
+ },
+ "show-visualizer": {
+ "description": "Display an audio visualizer when music is playing.",
+ "label": "Show visualizer"
+ },
+ "use-fixed-width": {
+ "description": "When enabled, the widget will always use the maximum width instead of dynamically adjusting to content.",
+ "label": "Use Fixed Width"
+ },
+ "visualizer-type": {
+ "description": "Choose the style of audio visualizer to display.",
+ "label": "Visualizer type"
+ }
+ },
+ "microphone": {
+ "display-mode": {
+ "description": "Choose how you'd like this value to appear.",
+ "label": "Display mode"
+ }
+ },
+ "notification-history": {
+ "hide-badge-when-zero": {
+ "description": "Hide the notification badge when there are no unread notifications.",
+ "label": "Hide badge when zero"
+ },
+ "show-unread-badge": {
+ "description": "Display a badge showing the number of unread notifications.",
+ "label": "Show unread badge"
+ }
},
"section-editor": {
"placeholder": "Select a widget to add...",
"search-placeholder": "Search widget..."
},
- "active-window": {
- "hide-mode": {
- "label": "Hiding mode",
- "description": "Controls how the widget behaves when no window is active."
- },
- "show-app-icon": {
- "label": "Show app icon",
- "description": "Display the application icon next to the window title."
- },
- "scrolling-mode": {
- "label": "Scrolling mode",
- "description": "Control when text scrolling is enabled for long window titles."
- },
- "max-width": {
- "label": "Maximum Width",
- "description": "Sets the maximum horizontal size of the widget. The widget will shrink to fit shorter content."
- },
- "use-fixed-width": {
- "label": "Use Fixed Width",
- "description": "When enabled, the widget will always use the maximum width instead of dynamically adjusting to content."
- },
- "colorize-icons": {
- "label": "Colorize Icons",
- "description": "Apply theme colors to active window icon."
+ "spacer": {
+ "width": {
+ "description": "Spacing width in pixels",
+ "label": "Width"
}
},
"system-monitor": {
- "cpu-usage": {
- "label": "CPU usage",
- "description": "Display current CPU usage percentage."
- },
"cpu-temperature": {
- "label": "CPU temperature",
- "description": "Show CPU temperature readings if available."
+ "description": "Show CPU temperature readings if available.",
+ "label": "CPU temperature"
},
- "memory-usage": {
- "label": "Memory usage",
- "description": "Display current RAM usage information."
+ "cpu-usage": {
+ "description": "Display current CPU usage percentage.",
+ "label": "CPU usage"
},
"memory-percentage": {
- "label": "Memory as percentage",
- "description": "Show memory usage as a percentage instead of absolute values."
+ "description": "Show memory usage as a percentage instead of absolute values.",
+ "label": "Memory as percentage"
+ },
+ "memory-usage": {
+ "description": "Display current RAM usage information.",
+ "label": "Memory usage"
},
"network-traffic": {
- "label": "Network traffic",
- "description": "Display network upload and download speeds."
+ "description": "Display network upload and download speeds.",
+ "label": "Network traffic"
},
"storage-usage": {
- "label": "Storage usage",
- "description": "Show disk space usage information."
+ "description": "Show disk space usage information.",
+ "label": "Storage usage"
}
},
- "notification-history": {
- "show-unread-badge": {
- "label": "Show unread badge",
- "description": "Display a badge showing the number of unread notifications."
- },
- "hide-badge-when-zero": {
- "label": "Hide badge when zero",
- "description": "Hide the notification badge when there are no unread notifications."
- }
- },
- "battery": {
- "display-mode": {
- "label": "Display mode",
- "description": "Choose how you'd like this value to appear."
- },
- "low-battery-threshold": {
- "label": "Low battery warning threshold",
- "description": "Show a warning when battery falls below this percentage."
- }
- },
- "control-center": {
- "use-distro-logo": {
- "label": "Use distro logo instead of icon",
- "description": "Use your distribution's logo instead of a custom icon."
- },
- "icon": {
- "label": "Icon",
- "description": "Select an icon from the library or a custom file."
- },
- "browse-library": "Browse Library",
- "browse-file": "Browse File",
- "select-custom-icon": "Select a custom icon"
- },
- "keyboard-layout": {
- "display-mode": {
- "label": "Display mode",
- "description": "Choose how you'd like this value to appear."
- }
- },
- "volume": {
- "display-mode": {
- "label": "Display mode",
- "description": "Choose how you'd like this value to appear."
- }
- },
- "workspace": {
- "label-mode": {
- "label": "Label Mode",
- "description": "Choose how workspace labels are displayed."
- },
- "hide-unoccupied": {
- "label": "Hide unoccupied",
- "description": "Don't display workspaces without windows."
- },
- "character-count": {
- "label": "Character count",
- "description": "Number of characters to display from workspace names (1-10)."
- }
- },
- "microphone": {
- "display-mode": {
- "label": "Display mode",
- "description": "Choose how you'd like this value to appear."
- }
- },
- "brightness": {
- "display-mode": {
- "label": "Display mode",
- "description": "Choose how you'd like this value to appear."
- }
- },
- "spacer": {
- "width": {
- "label": "Width",
- "description": "Spacing width in pixels"
- }
- },
- "custom-button": {
- "icon": {
- "label": "Icon",
- "description": "Select an icon from the library."
- },
- "browse": "Browse",
- "left-click": {
- "label": "Left click",
- "description": "Command to execute when the button is left-clicked."
- },
- "right-click": {
- "label": "Right click",
- "description": "Command to execute when the button is right-clicked."
- },
- "middle-click": {
- "label": "Middle click",
- "description": "Command to execute when the button is middle-clicked."
- },
- "dynamic-text": "Dynamic text",
- "text-stream": {
- "label": "Stream",
- "description": "Streamed lines from the command will be displayed as text on the button."
- },
- "display-command-output": {
- "label": "Display Command Output",
- "description": "Enter a command to run at a regular interval. The first line of its output will be displayed as text.",
- "stream-description": "Enter a command to run continuously."
- },
- "refresh-interval": {
- "label": "Refresh interval",
- "description": "Interval in milliseconds."
- },
- "collapse-condition": {
- "label": "Collapse condition",
- "description": "If the output text matches this value, the button will collapse."
- },
- "parse-json": {
- "label": "Parse output as JSON",
- "description": "Parse the command output as a JSON object to dynamically set text and icon."
- }
- },
- "media-mini": {
- "hide-mode": {
- "label": "Hiding mode",
- "description": "Controls how the widget behaves when no media is playing."
- },
- "show-album-art": {
- "label": "Show album art",
- "description": "Display the album artwork for the currently playing track."
- },
- "show-visualizer": {
- "label": "Show visualizer",
- "description": "Display an audio visualizer when music is playing."
- },
- "visualizer-type": {
- "label": "Visualizer type",
- "description": "Choose the style of audio visualizer to display."
- },
- "max-width": {
- "label": "Maximum Width",
- "description": "Sets the maximum horizontal size of the widget. The widget will shrink to fit shorter content."
- },
- "use-fixed-width": {
- "label": "Use Fixed Width",
- "description": "When enabled, the widget will always use the maximum width instead of dynamically adjusting to content."
- },
- "scrolling-mode": {
- "label": "Scrolling mode",
- "description": "Control when text scrolling is enabled for long track titles."
- },
- "no-active-player": "No active player"
- },
- "clock": {
- "use-primary-color": {
- "label": "Use primary color",
- "description": "When enabled, this applies the primary color for emphasis."
- },
- "use-monospaced-font": {
- "label": "Use monospaced font",
- "description": "When enabled, the clock will use the monospaced font."
- },
- "use-custom-font": {
- "label": "Use custom font",
- "description": "Override the default font selection with a custom font for the clock."
- },
- "custom-font": {
- "label": "Custom font",
- "description": "Select a custom font for the clock display.",
- "placeholder": "Select custom font...",
- "search-placeholder": "Search fonts..."
- },
- "clock-display": {
- "label": "Clock display",
- "description": "Customize your clock's display by adding tokens from the list below. To use the 12-hour format, you must include the 'AP' token."
- },
- "horizontal-bar": {
- "label": "Horizontal bar",
- "description": "Tip: Use \\n to create a line break."
- },
- "vertical-bar": {
- "label": "Vertical bar",
- "description": "Use a space to separate each part onto a new line."
- },
- "preview": "Preview"
- },
"taskbar": {
+ "colorize-icons": {
+ "description": "Apply theme colors to taskbar icons.",
+ "label": "Colorize Icons"
+ },
"hide-mode": {
- "label": "Hiding mode",
- "description": "Controls how the widget behaves when there are no matching windows."
+ "description": "Controls how the widget behaves when there are no matching windows.",
+ "label": "Hiding mode"
},
"only-active-workspaces": {
- "label": "Only from active workspaces",
- "description": "Show only apps from active workspaces."
+ "description": "Show only apps from active workspaces.",
+ "label": "Only from active workspaces"
},
"only-same-output": {
- "label": "Only from same output",
- "description": "Show only apps from the output where the bar is located."
- },
- "colorize-icons": {
- "label": "Colorize Icons",
- "description": "Apply theme colors to taskbar icons."
+ "description": "Show only apps from the output where the bar is located.",
+ "label": "Only from same output"
}
},
"taskbar-grouped": {
@@ -1253,181 +294,71 @@
},
"tray": {
"colorize-icons": {
- "label": "Colorize Icons",
- "description": "Apply theme colors to tray icons."
+ "description": "Apply theme colors to tray icons.",
+ "label": "Colorize Icons"
+ },
+ "drawer-enabled": {
+ "description": "When enabled, unpinned tray items are shown in a drawer panel. When disabled, all tray items are shown inline.",
+ "label": "Enable Drawer"
}
},
- "audio-visualizer": {
- "width": {
- "label": "Width",
- "description": "Custom component width."
- },
- "color-name": {
- "label": "Fill Color",
- "description": "Select the color for the visualizer."
- },
- "hide-when-idle": {
- "label": "Hide when no media is playing",
- "description": "When enabled, the visualizer is hidden unless a player is actively playing."
+ "volume": {
+ "display-mode": {
+ "description": "Choose how you'd like this value to appear.",
+ "label": "Display mode"
}
},
- "lock-keys": {
- "indicator-style": {
- "label": "Indicator Style",
- "description": "Icon style for the lock key indicators",
- "large": "Large Letters",
- "small": "Small Letters",
- "square": "Square Letters",
- "square-round": "Rounded Squre Letters",
- "circle": "Circle Letters",
- "circle-dash": "Dashed Circle Letters",
- "circle-dot": "Dotted Circle Letters",
- "hex": "Hexagon Letters"
+ "workspace": {
+ "character-count": {
+ "description": "Number of characters to display from workspace names (1-10).",
+ "label": "Character count"
},
- "show-caps-lock": {
- "label": "Caps Lock",
- "description": "Display caps lock status."
+ "hide-unoccupied": {
+ "description": "Don't display workspaces without windows.",
+ "label": "Hide unoccupied"
},
- "show-num-lock": {
- "label": "Num Lock",
- "description": "Display num lock status."
- },
- "show-scroll-lock": {
- "label": "Scroll Lock",
- "description": "Display scroll lock status."
+ "label-mode": {
+ "description": "Choose how workspace labels are displayed.",
+ "label": "Label Mode"
}
}
}
},
- "notifications": {
+ "battery": {
+ "charging": "Charging.",
+ "charging-rate": "Charging rate: {rate} W.",
+ "discharging": "Discharging.",
+ "discharging-rate": "Discharging rate: {rate} W.",
+ "health": "Health: {percent}%",
+ "idle": "Idle.",
+ "no-battery-detected": "No battery detected.",
"panel": {
- "title": "Notifications",
- "no-notifications": "No notifications",
- "description": "Your notifications will show up here as they arrive.",
- "click-to-expand": "Click to expand"
- }
- },
- "wallpaper": {
- "panel": {
- "title": "Wallpaper selector",
- "apply-all-monitors": {
- "label": "Apply to all monitors",
- "description": "Apply selected wallpaper to all monitors at once."
- },
- "search": "Search:"
+ "balanced": "Balanced ({percent}%)",
+ "disabled": "Battery manager disabled",
+ "full": "Full capacity ({percent}%)",
+ "lifespan": "Extended lifespan ({percent}%)",
+ "title": "Charge threshold"
},
- "transitions": {
- "none": "None",
- "random": "Random",
- "fade": "Fade",
- "disc": "Disc",
- "stripes": "Stripes",
- "wipe": "Wipe"
- },
- "fill-modes": {
- "center": "Center",
- "crop": "Crop (Fill)",
- "fit": "Fit (Contain)",
- "stretch": "Stretch"
- },
- "no-match": "No match found.",
- "no-wallpaper": "No wallpaper found.",
- "try-different-search": "Try a different search query.",
- "configure-directory": "Configure your wallpaper directory with images."
+ "plugged-in": "Plugged in.",
+ "time-left": "Time left: {time}.",
+ "time-until-full": "Time until full: {time}."
},
"bluetooth": {
"panel": {
- "title": "Bluetooth",
- "disabled": "Bluetooth is disabled",
- "enable-message": "Enable Bluetooth to see available devices.",
- "connected-devices": "Connected devices",
- "known-devices": "Known devices",
"available-devices": "Available devices",
- "scanning": "Scanning for devices...",
- "pairing-mode": "Make sure your device is in pairing mode."
- }
- },
- "wifi": {
- "panel": {
- "title": "Wi-Fi",
- "disabled": "Wi-Fi is disabled",
- "enable-message": "Enable Wi-Fi to see available networks.",
- "searching": "Searching for nearby networks...",
- "connected": "Connected",
- "disconnecting": "Disconnecting...",
- "forgetting": "Forgetting...",
- "saved": "Saved",
- "disconnect": "Disconnect",
- "enter-password": "Enter password...",
+ "blocked": "Blocked",
"connect": "Connect",
- "password": "Password",
- "forget-network": "Forget this network?",
- "forget": "Forget",
- "no-networks": "No networks found",
- "scan-again": "Scan again"
+ "connected-devices": "Connected devices",
+ "disabled": "Bluetooth is disabled",
+ "disconnect": "Disconnect",
+ "enable-message": "Enable Bluetooth to see available devices.",
+ "known-devices": "Known devices",
+ "pairing": "Pairing...",
+ "pairing-mode": "Make sure your device is in pairing mode.",
+ "scanning": "Scanning for devices...",
+ "title": "Bluetooth"
}
},
- "tooltips": {
- "refresh": "Refresh",
- "close": "Close",
- "up": "Up",
- "home": "Home",
- "refresh-wallpaper-list": "Refresh wallpaper list",
- "refresh-devices": "Refresh devices",
- "forget-network": "Forget network",
- "clear-history": "Clear history",
- "delete-notification": "Delete notification",
- "previous-month": "Previous month",
- "next-month": "Next month",
- "add-widget": "Add widget",
- "widget-settings": "Widget settings",
- "remove-widget": "Remove widget",
- "move-to-left-section": "Move to left section",
- "move-to-center-section": "Move to center section",
- "move-to-right-section": "Move to right section",
- "open-settings": "Open settings",
- "session-menu": "Session Menu",
- "cancel-timer": "Cancel timer",
- "start-screen-recording": "Start screen recording",
- "stop-screen-recording": "Stop screen recording",
- "screen-recorder-not-installed": "Screen recorder is not installed",
- "keep-awake": "Keep awake",
- "enable-keep-awake": "Enable keep awake",
- "disable-keep-awake": "Disable keep awake",
- "wallpaper-selector": "Left click: Open wallpaper selector.\nRight click: Set random wallpaper.",
- "do-not-disturb-enabled": "'Do not disturb' enabled",
- "do-not-disturb-disabled": "'Do not disturb' disabled",
- "connect-disconnect-devices": "Left click to connect. Right click to forget.",
- "set-power-profile": "Set \"{profile}\" power profile",
- "switch-to-light-mode": "Switch to light mode",
- "switch-to-dark-mode": "Switch to dark mode",
- "night-light-disabled": "Night light is disabled.\nLeft click to cycle mode.\nRight click to access settings.",
- "night-light-enabled": "Night light is enabled.\nLeft click to cycle mode.\nRight click to access settings.",
- "night-light-forced": "Night light is forced.\nLeft click to cycle mode.\nRight click to access settings.",
- "night-light-not-installed": "Night light is not available.\nwlsunset is not installed.",
- "click-to-start-recording": "Click to start recording",
- "click-to-stop-recording": "Click to stop recording",
- "open-control-center": "Open control center",
- "volume-at": "Output volume at {volume}%\nLeft click for settings. Right click to toggle mute.\nScroll to modify volume.",
- "microphone-volume-at": "Microphone volume at {volume}%\nLeft click for settings. Right click to toggle mute.\nScroll to modify volume.",
- "brightness-at": "Brightness: {brightness}%\nRight click for settings.\nScroll to modify brightness.",
- "manage-wifi": "Manage Wi-Fi",
- "bluetooth-devices": "Bluetooth devices",
- "open-notification-history-enable-dnd": "Open notification history\nRight-click to enable \"Do not disturb\".",
- "open-notification-history-disable-dnd": "Open notification history\nRight-click to disable \"Do not disturb\".",
- "open-wallpaper-selector": "Open wallpaper selector",
- "previous-media": "Previous media",
- "pause": "Pause",
- "play": "Play",
- "next-media": "Next media",
- "power-profile": "'{profile}' power profile",
- "keyboard-layout": "{layout} keyboard layout",
- "output-muted": "Toggle output mute",
- "input-muted": "Toggle input mute"
- },
- "clock": {
- "tooltip": "Open calendar"
- },
"calendar": {
"panel": {
"week": "Week"
@@ -1436,220 +367,246 @@
"loading": "Loading weather…"
}
},
+ "clock": {
+ "tooltip": "Open calendar"
+ },
"dock": {
"menu": {
+ "close": "Close",
"focus": "Focus",
"pin": "Pin",
- "unpin": "Unpin",
- "close": "Close"
+ "unpin": "Unpin"
}
},
+ "general": {
+ "no-results": "No results",
+ "no-summary": "No summary",
+ "unknown": "Unknown"
+ },
"launcher": {
"pin": "Pin to dock",
"unpin": "Unpin from dock"
},
- "placeholders": {
- "search-icons": "e.g., noctalia, niri, battery, cloud",
- "profile-picture-path": "/home/user/.face",
- "enter-width-pixels": "Enter width in pixels",
- "enter-command": "Enter command to execute (app or custom script)",
- "command-example": "echo \"Hello World\"",
- "enter-text-to-collapse": "e.g., 'nothing is playing'. Use /regex/ for patterns.",
- "search-wallpapers": "Type to filter wallpapers...",
- "search-launcher": "Search entries... or use > for commands",
- "search": "Search...",
- "select": "Select",
- "cancel": "Cancel",
- "test": "Test"
+ "lock-screen": {
+ "authenticating": "Authenticating...",
+ "authentication-failed": "Authentication failed",
+ "password": "Enter your password...",
+ "restart": "Restart",
+ "shut-down": "Shut down",
+ "suspend": "Suspend",
+ "welcome-back": "Welcome back,"
+ },
+ "notifications": {
+ "panel": {
+ "click-to-expand": "Click to expand",
+ "description": "Your notifications will show up here as they arrive.",
+ "no-notifications": "No notifications",
+ "title": "Notifications"
+ }
},
"options": {
- "colors": {
- "primary": "Primary",
- "secondary": "Secondary",
- "tertiary": "Tertiary",
- "error": "Error",
- "onSurface": "On Surface"
- },
"bar": {
- "position": {
- "top": "Top",
- "bottom": "Bottom",
- "left": "Left",
- "right": "Right"
- },
"density": {
- "mini": "Mini",
+ "comfortable": "Comfortable",
"compact": "Compact",
"default": "Default",
- "comfortable": "Comfortable"
+ "mini": "Mini"
+ },
+ "position": {
+ "bottom": "Bottom",
+ "left": "Left",
+ "right": "Right",
+ "top": "Top"
}
},
- "launcher": {
- "position": {
- "center": "Center (default)",
- "top_left": "Top left",
- "top_right": "Top right",
- "bottom_left": "Bottom left",
- "bottom_right": "Bottom right",
- "bottom_center": "Bottom center",
- "top_center": "Top center",
- "center_left": "Left center",
- "center_right": "Right center"
- }
+ "colors": {
+ "error": "Error",
+ "onSurface": "On Surface",
+ "primary": "Primary",
+ "secondary": "Secondary",
+ "tertiary": "Tertiary"
},
"control-center": {
"position": {
- "close_to_bar_button": "Close to bar button",
- "top_left": "Top left",
- "top_right": "Top right",
+ "bottom_center": "Bottom center",
"bottom_left": "Bottom left",
"bottom_right": "Bottom right",
- "bottom_center": "Bottom center",
+ "center": "Center",
+ "close_to_bar_button": "Close to bar button",
"top_center": "Top center",
- "center": "Center"
+ "top_left": "Top left",
+ "top_right": "Top right"
},
"quickSettingsStyle": {
- "modern": "Modern",
"classic": "Classic",
- "compact": "Compact"
+ "compact": "Compact",
+ "modern": "Modern"
+ }
+ },
+ "display-mode": {
+ "always-hide": "Always hide",
+ "always-show": "Always show",
+ "force-open": "Force Open",
+ "on-hover": "On hover"
+ },
+ "frame-rates": {
+ "fps": "{fps} FPS"
+ },
+ "hide-modes": {
+ "hidden": "Hide When Empty",
+ "idle": "Hide When Idle",
+ "transparent": "Transparent When Empty",
+ "visible": "Always Visible"
+ },
+ "launcher": {
+ "position": {
+ "bottom_center": "Bottom center",
+ "bottom_left": "Bottom left",
+ "bottom_right": "Bottom right",
+ "center": "Center",
+ "center_left": "Left center",
+ "center_right": "Right center",
+ "follow_bar": "Follow bar (default)",
+ "top_center": "Top center",
+ "top_left": "Top left",
+ "top_right": "Top right"
}
},
"osd": {
"position": {
- "top_left": "Top left",
- "top_right": "Top right",
- "top_center": "Top center",
+ "bottom_center": "Bottom center",
"bottom_left": "Bottom left",
"bottom_right": "Bottom right",
- "bottom_center": "Bottom center",
"center_left": "Left center",
- "center_right": "Right center"
+ "center_right": "Right center",
+ "top_center": "Top center",
+ "top_left": "Top left",
+ "top_right": "Top right"
}
},
- "display-mode": {
- "on-hover": "On hover",
- "always-show": "Always show",
- "always-hide": "Always hide",
- "force-open": "Force Open"
- },
- "workspace-labels": {
- "none": "None",
- "index": "Index",
- "name": "Name"
- },
- "visualizer-types": {
- "none": "None",
- "linear": "Linear",
- "mirrored": "Mirrored",
- "wave": "Wave"
+ "screen-recording": {
+ "audio-sources": {
+ "both": "System output + microphone input",
+ "microphone-input": "Microphone input",
+ "system-output": "System output"
+ },
+ "color-range": {
+ "full": "Full",
+ "limited": "Limited"
+ },
+ "quality": {
+ "high": "High",
+ "medium": "Medium",
+ "ultra": "Ultra",
+ "very-high": "Very high"
+ },
+ "sources": {
+ "portal": "Portal",
+ "screen": "Screen"
+ }
},
"scrolling-modes": {
"always": "Scroll Always",
"hover": "Scroll On Hover",
"never": "Never Scroll"
},
- "hide-modes": {
- "visible": "Always Visible",
- "hidden": "Hide When Empty",
- "transparent": "Transparent When Empty"
+ "shadow-direction": {
+ "bottom": "Below",
+ "bottom_left": "Bottom left",
+ "bottom_right": "Bottom right",
+ "center": "Overhead",
+ "left": "Left",
+ "right": "Right",
+ "top": "Above",
+ "top_left": "Top left",
+ "top_right": "Top right"
},
- "frame-rates": {
- "fps": "{fps} FPS"
+ "visualizer-quality": {
+ "high": "High",
+ "low": "Low"
},
- "screen-recording": {
- "sources": {
- "portal": "Portal",
- "screen": "Screen"
- },
- "quality": {
- "medium": "Medium",
- "high": "High",
- "very-high": "Very high",
- "ultra": "Ultra"
- },
- "color-range": {
- "limited": "Limited",
- "full": "Full"
- },
- "audio-sources": {
- "system-output": "System output",
- "microphone-input": "Microphone input",
- "both": "System output + microphone input"
- }
+ "visualizer-types": {
+ "linear": "Linear",
+ "mirrored": "Mirrored",
+ "none": "None",
+ "wave": "Wave"
+ },
+ "workspace-labels": {
+ "index": "Index",
+ "name": "Name",
+ "none": "None"
}
},
- "session-menu": {
- "title": "Session Menu",
- "action-in-seconds": "{action} in {seconds} seconds...",
- "lock": "Lock",
- "lock-and-suspend": "Lock and Suspend",
- "suspend": "Suspend",
- "reboot": "Reboot",
- "logout": "Logout",
- "shutdown": "Shutdown"
+ "placeholders": {
+ "cancel": "Cancel",
+ "command-example": "echo \"Hello World\"",
+ "enter-command": "Enter command to execute (app or custom script)",
+ "enter-text-to-collapse": "e.g., 'nothing is playing'. Use /regex/ for patterns.",
+ "enter-tooltip": "Enter tooltip",
+ "enter-width-pixels": "Enter width in pixels",
+ "profile-picture-path": "/home/user/.face",
+ "search": "Search...",
+ "search-icons": "e.g., noctalia, niri, battery, cloud",
+ "search-launcher": "Search entries... or use > for commands",
+ "search-wallpapers": "Type to filter wallpapers...",
+ "select": "Select",
+ "test": "Test"
},
"plugins": {
"applications": "Applications",
- "clipboard": "Clipboard history",
"calculator": "Calculator",
- "clipboard-search-description": "Search clipboard history",
+ "calculator-description": "Calculator - evaluate mathematical expressions",
+ "calculator-enter-expression": "Enter a mathematical expression",
+ "calculator-error": "Error",
+ "calculator-name": "Calculator",
+ "clipboard": "Clipboard history",
"clipboard-clear-description": "Clear all clipboard history",
+ "clipboard-clear-description-full": "Remove all items from clipboard history",
+ "clipboard-clear-history": "Clear clipboard history",
"clipboard-history-disabled": "Clipboard history disabled",
"clipboard-history-disabled-description": "Enable clipboard history in settings or install cliphist",
- "clipboard-clear-history": "Clear clipboard history",
- "clipboard-clear-description-full": "Remove all items from clipboard history",
"clipboard-loading": "Loading clipboard history...",
"clipboard-loading-description": "Please wait",
- "calculator-description": "Calculator - evaluate mathematical expressions",
- "calculator-name": "Calculator",
- "calculator-enter-expression": "Enter a mathematical expression",
- "calculator-error": "Error"
- },
- "system": {
- "uptime": "Uptime: {uptime}",
- "welcome-back": "Welcome back,",
- "monitor-description": "{model} ({width}x{height} @ {scale}x)",
- "scaling-percentage": "{percentage}%",
- "location-display": "{name} ({coordinates})",
- "signal-strength": "{signal}%",
- "cpu-temperature": "{temp}°C",
- "disk-usage": "{percent}%",
- "widget-settings-title": "{widget} Settings",
- "unknown-app": "Unknown App",
- "no-media-player-detected": "No media player detected",
- "user-requested": "User requested",
- "unknown": "Unknown",
- "unknown-version": "Unknown",
- "unknown-layout": "Unknown"
- },
- "lock-screen": {
- "password": "Enter your password...",
- "welcome-back": "Welcome back,",
- "authenticating": "Authenticating...",
- "authentication-failed": "Authentication failed",
- "shut-down": "Shut down",
- "restart": "Restart",
- "suspend": "Suspend"
+ "clipboard-search-description": "Search clipboard history"
},
"quickSettings": {
+ "bluetooth": {
+ "label": {
+ "disabled": "Bluetooth",
+ "enabled": "Bluetooth"
+ },
+ "tooltip": {
+ "action": "Click to manage Bluetooth devices"
+ }
+ },
+ "keepAwake": {
+ "label": {
+ "disabled": "Keep Awake",
+ "enabled": "Keep Awake"
+ },
+ "tooltip": {
+ "action": "Click to toggle keep awake mode"
+ }
+ },
+ "nightLight": {
+ "label": {
+ "disabled": "Night Light",
+ "enabled": "Night Light",
+ "forced": "Night Light"
+ },
+ "tooltip": {
+ "action": "Click to cycle Night Light mode\nRight click: Open settings"
+ }
+ },
"notifications": {
"label": {
- "enabled": "Notifications",
- "disabled": "Do Not Disturb"
+ "disabled": "Do Not Disturb",
+ "enabled": "Notifications"
},
"tooltip": {
"action": "Left click: Open notification history\nRight click: Toggle Do Not Disturb"
}
},
- "screenRecorder": {
- "label": {
- "recording": "Stop",
- "stopped": "Record"
- },
- "tooltip": {
- "action": "Click to start/stop screen recording"
- }
- },
"powerProfile": {
"label": {
"unavailable": "Power Profile"
@@ -1659,33 +616,13 @@
"disabled": "Install power-profiles-daemon to use power profiles"
}
},
- "wifi": {
+ "screenRecorder": {
"label": {
- "ethernet": "Ethernet",
- "wifi": "Wi-Fi",
- "disconnected": "Wi-Fi Disconnected"
+ "recording": "Stop",
+ "stopped": "Record"
},
"tooltip": {
- "action": "Click to manage Wi-Fi connections"
- }
- },
- "bluetooth": {
- "label": {
- "enabled": "Bluetooth",
- "disabled": "Bluetooth"
- },
- "tooltip": {
- "action": "Click to manage Bluetooth devices"
- }
- },
- "nightLight": {
- "label": {
- "enabled": "Night Light",
- "forced": "Night Light",
- "disabled": "Night Light"
- },
- "tooltip": {
- "action": "Click to cycle Night Light mode\nRight click: Open settings"
+ "action": "Click to start/stop screen recording"
}
},
"wallpaperSelector": {
@@ -1694,177 +631,1386 @@
"action": "Left click: Open wallpaper selector\nRight click: Set random wallpaper"
}
},
- "keepAwake": {
+ "wifi": {
"label": {
- "enabled": "Keep Awake",
- "disabled": "Keep Awake"
+ "disconnected": "Wi-Fi Disconnected",
+ "ethernet": "Ethernet",
+ "wifi": "Wi-Fi"
},
"tooltip": {
- "action": "Click to toggle keep awake mode"
+ "action": "Click to manage Wi-Fi connections"
}
}
},
+ "session-menu": {
+ "action-in-seconds": "{action} in {seconds} seconds...",
+ "lock": "Lock",
+ "lock-and-suspend": "Lock and Suspend",
+ "logout": "Logout",
+ "reboot": "Reboot",
+ "shutdown": "Shutdown",
+ "suspend": "Suspend",
+ "title": "Session Menu"
+ },
+ "settings": {
+ "about": {
+ "contributors": {
+ "section": {
+ "description": "Shout-out to our {count} awesome contributor!",
+ "description_plural": "Shout-out to our {count} awesome contributors!",
+ "label": "Contributors"
+ }
+ },
+ "noctalia": {
+ "download-latest": "Download latest release",
+ "installed-version": "Installed version:",
+ "latest-version": "Latest version:",
+ "section": {
+ "description": "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.",
+ "label": "Noctalia shell"
+ }
+ },
+ "support": "Support us",
+ "title": "About"
+ },
+ "audio": {
+ "devices": {
+ "input-device": {
+ "description": "Select the desired audio input device.",
+ "label": "Input device"
+ },
+ "output-device": {
+ "description": "Select the desired audio output device.",
+ "label": "Output device"
+ },
+ "section": {
+ "description": "Configure available audio input and output devices.",
+ "label": "Audio devices"
+ }
+ },
+ "media": {
+ "excluded-player": {
+ "description": "Add keywords for players you want the system to ignore. Each keyword should be on a new line.",
+ "label": "Excluded player",
+ "placeholder": "type substring and press +"
+ },
+ "frame-rate": {
+ "description": "Higher rates are smoother but use more resources.",
+ "label": "Frame rate"
+ },
+ "primary-player": {
+ "description": "Enter a keyword to identify your main player.",
+ "label": "Primary player",
+ "placeholder": "e.g. spotify, vlc, mpv"
+ },
+ "scrolling-speed": {
+ "description": "Time in seconds for the title to scroll from start to end.",
+ "label": "Scrolling speed"
+ },
+ "scrolling-title": {
+ "description": "Enable continuous scrolling for long media titles.",
+ "label": "Scrolling title"
+ },
+ "section": {
+ "description": "Set your preferred and ignored media applications.",
+ "label": "Media players"
+ },
+ "visualizer-quality": {
+ "description": "High quality use more GPU ressources.",
+ "label": "Visualization quality"
+ },
+ "visualizer-type": {
+ "description": "Choose a visualization type for media playback.",
+ "label": "Visualization type"
+ }
+ },
+ "title": "Audio",
+ "volumes": {
+ "input-volume": {
+ "description": "Microphone input volume level.",
+ "label": "Input volume"
+ },
+ "mute-input": {
+ "description": "Mute the default audio input (microphone).",
+ "label": "Mute audio input"
+ },
+ "mute-output": {
+ "description": "Mute the system's main audio output.",
+ "label": "Mute audio output"
+ },
+ "output-volume": {
+ "description": "System-wide volume level.",
+ "label": "Output volume"
+ },
+ "section": {
+ "description": "Adjust volume controls and audio levels.",
+ "label": "Volumes"
+ },
+ "step-size": {
+ "description": "Adjust the step size for volume changes (scroll wheel, keyboard shortcuts).",
+ "label": "Volume step size"
+ },
+ "volume-overdrive": {
+ "description": "Allow raising volume above 100%. May not be supported by all hardware.",
+ "label": "Allow volume overdrive"
+ }
+ }
+ },
+ "bar": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Adjust the background opacity of the bar.",
+ "label": "Background opacity"
+ },
+ "density": {
+ "description": "Adjust the bar's padding for a compact or spacious look.",
+ "label": "Bar density"
+ },
+ "floating": {
+ "description": "Displays the bar as a floating 'pill'.",
+ "label": "Floating bar"
+ },
+ "margins": {
+ "description": "Adjust the margins around the floating bar.",
+ "horizontal": "Horizontal",
+ "label": "Margins",
+ "vertical": "Vertical"
+ },
+ "outer-corners": {
+ "description": "Displays outwardly curved corners on the bar.",
+ "label": "Outer corners"
+ },
+ "position": {
+ "description": "Choose where to place the bar on the screen.",
+ "label": "Bar position"
+ },
+ "section": {
+ "description": "Customize the bar's appearance and position.",
+ "label": "Appearance"
+ },
+ "show-capsule": {
+ "description": "Show widget backgrounds.",
+ "label": "Show capsule"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Show bar on specific monitors. Defaults to all if none are chosen.",
+ "label": "Monitors display"
+ }
+ },
+ "title": "Bar",
+ "tray": {
+ "back": "Back",
+ "blacklist": {
+ "description": "Add tray exclusion rules, supports wildcards (*).",
+ "label": "Blacklist",
+ "placeholder": "e.g., nm-applet, Fcitx*"
+ },
+ "pin-application": "Pin Application",
+ "unpin-application": "Unpin Application"
+ },
+ "widgets": {
+ "section": {
+ "description": "Drag and drop widgets to reorder them. Badges indicate usage: [L]eft, [C]enter, [R]ight.",
+ "label": "Widgets positioning"
+ }
+ }
+ },
+ "color-scheme": {
+ "color-source": {
+ "matugen-scheme-type": {
+ "description": {
+ "scheme-content": "Derives colors that closely match the underlying image",
+ "scheme-expressive": "Vibrant palette with playful saturation",
+ "scheme-fidelity": "High-fidelity palette that preserves source hues",
+ "scheme-fruit-salad": "Colorful mix of bright contrasting accents",
+ "scheme-monochrome": "Minimal palette built around a single hue",
+ "scheme-neutral": "Muted palette with subdued and calming tones",
+ "scheme-rainbow": "Diverse palette spanning the full spectrum",
+ "scheme-tonal-spot": "Balanced palette with focused accents"
+ },
+ "label": "Matugen scheme type"
+ },
+ "section": {
+ "description": "Main settings for Noctalia's colors.",
+ "label": "Color source"
+ },
+ "use-wallpaper-colors": {
+ "description": "Generate color schemes from your wallpaper using Matugen. Automatically extracts colors to create a cohesive theme.",
+ "label": "Use wallpaper colors"
+ }
+ },
+ "dark-mode": {
+ "mode": {
+ "description": "Enables automatic switching between light and dark mode.",
+ "label": "Dark mode schedule",
+ "location": "Location",
+ "manual": "Manual",
+ "off": "Off"
+ },
+ "switch": {
+ "description": "Switches to a darker theme for easier viewing at night.",
+ "label": "Dark mode"
+ }
+ },
+ "predefined": {
+ "generate-templates": {
+ "description": "Generate Matugen templates (GTK, terminal themes, etc.) when using predefined color schemes.",
+ "label": "Generate templates for predefined schemes"
+ },
+ "section": {
+ "description": "Choose from a collection of predefined color schemes.",
+ "label": "Predefined color schemes"
+ }
+ },
+ "templates": {
+ "misc": {
+ "description": "Additional configuration options.",
+ "label": "Misc",
+ "user-templates": {
+ "description": "Enable user-defined Matugen config. A template file will be created at ~/.config/noctalia/user-templates.toml on first enable",
+ "label": "User templates"
+ }
+ },
+ "programs": {
+ "code": {
+ "description": "Write {filepath}. Hyprluna theme needs to be installed and activated manually.",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "spicetify": {
+ "description": "Write {filepath}. Comfy theme needs to be installed and activated manually.",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "description": "Application-specific theming.",
+ "discord": {
+ "description": "Write {filepath} for {client}. Hyprluna theme needs to be activated manually.",
+ "description-missing": "No Discord client detected. Install vencord, vesktop, webcord, armcord, equibop, lightcord, or dorion."
+ },
+ "fuzzel": {
+ "description": "Write {filepath} and reload",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "label": "Programs",
+ "pywalfox": {
+ "description": "Write {filepath} and run pywalfox update",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "vicinae": {
+ "description": "Write {filepath} and reload",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "walker": {
+ "description": "Write {filepath} and set theme to noctalia",
+ "description-missing": "Requires {app} to be installed"
+ }
+ },
+ "section": {
+ "description": "Apply colors to external applications.",
+ "label": "Templates"
+ },
+ "terminal": {
+ "alacritty": {
+ "description": "Write {filepath} and reload",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "description": "Terminal emulator theming.",
+ "foot": {
+ "description": "Write {filepath} and reload",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "ghostty": {
+ "description": "Write {filepath} and reload",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "kitty": {
+ "description": "Write {filepath} and reload",
+ "description-missing": "Requires {app} to be installed"
+ },
+ "label": "Terminal",
+ "wezterm": {
+ "description": "Write {filepath} and reload",
+ "description-missing": "Requires {app} to be installed"
+ }
+ },
+ "ui": {
+ "description": "Desktop environment and UI toolkit theming.",
+ "gtk": {
+ "description": "Write {filepath}"
+ },
+ "kcolorscheme": {
+ "description": "Write {filepath}"
+ },
+ "label": "UI",
+ "qt": {
+ "description": "Write {filepath}"
+ }
+ }
+ },
+ "title": "Color scheme"
+ },
+ "control-center": {
+ "cards": {
+ "section": {
+ "description": "Customize which controls appear in the Control Center and in what order.",
+ "label": "Cards"
+ }
+ },
+ "position": {
+ "description": "Choose where the Control Center panel appears when opened.",
+ "label": "Position"
+ },
+ "section": {
+ "description": "Configure the Control Center panel positioning and behavior.",
+ "label": "Appearance"
+ },
+ "shortcuts": {
+ "custom-button": {
+ "browse": "Browse",
+ "command": {
+ "description": "The command to execute when the button is clicked.",
+ "label": "Command"
+ },
+ "enable-on-state-logic": {
+ "description": "Enable a second icon and 'hot' state based on a check command.",
+ "label": "Enable On-State Logic"
+ },
+ "general-tooltip-text": {
+ "description": "General description for the button's tooltip.",
+ "label": "General Tooltip Text"
+ },
+ "icon": {
+ "description": "Select an icon from the library.",
+ "label": "Icon"
+ },
+ "on-clicked": {
+ "description": "Command to execute when the button is left-clicked.",
+ "label": "Left Click Command"
+ },
+ "on-middle-clicked": {
+ "description": "Command to execute when the button is middle-clicked.",
+ "label": "Middle Click Command"
+ },
+ "on-right-clicked": {
+ "description": "Command to execute when the button is right-clicked.",
+ "label": "Right Click Command"
+ },
+ "on-state-command": {
+ "description": "Command to execute to check if the button should be in the 'on' state. Returns 0 for on, non-zero for off.",
+ "label": "On State Check Command"
+ },
+ "on-state-icon": {
+ "description": "The icon for the button when it's in the 'on' state.",
+ "label": "On State Icon"
+ },
+ "state-checks": {
+ "add": "Add State Check",
+ "browse-icon": "Browse",
+ "command": "Command to execute for this state check",
+ "label": "State Checks",
+ "remove": "Remove"
+ },
+ "tooltip": {
+ "description": "The tooltip to show when hovering over the button.",
+ "label": "Tooltip"
+ }
+ },
+ "dialog": {
+ "apply": "Apply",
+ "cancel": "Cancel"
+ },
+ "section": {
+ "description": "Configure and manage the shortcuts widgets.",
+ "label": "Shortcuts widgets"
+ },
+ "sectionLeft": "Left",
+ "sectionRight": "Right"
+ },
+ "title": "Control Center"
+ },
+ "display": {
+ "monitors": {
+ "brightness": "Brightness",
+ "brightness-step": {
+ "description": "Adjust the step size for brightness changes (scroll wheel and keyboard shortcuts).",
+ "label": "Brightness step size"
+ },
+ "brightness-unavailable": {
+ "ddc-disabled": "Brightness control unavailable. Enable \"External brightness support\" to control this display's brightness.",
+ "generic": "Brightness control is not available for this display."
+ },
+ "enforce-minimum": {
+ "description": "Solves the problem of backlight completely turning off on some displays at 0% brightness.",
+ "label": "Enforce minimum brightness (1%)"
+ },
+ "external-brightness": {
+ "description": "Enable DDCUtil support for controlling brightness on external displays via DDC/CI protocol.",
+ "label": "External brightness support"
+ },
+ "section": {
+ "description": "Adjust settings brightness for each display.",
+ "label": "Per-monitor settings"
+ }
+ },
+ "night-light": {
+ "auto-schedule": {
+ "description": "Based on the sunset and sunrise time in {location} - recommended.",
+ "label": "Automatic scheduling"
+ },
+ "enable": {
+ "description": "Apply a warm color filter to reduce blue light emission.",
+ "label": "Enable night light"
+ },
+ "force-activation": {
+ "description": "Ignores the schedule and applies the night filter immediately.",
+ "label": "Force activation"
+ },
+ "manual-schedule": {
+ "description": "Set custom times for sunrise and sunset.",
+ "label": "Manual scheduling",
+ "select-start": "Select start time",
+ "select-stop": "Select stop time",
+ "sunrise": "Sunrise time",
+ "sunset": "Sunset time"
+ },
+ "section": {
+ "description": "Reduce blue light emission to help you sleep better and reduce eye strain.",
+ "label": "Night light"
+ },
+ "temperature": {
+ "day": "Day",
+ "description": "Set the color warmth for nighttime and daytime.",
+ "label": "Color temperature",
+ "night": "Night"
+ }
+ },
+ "title": "Display"
+ },
+ "dock": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Adjust the dock's background opacity.",
+ "label": "Background opacity"
+ },
+ "colorize-icons": {
+ "description": "Apply theme colors to dock app icons (non-focused apps only).",
+ "label": "Colorize Icons"
+ },
+ "display": {
+ "always-visible": "Always visible",
+ "auto-hide": "Auto hide",
+ "description": "Choose how the dock behaves.",
+ "exclusive": "Exclusive",
+ "label": "Display"
+ },
+ "floating-distance": {
+ "description": "Set the distance between the dock and the edge of the screen.",
+ "label": "Dock floating distance"
+ },
+ "icon-size": {
+ "description": "Adjust the overall size of the dock.",
+ "label": "Dock size"
+ },
+ "section": {
+ "description": "Customize the dock's behavior and appearance.",
+ "label": "Appearance"
+ }
+ },
+ "enabled": {
+ "description": "Show or hide the dock entirely",
+ "label": "Enable dock"
+ },
+ "monitors": {
+ "only-same-output": {
+ "description": "Show only apps from the output where the dock is located.",
+ "label": "Only apps from same output"
+ },
+ "section": {
+ "description": "Show dock on specific monitors. Defaults to all if none are chosen.",
+ "label": "Monitor display"
+ }
+ },
+ "title": "Dock"
+ },
+ "general": {
+ "fonts": {
+ "default": {
+ "description": "Main font used throughout the interface.",
+ "label": "Default font",
+ "placeholder": "Select default font...",
+ "scale": {
+ "description": "Increase or decrease the size of the standard text.",
+ "label": "Default font size"
+ },
+ "search-placeholder": "Search font..."
+ },
+ "monospace": {
+ "description": "Monospaced font used for numbers and stats display.",
+ "label": "Monospaced font",
+ "placeholder": "Select monospace font...",
+ "scale": {
+ "description": "Increase or decrease the size of the monospaced text.",
+ "label": "Monospaced font size"
+ },
+ "search-placeholder": "Search monospace font..."
+ },
+ "reset-scaling": "Reset scaling",
+ "section": {
+ "description": "Choose the fonts used throughout the interface.",
+ "label": "Fonts"
+ }
+ },
+ "language": {
+ "section": {
+ "description": "Choose your preferred language for the application.",
+ "label": "Language"
+ },
+ "select": {
+ "auto-detect": "Automatic",
+ "description": "Select the language used in the application's interface.",
+ "label": "Application Language"
+ }
+ },
+ "launch-setup-wizard": "Launch the setup wizard",
+ "profile": {
+ "picture": {
+ "description": "Your profile picture that appears throughout the interface.",
+ "label": "{user}'s Profile picture"
+ },
+ "section": {
+ "description": "Edit your user details and avatar.",
+ "label": "Profile"
+ },
+ "select-avatar": "Select avatar image"
+ },
+ "screen-corners": {
+ "radius": {
+ "description": "Adjust the rounded corners of the screen.",
+ "label": "Screen corners radius",
+ "reset": "Reset screen corners radius"
+ },
+ "section": {
+ "description": "Customize screen corner rounding and visual effects.",
+ "label": "Screen corners"
+ },
+ "show-corners": {
+ "description": "Display rounded corners on the edge of the screen.",
+ "label": "Show screen corners"
+ },
+ "solid-black": {
+ "description": "Use solid black instead of the bar background color.",
+ "label": "Solid black corners"
+ }
+ },
+ "title": "General"
+ },
+ "hooks": {
+ "info": {
+ "command-info": {
+ "description": "• Commands are executed via shell (sh -c)\n• Commands run in background (detached)\n• Test buttons execute with current values",
+ "label": "Hook Command Information"
+ },
+ "parameters": {
+ "description": "• Wallpaper Hook: $1 = wallpaper path, $2 = screen name\n• Theme Toggle Hook: $1 = true/false (dark mode state)",
+ "label": "Available Parameters"
+ }
+ },
+ "system-hooks": {
+ "enable": {
+ "description": "Enable or disable all hook commands.",
+ "label": "Enable hooks"
+ },
+ "section": {
+ "description": "Configure commands to be executed when system events occur.",
+ "label": "System hooks"
+ }
+ },
+ "theme-changed": {
+ "description": "Command to be executed when theme toggles between dark and light mode.",
+ "label": "Theme changed",
+ "placeholder": "e.g., notify-send \"Theme\" \"Toggled\""
+ },
+ "title": "Hooks",
+ "wallpaper-changed": {
+ "description": "Command to be executed when wallpaper changes.",
+ "label": "Wallpaper changed",
+ "placeholder": "e.g., notify-send \"Wallpaper\" \"Changed\""
+ }
+ },
+ "launcher": {
+ "settings": {
+ "background-opacity": {
+ "description": "Adjust the background opacity of the launcher.",
+ "label": "Background opacity"
+ },
+ "clipboard-history": {
+ "description": "Access previously copied items from the launcher.",
+ "label": "Enable clipboard history"
+ },
+ "custom-launch-prefix": {
+ "description": "Prefix commands with a custom launcher (e.g., 'runapp' for systemd integration).",
+ "label": "Custom launch prefix"
+ },
+ "custom-launch-prefix-enabled": {
+ "description": "Use a custom prefix for launching applications instead of the default method.",
+ "label": "Enable custom launch prefix"
+ },
+ "position": {
+ "description": "Choose where the launcher panel appears.",
+ "label": "Position"
+ },
+ "section": {
+ "description": "Customize the launcher's behavior and appearance.",
+ "label": "Appearance"
+ },
+ "sort-by-usage": {
+ "description": "When enabled, frequently launched apps appear first in the list.",
+ "label": "Sort by most used"
+ },
+ "terminal-command": {
+ "description": "Command to launch a terminal. E.g., 'kitty -e' or 'gnome-terminal --'.",
+ "label": "Terminal command"
+ },
+ "use-app2unit": {
+ "description": "Uses an alternative launch method to better manage app processes and prevent issues.",
+ "label": "Use App2Unit to launch applications"
+ }
+ },
+ "title": "Launcher"
+ },
+ "location": {
+ "date-time": {
+ "12hour-format": {
+ "description": "Displays time in 12-hour format on the lock screen and calendar. The bar clock has its own settings.",
+ "label": "Use 12-hour time format"
+ },
+ "first-day-of-week": {
+ "automatic": "Automatic (use system locale)",
+ "description": "Choose which day starts the week in the calendar.",
+ "label": "First day of week"
+ },
+ "section": {
+ "description": "Customize how date and time appear.",
+ "label": "Date & time"
+ },
+ "show-events": {
+ "description": "Display events in the calendar panel.",
+ "label": "Show calendar events"
+ },
+ "use-analog": {
+ "description": "Show an analog style clock on the calendar window and lock screen.",
+ "label": "Use analog style clock"
+ },
+ "week-numbers": {
+ "description": "Displays the week of the year (e.g., Week 38) in the calendar.",
+ "label": "Show week numbers"
+ }
+ },
+ "location": {
+ "search": {
+ "description": "e.g., Toronto, ON",
+ "label": "Search for a location",
+ "placeholder": "Enter the location name"
+ },
+ "section": {
+ "description": "Get accurate weather and night light scheduling by setting your location.",
+ "label": "Your location"
+ }
+ },
+ "title": "Location",
+ "weather": {
+ "enabled": {
+ "description": "Show weather information throughout the interface and fetch weather data. When disabled, all weather elements will be hidden and no network requests will be made.",
+ "label": "Enable weather"
+ },
+ "fahrenheit": {
+ "description": "Display temperature in Fahrenheit instead of Celsius.",
+ "label": "Display temperature in Fahrenheit (°F)"
+ },
+ "section": {
+ "description": "Choose your preferred temperature unit.",
+ "label": "Weather"
+ },
+ "show-in-calendar": {
+ "description": "Show the daily weather forecast directly in your calendar view.",
+ "label": "Display weather in calendar"
+ }
+ }
+ },
+ "lock-screen": {
+ "compact-lockscreen": {
+ "description": "Show only the login input and system controls, hiding weather and media widgets.",
+ "label": "Compact lock screen"
+ },
+ "lock-on-suspend": {
+ "description": "Automatically lock the screen when suspending the system.",
+ "label": "Lock on suspend"
+ },
+ "title": "Lock screen"
+ },
+ "network": {
+ "bluetooth": {
+ "label": "Enable Bluetooth"
+ },
+ "section": {
+ "description": "Manage Wi-Fi and Bluetooth connections."
+ },
+ "title": "Network",
+ "wifi": {
+ "label": "Enable Wi-Fi"
+ }
+ },
+ "notifications": {
+ "duration": {
+ "critical-urgency": {
+ "description": "How long critical priority notifications stay visible.",
+ "label": "Critical urgency"
+ },
+ "low-urgency": {
+ "description": "How long low priority notifications stay visible.",
+ "label": "Low urgency"
+ },
+ "normal-urgency": {
+ "description": "How long normal priority notifications stay visible.",
+ "label": "Normal urgency"
+ },
+ "respect-expire": {
+ "description": "Use the expire timeout set in the notification.",
+ "label": "Respect expire timeout"
+ },
+ "section": {
+ "description": "Configure how long notifications stay visible based on their urgency level.",
+ "label": "Notification duration"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Show notification on specific monitors. Defaults to all if none are chosen.",
+ "label": "Monitors display"
+ }
+ },
+ "settings": {
+ "always-on-top": {
+ "description": "Display notifications above fullscreen windows and other layers.",
+ "label": "Always on top"
+ },
+ "background-opacity": {
+ "description": "Adjust the opacity of notification backgrounds.",
+ "label": "Background opacity"
+ },
+ "do-not-disturb": {
+ "description": "Disable all notification popups when enabled.",
+ "label": "Do not disturb"
+ },
+ "enable-osd": {
+ "description": "Show volume and brightness changes in real-time.",
+ "label": "Enable on screen display"
+ },
+ "enabled": {
+ "description": "Enable or disable the notification daemon, requires a restart of noctalia-shell.",
+ "label": "Enable notifications"
+ },
+ "location": {
+ "description": "Where notifications appear on screen.",
+ "label": "Location"
+ },
+ "section": {
+ "description": "Configure notifications appearance and behavior.",
+ "label": "Appearance"
+ }
+ },
+ "title": "Notifications"
+ },
+ "osd": {
+ "always-on-top": {
+ "description": "Display OSD above fullscreen windows and other layers.",
+ "label": "Always on top"
+ },
+ "description": "Configure on-screen indicators such as volume and brightness overlays.",
+ "duration": {
+ "auto-hide": {
+ "description": "Adjust the time before OSD disappears.",
+ "label": "Auto-hide after"
+ },
+ "section": {
+ "description": "How long OSD remains visible before hiding automatically.",
+ "label": "Auto-hide timeout"
+ }
+ },
+ "enabled": {
+ "description": "Show volume and brightness changes in real-time.",
+ "label": "Enable on screen display"
+ },
+ "location": {
+ "description": "Where on-screen displays appear.",
+ "label": "Location"
+ },
+ "monitors": {
+ "section": {
+ "description": "Show OSD on specific monitors. Defaults to all if none are chosen.",
+ "label": "Monitors display"
+ }
+ },
+ "section": {
+ "general": {
+ "description": "Configure visibility and behavior of OSD.",
+ "label": "General"
+ }
+ },
+ "title": "On-Screen Display"
+ },
+ "screen-recorder": {
+ "audio": {
+ "audio-codec": {
+ "description": "Opus is recommended for best performance and smallest audio size.",
+ "label": "Audio codec"
+ },
+ "audio-source": {
+ "description": "Audio source to capture during recording.",
+ "label": "Audio source"
+ },
+ "section": {
+ "description": "Configure audio recording options.",
+ "label": "Audio settings"
+ }
+ },
+ "general": {
+ "output-folder": {
+ "description": "Folder where screen recordings will be saved.",
+ "label": "Output folder",
+ "tooltip": "Browse for output folder"
+ },
+ "section": {
+ "description": "Manage screen recording output and content.",
+ "label": "General settings"
+ },
+ "select-output-folder": "Select output folder",
+ "show-cursor": {
+ "description": "Record mouse cursor in the video.",
+ "label": "Show cursor"
+ }
+ },
+ "title": "Screen recorder",
+ "video": {
+ "color-range": {
+ "description": "Limited is recommended for better compatibility.",
+ "label": "Color range"
+ },
+ "frame-rate": {
+ "description": "Target frame rate for screen recordings.",
+ "label": "Frame rate"
+ },
+ "section": {
+ "description": "Configure video recording options.",
+ "label": "Video settings"
+ },
+ "video-codec": {
+ "description": "h264 is the most common codec.",
+ "label": "Video codec"
+ },
+ "video-quality": {
+ "description": "Higher quality results in larger file sizes.",
+ "label": "Video quality"
+ },
+ "video-source": {
+ "description": "Portal is recommended, if you get artifacts try Screen.",
+ "label": "Video source"
+ }
+ }
+ },
+ "user-interface": {
+ "animation-disable": {
+ "description": "Disable all animations for a faster, more responsive experience.",
+ "label": "Disable UI Animations"
+ },
+ "animation-speed": {
+ "description": "Adjust global animation speed.",
+ "label": "Animation speed",
+ "reset": "Reset animation speed"
+ },
+ "border-radius": {
+ "description": "Controls the corner roundness of windows, buttons, and other elements.",
+ "label": "Border radius",
+ "reset": "Reset border radius"
+ },
+ "dim-desktop": {
+ "description": "Dim the desktop when panels or menus are open.",
+ "label": "Dim desktop"
+ },
+ "panels-attached-to-bar": {
+ "description": "Panels lock to the bar and screen edges, creating a seamless look with stylish inverted corners.",
+ "label": "Snap panels to edges"
+ },
+ "panels-overlay": {
+ "description": "Ensures panels and the bar remain visible, even over fullscreen applications.",
+ "label": "Keep panels & bar on top"
+ },
+ "scaling": {
+ "description": "Changes the size of the general user interface, excluding the bar.",
+ "label": "Interface scaling",
+ "reset-scaling": "Reset interface scaling"
+ },
+ "section": {
+ "description": "Customize the look, feel, and behavior of the interface.",
+ "label": "Appearance"
+ },
+ "settings-panel-attached-to-bar": {
+ "description": "Keep the Settings window aligned with the bar for a unified look.",
+ "label": "Snap Settings window to bar"
+ },
+ "shadows": {
+ "description": "Enables drop shadows under bars and panels.",
+ "direction": {
+ "description": "Choose where the shadow is cast from.",
+ "label": "Shadow direction"
+ },
+ "label": "Drop shadows"
+ },
+ "title": "User interface",
+ "tooltips": {
+ "description": "Enable or disable tooltips throughout the interface.",
+ "label": "Show tooltips"
+ }
+ },
+ "wallpaper": {
+ "automation": {
+ "custom-interval": {
+ "description": "Enter time as HH:MM (e.g., 01:30).",
+ "label": "Custom interval"
+ },
+ "interval": {
+ "description": "How often to change wallpapers automatically.",
+ "label": "Wallpaper interval"
+ },
+ "random-wallpaper": {
+ "description": "Schedule random wallpaper changes at regular intervals.",
+ "label": "Random wallpaper"
+ },
+ "section": {
+ "label": "Automation"
+ }
+ },
+ "look-feel": {
+ "edge-smoothness": {
+ "description": "Applies a soft, feathered effect to the edge of transitions.",
+ "label": "Soften transition edge"
+ },
+ "fill-color": {
+ "description": "Choose a fill color that may appear behind the wallpaper.",
+ "label": "Fill color"
+ },
+ "fill-mode": {
+ "description": "Select how the image should scale to match your monitor's resolution.",
+ "label": "Fill mode"
+ },
+ "section": {
+ "label": "Look & feel"
+ },
+ "transition-duration": {
+ "description": "Duration of transition animations in seconds.",
+ "label": "Transition duration"
+ },
+ "transition-type": {
+ "description": "Animation type when switching between wallpapers.",
+ "label": "Transition type"
+ }
+ },
+ "settings": {
+ "enable-management": {
+ "description": "Manage wallpapers with Noctalia. Uncheck if you prefer using another application.",
+ "label": "Enable wallpaper management"
+ },
+ "enable-overview": {
+ "description": "Applies a blurred and dimmed wallpaper to the overview screen.",
+ "label": "Enable overview wallpaper"
+ },
+ "folder": {
+ "description": "Path to your main wallpaper folder.",
+ "label": "Wallpaper folder",
+ "tooltip": "Browse for wallpaper folder"
+ },
+ "monitor-specific": {
+ "description": "Set a different wallpaper folder for each monitor.",
+ "label": "Monitor-specific directories",
+ "tooltip": "Browse for wallpaper folder"
+ },
+ "recursive-search": {
+ "description": "Also search for wallpapers in subfolders of the wallpaper directory.",
+ "label": "Search subfolders"
+ },
+ "section": {
+ "description": "Control how wallpapers are managed and displayed.",
+ "label": "Wallpaper settings"
+ },
+ "select-folder": "Select wallpaper folder",
+ "select-monitor-folder": "Select monitor wallpaper folder",
+ "selector-position": {
+ "description": "Choose where the wallpaper selector panel appears.",
+ "label": "Position"
+ }
+ },
+ "title": "Wallpaper"
+ }
+ },
+ "setup": {
+ "appearance": {
+ "header": "Appearance",
+ "subheader": "Choose dark mode and color sources (Matugen or predefined)."
+ },
+ "customize": {
+ "header": "Customize your experience",
+ "subheader": "Adjust bar position, density, scaling and more."
+ },
+ "wallpaper": {
+ "choose-dir": "Choose a directory containing your wallpaper images",
+ "dir": {
+ "browse": "Browse for wallpaper folder",
+ "description": "Choose the folder containing your wallpapers",
+ "label": "Wallpaper directory",
+ "select-title": "Select wallpaper folder"
+ },
+ "header": "Choose your wallpaper",
+ "no-dir": "No wallpaper directory selected",
+ "no-valid": "No valid image files found in: {dir}",
+ "none-in-dir": "No wallpapers found in directory",
+ "preview-error": "Failed to load image",
+ "select-prompt": "Select a wallpaper below",
+ "subheader": "Set the mood with a beautiful background."
+ },
+ "welcome": {
+ "note": "Just a few basics to get you started - full options are in Settings"
+ }
+ },
+ "system": {
+ "cpu-temperature": "{temp}°C",
+ "disk-usage": "{percent}%",
+ "location-display": "{name} ({coordinates})",
+ "monitor-description": "{model} ({width}x{height} @ {scale}x)",
+ "no-media-player-detected": "No media player detected",
+ "scaling-percentage": "{percentage}%",
+ "signal-strength": "{signal}%",
+ "unknown": "Unknown",
+ "unknown-app": "Unknown App",
+ "unknown-layout": "Unknown",
+ "unknown-version": "Unknown",
+ "uptime": "Uptime: {uptime}",
+ "user-requested": "User requested",
+ "welcome-back": "Welcome back,",
+ "widget-settings-title": "{widget} Settings"
+ },
"toast": {
- "night-light": {
- "enabled": "Enabled",
- "disabled": "Disabled",
- "not-installed": "wlsunset not installed",
- "forced": "Forced activation",
- "normal": "Normal mode"
- },
- "keep-awake": {
- "enabled": "Enabled",
- "disabled": "Disabled"
- },
- "wallpaper-colors": {
- "enabled": "Wallpaper colors enabled",
- "disabled": "Wallpaper colors disabled",
- "not-installed": "Matugen not installed - required for wallpaper color extraction"
- },
- "matugen": {
- "failed": "Matugen processing failed",
- "failed-general": "Matugen encountered an error while processing templates"
- },
- "recording": {
- "stopping": "Stopping recording…",
- "started": "Recording started",
- "saved": "Recording saved",
- "failed-start": "Failed to start recording",
- "failed-gpu": "gpu-screen-recorder exited unexpectedly.",
- "failed-general": "The recorder exited with an error.",
- "no-portals": "Desktop portals not running",
- "no-portals-desc": "Start xdg-desktop-portal and a compositor portal (wlr/hyprland/gnome/kde).",
- "not-installed": "gpu-screen-recorder not installed",
- "not-installed-desc": "Please install gpu-screen-recorder to use screen recording features."
- },
- "clipboard": {
- "unavailable": "Clipboard history unavailable",
- "unavailable-desc": "The 'cliphist' application is not installed. Please install it to use clipboard history features."
- },
- "ipc": {
- "powerpanel-deprecated": "PowerPanel has been renamed to SessionMenu, this IPC call will be deprecated soon. Please use \"ipc call sessionMenu toggle\" instead.",
- "sidepanel-deprecated": "SidePanel has been renamed to ControlCenter, this IPC call will be deprecated soon. Please use \"ipc call controlCenter toggle\" instead."
- },
- "wifi": {
- "enabled": "Enabled",
- "disabled": "Disabled",
- "connected": "Connected to '{ssid}'",
- "disconnected": "Disconnected from '{ssid}'"
- },
- "bluetooth": {
- "enabled": "Enabled",
- "disabled": "Disabled"
- },
"airplane-mode": {
- "title": "Airplane mode",
+ "disabled": "Disabled",
"enabled": "Enabled",
- "disabled": "Disabled"
- },
- "internet": {
- "limited": "Connected without internet"
- },
- "kofi": {
- "opened": "Ko-fi page opened in your browser"
- },
- "do-not-disturb": {
- "enabled": "'Do not disturb' enabled",
- "disabled": "'Do not disturb' disabled",
- "enabled-desc": "You'll find these notifications in your history.",
- "disabled-desc": "Showing all notifications."
- },
- "power-profile": {
- "changed": "Power profile changed",
- "profile-name": "\"{profile}\""
+ "title": "Airplane mode"
},
"battery": {
"low": "Low battery",
"low-desc": "Battery is at {percent}%. Please connect the charger."
},
"battery-manager": {
- "title": "Battery threshold",
- "set-success-desc": "Battery threshold set to {percent}%",
"initial-setup": "Initial setup required",
- "set-failed": "Failed to set battery threshold",
- "install-success": "Installed successfully",
- "install-missing": "Required files are missing",
- "install-unsupported": "System is not supported",
"install-failed": "Installation failed",
+ "install-missing": "Required files are missing",
+ "install-success": "Installed successfully",
+ "install-unsupported": "System is not supported",
+ "set-failed": "Failed to set battery threshold",
+ "set-success-desc": "Battery threshold set to {percent}%",
+ "title": "Battery threshold",
+ "uninstall-failed": "Uninstallation failed",
"uninstall-setup": "Uninstalling, authentication required",
- "uninstall-success": "Uninstalled successfully",
- "uninstall-failed": "Uninstallation failed"
+ "uninstall-success": "Uninstalled successfully"
+ },
+ "bluetooth": {
+ "disabled": "Disabled",
+ "enabled": "Enabled"
+ },
+ "clipboard": {
+ "unavailable": "Clipboard history unavailable",
+ "unavailable-desc": "The 'cliphist' application is not installed. Please install it to use clipboard history features."
+ },
+ "do-not-disturb": {
+ "disabled": "'Do not disturb' disabled",
+ "disabled-desc": "Showing all notifications.",
+ "enabled": "'Do not disturb' enabled",
+ "enabled-desc": "You'll find these notifications in your history."
+ },
+ "internet": {
+ "limited": "Connected without internet"
+ },
+ "ipc": {
+ "powerpanel-deprecated": "PowerPanel has been renamed to SessionMenu, this IPC call will be deprecated soon. Please use \"ipc call sessionMenu toggle\" instead.",
+ "sidepanel-deprecated": "SidePanel has been renamed to ControlCenter, this IPC call will be deprecated soon. Please use \"ipc call controlCenter toggle\" instead."
+ },
+ "keep-awake": {
+ "disabled": "Disabled",
+ "enabled": "Enabled"
+ },
+ "kofi": {
+ "opened": "Ko-fi page opened in your browser"
},
"missing-control-center": {
- "label": "Last Control Center widget removed",
- "description": "The Control Center widget has been removed from the bar. To access it from the bar again, you will need to re-add the widget. You can open it with right clicking on the bar too."
+ "description": "The Control Center widget has been removed from the bar. To access it from the bar again, you will need to re-add the widget. You can open it with right clicking on the bar too.",
+ "label": "Last Control Center widget removed"
+ },
+ "night-light": {
+ "disabled": "Disabled",
+ "enabled": "Enabled",
+ "forced": "Forced activation",
+ "normal": "Normal mode",
+ "not-installed": "wlsunset not installed"
+ },
+ "noctalia-performance": {
+ "disabled": "Performance mode disabled",
+ "enabled": "Performance mode enabled",
+ "label": "Noctalia performance"
+ },
+ "power-profile": {
+ "changed": "Power profile changed",
+ "profile-name": "\"{profile}\""
+ },
+ "recording": {
+ "failed-general": "The recorder exited with an error.",
+ "failed-gpu": "gpu-screen-recorder exited unexpectedly.",
+ "failed-start": "Failed to start recording",
+ "no-portals": "Desktop portals not running",
+ "no-portals-desc": "Start xdg-desktop-portal and a compositor portal (wlr/hyprland/gnome/kde).",
+ "not-installed": "gpu-screen-recorder not installed",
+ "not-installed-desc": "Please install gpu-screen-recorder to use screen recording features.",
+ "saved": "Recording saved",
+ "started": "Recording started",
+ "stopping": "Stopping recording…"
+ },
+ "theming-processor-failed": {
+ "desc-generic": "Encountered an error while processing templates",
+ "title-matugen": "Matugen templating processing failed",
+ "title-predefined": "Predefined color cheme processing failed"
+ },
+ "wallpaper-colors": {
+ "disabled": "Wallpaper colors disabled",
+ "enabled": "Wallpaper colors enabled",
+ "label": "Wallpaper colors generation",
+ "not-installed": "Matugen not installed - required for wallpaper color extraction"
+ },
+ "wifi": {
+ "connected": "Connected to '{ssid}'",
+ "disabled": "Disabled",
+ "disconnected": "Disconnected from '{ssid}'",
+ "enabled": "Enabled"
}
},
+ "tooltips": {
+ "add-widget": "Add widget",
+ "bluetooth-devices": "Bluetooth devices",
+ "brightness-at": "Brightness: {brightness}%\nRight click for settings.\nScroll to modify brightness.",
+ "cancel-timer": "Cancel timer",
+ "clear-history": "Clear history",
+ "click-to-start-recording": "Click to start recording",
+ "click-to-stop-recording": "Click to stop recording",
+ "close": "Close",
+ "connect-disconnect-devices": "Left click to connect. Right click to forget.",
+ "delete-notification": "Delete notification",
+ "disable-keep-awake": "Disable keep awake",
+ "do-not-disturb-disabled": "'Do not disturb' disabled",
+ "do-not-disturb-enabled": "'Do not disturb' enabled",
+ "enable-keep-awake": "Enable keep awake",
+ "forget-network": "Forget network",
+ "home": "Home",
+ "input-muted": "Toggle input mute",
+ "keep-awake": "Keep awake",
+ "keyboard-layout": "{layout} keyboard layout",
+ "manage-wifi": "Manage Wi-Fi",
+ "microphone-volume-at": "Microphone volume at {volume}%\nLeft click for settings. Right click to toggle mute.\nScroll to modify volume.",
+ "move-to-center-section": "Move to center section",
+ "move-to-left-section": "Move to left section",
+ "move-to-right-section": "Move to right section",
+ "next-media": "Next media",
+ "next-month": "Next month",
+ "night-light-disabled": "Night light is disabled.\nLeft click to cycle mode.\nRight click to access settings.",
+ "night-light-enabled": "Night light is enabled.\nLeft click to cycle mode.\nRight click to access settings.",
+ "night-light-forced": "Night light is forced.\nLeft click to cycle mode.\nRight click to access settings.",
+ "night-light-not-installed": "Night light is not available.\nwlsunset is not installed.",
+ "noctalia-performance-disabled": "Noctalia performance mode is disabled.\nLeft click to enable.",
+ "noctalia-performance-enabled": "Noctalia performance mode is enabled.\nLeft click to disable.",
+ "open-control-center": "Open control center",
+ "open-notification-history-disable-dnd": "Open notification history\nRight-click to disable \"Do not disturb\".",
+ "open-notification-history-enable-dnd": "Open notification history\nRight-click to enable \"Do not disturb\".",
+ "open-settings": "Open settings",
+ "open-tray-dropdown": "Open tray dropdown",
+ "open-wallpaper-selector": "Open wallpaper selector",
+ "output-muted": "Toggle output mute",
+ "pause": "Pause",
+ "play": "Play",
+ "power-profile": "'{profile}' power profile",
+ "previous-media": "Previous media",
+ "previous-month": "Previous month",
+ "refresh": "Refresh",
+ "refresh-devices": "Refresh devices",
+ "refresh-wallpaper-list": "Refresh wallpaper list",
+ "remove-widget": "Remove widget",
+ "screen-recorder-not-installed": "Screen recorder is not installed",
+ "session-menu": "Session Menu",
+ "set-power-profile": "Set \"{profile}\" power profile",
+ "start-screen-recording": "Start screen recording",
+ "stop-screen-recording": "Stop screen recording",
+ "switch-to-dark-mode": "Switch to dark mode",
+ "switch-to-light-mode": "Switch to light mode",
+ "up": "Up",
+ "volume-at": "Output volume at {volume}%\nLeft click for settings. Right click to toggle mute.\nScroll to modify volume.",
+ "wallpaper-selector": "Left click: Open wallpaper selector.\nRight click: Set random wallpaper.",
+ "widget-settings": "Widget settings"
+ },
+ "wallpaper": {
+ "configure-directory": "Configure your wallpaper directory with images.",
+ "fill-modes": {
+ "center": "Center",
+ "crop": "Crop (Fill)",
+ "fit": "Fit (Contain)",
+ "stretch": "Stretch"
+ },
+ "no-match": "No match found.",
+ "no-wallpaper": "No wallpaper found.",
+ "panel": {
+ "apply-all-monitors": {
+ "description": "Apply selected wallpaper to all monitors at once.",
+ "label": "Apply to all monitors"
+ },
+ "search": "Search:",
+ "title": "Wallpaper selector"
+ },
+ "transitions": {
+ "disc": "Disc",
+ "fade": "Fade",
+ "none": "None",
+ "random": "Random",
+ "stripes": "Stripes",
+ "wipe": "Wipe"
+ },
+ "try-different-search": "Try a different search query."
+ },
"weather": {
"clear-sky": "Clear sky",
- "mainly-clear": "Mainly clear",
- "partly-cloudy": "Partly cloudy",
- "overcast": "Overcast",
- "fog": "Fog",
"drizzle": "Drizzle",
- "snow": "Snow",
+ "fog": "Fog",
+ "mainly-clear": "Mainly clear",
+ "overcast": "Overcast",
+ "partly-cloudy": "Partly cloudy",
"rain-showers": "Rain showers",
+ "snow": "Snow",
"thunderstorm": "Thunderstorm",
"unknown": "Unknown"
},
- "authentication": {
- "failed": "Authentication failed",
- "error": "Authentication error"
- },
- "general": {
- "no-results": "No results",
- "no-summary": "No summary",
- "unknown": "Unknown"
- },
- "battery": {
- "no-battery-detected": "No battery detected.",
- "charging-rate": "Charging rate: {rate} W.",
- "discharging-rate": "Discharging rate: {rate} W.",
- "charging": "Charging.",
- "discharging": "Discharging.",
- "plugged-in": "Plugged in.",
- "idle": "Idle.",
- "time-left": "Time left: {time}.",
- "time-until-full": "Time until full: {time}.",
- "health": "Health: {percent}%",
- "panel": {
- "title": "Charge threshold",
- "full": "Full capacity ({percent}%)",
- "balanced": "Balanced ({percent}%)",
- "lifespan": "Extended lifespan ({percent}%)",
- "disabled": "Battery manager disabled"
- }
- },
- "setup": {
- "customize": {
- "header": "Customize your experience",
- "subheader": "Adjust bar position, density, scaling and more."
+ "widgets": {
+ "color-picker": {
+ "apply": "Apply",
+ "brightness": "Brightness",
+ "cancel": "Cancel",
+ "hex": {
+ "description": "Enter a hexadecimal color code.",
+ "label": "Hex color"
+ },
+ "palette": {
+ "description": "Choose from a wide range of predefined colors.",
+ "label": "Palette"
+ },
+ "rgb": {
+ "description": "Adjust red, green, blue, and brightness values.",
+ "label": "RGB values"
+ },
+ "theme-colors": {
+ "description": "Quick access to your theme's color palette.",
+ "label": "Theme colors"
+ },
+ "title": "Color picker"
},
- "appearance": {
- "header": "Appearance",
- "subheader": "Choose dark mode and color sources (Matugen or predefined)."
- },
- "wallpaper": {
- "header": "Choose your wallpaper",
- "subheader": "Set the mood with a beautiful background.",
- "select-prompt": "Select a wallpaper below",
- "preview-error": "Failed to load image",
- "none-in-dir": "No wallpapers found in directory",
- "no-dir": "No wallpaper directory selected",
- "no-valid": "No valid image files found in: {dir}",
- "choose-dir": "Choose a directory containing your wallpaper images",
- "dir": {
- "label": "Wallpaper directory",
- "description": "Choose the folder containing your wallpapers",
- "browse": "Browse for wallpaper folder",
- "select-title": "Select wallpaper folder"
+ "datetime-tokens": {
+ "ampm": {
+ "lowercase": "am/pm in lowercase",
+ "uppercase": "AM/PM in uppercase"
+ },
+ "common": {
+ "12hour-time-minutes": "12-hour time with minutes",
+ "24hour-time-minutes": "24-hour time with minutes",
+ "24hour-time-seconds": "24-hour time with seconds",
+ "european-date": "European date format",
+ "iso-date": "ISO date format",
+ "us-date": "US date format",
+ "weekday-date": "Weekday with date",
+ "weekday-month-day": "Weekday, month and day"
+ },
+ "day": {
+ "abbreviated": "Abbreviated day name",
+ "full": "Full day name",
+ "leading-zero": "Day with leading zero (01-31)",
+ "no-leading-zero": "Day without leading zero (1-31)"
+ },
+ "hour": {
+ "leading-zero": "Hour with leading zero (00-23) - 24-hour format",
+ "no-leading-zero": "Hour without leading zero (0-23) - 24-hour format"
+ },
+ "minute": {
+ "leading-zero": "Minute with leading zero (00-59)",
+ "no-leading-zero": "Minute without leading zero (0-59)"
+ },
+ "month": {
+ "abbreviated": "Abbreviated month name",
+ "full": "Full month name",
+ "number-leading-zero": "Month as number with leading zero (01-12)",
+ "number-no-zero": "Month as number without leading zero (1-12)"
+ },
+ "second": {
+ "leading-zero": "Second with leading zero (00-59)",
+ "no-leading-zero": "Second without leading zero (0-59)"
+ },
+ "timezone": {
+ "abbreviation": "Timezone abbreviation"
+ },
+ "year": {
+ "four-digit": "Year as four-digit number",
+ "two-digit": "Year as two-digit number (00-99)"
}
},
- "welcome": {
- "note": "Just a few basics to get you started - full options are in Settings"
+ "file-picker": {
+ "cancel": "Cancel",
+ "search-placeholder": "Search files and folders...",
+ "select-current": "Select Current",
+ "select-file": "Select File",
+ "select-folder": "Select Folder",
+ "title": "File Picker"
+ },
+ "icon-picker": {
+ "apply": "Apply",
+ "cancel": "Cancel",
+ "search": {
+ "label": "Search"
+ },
+ "title": "Icon picker"
+ },
+ "text-input": {
+ "clear": "Clear"
+ },
+ "tooltip": {
+ "placeholder": "Placeholder"
+ }
+ },
+ "wifi": {
+ "panel": {
+ "connect": "Connect",
+ "connected": "Connected",
+ "disabled": "Wi-Fi is disabled",
+ "disconnect": "Disconnect",
+ "disconnecting": "Disconnecting...",
+ "enable-message": "Enable Wi-Fi to see available networks.",
+ "enter-password": "Enter password...",
+ "forget": "Forget",
+ "forget-network": "Forget this network?",
+ "forgetting": "Forgetting...",
+ "no-networks": "No networks found",
+ "password": "Password",
+ "saved": "Saved",
+ "scan-again": "Scan again",
+ "searching": "Searching for nearby networks...",
+ "title": "Wi-Fi"
}
}
}
diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json
index 3f9c3773..9f9f5e20 100644
--- a/Assets/Translations/es.json
+++ b/Assets/Translations/es.json
@@ -1,1232 +1,277 @@
{
- "settings": {
- "general": {
- "title": "General",
- "profile": {
- "section": {
- "label": "Perfil",
- "description": "Edita los detalles de tu usuario y tu avatar."
- },
- "picture": {
- "label": "Foto de perfil de {user}",
- "description": "Tu foto de perfil que aparece en toda la interfaz."
- },
- "select-avatar": "Seleccionar imagen de avatar"
- },
- "screen-corners": {
- "section": {
- "label": "Esquinas de la pantalla",
- "description": "Personaliza el redondeo de las esquinas de la pantalla y los efectos visuales."
- },
- "show-corners": {
- "label": "Mostrar esquinas de la pantalla",
- "description": "Muestra esquinas redondeadas en el borde de la pantalla."
- },
- "solid-black": {
- "label": "Esquinas negras sólidas",
- "description": "Usa negro sólido en lugar del color de fondo de la barra."
- },
- "radius": {
- "label": "Radio de las esquinas de la pantalla",
- "description": "Ajusta las esquinas redondeadas de la pantalla.",
- "reset": "Restablecer el radio de las esquinas de la pantalla"
- }
- },
- "fonts": {
- "section": {
- "label": "Fuentes",
- "description": "Elige las fuentes utilizadas en toda la interfaz."
- },
- "default": {
- "label": "Fuente predeterminada",
- "description": "Fuente principal utilizada en toda la interfaz.",
- "placeholder": "Seleccionar fuente predeterminada...",
- "search-placeholder": "Buscar fuentes...",
- "scale": {
- "description": "Aumentar o disminuir el tamaño del texto estándar.",
- "label": "Tamaño de fuente predeterminado"
- }
- },
- "monospace": {
- "label": "Fuente monoespaciada",
- "description": "Fuente monoespaciada utilizada para la visualización de números y estadísticas.",
- "placeholder": "Seleccionar fuente monoespaciada...",
- "search-placeholder": "Buscar fuentes monoespaciadas...",
- "scale": {
- "description": "Aumentar o disminuir el tamaño del texto monoespaciado.",
- "label": "Tamaño de fuente monoespaciada"
- }
- },
- "reset-scaling": "Restablecer la escala"
- },
- "language": {
- "section": {
- "label": "Idioma",
- "description": "Elige tu idioma preferido para la aplicación."
- },
- "select": {
- "label": "Idioma de la aplicación",
- "description": "Selecciona el idioma utilizado en la interfaz de la aplicación.",
- "auto-detect": "Automático"
- }
- },
- "launch-setup-wizard": "Inicie el asistente de configuración"
- },
- "audio": {
- "title": "Audio",
- "volumes": {
- "section": {
- "label": "Volúmenes",
- "description": "Ajusta los controles de volumen y los niveles de audio."
- },
- "output-volume": {
- "label": "Volumen de salida",
- "description": "Nivel de volumen de todo el sistema."
- },
- "mute-output": {
- "label": "Silenciar salida de audio",
- "description": "Silencia la salida de audio principal del sistema."
- },
- "input-volume": {
- "label": "Volumen de entrada",
- "description": "Nivel de volumen de entrada del micrófono."
- },
- "mute-input": {
- "label": "Silenciar entrada de audio",
- "description": "Silencia la entrada de audio predeterminada (micrófono)."
- },
- "step-size": {
- "label": "Tamaño del paso de volumen",
- "description": "Ajusta el tamaño del paso para los cambios de volumen (rueda de desplazamiento, atajos de teclado)."
- },
- "volume-overdrive": {
- "label": "Permitir sobreamplificación de volumen",
- "description": "Permite subir el volumen por encima del 100 %. Es posible que no sea compatible con todo el hardware."
- }
- },
- "devices": {
- "section": {
- "label": "Dispositivos de audio",
- "description": "Configura los dispositivos de entrada y salida de audio disponibles."
- },
- "output-device": {
- "label": "Dispositivo de salida",
- "description": "Selecciona el dispositivo de salida de audio deseado."
- },
- "input-device": {
- "label": "Dispositivo de entrada",
- "description": "Selecciona el dispositivo de entrada de audio deseado."
- }
- },
- "media": {
- "section": {
- "label": "Reproductores multimedia",
- "description": "Establece tus aplicaciones multimedia preferidas e ignoradas."
- },
- "primary-player": {
- "label": "Reproductor principal",
- "description": "Ingresa una palabra clave para identificar tu reproductor principal.",
- "placeholder": "ej. spotify, vlc, mpv"
- },
- "excluded-player": {
- "label": "Reproductor excluido",
- "description": "Agrega palabras clave para los reproductores que deseas que el sistema ignore. Cada palabra clave debe estar en una línea nueva.",
- "placeholder": "escribe una subcadena y presiona +"
- },
- "visualizer-type": {
- "label": "Tipo de visualización",
- "description": "Elige un tipo de visualización para la reproducción de medios."
- },
- "frame-rate": {
- "label": "Tasa de fotogramas",
- "description": "Tasas más altas son más fluidas pero usan más recursos."
- },
- "scrolling-title": {
- "label": "Título con desplazamiento",
- "description": "Activar desplazamiento continuo para títulos de medios largos"
- },
- "scrolling-speed": {
- "label": "Velocidad de desplazamiento",
- "description": "Tiempo en segundos para que el título se desplace del inicio al final"
- }
- }
- },
- "display": {
- "title": "Pantalla",
- "monitors": {
- "section": {
- "label": "Configuración por monitor",
- "description": "Ajustar el brillo de la configuración para cada pantalla."
- },
- "brightness": "Brillo",
- "brightness-step": {
- "label": "Tamaño del paso de brillo",
- "description": "Ajusta el tamaño del paso para los cambios de brillo (rueda de desplazamiento y atajos de teclado)."
- },
- "enforce-minimum": {
- "label": "Forzar brillo mínimo (1%)",
- "description": "Resuelve el problema de la retroiluminación que se apaga completamente en algunas pantallas al 0% de brillo."
- }
- },
- "night-light": {
- "section": {
- "label": "Luz nocturna",
- "description": "Reduce la emisión de luz azul para ayudarte a dormir mejor y reducir la fatiga visual."
- },
- "enable": {
- "label": "Activar luz nocturna",
- "description": "Aplica un filtro de color cálido para reducir la emisión de luz azul."
- },
- "temperature": {
- "label": "Temperatura de color",
- "description": "Establece la calidez del color para la noche y el día.",
- "night": "Noche",
- "day": "Día"
- },
- "auto-schedule": {
- "label": "Programación automática",
- "description": "Basado en la hora del atardecer y amanecer en {location} - recomendado."
- },
- "manual-schedule": {
- "label": "Programación manual",
- "description": "Establece horas personalizadas para el amanecer y el atardecer.",
- "sunrise": "Hora del amanecer",
- "sunset": "Hora del atardecer",
- "select-start": "Seleccionar hora de inicio",
- "select-stop": "Seleccionar hora de finalización"
- },
- "force-activation": {
- "label": "Forzar activación",
- "description": "Ignora la programación y aplica el filtro nocturno inmediatamente."
- }
- }
- },
- "bar": {
- "title": "Barra",
- "appearance": {
- "section": {
- "label": "Apariencia",
- "description": "Personaliza la apariencia y posición de la barra."
- },
- "position": {
- "label": "Posición de la barra",
- "description": "Elige dónde colocar la barra en la pantalla."
- },
- "density": {
- "label": "Densidad de la barra",
- "description": "Ajusta el relleno de la barra para un aspecto compacto o espacioso."
- },
- "background-opacity": {
- "label": "Opacidad del fondo",
- "description": "Ajusta la opacidad del fondo de la barra."
- },
- "show-capsule": {
- "label": "Mostrar cápsula",
- "description": "Mostrar fondos de los widgets."
- },
- "floating": {
- "label": "Barra flotante",
- "description": "Muestra la barra como una 'píldora' flotante. Nota: Esto moverá las esquinas de la pantalla a los bordes."
- },
- "margins": {
- "label": "Márgenes",
- "description": "Ajusta los márgenes alrededor de la barra flotante.",
- "vertical": "Vertical",
- "horizontal": "Horizontal"
- }
- },
- "widgets": {
- "section": {
- "label": "Posicionamiento de widgets",
- "description": "Arrastra y suelta widgets para reordenarlos. Las insignias indican el uso: [I]zquierda, [C]entro, [D]erecha."
- }
- },
- "monitors": {
- "section": {
- "label": "Visualización en monitores",
- "description": "Muestra la barra en monitores específicos. Por defecto, se muestra en todos si no se elige ninguno."
- }
- },
- "tray": {
- "blacklist": {
- "label": "Lista negra",
- "description": "Agregar reglas de exclusión de la bandeja, admite comodines (*).",
- "placeholder": "ej., nm-applet, Fcitx*"
- }
- }
- },
- "dock": {
- "title": "Dock",
- "enabled": {
- "label": "Habilitar dock",
- "description": "Mostrar u ocultar el dock por completo"
- },
- "appearance": {
- "section": {
- "label": "Apariencia",
- "description": "Personaliza el comportamiento y la apariencia del dock."
- },
- "display": {
- "label": "Pantalla",
- "description": "Elige cómo se comporta el dock.",
- "always-visible": "Siempre visible",
- "auto-hide": "Ocultar automáticamente",
- "exclusive": "Exclusivo"
- },
- "background-opacity": {
- "label": "Opacidad del fondo",
- "description": "Ajusta la opacidad del fondo del dock."
- },
- "floating-distance": {
- "label": "Distancia de flotación del dock",
- "description": "Ajusta la distancia de flotación desde el borde de la pantalla."
- },
- "icon-size": {
- "label": "Tamaño del Dock",
- "description": "Ajusta el tamaño general del Dock."
- },
- "colorize-icons": {
- "label": "Colorear iconos",
- "description": "Aplicar colores del tema a los iconos de aplicaciones del dock (solo aplicaciones no enfocadas)."
- }
- },
- "monitors": {
- "section": {
- "label": "Visualización en monitor",
- "description": "Mostrar el dock en monitores específicos. Por defecto, se muestra en todos si no se elige ninguno."
- },
- "only-same-output": {
- "description": "Mostrar solo las aplicaciones de la salida donde se encuentra el dock.",
- "label": "Solo aplicaciones de la misma salida"
- }
- }
- },
- "launcher": {
- "title": "Lanzador",
- "settings": {
- "section": {
- "label": "Apariencia",
- "description": "Personaliza el comportamiento y la apariencia del lanzador."
- },
- "position": {
- "label": "Posición",
- "description": "Elige dónde aparece el panel del lanzador."
- },
- "background-opacity": {
- "label": "Opacidad del fondo",
- "description": "Ajusta la opacidad del fondo del lanzador."
- },
- "clipboard-history": {
- "label": "Activar historial del portapapeles",
- "description": "Accede a los elementos copiados anteriormente desde el lanzador."
- },
- "sort-by-usage": {
- "label": "Ordenar por más usados",
- "description": "Cuando está activado, las aplicaciones más utilizadas aparecen primero en la lista."
- },
- "use-app2unit": {
- "label": "Usar App2Unit para lanzar aplicaciones",
- "description": "Usa un método de lanzamiento alternativo para gestionar mejor los procesos de las aplicaciones y prevenir problemas."
- },
- "terminal-command": {
- "label": "Comando de terminal",
- "description": "Comando para iniciar un terminal. Por ejemplo, 'kitty -e' o 'gnome-terminal --'."
- },
- "custom-launch-prefix": {
- "label": "Prefijo de lanzamiento personalizado",
- "description": "Prefijar comandos con un lanzador personalizado (ej. 'runapp' para integración con systemd)."
- },
- "custom-launch-prefix-enabled": {
- "label": "Habilitar prefijo de lanzamiento personalizado",
- "description": "Usar un prefijo personalizado para lanzar aplicaciones en lugar del método predeterminado."
- }
- }
- },
- "notifications": {
- "title": "Notificaciones",
- "settings": {
- "section": {
- "label": "Apariencia",
- "description": "Configura la apariencia y el comportamiento de las notificaciones."
- },
- "do-not-disturb": {
- "label": "No molestar",
- "description": "Desactiva todas las ventanas emergentes de notificación cuando está activado."
- },
- "enable-osd": {
- "label": "Activar visualización en pantalla",
- "description": "Muestra los cambios de volumen y brillo en tiempo real."
- },
- "location": {
- "label": "Ubicación",
- "description": "Dónde aparecen las notificaciones en la pantalla."
- },
- "always-on-top": {
- "label": "Siempre visible",
- "description": "Muestra las notificaciones sobre ventanas a pantalla completa y otras capas."
- },
- "background-opacity": {
- "label": "Opacidad del fondo",
- "description": "Ajustar la opacidad de los fondos de las notificaciones."
- }
- },
- "duration": {
- "section": {
- "label": "Duración de la notificación",
- "description": "Configura cuánto tiempo permanecen visibles las notificaciones según su nivel de urgencia."
- },
- "respect-expire": {
- "label": "Respetar tiempo de expiración",
- "description": "Usa el tiempo de expiración establecido en la notificación."
- },
- "low-urgency": {
- "label": "Urgencia baja",
- "description": "Cuánto tiempo permanecen visibles las notificaciones de baja prioridad."
- },
- "normal-urgency": {
- "label": "Urgencia normal",
- "description": "Cuánto tiempo permanecen visibles las notificaciones de prioridad normal."
- },
- "critical-urgency": {
- "label": "Urgencia crítica",
- "description": "Cuánto tiempo permanecen visibles las notificaciones de prioridad crítica."
- }
- },
- "monitors": {
- "section": {
- "label": "Visualización en monitores",
- "description": "Muestra notificaciones en monitores específicos. Por defecto, se muestra en todos si no se elige ninguno."
- }
- }
- },
- "osd": {
- "title": "Visualización en pantalla",
- "description": "Configura indicadores superpuestos como volumen y brillo.",
- "section": {
- "general": {
- "label": "General",
- "description": "Configura visibilidad y comportamiento del OSD."
- }
- },
- "enabled": {
- "label": "Activar visualización en pantalla",
- "description": "Mostrar cambios de volumen y brillo en tiempo real."
- },
- "always-on-top": {
- "label": "Siempre encima",
- "description": "Mostrar OSD por encima de ventanas de pantalla completa y otras capas."
- },
- "location": {
- "label": "Ubicación",
- "description": "Dónde aparece la visualización en pantalla."
- },
- "duration": {
- "section": {
- "label": "Tiempo de ocultación automática",
- "description": "Cuánto tiempo permanece visible el OSD antes de ocultarse automáticamente."
- },
- "auto-hide": {
- "label": "Ocultar después de",
- "description": "Ajusta el tiempo antes de que el OSD desaparezca."
- }
- },
- "monitors": {
- "section": {
- "label": "Visualización en monitores",
- "description": "Mostrar el OSD en monitores específicos. Por defecto, en todos si no se elige ninguno."
- }
- }
- },
- "wallpaper": {
- "title": "Fondo de pantalla",
- "settings": {
- "section": {
- "label": "Configuración del fondo de pantalla",
- "description": "Controla cómo se gestionan y muestran los fondos de pantalla."
- },
- "enable-management": {
- "label": "Activar gestión de fondos de pantalla",
- "description": "Gestiona fondos de pantalla con Noctalia. Desmarca si prefieres usar otra aplicación."
- },
- "folder": {
- "label": "Carpeta de fondos de pantalla",
- "description": "Ruta a tu carpeta principal de fondos de pantalla.",
- "tooltip": "Buscar carpeta de fondos de pantalla"
- },
- "recursive-search": {
- "label": "Buscar en subcarpetas",
- "description": "Buscar fondos de pantalla también en subcarpetas del directorio de fondos de pantalla."
- },
- "monitor-specific": {
- "label": "Directorios específicos por monitor",
- "description": "Establece una carpeta de fondos de pantalla diferente para cada monitor.",
- "tooltip": "Buscar carpeta de fondos de pantalla"
- },
- "select-folder": "Seleccionar carpeta de fondos de pantalla",
- "select-monitor-folder": "Seleccionar carpeta de fondos de pantalla del monitor"
- },
- "look-feel": {
- "section": {
- "label": "Apariencia y estilo"
- },
- "fill-mode": {
- "label": "Modo de relleno",
- "description": "Selecciona cómo debe escalar la imagen para coincidir con la resolución de tu monitor."
- },
- "fill-color": {
- "label": "Color de relleno",
- "description": "Elige un color de relleno que pueda aparecer detrás del fondo de pantalla."
- },
- "transition-type": {
- "label": "Tipo de transición",
- "description": "Tipo de animación al cambiar entre fondos de pantalla."
- },
- "transition-duration": {
- "label": "Duración de la transición",
- "description": "Duración de las animaciones de transición en segundos."
- },
- "edge-smoothness": {
- "label": "Suavizar borde de la transición",
- "description": "Aplica un efecto suave y difuminado al borde de las transiciones."
- }
- },
- "automation": {
- "section": {
- "label": "Automatización"
- },
- "random-wallpaper": {
- "label": "Fondo de pantalla aleatorio",
- "description": "Programa cambios aleatorios de fondo de pantalla a intervalos regulares."
- },
- "interval": {
- "label": "Intervalo del fondo de pantalla",
- "description": "Con qué frecuencia cambiar los fondos de pantalla automáticamente."
- },
- "custom-interval": {
- "label": "Intervalo personalizado",
- "description": "Ingresa el tiempo como HH:MM (ej., 01:30)."
- }
- }
- },
- "color-scheme": {
- "title": "Esquema de colores",
- "color-source": {
- "section": {
- "label": "Fuente de color",
- "description": "Configuración principal de los colores de Noctalia."
- },
- "use-wallpaper-colors": {
- "label": "Usar colores del fondo de pantalla",
- "description": "Generar esquemas de color desde tu fondo de pantalla usando Matugen. Extrae automáticamente colores para crear un tema cohesivo."
- },
- "matugen-scheme-type": {
- "description": {
- "scheme-content": "Deriva colores que coinciden estrechamente con la imagen subyacente.",
- "scheme-expressive": "Paleta vibrante con saturación juguetona.",
- "scheme-fidelity": "Paleta de alta fidelidad que preserva los tonos originales",
- "scheme-fruit-salad": "Mezcla colorida de acentos brillantes y contrastantes.",
- "scheme-monochrome": "Paleta minimalista construida en torno a un solo tono.",
- "scheme-neutral": "Paleta apagada con tonos suaves y relajantes.",
- "scheme-rainbow": "Paleta diversa que abarca todo el espectro.",
- "scheme-tonal-spot": "Paleta equilibrada con acentos definidos."
- },
- "label": "Tipo de esquema Matugen"
- }
- },
- "dark-mode": {
- "switch": {
- "label": "Modo oscuro",
- "description": "Cambia a un tema más oscuro para una visualización más fácil por la noche."
- },
- "mode": {
- "description": "Permite el cambio automático entre el modo claro y el modo oscuro.",
- "label": "Programación del modo oscuro",
- "location": "Ubicación",
- "manual": "Manual",
- "off": "Apagado"
- }
- },
- "predefined": {
- "section": {
- "label": "Esquemas de colores predefinidos",
- "description": "Elige entre una colección de esquemas de colores predefinidos."
- },
- "generate-templates": {
- "label": "Generar plantillas para esquemas predefinidos",
- "description": "Genera plantillas de Matugen (GTK, temas de terminal, etc.) al usar esquemas de colores predefinidos."
- }
- },
- "templates": {
- "ui": {
- "label": "UI",
- "description": "Tematización del entorno de escritorio y de la interfaz de usuario.",
- "gtk": {
- "description": "Escribir {filepath}"
- },
- "qt": {
- "description": "Escribir {filepath}"
- },
- "kcolorscheme": {
- "description": "Escribir {filepath}"
- }
- },
- "terminal": {
- "label": "Terminal",
- "description": "Tematización del emulador de terminal.",
- "kitty": {
- "description": "Escribir {filepath} y recargar",
- "description-missing": "Requiere que {app} esté instalado"
- },
- "ghostty": {
- "description": "Escribir {filepath} y recargar",
- "description-missing": "Requiere que {app} esté instalado"
- },
- "foot": {
- "description": "Escribir {filepath} y recargar",
- "description-missing": "Requiere que {app} esté instalado"
- }
- },
- "programs": {
- "label": "Programas",
- "description": "Tematización específica de aplicaciones.",
- "fuzzel": {
- "description": "Escribir {filepath} y recargar",
- "description-missing": "Requiere que {app} esté instalado"
- },
- "vicinae": {
- "description": "Escribir {filepath} y recargar",
- "description-missing": "Requiere que {app} esté instalado"
- },
- "walker": {
- "description": "Escribir {filepath} y establecer tema a noctalia",
- "description-missing": "Requiere que {app} esté instalado"
- },
- "discord": {
- "description": "Escribir {filepath} para {client}",
- "description-missing": "No se detectó cliente de Discord. Instala vencord, vesktop, webcord, armcord, equibop, lightcord o dorion."
- },
- "pywalfox": {
- "description": "Escribir {filepath} y ejecutar pywalfox update",
- "description-missing": "Requiere que {app} esté instalado"
- },
- "code": {
- "description": "Escribe {filepath}. El tema Hyprluna debe ser instalado y activado manualmente.",
- "description-missing": "Requiere que {app} esté instalado/a."
- }
- },
- "misc": {
- "label": "Varios",
- "description": "Opciones de configuración adicionales.",
- "user-templates": {
- "label": "Plantillas de usuario",
- "description": "Habilitar configuración de Matugen definida por el usuario. Se creará un archivo de plantilla en ~/.config/noctalia/user-templates.toml al activar por primera vez"
- }
- },
- "section": {
- "description": "Aplicar colores a aplicaciones externas.",
- "label": "Plantillas"
- }
- }
- },
- "location": {
- "title": "Ubicación",
- "location": {
- "section": {
- "label": "Tu ubicación",
- "description": "Obtén el clima y la programación de la luz nocturna precisos estableciendo tu ubicación."
- },
- "search": {
- "label": "Buscar una ubicación",
- "description": "ej., Toronto, ON",
- "placeholder": "Ingresa el nombre de la ubicación"
- }
- },
- "weather": {
- "section": {
- "label": "Clima",
- "description": "Elige tu unidad de temperatura preferida."
- },
- "enabled": {
- "label": "Activar clima",
- "description": "Mostrar información del clima en toda la interfaz y obtener datos meteorológicos. Cuando está desactivado, todos los elementos del clima estarán ocultos y no se realizarán solicitudes de red."
- },
- "fahrenheit": {
- "label": "Mostrar temperatura en Fahrenheit (°F)",
- "description": "Muestra la temperatura en Fahrenheit en lugar de Celsius."
- },
- "show-in-calendar": {
- "description": "Muestra el pronóstico del tiempo diario directamente en la vista de tu calendario.",
- "label": "Mostrar el clima en el calendario"
- }
- },
- "date-time": {
- "section": {
- "label": "Fecha y hora",
- "description": "Personaliza cómo aparecen la fecha y la hora."
- },
- "12hour-format": {
- "label": "Utilice el formato de hora de 12 horas",
- "description": "Muestra la hora en formato de 12 horas en la pantalla de bloqueo y el calendario. El reloj de la barra tiene su propia configuración."
- },
- "week-numbers": {
- "label": "Mostrar números de semana",
- "description": "Muestra la semana del año (ej., Semana 38) en el calendario."
- },
- "show-events": {
- "label": "Mostrar eventos del calendario",
- "description": "Mostrar eventos en el panel del calendario."
- },
- "use-analog": {
- "description": "Mostrar un reloj de estilo analógico en la pantalla del calendario.",
- "label": "Usar reloj de estilo analógico"
- },
- "first-day-of-week": {
- "automatic": "Automático (usar la configuración regional del sistema)",
- "description": "Elige qué día empieza la semana en el calendario.",
- "label": "Primer día de la semana"
- }
- }
- },
- "network": {
- "title": "Red",
- "section": {
- "description": "Gestiona las conexiones Wi-Fi y Bluetooth."
- },
- "wifi": {
- "label": "Activar Wi-Fi"
- },
- "bluetooth": {
- "label": "Activar Bluetooth"
- }
- },
- "screen-recorder": {
- "title": "Grabadora de pantalla",
- "general": {
- "section": {
- "label": "Configuración general",
- "description": "Gestiona la salida y el contenido de la grabación de pantalla."
- },
- "output-folder": {
- "label": "Carpeta de salida",
- "description": "Carpeta donde se guardarán las grabaciones de pantalla.",
- "tooltip": "Buscar carpeta de salida"
- },
- "show-cursor": {
- "label": "Mostrar cursor",
- "description": "Grabar el cursor del ratón en el video."
- },
- "select-output-folder": "Seleccionar carpeta de salida"
- },
- "video": {
- "section": {
- "label": "Configuración de video",
- "description": "Configura las opciones de grabación de video."
- },
- "video-source": {
- "label": "Fuente de video",
- "description": "Se recomienda Portal; si aparecen artefactos, prueba Screen."
- },
- "frame-rate": {
- "label": "Tasa de fotogramas",
- "description": "Tasa de fotogramas objetivo para las grabaciones de pantalla."
- },
- "video-quality": {
- "label": "Calidad de video",
- "description": "Una mayor calidad resulta en archivos de mayor tamaño."
- },
- "video-codec": {
- "label": "Códec de video",
- "description": "h264 es el códec más común."
- },
- "color-range": {
- "label": "Rango de color",
- "description": "Se recomienda limitado para una mejor compatibilidad."
- }
- },
- "audio": {
- "section": {
- "label": "Configuración de audio",
- "description": "Configura las opciones de grabación de audio."
- },
- "audio-source": {
- "label": "Fuente de audio",
- "description": "Fuente de audio a capturar durante la grabación."
- },
- "audio-codec": {
- "label": "Códec de audio",
- "description": "Se recomienda Opus para el mejor rendimiento y el menor tamaño de audio."
- }
- }
- },
- "about": {
- "title": "Acerca de",
- "noctalia": {
- "section": {
- "label": "Shell Noctalia",
- "description": "Un shell de escritorio elegante y minimalista cuidadosamente diseñado para Wayland, construido con Quickshell."
- },
- "latest-version": "Última versión:",
- "installed-version": "Versión instalada:",
- "download-latest": "Descargar la última versión"
- },
- "contributors": {
- "section": {
- "label": "Colaboradores",
- "description": "¡Un saludo a nuestro increíble colaborador número {count}!",
- "description_plural": "¡Un saludo a nuestros {count} increíbles colaboradores!"
- }
- },
- "support": "Apóyanos"
- },
- "hooks": {
- "title": "Hooks",
- "system-hooks": {
- "section": {
- "label": "Ganchos del sistema",
- "description": "Configura comandos para que se ejecuten cuando ocurran eventos del sistema."
- },
- "enable": {
- "label": "Activar hooks",
- "description": "Activa o desactiva todos los comandos de hooks."
- }
- },
- "wallpaper-changed": {
- "label": "Fondo de pantalla cambiado",
- "description": "Comando a ejecutar cuando cambia el fondo de pantalla.",
- "placeholder": "ej., notify-send \"Fondo de pantalla\" \"Cambiado\""
- },
- "theme-changed": {
- "label": "Tema cambiado",
- "description": "Comando a ejecutar cuando el tema cambia entre modo oscuro y claro.",
- "placeholder": "ej., notify-send \"Tema\" \"Cambiado\""
- },
- "info": {
- "command-info": {
- "label": "Información de comandos de Hook",
- "description": "• Los comandos se ejecutan a través de shell (sh -c)\n• Los comandos se ejecutan en segundo plano (desvinculados)\n• Los botones de prueba se ejecutan con los valores actuales"
- },
- "parameters": {
- "label": "Parámetros disponibles",
- "description": "• Hook de fondo de pantalla: $1 = ruta del fondo de pantalla, $2 = nombre de la pantalla\n• Hook de cambio de tema: $1 = true/false (estado del modo oscuro)"
- }
- }
- },
- "control-center": {
- "position": {
- "description": "Elige dónde aparece el panel del Centro de control cuando se abre.",
- "label": "Posición"
- },
- "section": {
- "description": "Configurar el posicionamiento y el comportamiento del panel del Centro de control.",
- "label": "Apariencia"
- },
- "title": "Centro de control",
- "cards": {
- "section": {
- "description": "Personaliza qué controles aparecen en el Centro de control y en qué orden.",
- "label": "Tarjetas"
- }
- },
- "shortcuts": {
- "section": {
- "description": "Configurar y administrar los widgets de accesos directos.",
- "label": "Widgets de accesos directos"
- },
- "sectionLeft": "Izquierda",
- "sectionRight": "Derecha"
- }
- },
- "user-interface": {
- "animation-disable": {
- "description": "Desactiva todas las animaciones para una experiencia más rápida y con mayor capacidad de respuesta.",
- "label": "Desactivar animaciones de la interfaz de usuario"
- },
- "panels-overlay": {
- "label": "Abrir paneles en capa superpuesta",
- "description": "Los paneles aparecerán sobre las ventanas en pantalla completa"
- },
- "animation-speed": {
- "description": "Ajustar la velocidad global de la animación.",
- "label": "Velocidad de animación",
- "reset": "Restablecer la velocidad de la animación"
- },
- "border-radius": {
- "description": "Controla la redondez de las esquinas de las ventanas, los botones y otros elementos.",
- "label": "Radio de borde",
- "reset": "Restablecer el radio del borde"
- },
- "scaling": {
- "description": "Cambia el tamaño de la interfaz de usuario general, excluyendo la barra.",
- "label": "Escalado de la interfaz",
- "reset-scaling": "Restablecer el escalado de la interfaz"
- },
- "section": {
- "description": "Personaliza la apariencia, el ambiente y el comportamiento de la interfaz.",
- "label": "Apariencia"
- },
- "title": "Interfaz de usuario",
- "tooltips": {
- "description": "Activar o desactivar los avisos emergentes en toda la interfaz.",
- "label": "Mostrar sugerencias"
- },
- "panels-attached-to-bar": {
- "description": "Cuando está habilitado, los paneles se adjuntarán a la barra con un hermoso diseño de esquina invertida.",
- "label": "Adjuntar paneles a la barra"
- }
- },
- "lock-screen": {
- "compact-lockscreen": {
- "description": "Mostrar solo el campo de inicio de sesión y los controles del sistema, ocultando los widgets del clima y multimedia.",
- "label": "Pantalla de bloqueo compacta"
- },
- "lock-on-suspend": {
- "description": "Bloquear la pantalla automáticamente al suspender el sistema.",
- "label": "Bloquear al suspender"
- },
- "title": "Pantalla de bloqueo"
- }
- },
- "widgets": {
- "tooltip": {
- "placeholder": "Marcador de posición"
- },
- "file-picker": {
- "select-folder": "Seleccionar carpeta",
- "select-file": "Seleccionar archivo",
- "cancel": "Cancelar",
- "search-placeholder": "Buscar archivos y carpetas...",
- "select-current": "Seleccionar actual",
- "title": "Selector de archivos"
- },
- "datetime-tokens": {
- "common": {
- "12hour-time-minutes": "Hora en formato de 12 horas con minutos",
- "24hour-time-minutes": "Hora en formato de 24 horas con minutos",
- "24hour-time-seconds": "Hora en formato de 24 horas con segundos",
- "weekday-month-day": "Día de la semana, mes y día",
- "iso-date": "Formato de fecha ISO",
- "us-date": "Formato de fecha estadounidense",
- "european-date": "Formato de fecha europeo",
- "weekday-date": "Día de la semana con fecha"
- },
- "hour": {
- "no-leading-zero": "Hora sin cero inicial (0-23) - formato de 24 horas",
- "leading-zero": "Hora con cero inicial (00-23) - formato de 24 horas"
- },
- "minute": {
- "no-leading-zero": "Minuto sin cero inicial (0-59)",
- "leading-zero": "Minuto con cero inicial (00-59)"
- },
- "second": {
- "no-leading-zero": "Segundo sin cero inicial (0-59)",
- "leading-zero": "Segundo con cero inicial (00-59)"
- },
- "ampm": {
- "uppercase": "AM/PM en mayúsculas",
- "lowercase": "am/pm en minúsculas"
- },
- "timezone": {
- "abbreviation": "Abreviatura de la zona horaria"
- },
- "year": {
- "two-digit": "Año como número de dos dígitos (00-99)",
- "four-digit": "Año como número de cuatro dígitos"
- },
- "month": {
- "number-no-zero": "Mes como número sin cero inicial (1-12)",
- "number-leading-zero": "Mes como número con cero inicial (01-12)",
- "abbreviated": "Nombre del mes abreviado",
- "full": "Nombre completo del mes"
- },
- "day": {
- "no-leading-zero": "Día sin cero inicial (1-31)",
- "leading-zero": "Día con cero inicial (01-31)",
- "abbreviated": "Nombre del día abreviado",
- "full": "Nombre completo del día"
- }
- },
- "icon-picker": {
- "title": "Selector de iconos",
- "search": {
- "label": "Buscar"
- },
- "cancel": "Cancelar",
- "apply": "Aplicar"
- },
- "color-picker": {
- "title": "Selector de colores",
- "hex": {
- "label": "Color hexadecimal",
- "description": "Ingresa un código de color hexadecimal."
- },
- "rgb": {
- "label": "Valores RGB",
- "description": "Ajusta los valores de rojo, verde, azul y brillo."
- },
- "brightness": "Brillo",
- "theme-colors": {
- "label": "Colores del tema",
- "description": "Acceso rápido a la paleta de colores de tu tema."
- },
- "palette": {
- "label": "Paleta",
- "description": "Elige entre una amplia gama de colores predefinidos."
- },
- "cancel": "Cancelar",
- "apply": "Aplicar"
- },
- "text-input": {
- "clear": "Borrar"
- }
+ "authentication": {
+ "error": "Error de autenticación",
+ "failed": "Autenticación fallida"
},
"bar": {
"widget-settings": {
- "dialog": {
- "cancel": "Cancelar",
- "apply": "Aplicar"
- },
- "section-editor": {
- "placeholder": "Selecciona un widget para añadir...",
- "search-placeholder": "Buscar widgets..."
- },
"active-window": {
+ "colorize-icons": {
+ "description": "Aplicar colores del tema al icono de la ventana activa.",
+ "label": "Colorear iconos"
+ },
"hide-mode": {
- "label": "Modo de ocultación",
- "description": "Controla el comportamiento del widget cuando no hay ventana activa."
- },
- "show-app-icon": {
- "label": "Mostrar icono de la aplicación",
- "description": "Mostrar el icono de la aplicación junto al título de la ventana."
- },
- "scrolling-mode": {
- "label": "Modo de desplazamiento",
- "description": "Controlar cuándo está habilitado el desplazamiento de texto para títulos de ventana largos."
+ "description": "Controla el comportamiento del widget cuando no hay ventana activa.",
+ "label": "Modo de ocultación"
},
"max-width": {
- "label": "Ancho Máximo",
- "description": "Establece el tamaño horizontal máximo del widget. El widget se reducirá para adaptarse a contenido más corto."
+ "description": "Establece el tamaño horizontal máximo del widget. El widget se reducirá para adaptarse a contenido más corto.",
+ "label": "Ancho Máximo"
+ },
+ "scrolling-mode": {
+ "description": "Controlar cuándo está habilitado el desplazamiento de texto para títulos de ventana largos.",
+ "label": "Modo de desplazamiento"
+ },
+ "show-app-icon": {
+ "description": "Mostrar el icono de la aplicación junto al título de la ventana.",
+ "label": "Mostrar icono de la aplicación"
},
"use-fixed-width": {
- "label": "Usar Ancho Fijo",
- "description": "Cuando está activado, el widget siempre usará el ancho máximo en lugar de ajustarse dinámicamente al contenido."
- },
- "colorize-icons": {
- "label": "Colorear iconos",
- "description": "Aplicar colores del tema al icono de la ventana activa."
+ "description": "Cuando está activado, el widget siempre usará el ancho máximo en lugar de ajustarse dinámicamente al contenido.",
+ "label": "Usar Ancho Fijo"
}
},
- "system-monitor": {
- "cpu-usage": {
- "label": "Uso de CPU",
- "description": "Mostrar el porcentaje actual de uso de CPU."
+ "audio-visualizer": {
+ "color-name": {
+ "description": "Selecciona el color para el visualizador.",
+ "label": "Color de relleno"
},
- "cpu-temperature": {
- "label": "Temperatura de la CPU",
- "description": "Mostrar lecturas de temperatura de CPU si están disponibles."
+ "hide-when-idle": {
+ "description": "Si está activado, el visualizador se oculta salvo que haya reproducción activa.",
+ "label": "Ocultar cuando no se reproduce"
},
- "memory-usage": {
- "label": "Uso de memoria",
- "description": "Mostrar información actual del uso de RAM."
- },
- "memory-percentage": {
- "label": "Memoria como porcentaje",
- "description": "Mostrar el uso de memoria como porcentaje en lugar de valores absolutos."
- },
- "network-traffic": {
- "label": "Tráfico de red",
- "description": "Mostrar velocidades de carga y descarga de la red."
- },
- "storage-usage": {
- "label": "Uso de almacenamiento",
- "description": "Mostrar información del uso del espacio en disco."
- }
- },
- "notification-history": {
- "show-unread-badge": {
- "label": "Mostrar insignia de no leídos",
- "description": "Mostrar una insignia con el número de notificaciones no leídas."
- },
- "hide-badge-when-zero": {
- "label": "Ocultar insignia cuando es cero",
- "description": "Ocultar la insignia de notificaciones cuando no hay notificaciones no leídas."
+ "width": {
+ "description": "Ancho del componente personalizado.",
+ "label": "Ancho"
}
},
"battery": {
"display-mode": {
- "label": "Modo de visualización",
- "description": "Elige cómo te gustaría que apareciera este valor."
+ "description": "Elige cómo te gustaría que apareciera este valor.",
+ "label": "Modo de visualización"
},
"low-battery-threshold": {
- "label": "Umbral de advertencia de batería baja",
- "description": "Muestra una advertencia cuando la batería cae por debajo de este porcentaje."
- }
- },
- "control-center": {
- "use-distro-logo": {
- "label": "Usar logo de la distribución en lugar de icono",
- "description": "Usar el logo de tu distribución en lugar de un icono personalizado."
- },
- "icon": {
- "label": "Icono",
- "description": "Selecciona un icono de la biblioteca o un archivo personalizado."
- },
- "browse-library": "Explorar biblioteca",
- "browse-file": "Explorar archivo",
- "select-custom-icon": "Seleccionar un icono personalizado"
- },
- "keyboard-layout": {
- "display-mode": {
- "label": "Modo de visualización",
- "description": "Elige cómo te gustaría que apareciera este valor."
- }
- },
- "volume": {
- "display-mode": {
- "label": "Modo de visualización",
- "description": "Elige cómo te gustaría que apareciera este valor."
- }
- },
- "workspace": {
- "label-mode": {
- "label": "Modo de etiqueta",
- "description": "Elegir cómo se muestran las etiquetas de los espacios de trabajo."
- },
- "hide-unoccupied": {
- "label": "Ocultar desocupados",
- "description": "No mostrar espacios de trabajo sin ventanas."
- },
- "character-count": {
- "label": "Número de caracteres",
- "description": "Número de caracteres a mostrar de los nombres de espacios de trabajo (1-10)."
- }
- },
- "microphone": {
- "display-mode": {
- "label": "Modo de visualización",
- "description": "Elige cómo te gustaría que apareciera este valor."
+ "description": "Muestra una advertencia cuando la batería cae por debajo de este porcentaje.",
+ "label": "Umbral de advertencia de batería baja"
}
},
"brightness": {
"display-mode": {
- "label": "Modo de visualización",
- "description": "Elige cómo te gustaría que apareciera este valor."
+ "description": "Elige cómo te gustaría que apareciera este valor.",
+ "label": "Modo de visualización"
}
},
- "spacer": {
- "width": {
- "label": "Ancho",
- "description": "Ancho del espaciado en píxeles"
- }
- },
- "custom-button": {
- "icon": {
- "label": "Icono",
- "description": "Selecciona un icono de la biblioteca."
- },
- "browse": "Explorar",
- "left-click": {
- "label": "Clic izquierdo",
- "description": "Comando a ejecutar cuando se hace clic izquierdo en el botón."
- },
- "right-click": {
- "label": "Clic derecho",
- "description": "Comando a ejecutar cuando se hace clic derecho en el botón."
- },
- "middle-click": {
- "label": "Clic medio",
- "description": "Comando a ejecutar cuando se hace clic medio en el botón."
- },
- "dynamic-text": "Texto dinámico",
- "display-command-output": {
- "label": "Mostrar salida de comando",
- "description": "Ingresa un comando para ejecutar a intervalos regulares. La primera línea de su salida se mostrará como texto.",
- "stream-description": "Introduce un comando para ejecutar continuamente."
- },
- "refresh-interval": {
- "label": "Intervalo de actualización",
- "description": "Intervalo en milisegundos."
- },
- "text-stream": {
- "label": "Transmisión",
- "description": "Las líneas transmitidas desde el comando se mostrarán como texto en el botón."
- },
- "collapse-condition": {
- "label": "Condición de colapso",
- "description": "Si el texto de salida coincide con este valor, el botón se colapsará."
- },
- "parse-json": {
- "label": "Analizar salida como JSON",
- "description": "Analizar la salida del comando como un objeto JSON para establecer dinámicamente el texto y el ícono."
- }
- },
- "media-mini": {
- "hide-mode": {
- "label": "Modo de ocultación",
- "description": "Controla el comportamiento del widget cuando no se está reproduciendo ningún medio."
- },
- "show-album-art": {
- "label": "Mostrar arte del álbum",
- "description": "Mostrar la portada del álbum de la pista que se está reproduciendo actualmente."
- },
- "show-visualizer": {
- "label": "Mostrar visualizador",
- "description": "Mostrar un visualizador de audio cuando se reproduce música."
- },
- "visualizer-type": {
- "label": "Tipo de visualizador",
- "description": "Elegir el estilo de visualizador de audio a mostrar."
- },
- "max-width": {
- "label": "Ancho Máximo",
- "description": "Establece el tamaño horizontal máximo del widget. El widget se reducirá para adaptarse a contenido más corto."
- },
- "use-fixed-width": {
- "label": "Usar Ancho Fijo",
- "description": "Cuando está activado, el widget siempre usará el ancho máximo en lugar de ajustarse dinámicamente al contenido."
- },
- "scrolling-mode": {
- "label": "Modo de desplazamiento",
- "description": "Controlar cuándo está habilitado el desplazamiento de texto para títulos de pista largos."
- },
- "no-active-player": "Sin reproductor activo"
- },
"clock": {
- "use-primary-color": {
- "label": "Usar color primario",
- "description": "Cuando está activado, se aplica el color primario para dar énfasis."
- },
- "use-monospaced-font": {
- "label": "Usar fuente monoespaciada",
- "description": "Cuando está activado, el reloj usará la fuente monoespaciada."
- },
"clock-display": {
- "label": "Visualización del reloj",
- "description": "Personaliza la visualización de tu reloj añadiendo tokens de la lista de abajo. Para usar el formato de 12 horas, debes incluir el token 'AP'."
+ "description": "Personaliza la visualización de tu reloj añadiendo tokens de la lista de abajo. Para usar el formato de 12 horas, debes incluir el token 'AP'.",
+ "label": "Visualización del reloj"
},
- "horizontal-bar": {
- "label": "Barra horizontal",
- "description": "Consejo: Usa \\n para crear un salto de línea."
- },
- "vertical-bar": {
- "label": "Barra vertical",
- "description": "Usa un espacio para separar cada parte en una nueva línea."
- },
- "preview": "Vista previa",
"custom-font": {
"description": "Seleccionar una fuente personalizada para la visualización del reloj.",
"label": "Fuente personalizada",
"placeholder": "Seleccionar fuente personalizada...",
"search-placeholder": "Buscar fuentes..."
},
+ "horizontal-bar": {
+ "description": "Consejo: Usa \\n para crear un salto de línea.",
+ "label": "Barra horizontal"
+ },
+ "preview": "Vista previa",
"use-custom-font": {
"description": "Anular la selección de fuente predeterminada con una fuente personalizada para el reloj.",
"label": "Usar fuente personalizada"
+ },
+ "use-monospaced-font": {
+ "description": "Cuando está activado, el reloj usará la fuente monoespaciada.",
+ "label": "Usar fuente monoespaciada"
+ },
+ "use-primary-color": {
+ "description": "Cuando está activado, se aplica el color primario para dar énfasis.",
+ "label": "Usar color primario"
+ },
+ "vertical-bar": {
+ "description": "Usa un espacio para separar cada parte en una nueva línea.",
+ "label": "Barra vertical"
+ }
+ },
+ "control-center": {
+ "browse-file": "Explorar archivo",
+ "browse-library": "Explorar biblioteca",
+ "icon": {
+ "description": "Selecciona un icono de la biblioteca o un archivo personalizado.",
+ "label": "Icono"
+ },
+ "select-custom-icon": "Seleccionar un icono personalizado",
+ "use-distro-logo": {
+ "description": "Usar el logo de tu distribución en lugar de un icono personalizado.",
+ "label": "Usar logo de la distribución en lugar de icono"
+ },
+ "colorize-distro-logo": {
+ "description": "Aplica los colores del tema a tu logotipo de distribución.",
+ "label": "Colorear logotipo de la distribución"
+ }
+ },
+ "custom-button": {
+ "browse": "Explorar",
+ "collapse-condition": {
+ "description": "Si el texto de salida coincide con este valor, el botón se colapsará.",
+ "label": "Condición de colapso"
+ },
+ "display-command-output": {
+ "description": "Ingresa un comando para ejecutar a intervalos regulares. La primera línea de su salida se mostrará como texto.",
+ "label": "Mostrar salida de comando",
+ "stream-description": "Introduce un comando para ejecutar continuamente."
+ },
+ "dynamic-text": "Texto dinámico",
+ "hide-vertical": {
+ "description": "Si está activado, el texto de la salida del comando no se mostrará cuando la barra esté en un diseño vertical (izquierda o derecha).",
+ "label": "Ocultar texto en barra vertical"
+ },
+ "icon": {
+ "description": "Selecciona un icono de la biblioteca.",
+ "label": "Icono"
+ },
+ "left-click": {
+ "description": "Comando a ejecutar cuando se hace clic izquierdo en el botón.",
+ "label": "Clic izquierdo"
+ },
+ "middle-click": {
+ "description": "Comando a ejecutar cuando se hace clic medio en el botón.",
+ "label": "Clic medio"
+ },
+ "parse-json": {
+ "description": "Analizar la salida del comando como un objeto JSON para establecer dinámicamente el texto y el ícono.",
+ "label": "Analizar salida como JSON"
+ },
+ "refresh-interval": {
+ "description": "Intervalo en milisegundos.",
+ "label": "Intervalo de actualización"
+ },
+ "right-click": {
+ "description": "Comando a ejecutar cuando se hace clic derecho en el botón.",
+ "label": "Clic derecho"
+ },
+ "text-stream": {
+ "description": "Las líneas transmitidas desde el comando se mostrarán como texto en el botón.",
+ "label": "Transmisión"
+ }
+ },
+ "dialog": {
+ "apply": "Aplicar",
+ "cancel": "Cancelar"
+ },
+ "keyboard-layout": {
+ "display-mode": {
+ "description": "Elige cómo te gustaría que apareciera este valor.",
+ "label": "Modo de visualización"
+ }
+ },
+ "lock-keys": {
+ "browse": "Navegar",
+ "show-caps-lock": {
+ "description": "Mostrar el estado de Bloq Mayús.",
+ "label": "Bloq Mayús"
+ },
+ "show-num-lock": {
+ "description": "Mostrar el estado de bloqueo numérico.",
+ "label": "Bloq Num"
+ },
+ "show-scroll-lock": {
+ "description": "Mostrar el estado de Bloq Despl.",
+ "label": "Bloq Despl"
+ }
+ },
+ "media-mini": {
+ "hide-mode": {
+ "description": "Controla el comportamiento del widget cuando no se está reproduciendo ningún medio.",
+ "label": "Modo de ocultación"
+ },
+ "max-width": {
+ "description": "Establece el tamaño horizontal máximo del widget. El widget se reducirá para adaptarse a contenido más corto.",
+ "label": "Ancho Máximo"
+ },
+ "no-active-player": "Sin reproductor activo",
+ "scrolling-mode": {
+ "description": "Controlar cuándo está habilitado el desplazamiento de texto para títulos de pista largos.",
+ "label": "Modo de desplazamiento"
+ },
+ "show-album-art": {
+ "description": "Mostrar la portada del álbum de la pista que se está reproduciendo actualmente.",
+ "label": "Mostrar arte del álbum"
+ },
+ "show-visualizer": {
+ "description": "Mostrar un visualizador de audio cuando se reproduce música.",
+ "label": "Mostrar visualizador"
+ },
+ "use-fixed-width": {
+ "description": "Cuando está activado, el widget siempre usará el ancho máximo en lugar de ajustarse dinámicamente al contenido.",
+ "label": "Usar Ancho Fijo"
+ },
+ "visualizer-type": {
+ "description": "Elegir el estilo de visualizador de audio a mostrar.",
+ "label": "Tipo de visualizador"
+ }
+ },
+ "microphone": {
+ "display-mode": {
+ "description": "Elige cómo te gustaría que apareciera este valor.",
+ "label": "Modo de visualización"
+ }
+ },
+ "notification-history": {
+ "hide-badge-when-zero": {
+ "description": "Ocultar la insignia de notificaciones cuando no hay notificaciones no leídas.",
+ "label": "Ocultar insignia cuando es cero"
+ },
+ "show-unread-badge": {
+ "description": "Mostrar una insignia con el número de notificaciones no leídas.",
+ "label": "Mostrar insignia de no leídos"
+ }
+ },
+ "section-editor": {
+ "placeholder": "Selecciona un widget para añadir...",
+ "search-placeholder": "Buscar widgets..."
+ },
+ "spacer": {
+ "width": {
+ "description": "Ancho del espaciado en píxeles",
+ "label": "Ancho"
+ }
+ },
+ "system-monitor": {
+ "cpu-temperature": {
+ "description": "Mostrar lecturas de temperatura de CPU si están disponibles.",
+ "label": "Temperatura de la CPU"
+ },
+ "cpu-usage": {
+ "description": "Mostrar el porcentaje actual de uso de CPU.",
+ "label": "Uso de CPU"
+ },
+ "memory-percentage": {
+ "description": "Mostrar el uso de memoria como porcentaje en lugar de valores absolutos.",
+ "label": "Memoria como porcentaje"
+ },
+ "memory-usage": {
+ "description": "Mostrar información actual del uso de RAM.",
+ "label": "Uso de memoria"
+ },
+ "network-traffic": {
+ "description": "Mostrar velocidades de carga y descarga de la red.",
+ "label": "Tráfico de red"
+ },
+ "storage-usage": {
+ "description": "Mostrar información del uso del espacio en disco.",
+ "label": "Uso de almacenamiento"
}
},
"taskbar": {
+ "colorize-icons": {
+ "description": "Aplicar colores del tema a los iconos de la barra de tareas.",
+ "label": "Colorear iconos"
+ },
"hide-mode": {
- "label": "Modo de ocultación",
- "description": "Controla cómo se comporta el widget cuando no hay ventanas coincidentes."
+ "description": "Controla cómo se comporta el widget cuando no hay ventanas coincidentes.",
+ "label": "Modo de ocultación"
},
"only-active-workspaces": {
"description": "Mostrar solo las aplicaciones de los espacios de trabajo activos.",
@@ -1235,10 +280,6 @@
"only-same-output": {
"description": "Muestra solo las aplicaciones del resultado donde se encuentra la barra.",
"label": "Solo de la misma salida"
- },
- "colorize-icons": {
- "label": "Colorear iconos",
- "description": "Aplicar colores del tema a los iconos de la barra de tareas."
}
},
"taskbar-grouped": {
@@ -1253,118 +294,69 @@
},
"tray": {
"colorize-icons": {
- "label": "Colorear iconos",
- "description": "Aplicar colores del tema a los iconos de la bandeja del sistema."
+ "description": "Aplicar colores del tema a los iconos de la bandeja del sistema.",
+ "label": "Colorear iconos"
+ },
+ "drawer-enabled": {
+ "description": "Cuando está habilitado, los elementos de la bandeja no anclados se muestran en un panel cajón. Cuando está deshabilitado, todos los elementos de la bandeja se muestran en línea.",
+ "label": "Habilitar cajón"
}
},
- "audio-visualizer": {
- "width": {
- "label": "Ancho",
- "description": "Ancho del componente personalizado."
- },
- "hide-when-idle": {
- "label": "Ocultar cuando no se reproduce",
- "description": "Si está activado, el visualizador se oculta salvo que haya reproducción activa."
- },
- "color-name": {
- "description": "Selecciona el color para el visualizador.",
- "label": "Color de relleno"
+ "volume": {
+ "display-mode": {
+ "description": "Elige cómo te gustaría que apareciera este valor.",
+ "label": "Modo de visualización"
}
},
- "lock-keys": {
- "show-caps-lock": {
- "description": "Mostrar el estado de Bloq Mayús.",
- "label": "Bloq Mayús"
+ "workspace": {
+ "character-count": {
+ "description": "Número de caracteres a mostrar de los nombres de espacios de trabajo (1-10).",
+ "label": "Número de caracteres"
},
- "show-num-lock": {
- "description": "Mostrar el estado de bloqueo numérico.",
- "label": "Bloq Num"
+ "hide-unoccupied": {
+ "description": "No mostrar espacios de trabajo sin ventanas.",
+ "label": "Ocultar desocupados"
},
- "show-scroll-lock": {
- "description": "Mostrar el estado de Bloq Despl.",
- "label": "Bloq Despl"
- },
- "indicator-style": {
- "circle": "Letras circulares",
- "circle-dash": "Letras de círculo discontinuo",
- "circle-dot": "Letras de círculo punteado",
- "description": "Estilo de icono para los indicadores de las teclas de bloqueo",
- "hex": "Letras hexagonales",
- "label": "Estilo del indicador",
- "large": "Letras grandes",
- "small": "Letras minúsculas",
- "square": "Letras cuadradas",
- "square-round": "Letras cuadradas redondeadas"
+ "label-mode": {
+ "description": "Elegir cómo se muestran las etiquetas de los espacios de trabajo.",
+ "label": "Modo de etiqueta"
}
}
}
},
- "notifications": {
+ "battery": {
+ "charging": "Cargando.",
+ "charging-rate": "Tasa de carga: {rate} W.",
+ "discharging": "Descargando.",
+ "discharging-rate": "Tasa de descarga: {rate} W.",
+ "health": "Salud: {percent}%",
+ "idle": "Inactivo.",
+ "no-battery-detected": "No se detectó ninguna batería.",
"panel": {
- "title": "Notificaciones",
- "no-notifications": "No hay notificaciones",
- "description": "Tus notificaciones aparecerán aquí a medida que lleguen.",
- "click-to-expand": "Haz clic para expandir"
- }
- },
- "wallpaper": {
- "panel": {
- "title": "Selector de fondo de pantalla",
- "apply-all-monitors": {
- "label": "Aplicar a todos los monitores",
- "description": "Aplica el fondo de pantalla seleccionado a todos los monitores a la vez."
- },
- "search": "Buscar:"
+ "balanced": "Equilibrado ({percent}%)",
+ "disabled": "Administrador de batería deshabilitado",
+ "full": "Capacidad total ({percent}%)",
+ "lifespan": "Vida útil prolongada ({percent}%)",
+ "title": "Umbral de carga"
},
- "transitions": {
- "none": "Ninguna",
- "random": "Aleatoria",
- "fade": "Desvanecer",
- "disc": "Disco",
- "stripes": "Rayas",
- "wipe": "Barrido"
- },
- "fill-modes": {
- "center": "Centrar",
- "crop": "Recortar (Rellenar)",
- "fit": "Ajustar (Contener)",
- "stretch": "Estirar"
- },
- "no-match": "No se encontraron coincidencias.",
- "no-wallpaper": "No se encontraron fondos de pantalla.",
- "try-different-search": "Intenta con una búsqueda diferente.",
- "configure-directory": "Configura tu directorio de fondos de pantalla con imágenes."
+ "plugged-in": "Conectado.",
+ "time-left": "Tiempo restante: {time}.",
+ "time-until-full": "Tiempo hasta carga completa: {time}."
},
"bluetooth": {
"panel": {
- "title": "Bluetooth",
- "disabled": "Bluetooth está desactivado",
- "enable-message": "Activa Bluetooth para ver los dispositivos disponibles.",
- "connected-devices": "Dispositivos conectados",
- "known-devices": "Dispositivos conocidos",
"available-devices": "Dispositivos disponibles",
- "scanning": "Buscando dispositivos...",
- "pairing-mode": "Asegúrate de que tu dispositivo esté en modo de emparejamiento."
- }
- },
- "wifi": {
- "panel": {
- "title": "Wi-Fi",
- "disabled": "Wi-Fi está desactivado",
- "enable-message": "Activa Wi-Fi para ver las redes disponibles.",
- "searching": "Buscando redes cercanas...",
- "connected": "Conectado",
- "disconnecting": "Desconectando...",
- "forgetting": "Olvidando...",
- "saved": "Guardado",
- "disconnect": "Desconectar",
- "enter-password": "Ingresa la contraseña...",
+ "blocked": "Bloqueado",
"connect": "Conectar",
- "password": "Contraseña",
- "forget-network": "¿Olvidar esta red?",
- "forget": "Olvidar",
- "no-networks": "No se encontraron redes",
- "scan-again": "Escanear de nuevo"
+ "connected-devices": "Dispositivos conectados",
+ "disabled": "Bluetooth está desactivado",
+ "disconnect": "Desconectar",
+ "enable-message": "Activa Bluetooth para ver los dispositivos disponibles.",
+ "known-devices": "Dispositivos conocidos",
+ "pairing": "Emparejamiento...",
+ "pairing-mode": "Asegúrate de que tu dispositivo esté en modo de emparejamiento.",
+ "scanning": "Buscando dispositivos...",
+ "title": "Bluetooth"
}
},
"calendar": {
@@ -1375,281 +367,246 @@
"loading": "Cargando el clima…"
}
},
- "tooltips": {
- "refresh": "Actualizar",
- "close": "Cerrar",
- "up": "Arriba",
- "home": "Inicio",
- "refresh-wallpaper-list": "Actualizar lista de fondos de pantalla",
- "refresh-devices": "Actualizar dispositivos",
- "forget-network": "Olvidar red",
- "clear-history": "Limpiar historial",
- "delete-notification": "Eliminar notificación",
- "previous-month": "Mes anterior",
- "next-month": "Mes siguiente",
- "add-widget": "Añadir widget",
- "widget-settings": "Configuración del widget",
- "remove-widget": "Eliminar widget",
- "move-to-left-section": "Mover a la sección izquierda",
- "move-to-center-section": "Mover a la sección central",
- "move-to-right-section": "Mover a la sección derecha",
- "open-settings": "Abrir configuración",
- "session-menu": "Menú de sesión",
- "cancel-timer": "Cancelar temporizador",
- "start-screen-recording": "Iniciar grabación de pantalla",
- "stop-screen-recording": "Detener grabación de pantalla",
- "screen-recorder-not-installed": "La grabadora de pantalla no está instalada",
- "keep-awake": "Mantener despierto",
- "enable-keep-awake": "Activar mantener despierto",
- "disable-keep-awake": "Desactivar mantener despierto",
- "wallpaper-selector": "Clic izquierdo: Abrir selector de fondos de pantalla.\nClic derecho: Establecer fondo de pantalla aleatorio.",
- "do-not-disturb-enabled": "'No molestar' activado",
- "do-not-disturb-disabled": "'No molestar' desactivado",
- "connect-disconnect-devices": "Clic izquierdo para conectar. Clic derecho para olvidar.",
- "set-power-profile": "Establecer perfil de energía \"{profile}\"",
- "switch-to-light-mode": "Cambiar a modo claro",
- "switch-to-dark-mode": "Cambiar a modo oscuro",
- "night-light-disabled": "Luz nocturna desactivada.\nClic izquierdo para cambiar de modo.\nClic derecho para acceder a la configuración.",
- "night-light-enabled": "Luz nocturna activada.\nClic izquierdo para cambiar de modo.\nClic derecho para acceder a la configuración.",
- "night-light-forced": "Luz nocturna forzada.\nClic izquierdo para cambiar de modo.\nClic derecho para acceder a la configuración.",
- "night-light-not-installed": "Luz nocturna no disponible.\nwlsunset no está instalado.",
- "click-to-start-recording": "Haz clic para iniciar la grabación",
- "click-to-stop-recording": "Haz clic para detener la grabación",
- "open-control-center": "Abrir el centro de control",
- "brightness-at": "Brillo: {brightness}%\nClic derecho para configuración.\nDesplaza para modificar el brillo.",
- "manage-wifi": "Gestionar Wi-Fi",
- "bluetooth-devices": "Dispositivos Bluetooth",
- "open-notification-history-enable-dnd": "Abrir historial de notificaciones\nClic derecho para activar \"No molestar\".",
- "open-notification-history-disable-dnd": "Abrir historial de notificaciones\nClic derecho para desactivar \"No molestar\".",
- "open-wallpaper-selector": "Abrir selector de fondos de pantalla",
- "previous-media": "Medio anterior",
- "pause": "Pausa",
- "play": "Reproducir",
- "next-media": "Siguiente medio",
- "power-profile": "Perfil de energía '{profile}'",
- "keyboard-layout": "Distribución de teclado {layout}",
- "output-muted": "Silenciar salida de audio",
- "input-muted": "Silenciar entrada de audio",
- "microphone-volume-at": "Volumen del micrófono al {volume}%.\nClic izquierdo para ajustes. Clic derecho para activar/desactivar el silencio.\nDesplázate para modificar el volumen.",
- "volume-at": "Volumen de salida al {volume}%.\nClic izquierdo para ajustes. Clic derecho para activar/desactivar el silencio.\nDesplázate para modificar el volumen."
- },
"clock": {
"tooltip": "Abrir calendario"
},
"dock": {
"menu": {
+ "close": "Cerrar",
"focus": "Enfocar",
"pin": "Anclar",
- "unpin": "Desanclar",
- "close": "Cerrar"
+ "unpin": "Desanclar"
}
},
+ "general": {
+ "no-results": "Sin resultados",
+ "no-summary": "Sin resumen",
+ "unknown": "Desconocido"
+ },
"launcher": {
"pin": "Anclar al dock",
"unpin": "Desanclar del dock"
},
- "placeholders": {
- "search-icons": "ej., noctalia, niri, battery, cloud",
- "profile-picture-path": "/home/usuario/.face",
- "enter-width-pixels": "Ingresa el ancho en píxeles",
- "enter-command": "Ingresa el comando a ejecutar (aplicación o script personalizado)",
- "command-example": "echo \"Hola Mundo\"",
- "enter-text-to-collapse": "por ejemplo, 'no se está reproduciendo nada'. Usa /regex/ para patrones.",
- "search-wallpapers": "Escribe para filtrar fondos de pantalla...",
- "search-launcher": "Buscar entradas... o usa > para comandos",
- "search": "Buscar...",
- "select": "Seleccionar",
- "cancel": "Cancelar",
- "test": "Probar"
+ "lock-screen": {
+ "authenticating": "Autenticando...",
+ "authentication-failed": "Autenticación fallida",
+ "password": "Ingresa tu contraseña...",
+ "restart": "Reiniciar",
+ "shut-down": "Apagar",
+ "suspend": "Suspender",
+ "welcome-back": "¡Bienvenido de nuevo,"
+ },
+ "notifications": {
+ "panel": {
+ "click-to-expand": "Haz clic para expandir",
+ "description": "Tus notificaciones aparecerán aquí a medida que lleguen.",
+ "no-notifications": "No hay notificaciones",
+ "title": "Notificaciones"
+ }
},
"options": {
"bar": {
- "position": {
- "top": "Superior",
- "bottom": "Inferior",
- "left": "Izquierda",
- "right": "Derecha"
- },
"density": {
+ "comfortable": "Cómoda",
"compact": "Compacta",
"default": "Predeterminada",
- "comfortable": "Cómoda",
"mini": "Mini"
+ },
+ "position": {
+ "bottom": "Inferior",
+ "left": "Izquierda",
+ "right": "Derecha",
+ "top": "Superior"
}
},
- "launcher": {
- "position": {
- "center": "Centro (predeterminado)",
- "top_left": "Superior izquierda",
- "top_right": "Superior derecha",
- "bottom_left": "Inferior izquierda",
- "bottom_right": "Inferior derecha",
- "bottom_center": "Inferior central",
- "top_center": "Superior central",
- "center_left": "Izquierda centrado",
- "center_right": "Derecha centrado"
- }
+ "colors": {
+ "error": "Error",
+ "onSurface": "En la superficie",
+ "primary": "Primario",
+ "secondary": "Secundario",
+ "tertiary": "Terciario"
},
"control-center": {
"position": {
- "close_to_bar_button": "Cerca del botón de la barra",
- "top_left": "Superior izquierda",
- "top_right": "Superior derecha",
+ "bottom_center": "Inferior central",
"bottom_left": "Inferior izquierda",
"bottom_right": "Inferior derecha",
- "bottom_center": "Inferior central",
+ "center": "Centro",
+ "close_to_bar_button": "Cerca del botón de la barra",
"top_center": "Superior central",
- "center": "Centro"
+ "top_left": "Superior izquierda",
+ "top_right": "Superior derecha"
},
"quickSettingsStyle": {
- "modern": "Moderno",
"classic": "Clásico",
- "compact": "Compacto"
+ "compact": "Compacto",
+ "modern": "Moderno"
+ }
+ },
+ "display-mode": {
+ "always-hide": "Ocultar siempre",
+ "always-show": "Mostrar siempre",
+ "force-open": "Forzar apertura",
+ "on-hover": "Al pasar el cursor"
+ },
+ "frame-rates": {
+ "fps": "{fps} FPS"
+ },
+ "hide-modes": {
+ "hidden": "Ocultar cuando esté vacío",
+ "idle": "Ocultar cuando está inactivo",
+ "transparent": "Transparente cuando está vacío",
+ "visible": "Siempre visible"
+ },
+ "launcher": {
+ "position": {
+ "bottom_center": "Inferior central",
+ "bottom_left": "Inferior izquierda",
+ "bottom_right": "Inferior derecha",
+ "center": "Centro (predeterminado)",
+ "center_left": "Izquierda centrado",
+ "center_right": "Derecha centrado",
+ "follow_bar": "Seguir barra (predeterminado)",
+ "top_center": "Superior central",
+ "top_left": "Superior izquierda",
+ "top_right": "Superior derecha"
}
},
"osd": {
"position": {
- "top_left": "Superior izquierda",
- "top_right": "Superior derecha",
- "top_center": "Superior central",
+ "bottom_center": "Inferior central",
"bottom_left": "Inferior izquierda",
"bottom_right": "Inferior derecha",
- "bottom_center": "Inferior central",
"center_left": "Izquierda centrado",
- "center_right": "Derecha centrado"
+ "center_right": "Derecha centrado",
+ "top_center": "Superior central",
+ "top_left": "Superior izquierda",
+ "top_right": "Superior derecha"
}
},
- "display-mode": {
- "on-hover": "Al pasar el cursor",
- "always-show": "Mostrar siempre",
- "always-hide": "Ocultar siempre",
- "force-open": "Forzar apertura"
- },
- "workspace-labels": {
- "none": "Ninguno",
- "index": "Índice",
- "name": "Nombre"
- },
- "visualizer-types": {
- "none": "Ninguno",
- "linear": "Lineal",
- "mirrored": "Espejado",
- "wave": "Onda"
+ "screen-recording": {
+ "audio-sources": {
+ "both": "Salida del sistema + entrada del micrófono",
+ "microphone-input": "Entrada del micrófono",
+ "system-output": "Salida del sistema"
+ },
+ "color-range": {
+ "full": "Completo",
+ "limited": "Limitado"
+ },
+ "quality": {
+ "high": "Alta",
+ "medium": "Media",
+ "ultra": "Ultra",
+ "very-high": "Muy alta"
+ },
+ "sources": {
+ "portal": "Portal",
+ "screen": "Pantalla"
+ }
},
"scrolling-modes": {
"always": "Desplazar Siempre",
"hover": "Desplazar al Pasar",
"never": "Nunca Desplazar"
},
- "frame-rates": {
- "fps": "{fps} FPS"
+ "shadow-direction": {
+ "bottom": "Inferior",
+ "bottom_left": "Inferior izquierda",
+ "bottom_right": "Inferior derecha",
+ "center": "Centro",
+ "left": "Izquierda",
+ "right": "Derecha",
+ "top": "Superior",
+ "top_left": "Superior izquierda",
+ "top_right": "Superior derecha"
},
- "screen-recording": {
- "sources": {
- "portal": "Portal",
- "screen": "Pantalla"
- },
- "quality": {
- "medium": "Media",
- "high": "Alta",
- "very-high": "Muy alta",
- "ultra": "Ultra"
- },
- "color-range": {
- "limited": "Limitado",
- "full": "Completo"
- },
- "audio-sources": {
- "system-output": "Salida del sistema",
- "microphone-input": "Entrada del micrófono",
- "both": "Salida del sistema + entrada del micrófono"
- }
+ "visualizer-quality": {
+ "high": "Alto/Alta",
+ "low": "Bajo"
},
- "hide-modes": {
- "hidden": "Ocultar cuando esté vacío",
- "transparent": "Transparente cuando está vacío",
- "visible": "Siempre visible"
+ "visualizer-types": {
+ "linear": "Lineal",
+ "mirrored": "Espejado",
+ "none": "Ninguno",
+ "wave": "Onda"
},
- "colors": {
- "error": "Error",
- "primary": "Primario",
- "secondary": "Secundario",
- "tertiary": "Terciario",
- "onSurface": "En la superficie"
+ "workspace-labels": {
+ "index": "Índice",
+ "name": "Nombre",
+ "none": "Ninguno"
}
},
- "session-menu": {
- "title": "Menú de sesión",
- "action-in-seconds": "{action} en {seconds} segundos...",
- "lock": "Bloquear",
- "lock-and-suspend": "Bloquear y Suspender",
- "suspend": "Suspender",
- "reboot": "Reiniciar",
- "logout": "Cerrar sesión",
- "shutdown": "Apagar"
+ "placeholders": {
+ "cancel": "Cancelar",
+ "command-example": "echo \"Hola Mundo\"",
+ "enter-command": "Ingresa el comando a ejecutar (aplicación o script personalizado)",
+ "enter-text-to-collapse": "por ejemplo, 'no se está reproduciendo nada'. Usa /regex/ para patrones.",
+ "enter-tooltip": "Introducir información sobre herramientas",
+ "enter-width-pixels": "Ingresa el ancho en píxeles",
+ "profile-picture-path": "/home/usuario/.face",
+ "search": "Buscar...",
+ "search-icons": "ej., noctalia, niri, battery, cloud",
+ "search-launcher": "Buscar entradas... o usa > para comandos",
+ "search-wallpapers": "Escribe para filtrar fondos de pantalla...",
+ "select": "Seleccionar",
+ "test": "Probar"
},
"plugins": {
"applications": "Aplicaciones",
- "clipboard": "Historial del portapapeles",
"calculator": "Calculadora",
- "clipboard-search-description": "Buscar en el historial del portapapeles",
+ "calculator-description": "Calculadora - evalúa expresiones matemáticas",
+ "calculator-enter-expression": "Ingresa una expresión matemática",
+ "calculator-error": "Error",
+ "calculator-name": "Calculadora",
+ "clipboard": "Historial del portapapeles",
"clipboard-clear-description": "Limpiar todo el historial del portapapeles",
+ "clipboard-clear-description-full": "Eliminar todos los elementos del historial del portapapeles",
+ "clipboard-clear-history": "Limpiar historial del portapapeles",
"clipboard-history-disabled": "Historial del portapapeles desactivado",
"clipboard-history-disabled-description": "Activa el historial del portapapeles en la configuración o instala cliphist",
- "clipboard-clear-history": "Limpiar historial del portapapeles",
- "clipboard-clear-description-full": "Eliminar todos los elementos del historial del portapapeles",
"clipboard-loading": "Cargando historial del portapapeles...",
"clipboard-loading-description": "Por favor espera",
- "calculator-description": "Calculadora - evalúa expresiones matemáticas",
- "calculator-name": "Calculadora",
- "calculator-enter-expression": "Ingresa una expresión matemática",
- "calculator-error": "Error"
- },
- "system": {
- "uptime": "Actividad: {uptime}",
- "welcome-back": "¡Bienvenido de nuevo,",
- "monitor-description": "{model} ({width}x{height} @ {scale}x)",
- "scaling-percentage": "{percentage}%",
- "location-display": "{name} ({coordinates})",
- "signal-strength": "{signal}%",
- "cpu-temperature": "{temp}°C",
- "disk-usage": "{percent}%",
- "widget-settings-title": "Configuración de {widget}",
- "unknown-app": "Aplicación desconocida",
- "no-media-player-detected": "No se detectó ningún reproductor multimedia",
- "user-requested": "Solicitado por el usuario",
- "unknown": "Desconocido",
- "unknown-version": "Desconocida",
- "unknown-layout": "Desconocida"
- },
- "lock-screen": {
- "password": "Ingresa tu contraseña...",
- "welcome-back": "¡Bienvenido de nuevo,",
- "authenticating": "Autenticando...",
- "authentication-failed": "Autenticación fallida",
- "shut-down": "Apagar",
- "restart": "Reiniciar",
- "suspend": "Suspender"
+ "clipboard-search-description": "Buscar en el historial del portapapeles"
},
"quickSettings": {
+ "bluetooth": {
+ "label": {
+ "disabled": "Bluetooth",
+ "enabled": "Bluetooth"
+ },
+ "tooltip": {
+ "action": "Hacer clic para gestionar los dispositivos Bluetooth"
+ }
+ },
+ "keepAwake": {
+ "label": {
+ "disabled": "Mantener despierto",
+ "enabled": "Mantener despierto"
+ },
+ "tooltip": {
+ "action": "Hacer clic para alternar el modo Mantener despierto"
+ }
+ },
+ "nightLight": {
+ "label": {
+ "disabled": "Luz nocturna",
+ "enabled": "Luz nocturna",
+ "forced": "Luz nocturna"
+ },
+ "tooltip": {
+ "action": "Hacer clic para alternar el modo Luz nocturna\nClic derecho: Abrir configuración"
+ }
+ },
"notifications": {
"label": {
- "enabled": "Notificaciones",
- "disabled": "No molestar"
+ "disabled": "No molestar",
+ "enabled": "Notificaciones"
},
"tooltip": {
"action": "Clic izquierdo: Abrir historial de notificaciones\nClic derecho: Alternar No molestar"
}
},
- "screenRecorder": {
- "label": {
- "recording": "Detener",
- "stopped": "Grabar"
- },
- "tooltip": {
- "action": "Hacer clic para iniciar/detener la grabación de pantalla"
- }
- },
"powerProfile": {
"label": {
"unavailable": "Perfil de energía"
@@ -1659,33 +616,13 @@
"disabled": "Instala power-profiles-daemon para usar perfiles de energía"
}
},
- "wifi": {
+ "screenRecorder": {
"label": {
- "ethernet": "Ethernet",
- "wifi": "Wi-Fi",
- "disconnected": "Wi-Fi desconectado"
+ "recording": "Detener",
+ "stopped": "Grabar"
},
"tooltip": {
- "action": "Hacer clic para gestionar las conexiones Wi-Fi"
- }
- },
- "bluetooth": {
- "label": {
- "enabled": "Bluetooth",
- "disabled": "Bluetooth"
- },
- "tooltip": {
- "action": "Hacer clic para gestionar los dispositivos Bluetooth"
- }
- },
- "nightLight": {
- "label": {
- "enabled": "Luz nocturna",
- "forced": "Luz nocturna",
- "disabled": "Luz nocturna"
- },
- "tooltip": {
- "action": "Hacer clic para alternar el modo Luz nocturna\nClic derecho: Abrir configuración"
+ "action": "Hacer clic para iniciar/detener la grabación de pantalla"
}
},
"wallpaperSelector": {
@@ -1694,177 +631,1386 @@
"action": "Clic izquierdo: Abrir selector de fondo de pantalla\nClic derecho: Establecer fondo de pantalla aleatorio"
}
},
- "keepAwake": {
+ "wifi": {
"label": {
- "enabled": "Mantener despierto",
- "disabled": "Mantener despierto"
+ "disconnected": "Wi-Fi desconectado",
+ "ethernet": "Ethernet",
+ "wifi": "Wi-Fi"
},
"tooltip": {
- "action": "Hacer clic para alternar el modo Mantener despierto"
+ "action": "Hacer clic para gestionar las conexiones Wi-Fi"
}
}
},
+ "session-menu": {
+ "action-in-seconds": "{action} en {seconds} segundos...",
+ "lock": "Bloquear",
+ "lock-and-suspend": "Bloquear y Suspender",
+ "logout": "Cerrar sesión",
+ "reboot": "Reiniciar",
+ "shutdown": "Apagar",
+ "suspend": "Suspender",
+ "title": "Menú de sesión"
+ },
+ "settings": {
+ "about": {
+ "contributors": {
+ "section": {
+ "description": "¡Un saludo a nuestro increíble colaborador número {count}!",
+ "description_plural": "¡Un saludo a nuestros {count} increíbles colaboradores!",
+ "label": "Colaboradores"
+ }
+ },
+ "noctalia": {
+ "download-latest": "Descargar la última versión",
+ "installed-version": "Versión instalada:",
+ "latest-version": "Última versión:",
+ "section": {
+ "description": "Un shell de escritorio elegante y minimalista cuidadosamente diseñado para Wayland, construido con Quickshell.",
+ "label": "Shell Noctalia"
+ }
+ },
+ "support": "Apóyanos",
+ "title": "Acerca de"
+ },
+ "audio": {
+ "devices": {
+ "input-device": {
+ "description": "Selecciona el dispositivo de entrada de audio deseado.",
+ "label": "Dispositivo de entrada"
+ },
+ "output-device": {
+ "description": "Selecciona el dispositivo de salida de audio deseado.",
+ "label": "Dispositivo de salida"
+ },
+ "section": {
+ "description": "Configura los dispositivos de entrada y salida de audio disponibles.",
+ "label": "Dispositivos de audio"
+ }
+ },
+ "media": {
+ "excluded-player": {
+ "description": "Agrega palabras clave para los reproductores que deseas que el sistema ignore. Cada palabra clave debe estar en una línea nueva.",
+ "label": "Reproductor excluido",
+ "placeholder": "escribe una subcadena y presiona +"
+ },
+ "frame-rate": {
+ "description": "Tasas más altas son más fluidas pero usan más recursos.",
+ "label": "Tasa de fotogramas"
+ },
+ "primary-player": {
+ "description": "Ingresa una palabra clave para identificar tu reproductor principal.",
+ "label": "Reproductor principal",
+ "placeholder": "ej. spotify, vlc, mpv"
+ },
+ "scrolling-speed": {
+ "description": "Tiempo en segundos para que el título se desplace del inicio al final",
+ "label": "Velocidad de desplazamiento"
+ },
+ "scrolling-title": {
+ "description": "Activar desplazamiento continuo para títulos de medios largos",
+ "label": "Título con desplazamiento"
+ },
+ "section": {
+ "description": "Establece tus aplicaciones multimedia preferidas e ignoradas.",
+ "label": "Reproductores multimedia"
+ },
+ "visualizer-quality": {
+ "description": "Alta calidad usa más recursos de la GPU.",
+ "label": "Calidad de visualización"
+ },
+ "visualizer-type": {
+ "description": "Elige un tipo de visualización para la reproducción de medios.",
+ "label": "Tipo de visualización"
+ }
+ },
+ "title": "Audio",
+ "volumes": {
+ "input-volume": {
+ "description": "Nivel de volumen de entrada del micrófono.",
+ "label": "Volumen de entrada"
+ },
+ "mute-input": {
+ "description": "Silencia la entrada de audio predeterminada (micrófono).",
+ "label": "Silenciar entrada de audio"
+ },
+ "mute-output": {
+ "description": "Silencia la salida de audio principal del sistema.",
+ "label": "Silenciar salida de audio"
+ },
+ "output-volume": {
+ "description": "Nivel de volumen de todo el sistema.",
+ "label": "Volumen de salida"
+ },
+ "section": {
+ "description": "Ajusta los controles de volumen y los niveles de audio.",
+ "label": "Volúmenes"
+ },
+ "step-size": {
+ "description": "Ajusta el tamaño del paso para los cambios de volumen (rueda de desplazamiento, atajos de teclado).",
+ "label": "Tamaño del paso de volumen"
+ },
+ "volume-overdrive": {
+ "description": "Permite subir el volumen por encima del 100 %. Es posible que no sea compatible con todo el hardware.",
+ "label": "Permitir sobreamplificación de volumen"
+ }
+ }
+ },
+ "bar": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Ajusta la opacidad del fondo de la barra.",
+ "label": "Opacidad del fondo"
+ },
+ "density": {
+ "description": "Ajusta el relleno de la barra para un aspecto compacto o espacioso.",
+ "label": "Densidad de la barra"
+ },
+ "floating": {
+ "description": "Muestra la barra como una 'píldora' flotante.",
+ "label": "Barra flotante"
+ },
+ "margins": {
+ "description": "Ajusta los márgenes alrededor de la barra flotante.",
+ "horizontal": "Horizontal",
+ "label": "Márgenes",
+ "vertical": "Vertical"
+ },
+ "outer-corners": {
+ "description": "Muestra esquinas curvadas hacia afuera en la barra.",
+ "label": "Esquinas exteriores"
+ },
+ "position": {
+ "description": "Elige dónde colocar la barra en la pantalla.",
+ "label": "Posición de la barra"
+ },
+ "section": {
+ "description": "Personaliza la apariencia y posición de la barra.",
+ "label": "Apariencia"
+ },
+ "show-capsule": {
+ "description": "Mostrar fondos de los widgets.",
+ "label": "Mostrar cápsula"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Muestra la barra en monitores específicos. Por defecto, se muestra en todos si no se elige ninguno.",
+ "label": "Visualización en monitores"
+ }
+ },
+ "title": "Barra",
+ "tray": {
+ "back": "Atrás",
+ "blacklist": {
+ "description": "Agregar reglas de exclusión de la bandeja, admite comodines (*).",
+ "label": "Lista negra",
+ "placeholder": "ej., nm-applet, Fcitx*"
+ },
+ "pin-application": "Fijar Aplicación",
+ "unpin-application": "Desfijar Aplicación"
+ },
+ "widgets": {
+ "section": {
+ "description": "Arrastra y suelta widgets para reordenarlos. Las insignias indican el uso: [I]zquierda, [C]entro, [D]erecha.",
+ "label": "Posicionamiento de widgets"
+ }
+ }
+ },
+ "color-scheme": {
+ "color-source": {
+ "matugen-scheme-type": {
+ "description": {
+ "scheme-content": "Deriva colores que coinciden estrechamente con la imagen subyacente.",
+ "scheme-expressive": "Paleta vibrante con saturación juguetona.",
+ "scheme-fidelity": "Paleta de alta fidelidad que preserva los tonos originales",
+ "scheme-fruit-salad": "Mezcla colorida de acentos brillantes y contrastantes.",
+ "scheme-monochrome": "Paleta minimalista construida en torno a un solo tono.",
+ "scheme-neutral": "Paleta apagada con tonos suaves y relajantes.",
+ "scheme-rainbow": "Paleta diversa que abarca todo el espectro.",
+ "scheme-tonal-spot": "Paleta equilibrada con acentos definidos."
+ },
+ "label": "Tipo de esquema Matugen"
+ },
+ "section": {
+ "description": "Configuración principal de los colores de Noctalia.",
+ "label": "Fuente de color"
+ },
+ "use-wallpaper-colors": {
+ "description": "Generar esquemas de color desde tu fondo de pantalla usando Matugen. Extrae automáticamente colores para crear un tema cohesivo.",
+ "label": "Usar colores del fondo de pantalla"
+ }
+ },
+ "dark-mode": {
+ "mode": {
+ "description": "Permite el cambio automático entre el modo claro y el modo oscuro.",
+ "label": "Programación del modo oscuro",
+ "location": "Ubicación",
+ "manual": "Manual",
+ "off": "Apagado"
+ },
+ "switch": {
+ "description": "Cambia a un tema más oscuro para una visualización más fácil por la noche.",
+ "label": "Modo oscuro"
+ }
+ },
+ "predefined": {
+ "generate-templates": {
+ "description": "Genera plantillas de Matugen (GTK, temas de terminal, etc.) al usar esquemas de colores predefinidos.",
+ "label": "Generar plantillas para esquemas predefinidos"
+ },
+ "section": {
+ "description": "Elige entre una colección de esquemas de colores predefinidos.",
+ "label": "Esquemas de colores predefinidos"
+ }
+ },
+ "templates": {
+ "misc": {
+ "description": "Opciones de configuración adicionales.",
+ "label": "Varios",
+ "user-templates": {
+ "description": "Habilitar configuración de Matugen definida por el usuario. Se creará un archivo de plantilla en ~/.config/noctalia/user-templates.toml al activar por primera vez",
+ "label": "Plantillas de usuario"
+ }
+ },
+ "programs": {
+ "code": {
+ "description": "Escribe {filepath}. El tema Hyprluna debe ser instalado y activado manualmente.",
+ "description-missing": "Requiere que {app} esté instalado/a."
+ },
+ "spicetify": {
+ "description": "Escribe {filepath}. El tema Comfy debe ser instalado y activado manualmente.",
+ "description-missing": "Requiere que {app} esté instalado/a."
+ },
+ "description": "Tematización específica de aplicaciones.",
+ "discord": {
+ "description": "Escribir {filepath} para {client}",
+ "description-missing": "No se detectó cliente de Discord. Instala vencord, vesktop, webcord, armcord, equibop, lightcord o dorion."
+ },
+ "fuzzel": {
+ "description": "Escribir {filepath} y recargar",
+ "description-missing": "Requiere que {app} esté instalado"
+ },
+ "label": "Programas",
+ "pywalfox": {
+ "description": "Escribir {filepath} y ejecutar pywalfox update",
+ "description-missing": "Requiere que {app} esté instalado"
+ },
+ "vicinae": {
+ "description": "Escribir {filepath} y recargar",
+ "description-missing": "Requiere que {app} esté instalado"
+ },
+ "walker": {
+ "description": "Escribir {filepath} y establecer tema a noctalia",
+ "description-missing": "Requiere que {app} esté instalado"
+ }
+ },
+ "section": {
+ "description": "Aplicar colores a aplicaciones externas.",
+ "label": "Plantillas"
+ },
+ "terminal": {
+ "alacritty": {
+ "description": "Escribe {filepath} y recarga",
+ "description-missing": "Requiere que {app} esté instalado/a."
+ },
+ "description": "Tematización del emulador de terminal.",
+ "foot": {
+ "description": "Escribir {filepath} y recargar",
+ "description-missing": "Requiere que {app} esté instalado"
+ },
+ "ghostty": {
+ "description": "Escribir {filepath} y recargar",
+ "description-missing": "Requiere que {app} esté instalado"
+ },
+ "kitty": {
+ "description": "Escribir {filepath} y recargar",
+ "description-missing": "Requiere que {app} esté instalado"
+ },
+ "label": "Terminal",
+ "wezterm": {
+ "description": "Escribe {filepath} y recarga.",
+ "description-missing": "Requiere que {app} esté instalado/a."
+ }
+ },
+ "ui": {
+ "description": "Tematización del entorno de escritorio y de la interfaz de usuario.",
+ "gtk": {
+ "description": "Escribir {filepath}"
+ },
+ "kcolorscheme": {
+ "description": "Escribir {filepath}"
+ },
+ "label": "UI",
+ "qt": {
+ "description": "Escribir {filepath}"
+ }
+ }
+ },
+ "title": "Esquema de colores"
+ },
+ "control-center": {
+ "cards": {
+ "section": {
+ "description": "Personaliza qué controles aparecen en el Centro de control y en qué orden.",
+ "label": "Tarjetas"
+ }
+ },
+ "position": {
+ "description": "Elige dónde aparece el panel del Centro de control cuando se abre.",
+ "label": "Posición"
+ },
+ "section": {
+ "description": "Configurar el posicionamiento y el comportamiento del panel del Centro de control.",
+ "label": "Apariencia"
+ },
+ "shortcuts": {
+ "custom-button": {
+ "browse": "Explorar",
+ "command": {
+ "description": "El comando a ejecutar cuando se hace clic en el botón.",
+ "label": "Comando"
+ },
+ "enable-on-state-logic": {
+ "description": "Habilita un segundo icono y un estado 'caliente' basado en un comando de comprobación.",
+ "label": "Habilitar lógica de estado activado"
+ },
+ "general-tooltip-text": {
+ "description": "Descripción general para el tooltip del botón.",
+ "label": "Texto de tooltip general"
+ },
+ "icon": {
+ "description": "Selecciona un icono de la biblioteca.",
+ "label": "Icono"
+ },
+ "on-clicked": {
+ "description": "Comando a ejecutar cuando se hace clic izquierdo en el botón.",
+ "label": "Comando de clic izquierdo"
+ },
+ "on-middle-clicked": {
+ "description": "Comando a ejecutar cuando se hace clic central en el botón.",
+ "label": "Comando de clic central"
+ },
+ "on-right-clicked": {
+ "description": "Comando a ejecutar cuando se hace clic derecho en el botón.",
+ "label": "Comando de clic derecho"
+ },
+ "on-state-command": {
+ "description": "Comando a ejecutar para comprobar si el botón debe estar en el estado 'encendido'. Devuelve 0 para encendido, no cero para apagado.",
+ "label": "Comando de comprobación de estado activado"
+ },
+ "on-state-icon": {
+ "description": "El icono para el botón cuando está en el estado 'encendido'.",
+ "label": "Icono de estado activado"
+ },
+ "state-checks": {
+ "add": "Añadir comprobación de estado",
+ "browse-icon": "Explorar",
+ "command": "Comando a ejecutar para esta comprobación de estado",
+ "label": "Comprobaciones de estado",
+ "remove": "Eliminar"
+ },
+ "tooltip": {
+ "description": "El tooltip a mostrar al pasar el cursor sobre el botón.",
+ "label": "Tooltip"
+ }
+ },
+ "dialog": {
+ "apply": "Aplicar",
+ "cancel": "Cancelar"
+ },
+ "section": {
+ "description": "Configurar y administrar los widgets de accesos directos.",
+ "label": "Widgets de accesos directos"
+ },
+ "sectionLeft": "Izquierda",
+ "sectionRight": "Derecha"
+ },
+ "title": "Centro de control"
+ },
+ "display": {
+ "monitors": {
+ "brightness": "Brillo",
+ "brightness-step": {
+ "description": "Ajusta el tamaño del paso para los cambios de brillo (rueda de desplazamiento y atajos de teclado).",
+ "label": "Tamaño del paso de brillo"
+ },
+ "brightness-unavailable": {
+ "ddc-disabled": "Control de brillo no disponible. Habilita \"Soporte de brillo externo\" para controlar el brillo de esta pantalla.",
+ "generic": "El control de brillo no está disponible para esta pantalla."
+ },
+ "enforce-minimum": {
+ "description": "Resuelve el problema de la retroiluminación que se apaga completamente en algunas pantallas al 0% de brillo.",
+ "label": "Forzar brillo mínimo (1%)"
+ },
+ "external-brightness": {
+ "description": "Habilitar soporte DDCUtil para controlar el brillo en pantallas externas mediante el protocolo DDC/CI.",
+ "label": "Soporte de brillo externo"
+ },
+ "section": {
+ "description": "Ajustar el brillo de la configuración para cada pantalla.",
+ "label": "Configuración por monitor"
+ }
+ },
+ "night-light": {
+ "auto-schedule": {
+ "description": "Basado en la hora del atardecer y amanecer en {location} - recomendado.",
+ "label": "Programación automática"
+ },
+ "enable": {
+ "description": "Aplica un filtro de color cálido para reducir la emisión de luz azul.",
+ "label": "Activar luz nocturna"
+ },
+ "force-activation": {
+ "description": "Ignora la programación y aplica el filtro nocturno inmediatamente.",
+ "label": "Forzar activación"
+ },
+ "manual-schedule": {
+ "description": "Establece horas personalizadas para el amanecer y el atardecer.",
+ "label": "Programación manual",
+ "select-start": "Seleccionar hora de inicio",
+ "select-stop": "Seleccionar hora de finalización",
+ "sunrise": "Hora del amanecer",
+ "sunset": "Hora del atardecer"
+ },
+ "section": {
+ "description": "Reduce la emisión de luz azul para ayudarte a dormir mejor y reducir la fatiga visual.",
+ "label": "Luz nocturna"
+ },
+ "temperature": {
+ "day": "Día",
+ "description": "Establece la calidez del color para la noche y el día.",
+ "label": "Temperatura de color",
+ "night": "Noche"
+ }
+ },
+ "title": "Pantalla"
+ },
+ "dock": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Ajusta la opacidad del fondo del dock.",
+ "label": "Opacidad del fondo"
+ },
+ "colorize-icons": {
+ "description": "Aplicar colores del tema a los iconos de aplicaciones del dock (solo aplicaciones no enfocadas).",
+ "label": "Colorear iconos"
+ },
+ "display": {
+ "always-visible": "Siempre visible",
+ "auto-hide": "Ocultar automáticamente",
+ "description": "Elige cómo se comporta el dock.",
+ "exclusive": "Exclusivo",
+ "label": "Pantalla"
+ },
+ "floating-distance": {
+ "description": "Ajusta la distancia de flotación desde el borde de la pantalla.",
+ "label": "Distancia de flotación del dock"
+ },
+ "icon-size": {
+ "description": "Ajusta el tamaño general del Dock.",
+ "label": "Tamaño del Dock"
+ },
+ "section": {
+ "description": "Personaliza el comportamiento y la apariencia del dock.",
+ "label": "Apariencia"
+ }
+ },
+ "enabled": {
+ "description": "Mostrar u ocultar el dock por completo",
+ "label": "Habilitar dock"
+ },
+ "monitors": {
+ "only-same-output": {
+ "description": "Mostrar solo las aplicaciones de la salida donde se encuentra el dock.",
+ "label": "Solo aplicaciones de la misma salida"
+ },
+ "section": {
+ "description": "Mostrar el dock en monitores específicos. Por defecto, se muestra en todos si no se elige ninguno.",
+ "label": "Visualización en monitor"
+ }
+ },
+ "title": "Dock"
+ },
+ "general": {
+ "fonts": {
+ "default": {
+ "description": "Fuente principal utilizada en toda la interfaz.",
+ "label": "Fuente predeterminada",
+ "placeholder": "Seleccionar fuente predeterminada...",
+ "scale": {
+ "description": "Aumentar o disminuir el tamaño del texto estándar.",
+ "label": "Tamaño de fuente predeterminado"
+ },
+ "search-placeholder": "Buscar fuentes..."
+ },
+ "monospace": {
+ "description": "Fuente monoespaciada utilizada para la visualización de números y estadísticas.",
+ "label": "Fuente monoespaciada",
+ "placeholder": "Seleccionar fuente monoespaciada...",
+ "scale": {
+ "description": "Aumentar o disminuir el tamaño del texto monoespaciado.",
+ "label": "Tamaño de fuente monoespaciada"
+ },
+ "search-placeholder": "Buscar fuentes monoespaciadas..."
+ },
+ "reset-scaling": "Restablecer la escala",
+ "section": {
+ "description": "Elige las fuentes utilizadas en toda la interfaz.",
+ "label": "Fuentes"
+ }
+ },
+ "language": {
+ "section": {
+ "description": "Elige tu idioma preferido para la aplicación.",
+ "label": "Idioma"
+ },
+ "select": {
+ "auto-detect": "Automático",
+ "description": "Selecciona el idioma utilizado en la interfaz de la aplicación.",
+ "label": "Idioma de la aplicación"
+ }
+ },
+ "launch-setup-wizard": "Inicie el asistente de configuración",
+ "profile": {
+ "picture": {
+ "description": "Tu foto de perfil que aparece en toda la interfaz.",
+ "label": "Foto de perfil de {user}"
+ },
+ "section": {
+ "description": "Edita los detalles de tu usuario y tu avatar.",
+ "label": "Perfil"
+ },
+ "select-avatar": "Seleccionar imagen de avatar"
+ },
+ "screen-corners": {
+ "radius": {
+ "description": "Ajusta las esquinas redondeadas de la pantalla.",
+ "label": "Radio de las esquinas de la pantalla",
+ "reset": "Restablecer el radio de las esquinas de la pantalla"
+ },
+ "section": {
+ "description": "Personaliza el redondeo de las esquinas de la pantalla y los efectos visuales.",
+ "label": "Esquinas de la pantalla"
+ },
+ "show-corners": {
+ "description": "Muestra esquinas redondeadas en el borde de la pantalla.",
+ "label": "Mostrar esquinas de la pantalla"
+ },
+ "solid-black": {
+ "description": "Usa negro sólido en lugar del color de fondo de la barra.",
+ "label": "Esquinas negras sólidas"
+ }
+ },
+ "title": "General"
+ },
+ "hooks": {
+ "info": {
+ "command-info": {
+ "description": "• Los comandos se ejecutan a través de shell (sh -c)\n• Los comandos se ejecutan en segundo plano (desvinculados)\n• Los botones de prueba se ejecutan con los valores actuales",
+ "label": "Información de comandos de Hook"
+ },
+ "parameters": {
+ "description": "• Hook de fondo de pantalla: $1 = ruta del fondo de pantalla, $2 = nombre de la pantalla\n• Hook de cambio de tema: $1 = true/false (estado del modo oscuro)",
+ "label": "Parámetros disponibles"
+ }
+ },
+ "system-hooks": {
+ "enable": {
+ "description": "Activa o desactiva todos los comandos de hooks.",
+ "label": "Activar hooks"
+ },
+ "section": {
+ "description": "Configura comandos para que se ejecuten cuando ocurran eventos del sistema.",
+ "label": "Ganchos del sistema"
+ }
+ },
+ "theme-changed": {
+ "description": "Comando a ejecutar cuando el tema cambia entre modo oscuro y claro.",
+ "label": "Tema cambiado",
+ "placeholder": "ej., notify-send \"Tema\" \"Cambiado\""
+ },
+ "title": "Hooks",
+ "wallpaper-changed": {
+ "description": "Comando a ejecutar cuando cambia el fondo de pantalla.",
+ "label": "Fondo de pantalla cambiado",
+ "placeholder": "ej., notify-send \"Fondo de pantalla\" \"Cambiado\""
+ }
+ },
+ "launcher": {
+ "settings": {
+ "background-opacity": {
+ "description": "Ajusta la opacidad del fondo del lanzador.",
+ "label": "Opacidad del fondo"
+ },
+ "clipboard-history": {
+ "description": "Accede a los elementos copiados anteriormente desde el lanzador.",
+ "label": "Activar historial del portapapeles"
+ },
+ "custom-launch-prefix": {
+ "description": "Prefijar comandos con un lanzador personalizado (ej. 'runapp' para integración con systemd).",
+ "label": "Prefijo de lanzamiento personalizado"
+ },
+ "custom-launch-prefix-enabled": {
+ "description": "Usar un prefijo personalizado para lanzar aplicaciones en lugar del método predeterminado.",
+ "label": "Habilitar prefijo de lanzamiento personalizado"
+ },
+ "position": {
+ "description": "Elige dónde aparece el panel del lanzador.",
+ "label": "Posición"
+ },
+ "section": {
+ "description": "Personaliza el comportamiento y la apariencia del lanzador.",
+ "label": "Apariencia"
+ },
+ "sort-by-usage": {
+ "description": "Cuando está activado, las aplicaciones más utilizadas aparecen primero en la lista.",
+ "label": "Ordenar por más usados"
+ },
+ "terminal-command": {
+ "description": "Comando para iniciar un terminal. Por ejemplo, 'kitty -e' o 'gnome-terminal --'.",
+ "label": "Comando de terminal"
+ },
+ "use-app2unit": {
+ "description": "Usa un método de lanzamiento alternativo para gestionar mejor los procesos de las aplicaciones y prevenir problemas.",
+ "label": "Usar App2Unit para lanzar aplicaciones"
+ }
+ },
+ "title": "Lanzador"
+ },
+ "location": {
+ "date-time": {
+ "12hour-format": {
+ "description": "Muestra la hora en formato de 12 horas en la pantalla de bloqueo y el calendario. El reloj de la barra tiene su propia configuración.",
+ "label": "Utilice el formato de hora de 12 horas"
+ },
+ "first-day-of-week": {
+ "automatic": "Automático (usar la configuración regional del sistema)",
+ "description": "Elige qué día empieza la semana en el calendario.",
+ "label": "Primer día de la semana"
+ },
+ "section": {
+ "description": "Personaliza cómo aparecen la fecha y la hora.",
+ "label": "Fecha y hora"
+ },
+ "show-events": {
+ "description": "Mostrar eventos en el panel del calendario.",
+ "label": "Mostrar eventos del calendario"
+ },
+ "use-analog": {
+ "description": "Mostrar un reloj de estilo analógico en la ventana del calendario y en la pantalla de bloqueo.",
+ "label": "Usar reloj de estilo analógico"
+ },
+ "week-numbers": {
+ "description": "Muestra la semana del año (ej., Semana 38) en el calendario.",
+ "label": "Mostrar números de semana"
+ }
+ },
+ "location": {
+ "search": {
+ "description": "ej., Toronto, ON",
+ "label": "Buscar una ubicación",
+ "placeholder": "Ingresa el nombre de la ubicación"
+ },
+ "section": {
+ "description": "Obtén el clima y la programación de la luz nocturna precisos estableciendo tu ubicación.",
+ "label": "Tu ubicación"
+ }
+ },
+ "title": "Ubicación",
+ "weather": {
+ "enabled": {
+ "description": "Mostrar información del clima en toda la interfaz y obtener datos meteorológicos. Cuando está desactivado, todos los elementos del clima estarán ocultos y no se realizarán solicitudes de red.",
+ "label": "Activar clima"
+ },
+ "fahrenheit": {
+ "description": "Muestra la temperatura en Fahrenheit en lugar de Celsius.",
+ "label": "Mostrar temperatura en Fahrenheit (°F)"
+ },
+ "section": {
+ "description": "Elige tu unidad de temperatura preferida.",
+ "label": "Clima"
+ },
+ "show-in-calendar": {
+ "description": "Muestra el pronóstico del tiempo diario directamente en la vista de tu calendario.",
+ "label": "Mostrar el clima en el calendario"
+ }
+ }
+ },
+ "lock-screen": {
+ "compact-lockscreen": {
+ "description": "Mostrar solo el campo de inicio de sesión y los controles del sistema, ocultando los widgets del clima y multimedia.",
+ "label": "Pantalla de bloqueo compacta"
+ },
+ "lock-on-suspend": {
+ "description": "Bloquear la pantalla automáticamente al suspender el sistema.",
+ "label": "Bloquear al suspender"
+ },
+ "title": "Pantalla de bloqueo"
+ },
+ "network": {
+ "bluetooth": {
+ "label": "Activar Bluetooth"
+ },
+ "section": {
+ "description": "Gestiona las conexiones Wi-Fi y Bluetooth."
+ },
+ "title": "Red",
+ "wifi": {
+ "label": "Activar Wi-Fi"
+ }
+ },
+ "notifications": {
+ "duration": {
+ "critical-urgency": {
+ "description": "Cuánto tiempo permanecen visibles las notificaciones de prioridad crítica.",
+ "label": "Urgencia crítica"
+ },
+ "low-urgency": {
+ "description": "Cuánto tiempo permanecen visibles las notificaciones de baja prioridad.",
+ "label": "Urgencia baja"
+ },
+ "normal-urgency": {
+ "description": "Cuánto tiempo permanecen visibles las notificaciones de prioridad normal.",
+ "label": "Urgencia normal"
+ },
+ "respect-expire": {
+ "description": "Usa el tiempo de expiración establecido en la notificación.",
+ "label": "Respetar tiempo de expiración"
+ },
+ "section": {
+ "description": "Configura cuánto tiempo permanecen visibles las notificaciones según su nivel de urgencia.",
+ "label": "Duración de la notificación"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Muestra notificaciones en monitores específicos. Por defecto, se muestra en todos si no se elige ninguno.",
+ "label": "Visualización en monitores"
+ }
+ },
+ "settings": {
+ "always-on-top": {
+ "description": "Muestra las notificaciones sobre ventanas a pantalla completa y otras capas.",
+ "label": "Siempre visible"
+ },
+ "background-opacity": {
+ "description": "Ajustar la opacidad de los fondos de las notificaciones.",
+ "label": "Opacidad del fondo"
+ },
+ "do-not-disturb": {
+ "description": "Desactiva todas las ventanas emergentes de notificación cuando está activado.",
+ "label": "No molestar"
+ },
+ "enable-osd": {
+ "description": "Muestra los cambios de volumen y brillo en tiempo real.",
+ "label": "Activar visualización en pantalla"
+ },
+ "enabled": {
+ "description": "Activar o desactivar el demonio de notificaciones, requiere reiniciar noctalia-shell.",
+ "label": "Activar notificaciones"
+ },
+ "location": {
+ "description": "Dónde aparecen las notificaciones en la pantalla.",
+ "label": "Ubicación"
+ },
+ "section": {
+ "description": "Configura la apariencia y el comportamiento de las notificaciones.",
+ "label": "Apariencia"
+ }
+ },
+ "title": "Notificaciones"
+ },
+ "osd": {
+ "always-on-top": {
+ "description": "Mostrar OSD por encima de ventanas de pantalla completa y otras capas.",
+ "label": "Siempre encima"
+ },
+ "description": "Configura indicadores superpuestos como volumen y brillo.",
+ "duration": {
+ "auto-hide": {
+ "description": "Ajusta el tiempo antes de que el OSD desaparezca.",
+ "label": "Ocultar después de"
+ },
+ "section": {
+ "description": "Cuánto tiempo permanece visible el OSD antes de ocultarse automáticamente.",
+ "label": "Tiempo de ocultación automática"
+ }
+ },
+ "enabled": {
+ "description": "Mostrar cambios de volumen y brillo en tiempo real.",
+ "label": "Activar visualización en pantalla"
+ },
+ "location": {
+ "description": "Dónde aparece la visualización en pantalla.",
+ "label": "Ubicación"
+ },
+ "monitors": {
+ "section": {
+ "description": "Mostrar el OSD en monitores específicos. Por defecto, en todos si no se elige ninguno.",
+ "label": "Visualización en monitores"
+ }
+ },
+ "section": {
+ "general": {
+ "description": "Configura visibilidad y comportamiento del OSD.",
+ "label": "General"
+ }
+ },
+ "title": "Visualización en pantalla"
+ },
+ "screen-recorder": {
+ "audio": {
+ "audio-codec": {
+ "description": "Se recomienda Opus para el mejor rendimiento y el menor tamaño de audio.",
+ "label": "Códec de audio"
+ },
+ "audio-source": {
+ "description": "Fuente de audio a capturar durante la grabación.",
+ "label": "Fuente de audio"
+ },
+ "section": {
+ "description": "Configura las opciones de grabación de audio.",
+ "label": "Configuración de audio"
+ }
+ },
+ "general": {
+ "output-folder": {
+ "description": "Carpeta donde se guardarán las grabaciones de pantalla.",
+ "label": "Carpeta de salida",
+ "tooltip": "Buscar carpeta de salida"
+ },
+ "section": {
+ "description": "Gestiona la salida y el contenido de la grabación de pantalla.",
+ "label": "Configuración general"
+ },
+ "select-output-folder": "Seleccionar carpeta de salida",
+ "show-cursor": {
+ "description": "Grabar el cursor del ratón en el video.",
+ "label": "Mostrar cursor"
+ }
+ },
+ "title": "Grabadora de pantalla",
+ "video": {
+ "color-range": {
+ "description": "Se recomienda limitado para una mejor compatibilidad.",
+ "label": "Rango de color"
+ },
+ "frame-rate": {
+ "description": "Tasa de fotogramas objetivo para las grabaciones de pantalla.",
+ "label": "Tasa de fotogramas"
+ },
+ "section": {
+ "description": "Configura las opciones de grabación de video.",
+ "label": "Configuración de video"
+ },
+ "video-codec": {
+ "description": "h264 es el códec más común.",
+ "label": "Códec de video"
+ },
+ "video-quality": {
+ "description": "Una mayor calidad resulta en archivos de mayor tamaño.",
+ "label": "Calidad de video"
+ },
+ "video-source": {
+ "description": "Se recomienda Portal; si aparecen artefactos, prueba Screen.",
+ "label": "Fuente de video"
+ }
+ }
+ },
+ "user-interface": {
+ "animation-disable": {
+ "description": "Desactiva todas las animaciones para una experiencia más rápida y con mayor capacidad de respuesta.",
+ "label": "Desactivar animaciones de la interfaz de usuario"
+ },
+ "animation-speed": {
+ "description": "Ajustar la velocidad global de la animación.",
+ "label": "Velocidad de animación",
+ "reset": "Restablecer la velocidad de la animación"
+ },
+ "border-radius": {
+ "description": "Controla la redondez de las esquinas de las ventanas, los botones y otros elementos.",
+ "label": "Radio de borde",
+ "reset": "Restablecer el radio del borde"
+ },
+ "dim-desktop": {
+ "description": "Atenuar el escritorio cuando los paneles o menús estén abiertos.",
+ "label": "Dim escritorio"
+ },
+ "panels-attached-to-bar": {
+ "description": "Los paneles se fijan a la barra y a los bordes de la pantalla, creando una apariencia impecable con elegantes esquinas invertidas.",
+ "label": "Ajustar paneles a los bordes"
+ },
+ "panels-overlay": {
+ "description": "Garantiza que los paneles y la barra permanezcan visibles, incluso sobre aplicaciones en pantalla completa.",
+ "label": "Mantener paneles y barra en la parte superior"
+ },
+ "scaling": {
+ "description": "Cambia el tamaño de la interfaz de usuario general, excluyendo la barra.",
+ "label": "Escalado de la interfaz",
+ "reset-scaling": "Restablecer el escalado de la interfaz"
+ },
+ "section": {
+ "description": "Personaliza la apariencia, el ambiente y el comportamiento de la interfaz.",
+ "label": "Apariencia"
+ },
+ "settings-panel-attached-to-bar": {
+ "description": "Mantén la ventana de Configuración alineada con la barra para un aspecto unificado.",
+ "label": "Ajustar Configuración a la barra"
+ },
+ "shadows": {
+ "description": "Habilita sombras paralelas debajo de las barras y los paneles.",
+ "direction": {
+ "description": "Elige desde dónde se proyecta la sombra.",
+ "label": "Dirección de la sombra"
+ },
+ "label": "Sombras paralelas"
+ },
+ "title": "Interfaz de usuario",
+ "tooltips": {
+ "description": "Activar o desactivar los avisos emergentes en toda la interfaz.",
+ "label": "Mostrar sugerencias"
+ }
+ },
+ "wallpaper": {
+ "automation": {
+ "custom-interval": {
+ "description": "Ingresa el tiempo como HH:MM (ej., 01:30).",
+ "label": "Intervalo personalizado"
+ },
+ "interval": {
+ "description": "Con qué frecuencia cambiar los fondos de pantalla automáticamente.",
+ "label": "Intervalo del fondo de pantalla"
+ },
+ "random-wallpaper": {
+ "description": "Programa cambios aleatorios de fondo de pantalla a intervalos regulares.",
+ "label": "Fondo de pantalla aleatorio"
+ },
+ "section": {
+ "label": "Automatización"
+ }
+ },
+ "look-feel": {
+ "edge-smoothness": {
+ "description": "Aplica un efecto suave y difuminado al borde de las transiciones.",
+ "label": "Suavizar borde de la transición"
+ },
+ "fill-color": {
+ "description": "Elige un color de relleno que pueda aparecer detrás del fondo de pantalla.",
+ "label": "Color de relleno"
+ },
+ "fill-mode": {
+ "description": "Selecciona cómo debe escalar la imagen para coincidir con la resolución de tu monitor.",
+ "label": "Modo de relleno"
+ },
+ "section": {
+ "label": "Apariencia y estilo"
+ },
+ "transition-duration": {
+ "description": "Duración de las animaciones de transición en segundos.",
+ "label": "Duración de la transición"
+ },
+ "transition-type": {
+ "description": "Tipo de animación al cambiar entre fondos de pantalla.",
+ "label": "Tipo de transición"
+ }
+ },
+ "settings": {
+ "enable-management": {
+ "description": "Gestiona fondos de pantalla con Noctalia. Desmarca si prefieres usar otra aplicación.",
+ "label": "Activar gestión de fondos de pantalla"
+ },
+ "enable-overview": {
+ "description": "Aplica un fondo de pantalla difuminado y atenuado a la pantalla de vista general.",
+ "label": "Activar fondo de pantalla de vista general"
+ },
+ "folder": {
+ "description": "Ruta a tu carpeta principal de fondos de pantalla.",
+ "label": "Carpeta de fondos de pantalla",
+ "tooltip": "Buscar carpeta de fondos de pantalla"
+ },
+ "monitor-specific": {
+ "description": "Establece una carpeta de fondos de pantalla diferente para cada monitor.",
+ "label": "Directorios específicos por monitor",
+ "tooltip": "Buscar carpeta de fondos de pantalla"
+ },
+ "recursive-search": {
+ "description": "Buscar fondos de pantalla también en subcarpetas del directorio de fondos de pantalla.",
+ "label": "Buscar en subcarpetas"
+ },
+ "section": {
+ "description": "Controla cómo se gestionan y muestran los fondos de pantalla.",
+ "label": "Configuración del fondo de pantalla"
+ },
+ "select-folder": "Seleccionar carpeta de fondos de pantalla",
+ "select-monitor-folder": "Seleccionar carpeta de fondos de pantalla del monitor",
+ "selector-position": {
+ "description": "Elige dónde aparece el panel selector de fondo de pantalla.",
+ "label": "Posición"
+ }
+ },
+ "title": "Fondo de pantalla"
+ }
+ },
+ "setup": {
+ "appearance": {
+ "header": "Apariencia",
+ "subheader": "Elige modo oscuro y fuentes de color (Matugen o predefinido)."
+ },
+ "customize": {
+ "header": "Personaliza tu experiencia",
+ "subheader": "Ajusta la posición de la barra, densidad, escala y más."
+ },
+ "wallpaper": {
+ "choose-dir": "Elige un directorio que contenga tus fondos",
+ "dir": {
+ "browse": "Seleccionar carpeta",
+ "description": "Elige la carpeta que contiene tus fondos",
+ "label": "Directorio de fondos",
+ "select-title": "Seleccionar carpeta de fondos"
+ },
+ "header": "Elige tu fondo",
+ "no-dir": "No se seleccionó un directorio de fondos",
+ "no-valid": "No se encontraron imágenes válidas en: {dir}",
+ "none-in-dir": "No se encontraron fondos en el directorio",
+ "preview-error": "No se pudo cargar la imagen",
+ "select-prompt": "Selecciona un fondo abajo",
+ "subheader": "Define el ambiente con un bonito fondo."
+ },
+ "welcome": {
+ "note": "Solo algunos conceptos básicos para empezar: todas las opciones están en Configuración."
+ }
+ },
+ "system": {
+ "cpu-temperature": "{temp}°C",
+ "disk-usage": "{percent}%",
+ "location-display": "{name} ({coordinates})",
+ "monitor-description": "{model} ({width}x{height} @ {scale}x)",
+ "no-media-player-detected": "No se detectó ningún reproductor multimedia",
+ "scaling-percentage": "{percentage}%",
+ "signal-strength": "{signal}%",
+ "unknown": "Desconocido",
+ "unknown-app": "Aplicación desconocida",
+ "unknown-layout": "Desconocida",
+ "unknown-version": "Desconocida",
+ "uptime": "Actividad: {uptime}",
+ "user-requested": "Solicitado por el usuario",
+ "welcome-back": "¡Bienvenido de nuevo,",
+ "widget-settings-title": "Configuración de {widget}"
+ },
"toast": {
- "night-light": {
- "enabled": "Activada",
- "disabled": "Desactivada",
- "not-installed": "wlsunset no está instalado",
- "forced": "Activación forzada",
- "normal": "Modo normal"
- },
- "keep-awake": {
- "enabled": "Activado",
- "disabled": "Desactivado"
- },
- "wallpaper-colors": {
- "enabled": "Colores del fondo de pantalla activados",
- "disabled": "Colores del fondo de pantalla desactivados",
- "not-installed": "Matugen no instalado - requerido para extraer colores del fondo de pantalla"
- },
- "matugen": {
- "failed": "Error en el procesamiento de Matugen",
- "failed-general": "Matugen encontró un error al procesar las plantillas"
- },
- "recording": {
- "stopping": "Deteniendo grabación...",
- "started": "Grabación iniciada",
- "saved": "Grabación guardada",
- "failed-start": "Fallo al iniciar la grabación",
- "failed-gpu": "gpu-screen-recorder se cerró inesperadamente.",
- "failed-general": "La grabadora se cerró con un error.",
- "no-portals": "Los portales de escritorio no están en ejecución",
- "no-portals-desc": "Inicia xdg-desktop-portal y un portal de compositor (wlr/hyprland/gnome/kde).",
- "not-installed": "gpu-screen-recorder no instalado",
- "not-installed-desc": "Por favor instala gpu-screen-recorder para usar las funciones de grabación de pantalla."
- },
- "clipboard": {
- "unavailable": "Historial del portapapeles no disponible",
- "unavailable-desc": "La aplicación 'cliphist' no está instalada. Por favor, instálala para usar las funciones de historial del portapapeles."
- },
- "ipc": {
- "powerpanel-deprecated": "PowerPanel ha sido renombrado a SessionMenu, esta llamada IPC será obsoleta pronto. Por favor, usa \"ipc call sessionMenu toggle\" en su lugar.",
- "sidepanel-deprecated": "SidePanel ha sido renombrado a ControlCenter, esta llamada IPC será obsoleta pronto. Por favor, usa \"ipc call controlCenter toggle\" en su lugar."
- },
- "wifi": {
- "enabled": "Activado",
- "disabled": "Desactivado",
- "connected": "Conectado a '{ssid}'",
- "disconnected": "Desconectado de '{ssid}'"
- },
- "bluetooth": {
- "enabled": "Activado",
- "disabled": "Desactivado"
- },
"airplane-mode": {
- "title": "Modo avión",
+ "disabled": "Desactivado",
"enabled": "Activado",
- "disabled": "Desactivado"
- },
- "internet": {
- "limited": "Conectado sin Internet"
- },
- "kofi": {
- "opened": "Página de Ko-fi abierta en tu navegador"
- },
- "do-not-disturb": {
- "enabled": "'No molestar' activado",
- "disabled": "'No molestar' desactivado",
- "enabled-desc": "Encontrarás estas notificaciones en tu historial.",
- "disabled-desc": "Mostrando todas las notificaciones."
- },
- "power-profile": {
- "changed": "Perfil de energía cambiado",
- "profile-name": "\"{profile}\""
+ "title": "Modo avión"
},
"battery": {
"low": "Batería baja",
"low-desc": "La batería está al {percent}%. Por favor, conecta el cargador."
},
"battery-manager": {
- "title": "Umbral de batería",
- "set-success-desc": "Umbral de batería establecido en {percent}%",
"initial-setup": "Configuración inicial requerida",
- "set-failed": "No se pudo establecer el umbral de batería",
- "install-success": "Instalado correctamente",
- "install-missing": "Faltan archivos requeridos",
- "install-unsupported": "El sistema no es compatible",
"install-failed": "Error en la instalación",
+ "install-missing": "Faltan archivos requeridos",
+ "install-success": "Instalado correctamente",
+ "install-unsupported": "El sistema no es compatible",
+ "set-failed": "No se pudo establecer el umbral de batería",
+ "set-success-desc": "Umbral de batería establecido en {percent}%",
+ "title": "Umbral de batería",
+ "uninstall-failed": "Error en la desinstalación",
"uninstall-setup": "Desinstalando, se requiere autenticación",
- "uninstall-success": "Desinstalado correctamente",
- "uninstall-failed": "Error en la desinstalación"
+ "uninstall-success": "Desinstalado correctamente"
+ },
+ "bluetooth": {
+ "disabled": "Desactivado",
+ "enabled": "Activado"
+ },
+ "clipboard": {
+ "unavailable": "Historial del portapapeles no disponible",
+ "unavailable-desc": "La aplicación 'cliphist' no está instalada. Por favor, instálala para usar las funciones de historial del portapapeles."
+ },
+ "do-not-disturb": {
+ "disabled": "'No molestar' desactivado",
+ "disabled-desc": "Mostrando todas las notificaciones.",
+ "enabled": "'No molestar' activado",
+ "enabled-desc": "Encontrarás estas notificaciones en tu historial."
+ },
+ "internet": {
+ "limited": "Conectado sin Internet"
+ },
+ "ipc": {
+ "powerpanel-deprecated": "PowerPanel ha sido renombrado a SessionMenu, esta llamada IPC será obsoleta pronto. Por favor, usa \"ipc call sessionMenu toggle\" en su lugar.",
+ "sidepanel-deprecated": "SidePanel ha sido renombrado a ControlCenter, esta llamada IPC será obsoleta pronto. Por favor, usa \"ipc call controlCenter toggle\" en su lugar."
+ },
+ "keep-awake": {
+ "disabled": "Desactivado",
+ "enabled": "Activado"
+ },
+ "kofi": {
+ "opened": "Página de Ko-fi abierta en tu navegador"
},
"missing-control-center": {
- "label": "Se eliminó el último widget del Centro de control",
- "description": "El widget del Centro de control se eliminó de la barra. Para acceder a él nuevamente desde la barra, debes volver a añadir el widget. También puedes abrirlo haciendo clic derecho en la barra."
+ "description": "El widget del Centro de control se eliminó de la barra. Para acceder a él nuevamente desde la barra, debes volver a añadir el widget. También puedes abrirlo haciendo clic derecho en la barra.",
+ "label": "Se eliminó el último widget del Centro de control"
+ },
+ "night-light": {
+ "disabled": "Desactivada",
+ "enabled": "Activada",
+ "forced": "Activación forzada",
+ "normal": "Modo normal",
+ "not-installed": "wlsunset no está instalado"
+ },
+ "noctalia-performance": {
+ "disabled": "Modo de rendimiento desactivado",
+ "enabled": "Modo de rendimiento activado",
+ "label": "Actuación de Noctalia"
+ },
+ "power-profile": {
+ "changed": "Perfil de energía cambiado",
+ "profile-name": "\"{profile}\""
+ },
+ "recording": {
+ "failed-general": "La grabadora se cerró con un error.",
+ "failed-gpu": "gpu-screen-recorder se cerró inesperadamente.",
+ "failed-start": "Fallo al iniciar la grabación",
+ "no-portals": "Los portales de escritorio no están en ejecución",
+ "no-portals-desc": "Inicia xdg-desktop-portal y un portal de compositor (wlr/hyprland/gnome/kde).",
+ "not-installed": "gpu-screen-recorder no instalado",
+ "not-installed-desc": "Por favor instala gpu-screen-recorder para usar las funciones de grabación de pantalla.",
+ "saved": "Grabación guardada",
+ "started": "Grabación iniciada",
+ "stopping": "Deteniendo grabación..."
+ },
+ "theming-processor-failed": {
+ "desc-generic": "Se encontró un error al procesar las plantillas.",
+ "title-matugen": "Falló el procesamiento de la plantilla Matugen.",
+ "title-predefined": "Falló el procesamiento del esquema de color predefinido."
+ },
+ "wallpaper-colors": {
+ "disabled": "Colores del fondo de pantalla desactivados",
+ "enabled": "Colores del fondo de pantalla activados",
+ "label": "Generación de colores para fondos de pantalla",
+ "not-installed": "Matugen no instalado - requerido para extraer colores del fondo de pantalla"
+ },
+ "wifi": {
+ "connected": "Conectado a '{ssid}'",
+ "disabled": "Desactivado",
+ "disconnected": "Desconectado de '{ssid}'",
+ "enabled": "Activado"
}
},
+ "tooltips": {
+ "add-widget": "Añadir widget",
+ "bluetooth-devices": "Dispositivos Bluetooth",
+ "brightness-at": "Brillo: {brightness}%\nClic derecho para configuración.\nDesplaza para modificar el brillo.",
+ "cancel-timer": "Cancelar temporizador",
+ "clear-history": "Limpiar historial",
+ "click-to-start-recording": "Haz clic para iniciar la grabación",
+ "click-to-stop-recording": "Haz clic para detener la grabación",
+ "close": "Cerrar",
+ "connect-disconnect-devices": "Clic izquierdo para conectar. Clic derecho para olvidar.",
+ "delete-notification": "Eliminar notificación",
+ "disable-keep-awake": "Desactivar mantener despierto",
+ "do-not-disturb-disabled": "'No molestar' desactivado",
+ "do-not-disturb-enabled": "'No molestar' activado",
+ "enable-keep-awake": "Activar mantener despierto",
+ "forget-network": "Olvidar red",
+ "home": "Inicio",
+ "input-muted": "Silenciar entrada de audio",
+ "keep-awake": "Mantener despierto",
+ "keyboard-layout": "Distribución de teclado {layout}",
+ "manage-wifi": "Gestionar Wi-Fi",
+ "microphone-volume-at": "Volumen del micrófono al {volume}%.\nClic izquierdo para ajustes. Clic derecho para activar/desactivar el silencio.\nDesplázate para modificar el volumen.",
+ "move-to-center-section": "Mover a la sección central",
+ "move-to-left-section": "Mover a la sección izquierda",
+ "move-to-right-section": "Mover a la sección derecha",
+ "next-media": "Siguiente medio",
+ "next-month": "Mes siguiente",
+ "night-light-disabled": "Luz nocturna desactivada.\nClic izquierdo para cambiar de modo.\nClic derecho para acceder a la configuración.",
+ "night-light-enabled": "Luz nocturna activada.\nClic izquierdo para cambiar de modo.\nClic derecho para acceder a la configuración.",
+ "night-light-forced": "Luz nocturna forzada.\nClic izquierdo para cambiar de modo.\nClic derecho para acceder a la configuración.",
+ "night-light-not-installed": "Luz nocturna no disponible.\nwlsunset no está instalado.",
+ "noctalia-performance-disabled": "El modo de rendimiento de Noctalia está desactivado.\nClic izquierdo para activar.",
+ "noctalia-performance-enabled": "El modo de rendimiento de Noctalia está activado.\nClic izquierdo para desactivar.",
+ "open-control-center": "Abrir el centro de control",
+ "open-notification-history-disable-dnd": "Abrir historial de notificaciones\nClic derecho para desactivar \"No molestar\".",
+ "open-notification-history-enable-dnd": "Abrir historial de notificaciones\nClic derecho para activar \"No molestar\".",
+ "open-settings": "Abrir configuración",
+ "open-tray-dropdown": "Abrir menú desplegable de bandeja",
+ "open-wallpaper-selector": "Abrir selector de fondos de pantalla",
+ "output-muted": "Silenciar salida de audio",
+ "pause": "Pausa",
+ "play": "Reproducir",
+ "power-profile": "Perfil de energía '{profile}'",
+ "previous-media": "Medio anterior",
+ "previous-month": "Mes anterior",
+ "refresh": "Actualizar",
+ "refresh-devices": "Actualizar dispositivos",
+ "refresh-wallpaper-list": "Actualizar lista de fondos de pantalla",
+ "remove-widget": "Eliminar widget",
+ "screen-recorder-not-installed": "La grabadora de pantalla no está instalada",
+ "session-menu": "Menú de sesión",
+ "set-power-profile": "Establecer perfil de energía \"{profile}\"",
+ "start-screen-recording": "Iniciar grabación de pantalla",
+ "stop-screen-recording": "Detener grabación de pantalla",
+ "switch-to-dark-mode": "Cambiar a modo oscuro",
+ "switch-to-light-mode": "Cambiar a modo claro",
+ "up": "Arriba",
+ "volume-at": "Volumen de salida al {volume}%.\nClic izquierdo para ajustes. Clic derecho para activar/desactivar el silencio.\nDesplázate para modificar el volumen.",
+ "wallpaper-selector": "Clic izquierdo: Abrir selector de fondos de pantalla.\nClic derecho: Establecer fondo de pantalla aleatorio.",
+ "widget-settings": "Configuración del widget"
+ },
+ "wallpaper": {
+ "configure-directory": "Configura tu directorio de fondos de pantalla con imágenes.",
+ "fill-modes": {
+ "center": "Centrar",
+ "crop": "Recortar (Rellenar)",
+ "fit": "Ajustar (Contener)",
+ "stretch": "Estirar"
+ },
+ "no-match": "No se encontraron coincidencias.",
+ "no-wallpaper": "No se encontraron fondos de pantalla.",
+ "panel": {
+ "apply-all-monitors": {
+ "description": "Aplica el fondo de pantalla seleccionado a todos los monitores a la vez.",
+ "label": "Aplicar a todos los monitores"
+ },
+ "search": "Buscar:",
+ "title": "Selector de fondo de pantalla"
+ },
+ "transitions": {
+ "disc": "Disco",
+ "fade": "Desvanecer",
+ "none": "Ninguna",
+ "random": "Aleatoria",
+ "stripes": "Rayas",
+ "wipe": "Barrido"
+ },
+ "try-different-search": "Intenta con una búsqueda diferente."
+ },
"weather": {
"clear-sky": "Cielo despejado",
- "mainly-clear": "Mayormente despejado",
- "partly-cloudy": "Parcialmente nublado",
- "overcast": "Nublado",
- "fog": "Niebla",
"drizzle": "Llovizna",
- "snow": "Nieve",
+ "fog": "Niebla",
+ "mainly-clear": "Mayormente despejado",
+ "overcast": "Nublado",
+ "partly-cloudy": "Parcialmente nublado",
"rain-showers": "Chubascos",
+ "snow": "Nieve",
"thunderstorm": "Tormenta",
"unknown": "Desconocido"
},
- "authentication": {
- "failed": "Autenticación fallida",
- "error": "Error de autenticación"
- },
- "general": {
- "no-results": "Sin resultados",
- "no-summary": "Sin resumen",
- "unknown": "Desconocido"
- },
- "battery": {
- "no-battery-detected": "No se detectó ninguna batería.",
- "charging-rate": "Tasa de carga: {rate} W.",
- "discharging-rate": "Tasa de descarga: {rate} W.",
- "charging": "Cargando.",
- "discharging": "Descargando.",
- "plugged-in": "Conectado.",
- "idle": "Inactivo.",
- "time-left": "Tiempo restante: {time}.",
- "time-until-full": "Tiempo hasta carga completa: {time}.",
- "health": "Salud: {percent}%",
- "panel": {
- "title": "Umbral de carga",
- "full": "Capacidad total ({percent}%)",
- "balanced": "Equilibrado ({percent}%)",
- "lifespan": "Vida útil prolongada ({percent}%)",
- "disabled": "Administrador de batería deshabilitado"
- }
- },
- "setup": {
- "customize": {
- "header": "Personaliza tu experiencia",
- "subheader": "Ajusta la posición de la barra, densidad, escala y más."
+ "widgets": {
+ "color-picker": {
+ "apply": "Aplicar",
+ "brightness": "Brillo",
+ "cancel": "Cancelar",
+ "hex": {
+ "description": "Ingresa un código de color hexadecimal.",
+ "label": "Color hexadecimal"
+ },
+ "palette": {
+ "description": "Elige entre una amplia gama de colores predefinidos.",
+ "label": "Paleta"
+ },
+ "rgb": {
+ "description": "Ajusta los valores de rojo, verde, azul y brillo.",
+ "label": "Valores RGB"
+ },
+ "theme-colors": {
+ "description": "Acceso rápido a la paleta de colores de tu tema.",
+ "label": "Colores del tema"
+ },
+ "title": "Selector de colores"
},
- "appearance": {
- "header": "Apariencia",
- "subheader": "Elige modo oscuro y fuentes de color (Matugen o predefinido)."
- },
- "wallpaper": {
- "header": "Elige tu fondo",
- "subheader": "Define el ambiente con un bonito fondo.",
- "select-prompt": "Selecciona un fondo abajo",
- "preview-error": "No se pudo cargar la imagen",
- "none-in-dir": "No se encontraron fondos en el directorio",
- "no-dir": "No se seleccionó un directorio de fondos",
- "no-valid": "No se encontraron imágenes válidas en: {dir}",
- "choose-dir": "Elige un directorio que contenga tus fondos",
- "dir": {
- "label": "Directorio de fondos",
- "description": "Elige la carpeta que contiene tus fondos",
- "browse": "Seleccionar carpeta",
- "select-title": "Seleccionar carpeta de fondos"
+ "datetime-tokens": {
+ "ampm": {
+ "lowercase": "am/pm en minúsculas",
+ "uppercase": "AM/PM en mayúsculas"
+ },
+ "common": {
+ "12hour-time-minutes": "Hora en formato de 12 horas con minutos",
+ "24hour-time-minutes": "Hora en formato de 24 horas con minutos",
+ "24hour-time-seconds": "Hora en formato de 24 horas con segundos",
+ "european-date": "Formato de fecha europeo",
+ "iso-date": "Formato de fecha ISO",
+ "us-date": "Formato de fecha estadounidense",
+ "weekday-date": "Día de la semana con fecha",
+ "weekday-month-day": "Día de la semana, mes y día"
+ },
+ "day": {
+ "abbreviated": "Nombre del día abreviado",
+ "full": "Nombre completo del día",
+ "leading-zero": "Día con cero inicial (01-31)",
+ "no-leading-zero": "Día sin cero inicial (1-31)"
+ },
+ "hour": {
+ "leading-zero": "Hora con cero inicial (00-23) - formato de 24 horas",
+ "no-leading-zero": "Hora sin cero inicial (0-23) - formato de 24 horas"
+ },
+ "minute": {
+ "leading-zero": "Minuto con cero inicial (00-59)",
+ "no-leading-zero": "Minuto sin cero inicial (0-59)"
+ },
+ "month": {
+ "abbreviated": "Nombre del mes abreviado",
+ "full": "Nombre completo del mes",
+ "number-leading-zero": "Mes como número con cero inicial (01-12)",
+ "number-no-zero": "Mes como número sin cero inicial (1-12)"
+ },
+ "second": {
+ "leading-zero": "Segundo con cero inicial (00-59)",
+ "no-leading-zero": "Segundo sin cero inicial (0-59)"
+ },
+ "timezone": {
+ "abbreviation": "Abreviatura de la zona horaria"
+ },
+ "year": {
+ "four-digit": "Año como número de cuatro dígitos",
+ "two-digit": "Año como número de dos dígitos (00-99)"
}
},
- "welcome": {
- "note": "Solo algunos conceptos básicos para empezar: todas las opciones están en Configuración."
+ "file-picker": {
+ "cancel": "Cancelar",
+ "search-placeholder": "Buscar archivos y carpetas...",
+ "select-current": "Seleccionar actual",
+ "select-file": "Seleccionar archivo",
+ "select-folder": "Seleccionar carpeta",
+ "title": "Selector de archivos"
+ },
+ "icon-picker": {
+ "apply": "Aplicar",
+ "cancel": "Cancelar",
+ "search": {
+ "label": "Buscar"
+ },
+ "title": "Selector de iconos"
+ },
+ "text-input": {
+ "clear": "Borrar"
+ },
+ "tooltip": {
+ "placeholder": "Marcador de posición"
+ }
+ },
+ "wifi": {
+ "panel": {
+ "connect": "Conectar",
+ "connected": "Conectado",
+ "disabled": "Wi-Fi está desactivado",
+ "disconnect": "Desconectar",
+ "disconnecting": "Desconectando...",
+ "enable-message": "Activa Wi-Fi para ver las redes disponibles.",
+ "enter-password": "Ingresa la contraseña...",
+ "forget": "Olvidar",
+ "forget-network": "¿Olvidar esta red?",
+ "forgetting": "Olvidando...",
+ "no-networks": "No se encontraron redes",
+ "password": "Contraseña",
+ "saved": "Guardado",
+ "scan-again": "Escanear de nuevo",
+ "searching": "Buscando redes cercanas...",
+ "title": "Wi-Fi"
}
}
}
diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json
index 8d2df536..857c982b 100644
--- a/Assets/Translations/fr.json
+++ b/Assets/Translations/fr.json
@@ -1,1232 +1,277 @@
{
- "settings": {
- "general": {
- "title": "Général",
- "profile": {
- "section": {
- "label": "Profil",
- "description": "Modifiez vos informations utilisateur et votre avatar."
- },
- "picture": {
- "label": "Photo de profil de {user}",
- "description": "Votre photo de profil qui apparaît dans toute l'interface."
- },
- "select-avatar": "Sélectionner une image d'avatar"
- },
- "screen-corners": {
- "section": {
- "label": "Coins de l'écran",
- "description": "Personnalisez l'arrondi des coins de l'écran et les effets visuels."
- },
- "show-corners": {
- "label": "Afficher les coins de l'écran",
- "description": "Affichez des coins arrondis sur le bord de l'écran."
- },
- "solid-black": {
- "label": "Coins noirs unis",
- "description": "Utilisez du noir uni au lieu de la couleur de fond de la barre."
- },
- "radius": {
- "label": "Rayon des coins de l'écran",
- "description": "Ajustez l'arrondi des coins de l'écran.",
- "reset": "Réinitialiser le rayon des coins de l'écran"
- }
- },
- "fonts": {
- "section": {
- "label": "Polices",
- "description": "Choisissez les polices utilisées dans toute l'interface."
- },
- "default": {
- "label": "Police par défaut",
- "description": "Police principale utilisée dans toute l'interface.",
- "placeholder": "Sélectionner la police par défaut...",
- "search-placeholder": "Rechercher des polices...",
- "scale": {
- "description": "Augmenter ou diminuer la taille du texte standard.",
- "label": "Taille de police par défaut"
- }
- },
- "monospace": {
- "label": "Police à chasse fixe",
- "description": "Police à chasse fixe utilisée pour l'affichage des chiffres et des statistiques.",
- "placeholder": "Sélectionner la police à chasse fixe...",
- "search-placeholder": "Rechercher des polices à chasse fixe...",
- "scale": {
- "description": "Augmenter ou diminuer la taille du texte à chasse fixe.",
- "label": "Taille de police à chasse fixe"
- }
- },
- "reset-scaling": "Réinitialiser l'échelle"
- },
- "language": {
- "section": {
- "label": "Langue",
- "description": "Choisissez votre langue préférée pour l'application."
- },
- "select": {
- "label": "Langue de l'application",
- "description": "Sélectionnez la langue utilisée dans l'interface de l'application.",
- "auto-detect": "Automatique"
- }
- },
- "launch-setup-wizard": "Lancer l'assistant d'installation"
- },
- "audio": {
- "title": "Audio",
- "volumes": {
- "section": {
- "label": "Volumes",
- "description": "Ajustez les contrôles de volume et les niveaux audio."
- },
- "output-volume": {
- "label": "Volume de sortie",
- "description": "Niveau de volume de l'ensemble du système."
- },
- "mute-output": {
- "label": "Couper la sortie audio",
- "description": "Couper la sortie audio principale du système."
- },
- "input-volume": {
- "label": "Volume d'entrée",
- "description": "Niveau du volume d'entrée du microphone."
- },
- "mute-input": {
- "label": "Couper l'entrée audio",
- "description": "Couper l'entrée audio par défaut (microphone)."
- },
- "step-size": {
- "label": "Incrément de volume",
- "description": "Ajustez l'incrément pour les changements de volume (molette de la souris, raccourcis clavier)."
- },
- "volume-overdrive": {
- "label": "Autoriser le dépassement du volume",
- "description": "Permet d'augmenter le volume au-delà de 100 %. Cette fonctionnalité peut ne pas être prise en charge par tout le matériel."
- }
- },
- "devices": {
- "section": {
- "label": "Périphériques audio",
- "description": "Configurez les périphériques d'entrée et de sortie audio disponibles."
- },
- "output-device": {
- "label": "Périphérique de sortie",
- "description": "Sélectionnez le périphérique de sortie audio souhaité."
- },
- "input-device": {
- "label": "Périphérique d'entrée",
- "description": "Sélectionnez le périphérique d'entrée audio souhaité."
- }
- },
- "media": {
- "section": {
- "label": "Lecteurs multimédias",
- "description": "Définissez vos applications multimédias préférées et ignorées."
- },
- "primary-player": {
- "label": "Lecteur principal",
- "description": "Entrez un mot-clé pour identifier votre lecteur principal.",
- "placeholder": "ex. spotify, vlc, mpv"
- },
- "excluded-player": {
- "label": "Lecteur exclu",
- "description": "Ajoutez des mots-clés pour les lecteurs que le système doit ignorer. Chaque mot-clé doit être sur une nouvelle ligne.",
- "placeholder": "tapez la sous-chaîne et appuyez sur +"
- },
- "visualizer-type": {
- "label": "Type de visualisation",
- "description": "Choisissez un type de visualisation pour la lecture multimédia."
- },
- "frame-rate": {
- "label": "Fréquence d'images",
- "description": "Des fréquences plus élevées sont plus fluides mais utilisent plus de ressources."
- },
- "scrolling-title": {
- "label": "Titre défilant",
- "description": "Activer le défilement continu pour les titres multimédias longs"
- },
- "scrolling-speed": {
- "label": "Vitesse de défilement",
- "description": "Temps en secondes pour que le titre défile du début à la fin"
- }
- }
- },
- "display": {
- "title": "Affichage",
- "monitors": {
- "section": {
- "label": "Paramètres par moniteur",
- "description": "Ajustez la luminosité pour chaque écran."
- },
- "brightness": "Luminosité",
- "brightness-step": {
- "label": "Incrément de luminosité",
- "description": "Ajustez l'incrément pour les changements de luminosité (molette de la souris et raccourcis clavier)."
- },
- "enforce-minimum": {
- "label": "Imposer une luminosité minimale (1%)",
- "description": "Résout le problème de l'extinction complète du rétroéclairage sur certains écrans à 0% de luminosité."
- }
- },
- "night-light": {
- "section": {
- "label": "Éclairage nocturne",
- "description": "Réduit l'émission de lumière bleue pour vous aider à mieux dormir et réduire la fatigue oculaire."
- },
- "enable": {
- "label": "Activer l'éclairage nocturne",
- "description": "Applique un filtre de couleur chaude pour réduire l'émission de lumière bleue."
- },
- "temperature": {
- "label": "Température de couleur",
- "description": "Réglez la chaleur des couleurs pour la nuit et le jour.",
- "night": "Nuit",
- "day": "Jour"
- },
- "auto-schedule": {
- "label": "Planification automatique",
- "description": "Basé sur l'heure du coucher et du lever du soleil à {location} - recommandé."
- },
- "manual-schedule": {
- "label": "Planification manuelle",
- "description": "Définissez des heures personnalisées pour le lever et le coucher du soleil.",
- "sunrise": "Heure du lever du soleil",
- "sunset": "Heure du coucher du soleil",
- "select-start": "Sélectionner l'heure de début",
- "select-stop": "Sélectionner l'heure de fin"
- },
- "force-activation": {
- "label": "Forcer l'activation",
- "description": "Ignore la planification et applique le filtre nocturne immédiatement."
- }
- }
- },
- "bar": {
- "title": "Barre",
- "appearance": {
- "section": {
- "label": "Apparence",
- "description": "Personnalisez l'apparence et la position de la barre."
- },
- "position": {
- "label": "Position de la barre",
- "description": "Choisissez où placer la barre sur l'écran."
- },
- "density": {
- "label": "Densité de la barre",
- "description": "Ajustez le remplissage de la barre pour un aspect compact ou spacieux."
- },
- "background-opacity": {
- "label": "Opacité de l'arrière-plan",
- "description": "Ajustez l'opacité de l'arrière-plan de la barre."
- },
- "show-capsule": {
- "label": "Afficher la capsule",
- "description": "Afficher les arrière-plans des widgets."
- },
- "floating": {
- "label": "Barre flottante",
- "description": "Affiche la barre sous forme de 'pilule' flottante. Note : Ceci déplacera les coins de l'écran vers les bords."
- },
- "margins": {
- "label": "Marges",
- "description": "Ajustez les marges autour de la barre flottante.",
- "vertical": "Verticale",
- "horizontal": "Horizontale"
- }
- },
- "widgets": {
- "section": {
- "label": "Positionnement des widgets",
- "description": "Faites glisser et déposez les widgets pour les réorganiser. Les badges indiquent l'utilisation : [G]auche, [C]entre, [D]roite."
- }
- },
- "monitors": {
- "section": {
- "label": "Affichage sur les moniteurs",
- "description": "Afficher la barre sur des moniteurs spécifiques. Par défaut, sur tous si aucun n'est choisi."
- }
- },
- "tray": {
- "blacklist": {
- "label": "Liste noire",
- "description": "Ajouter des règles d'exclusion pour la boîte à miniatures, prend en charge les caractères génériques (*).",
- "placeholder": "ex: nm-applet, Fcitx*"
- }
- }
- },
- "dock": {
- "title": "Dock",
- "enabled": {
- "label": "Activer le dock",
- "description": "Afficher ou masquer complètement le dock"
- },
- "appearance": {
- "section": {
- "label": "Apparence",
- "description": "Personnalisez le comportement et l'apparence du dock."
- },
- "display": {
- "label": "Affichage",
- "description": "Choisissez comment le dock se comporte.",
- "always-visible": "Toujours visible",
- "auto-hide": "Masquer automatiquement",
- "exclusive": "Exclusif"
- },
- "background-opacity": {
- "label": "Opacité de l'arrière-plan",
- "description": "Ajustez l'opacité de l'arrière-plan du dock."
- },
- "floating-distance": {
- "label": "Distance de flottaison du dock",
- "description": "Ajustez la distance de flottaison par rapport au bord de l'écran."
- },
- "icon-size": {
- "label": "Taille du Dock",
- "description": "Ajuster la taille globale du Dock."
- },
- "colorize-icons": {
- "label": "Coloriser les icônes",
- "description": "Appliquer les couleurs du thème aux icônes d'applications du dock (applications non focalisées uniquement)."
- }
- },
- "monitors": {
- "section": {
- "label": "Affichage sur les moniteur",
- "description": "Afficher le dock sur des écrans spécifiques. Par défaut, il s'affiche sur tous les écrans si aucun n'est sélectionné."
- },
- "only-same-output": {
- "description": "Afficher uniquement les applications de la sortie où le dock est situé.",
- "label": "Seulement les applications de la même sortie"
- }
- }
- },
- "launcher": {
- "title": "Lanceur",
- "settings": {
- "section": {
- "label": "Apparence",
- "description": "Personnalisez le comportement et l'apparence du lanceur."
- },
- "position": {
- "label": "Position",
- "description": "Choisissez où le panneau du lanceur apparaît."
- },
- "background-opacity": {
- "label": "Opacité de l'arrière-plan",
- "description": "Ajustez l'opacité de l'arrière-plan du lanceur."
- },
- "clipboard-history": {
- "label": "Activer l'historique du presse-papiers",
- "description": "Accédez aux éléments précédemment copiés depuis le lanceur."
- },
- "sort-by-usage": {
- "label": "Trier par les plus utilisés",
- "description": "Lorsque cette option est activée, les applications fréquemment lancées apparaissent en premier dans la liste."
- },
- "use-app2unit": {
- "label": "Utiliser App2Unit pour lancer les applications",
- "description": "Utilise une méthode de lancement alternative pour mieux gérer les processus des applications et prévenir les problèmes."
- },
- "terminal-command": {
- "label": "Commande du terminal",
- "description": "Commande pour lancer un terminal. Ex: 'kitty -e' ou 'gnome-terminal --'."
- },
- "custom-launch-prefix": {
- "label": "Préfixe de lancement personnalisé",
- "description": "Préfixer les commandes avec un lanceur personnalisé (ex. 'runapp' pour l'intégration systemd)."
- },
- "custom-launch-prefix-enabled": {
- "label": "Activer le préfixe de lancement personnalisé",
- "description": "Utiliser un préfixe personnalisé pour lancer les applications au lieu de la méthode par défaut."
- }
- }
- },
- "notifications": {
- "title": "Notifications",
- "settings": {
- "section": {
- "label": "Apparence",
- "description": "Configurez l'apparence et le comportement des notifications."
- },
- "do-not-disturb": {
- "label": "Ne pas déranger",
- "description": "Désactive toutes les fenêtres de notification lorsque cette option est activée."
- },
- "enable-osd": {
- "label": "Activer l'affichage à l'écran (OSD)",
- "description": "Afficher les changements de volume et de luminosité en temps réel."
- },
- "location": {
- "label": "Emplacement",
- "description": "Où les notifications apparaissent à l'écran."
- },
- "always-on-top": {
- "label": "Toujours au premier plan",
- "description": "Afficher les notifications au-dessus des fenêtres en plein écran et des autres applications."
- },
- "background-opacity": {
- "label": "Opacité du fond",
- "description": "Ajuster l'opacité des arrière-plans de notifications."
- }
- },
- "duration": {
- "section": {
- "label": "Durée des notifications",
- "description": "Configurer la durée d'affichage des notifications en fonction de leur niveau d'urgence."
- },
- "respect-expire": {
- "label": "Respecter le délai d'expiration",
- "description": "Utiliser le délai d'expiration défini dans la notification."
- },
- "low-urgency": {
- "label": "Urgence faible",
- "description": "Durée de visibilité des notifications de faible priorité."
- },
- "normal-urgency": {
- "label": "Urgence normale",
- "description": "Durée de visibilité des notifications de priorité normale."
- },
- "critical-urgency": {
- "label": "Urgence critique",
- "description": "Durée de visibilité des notifications de priorité critique."
- }
- },
- "monitors": {
- "section": {
- "label": "Affichage sur les moniteurs",
- "description": "Afficher les notifications sur des moniteurs spécifiques. Par défaut, sur tous si aucun n'est choisi."
- }
- }
- },
- "osd": {
- "title": "Affichage à l'écran",
- "description": "Configurer les indicateurs à l'écran tels que le volume et la luminosité.",
- "section": {
- "general": {
- "label": "Général",
- "description": "Configurer la visibilité et le comportement de l'OSD."
- }
- },
- "enabled": {
- "label": "Activer l'affichage à l'écran",
- "description": "Afficher en temps réel les changements de volume et de luminosité."
- },
- "always-on-top": {
- "label": "Toujours au premier plan",
- "description": "Afficher l'OSD au-dessus des fenêtres plein écran et autres couches."
- },
- "location": {
- "label": "Emplacement",
- "description": "Emplacement des affichages à l'écran."
- },
- "duration": {
- "section": {
- "label": "Délai d'auto‑masquage",
- "description": "Temps d'affichage avant masquage automatique de l'OSD."
- },
- "auto-hide": {
- "label": "Masquer après",
- "description": "Ajuster le délai avant disparition de l'OSD."
- }
- },
- "monitors": {
- "section": {
- "label": "Affichage sur les moniteurs",
- "description": "Afficher l'OSD sur des moniteurs spécifiques. Par défaut sur tous si aucun n'est choisi."
- }
- }
- },
- "wallpaper": {
- "title": "Fond d'écran",
- "settings": {
- "section": {
- "label": "Paramètres du fond d'écran",
- "description": "Contrôlez comment les fonds d'écran sont gérés et affichés."
- },
- "enable-management": {
- "label": "Activer la gestion des fonds d'écran",
- "description": "Gérez les fonds d'écran avec Noctalia. Décochez si vous préférez utiliser une autre application."
- },
- "folder": {
- "label": "Dossier des fonds d'écran",
- "description": "Chemin vers votre dossier principal de fonds d'écran.",
- "tooltip": "Parcourir le dossier des fonds d'écran"
- },
- "recursive-search": {
- "label": "Rechercher dans les sous-dossiers",
- "description": "Rechercher également des fonds d'écran dans les sous-dossiers du répertoire des fonds d'écran."
- },
- "monitor-specific": {
- "label": "Dossiers spécifiques au moniteur",
- "description": "Définissez un dossier de fond d'écran différent pour chaque moniteur.",
- "tooltip": "Parcourir le dossier des fonds d'écran"
- },
- "select-folder": "Sélectionner le dossier des fonds d'écran",
- "select-monitor-folder": "Sélectionner le dossier des fonds d'écran du moniteur"
- },
- "look-feel": {
- "section": {
- "label": "Apparence et ergonomie"
- },
- "fill-mode": {
- "label": "Mode de remplissage",
- "description": "Sélectionnez comment l'image doit être mise à l'échelle pour correspondre à la résolution de votre moniteur."
- },
- "fill-color": {
- "label": "Couleur de remplissage",
- "description": "Choisissez une couleur de remplissage qui peut apparaître derrière le fond d'écran."
- },
- "transition-type": {
- "label": "Type de transition",
- "description": "Type d'animation lors du changement de fond d'écran."
- },
- "transition-duration": {
- "label": "Durée de la transition",
- "description": "Durée des animations de transition en secondes."
- },
- "edge-smoothness": {
- "label": "Adoucir le bord de la transition",
- "description": "Applique un effet de flou progressif au bord des transitions."
- }
- },
- "automation": {
- "section": {
- "label": "Automatisation"
- },
- "random-wallpaper": {
- "label": "Fond d'écran aléatoire",
- "description": "Planifiez des changements de fond d'écran aléatoires à intervalles réguliers."
- },
- "interval": {
- "label": "Intervalle du fond d'écran",
- "description": "Fréquence de changement automatique des fonds d'écran."
- },
- "custom-interval": {
- "label": "Intervalle personnalisé",
- "description": "Entrez l'heure au format HH:MM (ex: 01:30)."
- }
- }
- },
- "color-scheme": {
- "title": "Jeu de couleurs",
- "color-source": {
- "section": {
- "label": "Source des couleurs",
- "description": "Paramètres principaux pour les couleurs de Noctalia."
- },
- "use-wallpaper-colors": {
- "label": "Utiliser les couleurs du fond d'écran",
- "description": "Générer des schémas de couleurs à partir de votre fond d'écran avec Matugen. Extrait automatiquement les couleurs pour créer un thème cohérent."
- },
- "matugen-scheme-type": {
- "label": "Type de schéma Matugen",
- "description": {
- "scheme-content": "Dérive des couleurs qui correspondent étroitement à l'image sous-jacente.",
- "scheme-expressive": "Palette vibrante avec une saturation ludique.",
- "scheme-fidelity": "Palette haute fidélité qui préserve les teintes sources.",
- "scheme-fruit-salad": "Mélange coloré d'accents vifs et contrastés.",
- "scheme-monochrome": "Palette minimaliste construite autour d'une seule teinte.",
- "scheme-neutral": "Palette de couleurs atténuées aux tons doux et apaisants.",
- "scheme-rainbow": "Palette diversifiée couvrant tout le spectre.",
- "scheme-tonal-spot": "Palette équilibrée avec des accents ciblés."
- }
- }
- },
- "dark-mode": {
- "switch": {
- "label": "Mode sombre",
- "description": "Passe à un thème plus sombre pour une visualisation plus facile la nuit."
- },
- "mode": {
- "description": "Active la commutation automatique entre le mode clair et le mode sombre.",
- "label": "Programmation du mode sombre",
- "location": "Emplacement",
- "manual": "Manuel",
- "off": "Éteint"
- }
- },
- "predefined": {
- "section": {
- "label": "Jeux de couleurs prédéfinis",
- "description": "Choisissez parmi une collection de jeux de couleurs prédéfinis."
- },
- "generate-templates": {
- "label": "Générer des modèles pour les schémas prédéfinis",
- "description": "Génère des modèles Matugen (GTK, thèmes de terminal, etc.) lors de l'utilisation de schémas de couleurs prédéfinis."
- }
- },
- "templates": {
- "ui": {
- "label": "UI",
- "description": "Thématisation de l'environnement de bureau et de la boîte à outils d'interface utilisateur.",
- "gtk": {
- "description": "Écrire {filepath}"
- },
- "qt": {
- "description": "Écrire {filepath}"
- },
- "kcolorscheme": {
- "description": "Écrire {filepath}"
- }
- },
- "terminal": {
- "label": "Terminal",
- "description": "Thématisation de l'émulateur de terminal.",
- "kitty": {
- "description": "Écrire ~/.config/kitty/themes/noctalia.conf et recharger",
- "description-missing": "Nécessite que le terminal Kitty soit installé"
- },
- "ghostty": {
- "description": "Écrire ~/.config/ghostty/themes/noctalia et recharger",
- "description-missing": "Nécessite que le terminal ghostty soit installé"
- },
- "foot": {
- "description": "Écrire ~/.config/foot/themes/noctalia et recharger",
- "description-missing": "Nécessite que le terminal foot soit installé"
- }
- },
- "programs": {
- "label": "Programmes",
- "description": "Thématisation spécifique aux applications.",
- "fuzzel": {
- "description": "Écrire ~/.config/fuzzel/themes/noctalia et recharger",
- "description-missing": "Nécessite que le lanceur fuzzel soit installé"
- },
- "vicinae": {
- "description": "Écrire {filepath} et recharger",
- "description-missing": "Nécessite que le lanceur {app} soit installé"
- },
- "walker": {
- "description": "Écrire {filepath} et définir le thème sur noctalia",
- "description-missing": "Nécessite que le lanceur {app} soit installé"
- },
- "discord": {
- "description": "Écrire {filepath} pour {client}",
- "description-missing": "Aucun client Discord détecté. Installez vencord, vesktop, webcord, armcord, equibop, lightcord ou dorion."
- },
- "pywalfox": {
- "description": "Écrire ~/.cache/wal/colors.json et exécuter pywalfox update",
- "description-missing": "Nécessite que le paquet pywalfox soit installé"
- },
- "code": {
- "description": "Écrire {filepath}. Le thème Hyprluna doit être installé et activé manuellement.",
- "description-missing": "Nécessite l'installation de {app}"
- }
- },
- "misc": {
- "label": "Divers",
- "description": "Options de configuration supplémentaires.",
- "user-templates": {
- "label": "Modèles utilisateur",
- "description": "Activer la configuration Matugen définie par l'utilisateur. Un fichier modèle sera créé dans ~/.config/noctalia/user-templates.toml lors de la première activation"
- }
- },
- "section": {
- "description": "Appliquer des couleurs aux applications externes.",
- "label": "Modèles"
- }
- }
- },
- "location": {
- "title": "Emplacement",
- "location": {
- "section": {
- "label": "Votre emplacement",
- "description": "Obtenez une météo et une planification de l'éclairage nocturne précises en définissant votre emplacement."
- },
- "search": {
- "label": "Rechercher un emplacement",
- "description": "ex: Toronto, ON",
- "placeholder": "Entrez le nom de l'emplacement"
- }
- },
- "weather": {
- "section": {
- "label": "Météo",
- "description": "Choisissez votre unité de température préférée."
- },
- "enabled": {
- "label": "Activer la météo",
- "description": "Afficher les informations météorologiques dans toute l'interface et récupérer les données météo. Lorsque désactivé, tous les éléments météo seront masqués et aucune requête réseau ne sera effectuée."
- },
- "fahrenheit": {
- "label": "Afficher la température en Fahrenheit (°F)",
- "description": "Afficher la température en Fahrenheit au lieu de Celsius."
- },
- "show-in-calendar": {
- "description": "Afficher les prévisions météo quotidiennes directement dans votre vue calendrier.",
- "label": "Afficher la météo dans le calendrier"
- }
- },
- "date-time": {
- "section": {
- "label": "Date & heure",
- "description": "Personnalisez l'affichage de la date et de l'heure."
- },
- "12hour-format": {
- "label": "Utiliser le format horaire de 12 heures",
- "description": "Affiche l'heure au format 12 heures sur l'écran de verrouillage et dans le calendrier. L'horloge de la barre possède ses propres paramètres."
- },
- "week-numbers": {
- "label": "Afficher les numéros de semaine",
- "description": "Affiche la semaine de l'année (ex: Semaine 38) dans le calendrier."
- },
- "show-events": {
- "label": "Afficher les événements du calendrier",
- "description": "Afficher les événements dans le panneau du calendrier."
- },
- "use-analog": {
- "description": "Afficher une horloge de style analogique sur l'écran du calendrier.",
- "label": "Utiliser une horloge de style analogique."
- },
- "first-day-of-week": {
- "automatic": "Automatique (utiliser les paramètres régionaux du système)",
- "description": "Choisissez quel jour commence la semaine dans le calendrier.",
- "label": "Premier jour de la semaine"
- }
- }
- },
- "network": {
- "title": "Réseau",
- "section": {
- "description": "Gérez les connexions Wi-Fi et Bluetooth."
- },
- "wifi": {
- "label": "Activer le Wi-Fi"
- },
- "bluetooth": {
- "label": "Activer le Bluetooth"
- }
- },
- "screen-recorder": {
- "title": "Enregistreur d'écran",
- "general": {
- "section": {
- "label": "Paramètres généraux",
- "description": "Gérez la sortie et le contenu de l'enregistrement d'écran."
- },
- "output-folder": {
- "label": "Dossier de sortie",
- "description": "Dossier où les enregistrements d'écran seront sauvegardés.",
- "tooltip": "Parcourir le dossier de sortie"
- },
- "show-cursor": {
- "label": "Afficher le curseur",
- "description": "Enregistrez le curseur de la souris dans la vidéo."
- },
- "select-output-folder": "Sélectionner le dossier de sortie"
- },
- "video": {
- "section": {
- "label": "Paramètres vidéo",
- "description": "Configurez les options d'enregistrement vidéo."
- },
- "video-source": {
- "label": "Source vidéo",
- "description": "Portail est recommandé, si vous obtenez des artefacts, essayez Écran."
- },
- "frame-rate": {
- "label": "Fréquence d'images",
- "description": "Fréquence d'images cible pour les enregistrements d'écran."
- },
- "video-quality": {
- "label": "Qualité vidéo",
- "description": "Une qualité supérieure entraîne des fichiers de plus grande taille."
- },
- "video-codec": {
- "label": "Codec vidéo",
- "description": "h264 est le codec le plus courant."
- },
- "color-range": {
- "label": "Gamme de couleurs",
- "description": "Limitée est recommandée pour une meilleure compatibilité."
- }
- },
- "audio": {
- "section": {
- "label": "Paramètres audio",
- "description": "Configurez les options d'enregistrement audio."
- },
- "audio-source": {
- "label": "Source audio",
- "description": "Source audio à capturer pendant l'enregistrement."
- },
- "audio-codec": {
- "label": "Codec audio",
- "description": "Opus est recommandé pour de meilleures performances et une taille audio minimale."
- }
- }
- },
- "about": {
- "title": "À propos",
- "noctalia": {
- "section": {
- "label": "Shell Noctalia",
- "description": "Un shell de bureau élégant et minimaliste, conçu avec soin pour Wayland et bâti avec Quickshell."
- },
- "latest-version": "Dernière version :",
- "installed-version": "Version installée :",
- "download-latest": "Télécharger la dernière version"
- },
- "contributors": {
- "section": {
- "label": "Contributeurs",
- "description": "Un grand merci à notre {count} super contributeur !",
- "description_plural": "Un grand merci à nos {count} super contributeurs !"
- }
- },
- "support": "Soutenez-nous"
- },
- "hooks": {
- "title": "Hooks",
- "system-hooks": {
- "section": {
- "label": "Hooks système",
- "description": "Configurez des commandes à exécuter lors d'événements système."
- },
- "enable": {
- "label": "Activer les hooks",
- "description": "Activer ou désactiver toutes les commandes de hook."
- }
- },
- "wallpaper-changed": {
- "label": "Fond d'écran changé",
- "description": "Commande à exécuter lorsque le fond d'écran change.",
- "placeholder": "ex: notify-send \"Fond d'écran\" \"Changé\""
- },
- "theme-changed": {
- "label": "Thème changé",
- "description": "Commande à exécuter lorsque le thème bascule entre le mode sombre et clair.",
- "placeholder": "ex: notify-send \"Thème\" \"Basculé\""
- },
- "info": {
- "command-info": {
- "label": "Informations sur la commande de hook",
- "description": "• Les commandes sont exécutées via le shell (sh -c)\n• Les commandes s'exécutent en arrière-plan (détachées)\n• Les boutons de test s'exécutent avec les valeurs actuelles"
- },
- "parameters": {
- "label": "Paramètres disponibles",
- "description": "• Hook Fond d'écran : $1 = chemin du fond d'écran, $2 = nom de l'écran\n• Hook de bascule de thème : $1 = true/false (état du mode sombre)"
- }
- }
- },
- "control-center": {
- "position": {
- "description": "Choisissez où le panneau du Centre de contrôle apparaît lorsqu'il est ouvert.",
- "label": "Position"
- },
- "section": {
- "description": "Configurer le positionnement et le comportement du panneau du Centre de contrôle.",
- "label": "Apparence"
- },
- "title": "Centre de contrôle",
- "cards": {
- "section": {
- "description": "Personnalisez les commandes qui apparaissent dans le Centre de contrôle et leur ordre d'affichage.",
- "label": "Cartes"
- }
- },
- "shortcuts": {
- "section": {
- "description": "Configurer et gérer les widgets de raccourcis.",
- "label": "Widgets de raccourcis"
- },
- "sectionLeft": "Gauche",
- "sectionRight": "Droite"
- }
- },
- "user-interface": {
- "animation-disable": {
- "description": "Désactiver toutes les animations pour une expérience plus rapide et plus réactive.",
- "label": "Désactiver les animations de l'interface utilisateur"
- },
- "panels-overlay": {
- "label": "Ouvrir les panneaux en couche superposée",
- "description": "Les panneaux apparaîtront au-dessus des fenêtres en plein écran"
- },
- "animation-speed": {
- "description": "Ajuster la vitesse globale de l'animation.",
- "label": "Vitesse d'animation",
- "reset": "Réinitialiser la vitesse de l'animation"
- },
- "border-radius": {
- "description": "Contrôle l'arrondi des angles des fenêtres, des boutons et d'autres éléments.",
- "label": "Rayon de bordure",
- "reset": "Réinitialiser le rayon de la bordure"
- },
- "scaling": {
- "description": "Modifie la taille de l'interface utilisateur générale, à l'exception de la barre.",
- "label": "Mise à l'échelle de l'interface",
- "reset-scaling": "Réinitialiser l'échelle de l'interface"
- },
- "section": {
- "description": "Personnaliser l'apparence, l'ergonomie et le comportement de l'interface.",
- "label": "Apparence"
- },
- "title": "Interface utilisateur",
- "tooltips": {
- "description": "Activer ou désactiver les info-bulles dans toute l'interface.",
- "label": "Afficher les infobulles"
- },
- "panels-attached-to-bar": {
- "description": "Lorsque cette option est activée, les panneaux seront attachés à la barre avec un design élégant de coin inversé.",
- "label": "Fixer les panneaux à la barre."
- }
- },
- "lock-screen": {
- "compact-lockscreen": {
- "description": "Afficher uniquement le champ de saisie de connexion et les commandes système, en masquant les widgets météo et multimédia.",
- "label": "Écran de verrouillage compact"
- },
- "lock-on-suspend": {
- "description": "Verrouiller automatiquement l'écran lors de la mise en veille du système.",
- "label": "Verrouiller à la suspension"
- },
- "title": "Écran de verrouillage"
- }
- },
- "widgets": {
- "tooltip": {
- "placeholder": "Espace réservé"
- },
- "file-picker": {
- "select-folder": "Sélectionner un dossier",
- "select-file": "Sélectionner un fichier",
- "cancel": "Annuler",
- "search-placeholder": "Rechercher des fichiers et des dossiers...",
- "select-current": "Sélectionner Actuel",
- "title": "Sélecteur de fichiers"
- },
- "datetime-tokens": {
- "common": {
- "12hour-time-minutes": "Heure sur 12 heures avec minutes",
- "24hour-time-minutes": "Heure sur 24 heures avec minutes",
- "24hour-time-seconds": "Heure sur 24 heures avec secondes",
- "weekday-month-day": "Jour de la semaine, mois et jour",
- "iso-date": "Format de date ISO",
- "us-date": "Format de date américain",
- "european-date": "Format de date européen",
- "weekday-date": "Jour de la semaine avec date"
- },
- "hour": {
- "no-leading-zero": "Heure sans zéro initial (0-23) - format 24 heures",
- "leading-zero": "Heure avec zéro initial (00-23) - format 24 heures"
- },
- "minute": {
- "no-leading-zero": "Minute sans zéro initial (0-59)",
- "leading-zero": "Minute avec zéro initial (00-59)"
- },
- "second": {
- "no-leading-zero": "Seconde sans zéro initial (0-59)",
- "leading-zero": "Seconde avec zéro initial (00-59)"
- },
- "ampm": {
- "uppercase": "AM/PM en majuscules",
- "lowercase": "am/pm en minuscules"
- },
- "timezone": {
- "abbreviation": "Abréviation du fuseau horaire"
- },
- "year": {
- "two-digit": "Année sur deux chiffres (00-99)",
- "four-digit": "Année sur quatre chiffres"
- },
- "month": {
- "number-no-zero": "Mois en chiffre sans zéro initial (1-12)",
- "number-leading-zero": "Mois en chiffre avec zéro initial (01-12)",
- "abbreviated": "Nom du mois abrégé",
- "full": "Nom complet du mois"
- },
- "day": {
- "no-leading-zero": "Jour sans zéro initial (1-31)",
- "leading-zero": "Jour avec zéro initial (01-31)",
- "abbreviated": "Nom du jour abrégé",
- "full": "Nom complet du jour"
- }
- },
- "icon-picker": {
- "title": "Sélecteur d'icônes",
- "search": {
- "label": "Rechercher"
- },
- "cancel": "Annuler",
- "apply": "Appliquer"
- },
- "color-picker": {
- "title": "Sélecteur de couleurs",
- "hex": {
- "label": "Couleur hexadécimale",
- "description": "Entrez un code couleur hexadécimal."
- },
- "rgb": {
- "label": "Valeurs RVB",
- "description": "Ajustez les valeurs de rouge, vert, bleu et de luminosité."
- },
- "brightness": "Luminosité",
- "theme-colors": {
- "label": "Couleurs du thème",
- "description": "Accès rapide à la palette de couleurs de votre thème."
- },
- "palette": {
- "label": "Palette",
- "description": "Choisissez parmi une large gamme de couleurs prédéfinies."
- },
- "cancel": "Annuler",
- "apply": "Appliquer"
- },
- "text-input": {
- "clear": "Effacer"
- }
+ "authentication": {
+ "error": "Erreur d'authentification",
+ "failed": "Échec de l'authentification"
},
"bar": {
"widget-settings": {
- "dialog": {
- "cancel": "Annuler",
- "apply": "Appliquer"
- },
- "section-editor": {
- "placeholder": "Sélectionnez un widget à ajouter...",
- "search-placeholder": "Rechercher des widgets..."
- },
"active-window": {
- "show-app-icon": {
- "label": "Afficher l'icône de l'application",
- "description": "Afficher l'icône de l'application à côté du titre de la fenêtre."
- },
- "scrolling-mode": {
- "label": "Mode de défilement",
- "description": "Contrôler quand le défilement de texte est activé pour les titres de fenêtre longs."
+ "colorize-icons": {
+ "description": "Appliquer les couleurs du thème à l'icône de la fenêtre active.",
+ "label": "Coloriser les icônes"
},
"hide-mode": {
- "label": "Mode de masquage",
- "description": "Contrôle le comportement du widget lorsqu'aucune fenêtre n'est active."
+ "description": "Contrôle le comportement du widget lorsqu'aucune fenêtre n'est active.",
+ "label": "Mode de masquage"
},
"max-width": {
- "label": "Largeur Maximale",
- "description": "Définit la taille horizontale maximale du widget. Le widget se rétrécira pour s'adapter à un contenu plus court."
+ "description": "Définit la taille horizontale maximale du widget. Le widget se rétrécira pour s'adapter à un contenu plus court.",
+ "label": "Largeur Maximale"
+ },
+ "scrolling-mode": {
+ "description": "Contrôler quand le défilement de texte est activé pour les titres de fenêtre longs.",
+ "label": "Mode de défilement"
+ },
+ "show-app-icon": {
+ "description": "Afficher l'icône de l'application à côté du titre de la fenêtre.",
+ "label": "Afficher l'icône de l'application"
},
"use-fixed-width": {
- "label": "Utiliser une Largeur Fixe",
- "description": "Lorsque activé, le widget utilisera toujours la largeur maximale au lieu de s'ajuster dynamiquement au contenu."
- },
- "colorize-icons": {
- "label": "Coloriser les icônes",
- "description": "Appliquer les couleurs du thème à l'icône de la fenêtre active."
+ "description": "Lorsque activé, le widget utilisera toujours la largeur maximale au lieu de s'ajuster dynamiquement au contenu.",
+ "label": "Utiliser une Largeur Fixe"
}
},
- "system-monitor": {
- "cpu-usage": {
- "label": "Utilisation du CPU",
- "description": "Afficher le pourcentage d'utilisation actuel du CPU."
+ "audio-visualizer": {
+ "color-name": {
+ "description": "Sélectionnez la couleur du visualiseur.",
+ "label": "Couleur de remplissage"
},
- "cpu-temperature": {
- "label": "Température du CPU",
- "description": "Afficher les lectures de température du CPU si disponibles."
+ "hide-when-idle": {
+ "description": "Si activé, le visualiseur est masqué sauf lorsqu'un lecteur est en lecture.",
+ "label": "Masquer lorsqu'aucun média n'est en lecture"
},
- "memory-usage": {
- "label": "Utilisation de la mémoire",
- "description": "Afficher les informations actuelles d'utilisation de la RAM."
- },
- "memory-percentage": {
- "label": "Mémoire en pourcentage",
- "description": "Afficher l'utilisation de la mémoire en pourcentage au lieu de valeurs absolues."
- },
- "network-traffic": {
- "label": "Trafic réseau",
- "description": "Afficher les vitesses de téléchargement et de téléversement du réseau."
- },
- "storage-usage": {
- "label": "Utilisation du stockage",
- "description": "Afficher les informations d'utilisation de l'espace disque."
- }
- },
- "notification-history": {
- "show-unread-badge": {
- "label": "Afficher le badge non lu",
- "description": "Afficher un badge montrant le nombre de notifications non lues."
- },
- "hide-badge-when-zero": {
- "label": "Masquer le badge quand zéro",
- "description": "Masquer le badge de notification quand il n'y a pas de notifications non lues."
+ "width": {
+ "description": "Largeur personnalisée du composant.",
+ "label": "Largeur"
}
},
"battery": {
"display-mode": {
- "label": "Mode d'affichage",
- "description": "Choisissez comment vous souhaitez que cette valeur apparaisse."
+ "description": "Choisissez comment vous souhaitez que cette valeur apparaisse.",
+ "label": "Mode d'affichage"
},
"low-battery-threshold": {
- "label": "Seuil d'avertissement de batterie faible",
- "description": "Afficher un avertissement lorsque la batterie tombe en dessous de ce pourcentage."
- }
- },
- "control-center": {
- "use-distro-logo": {
- "label": "Utiliser le logo de la distribution au lieu de l'icône",
- "description": "Utiliser le logo de votre distribution au lieu d'une icône personnalisée."
- },
- "icon": {
- "label": "Icône",
- "description": "Sélectionnez une icône de la bibliothèque ou un fichier personnalisé."
- },
- "browse-library": "Parcourir la bibliothèque",
- "browse-file": "Parcourir les fichiers",
- "select-custom-icon": "Sélectionner une icône personnalisée"
- },
- "keyboard-layout": {
- "display-mode": {
- "label": "Mode d'affichage",
- "description": "Choisissez comment vous souhaitez que cette valeur apparaisse."
- }
- },
- "volume": {
- "display-mode": {
- "label": "Mode d'affichage",
- "description": "Choisissez comment vous souhaitez que cette valeur apparaisse."
- }
- },
- "workspace": {
- "label-mode": {
- "label": "Mode d'étiquette",
- "description": "Choisir comment les étiquettes d'espace de travail sont affichées."
- },
- "hide-unoccupied": {
- "label": "Masquer les inoccupés",
- "description": "Ne pas afficher les espaces de travail sans fenêtres."
- },
- "character-count": {
- "label": "Nombre de caractères",
- "description": "Nombre de caractères à afficher des noms d'espaces de travail (1-10)."
- }
- },
- "microphone": {
- "display-mode": {
- "label": "Mode d'affichage",
- "description": "Choisissez comment vous souhaitez que cette valeur apparaisse."
+ "description": "Afficher un avertissement lorsque la batterie tombe en dessous de ce pourcentage.",
+ "label": "Seuil d'avertissement de batterie faible"
}
},
"brightness": {
"display-mode": {
- "label": "Mode d'affichage",
- "description": "Choisissez comment vous souhaitez que cette valeur apparaisse."
+ "description": "Choisissez comment vous souhaitez que cette valeur apparaisse.",
+ "label": "Mode d'affichage"
}
},
- "spacer": {
- "width": {
- "label": "Largeur",
- "description": "Largeur de l'espacement en pixels"
- }
- },
- "custom-button": {
- "icon": {
- "label": "Icône",
- "description": "Sélectionnez une icône dans la bibliothèque."
- },
- "browse": "Parcourir",
- "left-click": {
- "label": "Clic gauche",
- "description": "Commande à exécuter quand le bouton est cliqué à gauche."
- },
- "right-click": {
- "label": "Clic droit",
- "description": "Commande à exécuter quand le bouton est cliqué à droite."
- },
- "middle-click": {
- "label": "Clic milieu",
- "description": "Commande à exécuter quand le bouton est cliqué au milieu."
- },
- "dynamic-text": "Texte dynamique",
- "display-command-output": {
- "label": "Afficher la sortie de la commande",
- "description": "Entrez une commande à exécuter à intervalle régulier. La première ligne de sa sortie sera affichée sous forme de texte.",
- "stream-description": "Entrez une commande à exécuter en continu."
- },
- "refresh-interval": {
- "label": "Intervalle d'actualisation",
- "description": "Intervalle en millisecondes."
- },
- "text-stream": {
- "label": "Flux",
- "description": "Les lignes diffusées depuis la commande seront affichées sous forme de texte sur le bouton."
- },
- "collapse-condition": {
- "label": "Condition d’effondrement",
- "description": "Si le texte de sortie correspond à cette valeur, le bouton se repliera."
- },
- "parse-json": {
- "label": "Analyser la sortie en JSON",
- "description": "Analyser la sortie de la commande en tant qu'objet JSON pour définir dynamiquement le texte et l'icône."
- }
- },
- "media-mini": {
- "show-album-art": {
- "label": "Afficher la pochette d'album",
- "description": "Afficher la pochette d'album de la piste en cours de lecture."
- },
- "show-visualizer": {
- "label": "Afficher le visualiseur",
- "description": "Afficher un visualiseur audio quand la musique est en cours de lecture."
- },
- "visualizer-type": {
- "label": "Type de visualiseur",
- "description": "Choisir le style de visualiseur audio à afficher."
- },
- "max-width": {
- "label": "Largeur Maximale",
- "description": "Définit la taille horizontale maximale du widget. Le widget se rétrécira pour s'adapter à un contenu plus court."
- },
- "use-fixed-width": {
- "label": "Utiliser une Largeur Fixe",
- "description": "Lorsque activé, le widget utilisera toujours la largeur maximale au lieu de s'ajuster dynamiquement au contenu."
- },
- "scrolling-mode": {
- "label": "Mode de défilement",
- "description": "Contrôler quand le défilement de texte est activé pour les titres de piste longs."
- },
- "hide-mode": {
- "label": "Mode de masquage",
- "description": "Contrôle le comportement du widget lorsqu'aucun média n'est en cours de lecture."
- },
- "no-active-player": "Aucun lecteur actif"
- },
"clock": {
- "use-primary-color": {
- "label": "Utiliser la couleur principale",
- "description": "Lorsque cette option est activée, la couleur principale est utilisée pour la mise en évidence."
- },
- "use-monospaced-font": {
- "label": "Utiliser une police à chasse fixe",
- "description": "Lorsque cette option est activée, l'horloge utilisera la police à chasse fixe."
- },
"clock-display": {
- "label": "Affichage de l'horloge",
- "description": "Personnalisez l'affichage de votre horloge en ajoutant des jetons de la liste ci-dessous. Pour utiliser le format 12 heures, vous devez inclure le jeton 'AP'."
+ "description": "Personnalisez l'affichage de votre horloge en ajoutant des jetons de la liste ci-dessous. Pour utiliser le format 12 heures, vous devez inclure le jeton 'AP'.",
+ "label": "Affichage de l'horloge"
},
- "horizontal-bar": {
- "label": "Barre horizontale",
- "description": "Astuce : Utilisez \\n pour créer un saut de ligne."
- },
- "vertical-bar": {
- "label": "Barre verticale",
- "description": "Utilisez un espace pour séparer chaque partie sur une nouvelle ligne."
- },
- "preview": "Aperçu",
"custom-font": {
"description": "Sélectionner une police personnalisée pour l'affichage de l'horloge.",
"label": "Police personnalisée",
"placeholder": "Sélectionner une police personnalisée...",
"search-placeholder": "Rechercher des polices..."
},
+ "horizontal-bar": {
+ "description": "Astuce : Utilisez \\n pour créer un saut de ligne.",
+ "label": "Barre horizontale"
+ },
+ "preview": "Aperçu",
"use-custom-font": {
"description": "Remplacez la police par défaut par une police personnalisée pour l'horloge.",
"label": "Utiliser une police personnalisée."
+ },
+ "use-monospaced-font": {
+ "description": "Lorsque cette option est activée, l'horloge utilisera la police à chasse fixe.",
+ "label": "Utiliser une police à chasse fixe"
+ },
+ "use-primary-color": {
+ "description": "Lorsque cette option est activée, la couleur principale est utilisée pour la mise en évidence.",
+ "label": "Utiliser la couleur principale"
+ },
+ "vertical-bar": {
+ "description": "Utilisez un espace pour séparer chaque partie sur une nouvelle ligne.",
+ "label": "Barre verticale"
+ }
+ },
+ "control-center": {
+ "browse-file": "Parcourir les fichiers",
+ "browse-library": "Parcourir la bibliothèque",
+ "icon": {
+ "description": "Sélectionnez une icône de la bibliothèque ou un fichier personnalisé.",
+ "label": "Icône"
+ },
+ "select-custom-icon": "Sélectionner une icône personnalisée",
+ "use-distro-logo": {
+ "description": "Utiliser le logo de votre distribution au lieu d'une icône personnalisée.",
+ "label": "Utiliser le logo de la distribution au lieu de l'icône"
+ },
+ "colorize-distro-logo": {
+ "description": "Appliquez les couleurs du thème au logo de votre distribution.",
+ "label": "Coloriser le logo de la distribution"
+ }
+ },
+ "custom-button": {
+ "browse": "Parcourir",
+ "collapse-condition": {
+ "description": "Si le texte de sortie correspond à cette valeur, le bouton se repliera.",
+ "label": "Condition d’effondrement"
+ },
+ "display-command-output": {
+ "description": "Entrez une commande à exécuter à intervalle régulier. La première ligne de sa sortie sera affichée sous forme de texte.",
+ "label": "Afficher la sortie de la commande",
+ "stream-description": "Entrez une commande à exécuter en continu."
+ },
+ "dynamic-text": "Texte dynamique",
+ "hide-vertical": {
+ "description": "Si activé, le texte de la sortie de la commande ne sera pas affiché lorsque la barre est en disposition verticale (gauche ou droite).",
+ "label": "Masquer le texte dans la barre verticale"
+ },
+ "icon": {
+ "description": "Sélectionnez une icône dans la bibliothèque.",
+ "label": "Icône"
+ },
+ "left-click": {
+ "description": "Commande à exécuter quand le bouton est cliqué à gauche.",
+ "label": "Clic gauche"
+ },
+ "middle-click": {
+ "description": "Commande à exécuter quand le bouton est cliqué au milieu.",
+ "label": "Clic milieu"
+ },
+ "parse-json": {
+ "description": "Analyser la sortie de la commande en tant qu'objet JSON pour définir dynamiquement le texte et l'icône.",
+ "label": "Analyser la sortie en JSON"
+ },
+ "refresh-interval": {
+ "description": "Intervalle en millisecondes.",
+ "label": "Intervalle d'actualisation"
+ },
+ "right-click": {
+ "description": "Commande à exécuter quand le bouton est cliqué à droite.",
+ "label": "Clic droit"
+ },
+ "text-stream": {
+ "description": "Les lignes diffusées depuis la commande seront affichées sous forme de texte sur le bouton.",
+ "label": "Flux"
+ }
+ },
+ "dialog": {
+ "apply": "Appliquer",
+ "cancel": "Annuler"
+ },
+ "keyboard-layout": {
+ "display-mode": {
+ "description": "Choisissez comment vous souhaitez que cette valeur apparaisse.",
+ "label": "Mode d'affichage"
+ }
+ },
+ "lock-keys": {
+ "browse": "Parcourir",
+ "show-caps-lock": {
+ "description": "Afficher l'état du verrouillage majuscule.",
+ "label": "Verr Maj"
+ },
+ "show-num-lock": {
+ "description": "Afficher l'état du verrouillage numérique.",
+ "label": "Verr Num"
+ },
+ "show-scroll-lock": {
+ "description": "Afficher l'état du verrouillage du défilement.",
+ "label": "Verr Maj"
+ }
+ },
+ "media-mini": {
+ "hide-mode": {
+ "description": "Contrôle le comportement du widget lorsqu'aucun média n'est en cours de lecture.",
+ "label": "Mode de masquage"
+ },
+ "max-width": {
+ "description": "Définit la taille horizontale maximale du widget. Le widget se rétrécira pour s'adapter à un contenu plus court.",
+ "label": "Largeur Maximale"
+ },
+ "no-active-player": "Aucun lecteur actif",
+ "scrolling-mode": {
+ "description": "Contrôler quand le défilement de texte est activé pour les titres de piste longs.",
+ "label": "Mode de défilement"
+ },
+ "show-album-art": {
+ "description": "Afficher la pochette d'album de la piste en cours de lecture.",
+ "label": "Afficher la pochette d'album"
+ },
+ "show-visualizer": {
+ "description": "Afficher un visualiseur audio quand la musique est en cours de lecture.",
+ "label": "Afficher le visualiseur"
+ },
+ "use-fixed-width": {
+ "description": "Lorsque activé, le widget utilisera toujours la largeur maximale au lieu de s'ajuster dynamiquement au contenu.",
+ "label": "Utiliser une Largeur Fixe"
+ },
+ "visualizer-type": {
+ "description": "Choisir le style de visualiseur audio à afficher.",
+ "label": "Type de visualiseur"
+ }
+ },
+ "microphone": {
+ "display-mode": {
+ "description": "Choisissez comment vous souhaitez que cette valeur apparaisse.",
+ "label": "Mode d'affichage"
+ }
+ },
+ "notification-history": {
+ "hide-badge-when-zero": {
+ "description": "Masquer le badge de notification quand il n'y a pas de notifications non lues.",
+ "label": "Masquer le badge quand zéro"
+ },
+ "show-unread-badge": {
+ "description": "Afficher un badge montrant le nombre de notifications non lues.",
+ "label": "Afficher le badge non lu"
+ }
+ },
+ "section-editor": {
+ "placeholder": "Sélectionnez un widget à ajouter...",
+ "search-placeholder": "Rechercher des widgets..."
+ },
+ "spacer": {
+ "width": {
+ "description": "Largeur de l'espacement en pixels",
+ "label": "Largeur"
+ }
+ },
+ "system-monitor": {
+ "cpu-temperature": {
+ "description": "Afficher les lectures de température du CPU si disponibles.",
+ "label": "Température du CPU"
+ },
+ "cpu-usage": {
+ "description": "Afficher le pourcentage d'utilisation actuel du CPU.",
+ "label": "Utilisation du CPU"
+ },
+ "memory-percentage": {
+ "description": "Afficher l'utilisation de la mémoire en pourcentage au lieu de valeurs absolues.",
+ "label": "Mémoire en pourcentage"
+ },
+ "memory-usage": {
+ "description": "Afficher les informations actuelles d'utilisation de la RAM.",
+ "label": "Utilisation de la mémoire"
+ },
+ "network-traffic": {
+ "description": "Afficher les vitesses de téléchargement et de téléversement du réseau.",
+ "label": "Trafic réseau"
+ },
+ "storage-usage": {
+ "description": "Afficher les informations d'utilisation de l'espace disque.",
+ "label": "Utilisation du stockage"
}
},
"taskbar": {
+ "colorize-icons": {
+ "description": "Appliquer les couleurs du thème aux icônes de la barre des tâches.",
+ "label": "Coloriser les icônes"
+ },
"hide-mode": {
- "label": "Mode de masquage",
- "description": "Contrôle le comportement du widget lorsqu'il n'y a pas de fenêtres correspondantes."
+ "description": "Contrôle le comportement du widget lorsqu'il n'y a pas de fenêtres correspondantes.",
+ "label": "Mode de masquage"
},
"only-active-workspaces": {
"description": "Afficher uniquement les applications des espaces de travail actifs.",
@@ -1235,10 +280,6 @@
"only-same-output": {
"description": "Afficher uniquement les applications de la sortie où la barre est située.",
"label": "Seulement à partir de la même sortie"
- },
- "colorize-icons": {
- "label": "Coloriser les icônes",
- "description": "Appliquer les couleurs du thème aux icônes de la barre des tâches."
}
},
"taskbar-grouped": {
@@ -1253,118 +294,69 @@
},
"tray": {
"colorize-icons": {
- "label": "Coloriser les icônes",
- "description": "Appliquer les couleurs du thème aux icônes de la barre système."
+ "description": "Appliquer les couleurs du thème aux icônes de la barre système.",
+ "label": "Coloriser les icônes"
+ },
+ "drawer-enabled": {
+ "description": "Lorsqu'elle est activée, les éléments de la barre système non épinglés sont affichés dans un panneau tiroir. Lorsqu'elle est désactivée, tous les éléments de la barre système sont affichés en ligne.",
+ "label": "Activer le tiroir"
}
},
- "audio-visualizer": {
- "width": {
- "label": "Largeur",
- "description": "Largeur personnalisée du composant."
- },
- "hide-when-idle": {
- "label": "Masquer lorsqu'aucun média n'est en lecture",
- "description": "Si activé, le visualiseur est masqué sauf lorsqu'un lecteur est en lecture."
- },
- "color-name": {
- "description": "Sélectionnez la couleur du visualiseur.",
- "label": "Couleur de remplissage"
+ "volume": {
+ "display-mode": {
+ "description": "Choisissez comment vous souhaitez que cette valeur apparaisse.",
+ "label": "Mode d'affichage"
}
},
- "lock-keys": {
- "show-caps-lock": {
- "description": "Afficher l'état du verrouillage majuscule.",
- "label": "Verr Maj"
+ "workspace": {
+ "character-count": {
+ "description": "Nombre de caractères à afficher des noms d'espaces de travail (1-10).",
+ "label": "Nombre de caractères"
},
- "show-num-lock": {
- "description": "Afficher l'état du verrouillage numérique.",
- "label": "Verr Num"
+ "hide-unoccupied": {
+ "description": "Ne pas afficher les espaces de travail sans fenêtres.",
+ "label": "Masquer les inoccupés"
},
- "show-scroll-lock": {
- "description": "Afficher l'état du verrouillage du défilement.",
- "label": "Verr Maj"
- },
- "indicator-style": {
- "circle": "Lettres circulaires",
- "circle-dash": "Lettres en cercle pointillé",
- "circle-dot": "Lettres en pointillés dans un cercle",
- "description": "Style d'icône pour les indicateurs de la touche de verrouillage",
- "hex": "Lettres hexagonales",
- "label": "Style d'indicateur",
- "large": "Grandes lettres",
- "small": "Petites lettres",
- "square": "Lettres carrées",
- "square-round": "Lettres carrées arrondies"
+ "label-mode": {
+ "description": "Choisir comment les étiquettes d'espace de travail sont affichées.",
+ "label": "Mode d'étiquette"
}
}
}
},
- "notifications": {
+ "battery": {
+ "charging": "En charge.",
+ "charging-rate": "Taux de charge : {rate} W.",
+ "discharging": "En décharge.",
+ "discharging-rate": "Taux de décharge : {rate} W.",
+ "health": "État : {percent}%",
+ "idle": "Inactif.",
+ "no-battery-detected": "Aucune batterie détectée.",
"panel": {
- "title": "Notifications",
- "no-notifications": "Aucune notification",
- "description": "Vos notifications apparaîtront ici à mesure qu'elles arriveront.",
- "click-to-expand": "Cliquez pour développer"
- }
- },
- "wallpaper": {
- "panel": {
- "title": "Sélecteur de fond d'écran",
- "apply-all-monitors": {
- "label": "Appliquer à tous les moniteurs",
- "description": "Appliquer le fond d'écran sélectionné à tous les moniteurs en même temps."
- },
- "search": "Rechercher :"
+ "balanced": "Équilibré ({percent}%)",
+ "disabled": "Gestionnaire de batterie désactivé",
+ "full": "Capacité totale ({percent}%)",
+ "lifespan": "Durée de vie prolongée ({percent}%)",
+ "title": "Seuil de charge"
},
- "transitions": {
- "none": "Aucune",
- "random": "Aléatoire",
- "fade": "Fondu",
- "disc": "Disque",
- "stripes": "Rayures",
- "wipe": "Balayage"
- },
- "fill-modes": {
- "center": "Centrer",
- "crop": "Recadrer (Remplir)",
- "fit": "Ajuster (Contenir)",
- "stretch": "Étirer"
- },
- "no-match": "Aucun résultat trouvé.",
- "no-wallpaper": "Aucun fond d'écran trouvé.",
- "try-different-search": "Essayez une autre requête de recherche.",
- "configure-directory": "Configurez votre répertoire de fonds d'écran avec des images."
+ "plugged-in": "Branché.",
+ "time-left": "Temps restant : {time}.",
+ "time-until-full": "Temps jusqu'à charge complète : {time}."
},
"bluetooth": {
"panel": {
- "title": "Bluetooth",
- "disabled": "Le Bluetooth est désactivé",
- "enable-message": "Activez le Bluetooth pour voir les appareils disponibles.",
- "connected-devices": "Appareils connectés",
- "known-devices": "Appareils connus",
"available-devices": "Appareils disponibles",
- "scanning": "Recherche d'appareils en cours...",
- "pairing-mode": "Assurez-vous que votre appareil est en mode d'appairage."
- }
- },
- "wifi": {
- "panel": {
- "title": "Wi-Fi",
- "disabled": "Le Wi-Fi est désactivé",
- "enable-message": "Activez le Wi-Fi pour voir les réseaux disponibles.",
- "searching": "Recherche de réseaux à proximité en cours...",
- "connected": "Connecté",
- "disconnecting": "Déconnexion en cours...",
- "forgetting": "Oubli en cours...",
- "saved": "Enregistré",
- "disconnect": "Déconnecter",
- "enter-password": "Entrez le mot de passe...",
+ "blocked": "Bloqué",
"connect": "Connecter",
- "password": "Mot de passe",
- "forget-network": "Oublier ce réseau ?",
- "forget": "Oublier",
- "no-networks": "Aucun réseau trouvé",
- "scan-again": "Analyser à nouveau"
+ "connected-devices": "Appareils connectés",
+ "disabled": "Le Bluetooth est désactivé",
+ "disconnect": "Déconnecter",
+ "enable-message": "Activez le Bluetooth pour voir les appareils disponibles.",
+ "known-devices": "Appareils connus",
+ "pairing": "Appairage...",
+ "pairing-mode": "Assurez-vous que votre appareil est en mode d'appairage.",
+ "scanning": "Recherche d'appareils en cours...",
+ "title": "Bluetooth"
}
},
"calendar": {
@@ -1375,281 +367,246 @@
"loading": "Chargement de la météo…"
}
},
- "tooltips": {
- "refresh": "Actualiser",
- "close": "Fermer",
- "up": "Remonter",
- "home": "Accueil",
- "refresh-wallpaper-list": "Actualiser la liste des fonds d'écran",
- "refresh-devices": "Actualiser les appareils",
- "forget-network": "Oublier le réseau",
- "clear-history": "Effacer l'historique",
- "delete-notification": "Supprimer la notification",
- "previous-month": "Mois précédent",
- "next-month": "Mois suivant",
- "add-widget": "Ajouter un widget",
- "widget-settings": "Paramètres du widget",
- "remove-widget": "Supprimer le widget",
- "move-to-left-section": "Déplacer vers la section de gauche",
- "move-to-center-section": "Déplacer vers la section centrale",
- "move-to-right-section": "Déplacer vers la section de droite",
- "open-settings": "Ouvrir les paramètres",
- "session-menu": "Menu de session",
- "cancel-timer": "Annuler le minuteur",
- "start-screen-recording": "Démarrer l'enregistrement d'écran",
- "stop-screen-recording": "Arrêter l'enregistrement d'écran",
- "screen-recorder-not-installed": "L'enregistreur d'écran n'est pas installé",
- "keep-awake": "Rester éveillé",
- "enable-keep-awake": "Activer le mode 'rester éveillé'",
- "disable-keep-awake": "Désactiver le mode 'rester éveillé'",
- "wallpaper-selector": "Clic gauche : Ouvrir le sélecteur de fond d'écran.\nClic droit : Définir un fond d'écran aléatoire.",
- "do-not-disturb-enabled": "'Ne pas déranger' activé",
- "do-not-disturb-disabled": "'Ne pas déranger' désactivé",
- "connect-disconnect-devices": "Clic gauche pour connecter. Clic droit pour oublier.",
- "set-power-profile": "Définir le profil d'alimentation \"{profile}\"",
- "switch-to-light-mode": "Passer en mode clair",
- "switch-to-dark-mode": "Passer en mode sombre",
- "night-light-disabled": "L'éclairage nocturne est désactivé.\nClic gauche pour changer de mode.\nClic droit pour accéder aux paramètres.",
- "night-light-enabled": "L'éclairage nocturne est activé.\nClic gauche pour changer de mode.\nClic droit pour accéder aux paramètres.",
- "night-light-forced": "L'éclairage nocturne est forcé.\nClic gauche pour changer de mode.\nClic droit pour accéder aux paramètres.",
- "night-light-not-installed": "L'éclairage nocturne n'est pas disponible.\nwlsunset n'est pas installé.",
- "click-to-start-recording": "Cliquez pour démarrer l'enregistrement",
- "click-to-stop-recording": "Cliquez pour arrêter l'enregistrement",
- "open-control-center": "Ouvrir le centre de contrôle",
- "brightness-at": "Luminosité : {brightness}%\nClic droit pour les paramètres.\nFaites défiler pour modifier la luminosité.",
- "manage-wifi": "Gérer le Wi-Fi",
- "bluetooth-devices": "Appareils Bluetooth",
- "open-notification-history-enable-dnd": "Ouvrir l'historique des notifications\nClic droit pour activer \"Ne pas déranger\".",
- "open-notification-history-disable-dnd": "Ouvrir l'historique des notifications\nClic droit pour désactiver \"Ne pas déranger\".",
- "open-wallpaper-selector": "Ouvrir le sélecteur de fond d'écran",
- "previous-media": "Média précédent",
- "pause": "Pause",
- "play": "Lecture",
- "next-media": "Média suivant",
- "power-profile": "Profil d'alimentation '{profile}'",
- "keyboard-layout": "Disposition du clavier {layout}",
- "output-muted": "Couper la sortie audio",
- "input-muted": "Couper l'entrée audio",
- "microphone-volume-at": "Volume du microphone à {volume}%.\nClic gauche pour les paramètres. Clic droit pour activer/désactiver le mode muet.\nFaites défiler pour modifier le volume.",
- "volume-at": "Volume de sortie à {volume}%.\nClic gauche pour les paramètres. Clic droit pour activer/désactiver le mode muet.\nFaites défiler pour modifier le volume."
- },
"clock": {
"tooltip": "Ouvrir le calendrier"
},
"dock": {
"menu": {
+ "close": "Fermer",
"focus": "Mettre au premier plan",
"pin": "Épingler",
- "unpin": "Détacher",
- "close": "Fermer"
+ "unpin": "Détacher"
}
},
+ "general": {
+ "no-results": "Aucun résultat",
+ "no-summary": "Aucun résumé",
+ "unknown": "Inconnu"
+ },
"launcher": {
"pin": "Épingler au dock",
"unpin": "Retirer du dock"
},
- "placeholders": {
- "search-icons": "ex: noctalia, niri, batterie, nuage",
- "profile-picture-path": "/home/user/.face",
- "enter-width-pixels": "Entrez la largeur en pixels",
- "enter-command": "Entrez la commande à exécuter (application ou script personnalisé)",
- "command-example": "echo \"Bonjour le monde\"",
- "enter-text-to-collapse": "par ex., 'rien n’est en lecture'. Utilisez /regex/ pour les modèles.",
- "search-wallpapers": "Tapez pour filtrer les fonds d'écran...",
- "search-launcher": "Rechercher des entrées... ou utilisez > pour les commandes",
- "search": "Rechercher...",
- "select": "Sélectionner",
- "cancel": "Annuler",
- "test": "Tester"
+ "lock-screen": {
+ "authenticating": "Authentification...",
+ "authentication-failed": "Échec de l'authentification",
+ "password": "Entrez votre mot de passe...",
+ "restart": "Redémarrer",
+ "shut-down": "Éteindre",
+ "suspend": "Mettre en veille",
+ "welcome-back": "Bon retour,"
+ },
+ "notifications": {
+ "panel": {
+ "click-to-expand": "Cliquez pour développer",
+ "description": "Vos notifications apparaîtront ici à mesure qu'elles arriveront.",
+ "no-notifications": "Aucune notification",
+ "title": "Notifications"
+ }
},
"options": {
"bar": {
- "position": {
- "top": "Haut",
- "bottom": "Bas",
- "left": "Gauche",
- "right": "Droite"
- },
"density": {
+ "comfortable": "Confortable",
"compact": "Compact",
"default": "Défaut",
- "comfortable": "Confortable",
"mini": "Mini"
+ },
+ "position": {
+ "bottom": "Bas",
+ "left": "Gauche",
+ "right": "Droite",
+ "top": "Haut"
}
},
- "launcher": {
- "position": {
- "center": "Centre (par défaut)",
- "top_left": "En haut à gauche",
- "top_right": "En haut à droite",
- "bottom_left": "En bas à gauche",
- "bottom_right": "En bas à droite",
- "bottom_center": "En bas au centre",
- "top_center": "En haut au centre",
- "center_left": "Gauche centre",
- "center_right": "Droite centre"
- }
+ "colors": {
+ "error": "Erreur",
+ "onSurface": "En surface",
+ "primary": "Primaire",
+ "secondary": "Secondaire",
+ "tertiary": "Tertiaire"
},
"control-center": {
"position": {
- "close_to_bar_button": "Près du bouton de la barre",
- "top_left": "En haut à gauche",
- "top_right": "En haut à droite",
+ "bottom_center": "En bas au centre",
"bottom_left": "En bas à gauche",
"bottom_right": "En bas à droite",
- "bottom_center": "En bas au centre",
+ "center": "Centre",
+ "close_to_bar_button": "Près du bouton de la barre",
"top_center": "En haut au centre",
- "center": "Centre"
+ "top_left": "En haut à gauche",
+ "top_right": "En haut à droite"
},
"quickSettingsStyle": {
- "modern": "Moderne",
"classic": "Classique",
- "compact": "Compact"
+ "compact": "Compact",
+ "modern": "Moderne"
+ }
+ },
+ "display-mode": {
+ "always-hide": "Toujours masquer",
+ "always-show": "Toujours afficher",
+ "force-open": "Forcer l'ouverture",
+ "on-hover": "Au survol"
+ },
+ "frame-rates": {
+ "fps": "{fps} FPS"
+ },
+ "hide-modes": {
+ "hidden": "Masquer si vide",
+ "idle": "Masquer à l'arrêt",
+ "transparent": "Transparent quand vide",
+ "visible": "Toujours visible"
+ },
+ "launcher": {
+ "position": {
+ "bottom_center": "En bas au centre",
+ "bottom_left": "En bas à gauche",
+ "bottom_right": "En bas à droite",
+ "center": "Centre (par défaut)",
+ "center_left": "Gauche centre",
+ "center_right": "Droite centre",
+ "follow_bar": "Suivre la barre (par défaut)",
+ "top_center": "En haut au centre",
+ "top_left": "En haut à gauche",
+ "top_right": "En haut à droite"
}
},
"osd": {
"position": {
- "top_left": "En haut à gauche",
- "top_right": "En haut à droite",
- "top_center": "En haut au centre",
+ "bottom_center": "En bas au centre",
"bottom_left": "En bas à gauche",
"bottom_right": "En bas à droite",
- "bottom_center": "En bas au centre",
"center_left": "Gauche centre",
- "center_right": "Droite centre"
+ "center_right": "Droite centre",
+ "top_center": "En haut au centre",
+ "top_left": "En haut à gauche",
+ "top_right": "En haut à droite"
}
},
- "display-mode": {
- "on-hover": "Au survol",
- "always-show": "Toujours afficher",
- "always-hide": "Toujours masquer",
- "force-open": "Forcer l'ouverture"
- },
- "workspace-labels": {
- "none": "Aucun",
- "index": "Index",
- "name": "Nom"
- },
- "visualizer-types": {
- "none": "Aucun",
- "linear": "Linéaire",
- "mirrored": "Miroir",
- "wave": "Vague"
+ "screen-recording": {
+ "audio-sources": {
+ "both": "Sortie système + entrée microphone",
+ "microphone-input": "Entrée microphone",
+ "system-output": "Sortie système"
+ },
+ "color-range": {
+ "full": "Complète",
+ "limited": "Limitée"
+ },
+ "quality": {
+ "high": "Haute",
+ "medium": "Moyenne",
+ "ultra": "Ultra",
+ "very-high": "Très haute"
+ },
+ "sources": {
+ "portal": "Portail",
+ "screen": "Écran"
+ }
},
"scrolling-modes": {
"always": "Défiler Toujours",
"hover": "Défiler au Survol",
"never": "Ne Jamais Défiler"
},
- "frame-rates": {
- "fps": "{fps} FPS"
+ "shadow-direction": {
+ "bottom": "En bas",
+ "bottom_left": "En bas à gauche",
+ "bottom_right": "En bas à droite",
+ "center": "Au centre",
+ "left": "Gauche",
+ "right": "Droite",
+ "top": "En haut",
+ "top_left": "En haut à gauche",
+ "top_right": "En haut à droite"
},
- "screen-recording": {
- "sources": {
- "portal": "Portail",
- "screen": "Écran"
- },
- "quality": {
- "medium": "Moyenne",
- "high": "Haute",
- "very-high": "Très haute",
- "ultra": "Ultra"
- },
- "color-range": {
- "limited": "Limitée",
- "full": "Complète"
- },
- "audio-sources": {
- "system-output": "Sortie système",
- "microphone-input": "Entrée microphone",
- "both": "Sortie système + entrée microphone"
- }
+ "visualizer-quality": {
+ "high": "Haut/Haute",
+ "low": "Bas"
},
- "hide-modes": {
- "hidden": "Masquer si vide",
- "transparent": "Transparent quand vide",
- "visible": "Toujours visible"
+ "visualizer-types": {
+ "linear": "Linéaire",
+ "mirrored": "Miroir",
+ "none": "Aucun",
+ "wave": "Vague"
},
- "colors": {
- "error": "Erreur",
- "primary": "Primaire",
- "secondary": "Secondaire",
- "tertiary": "Tertiaire",
- "onSurface": "En surface"
+ "workspace-labels": {
+ "index": "Index",
+ "name": "Nom",
+ "none": "Aucun"
}
},
- "session-menu": {
- "title": "Menu de session",
- "action-in-seconds": "{action} dans {seconds} secondes...",
- "lock": "Verrouiller",
- "lock-and-suspend": "Verrouiller et Mettre en veille",
- "suspend": "Mettre en veille",
- "reboot": "Redémarrer",
- "logout": "Déconnexion",
- "shutdown": "Éteindre"
+ "placeholders": {
+ "cancel": "Annuler",
+ "command-example": "echo \"Bonjour le monde\"",
+ "enter-command": "Entrez la commande à exécuter (application ou script personnalisé)",
+ "enter-text-to-collapse": "par ex., 'rien n’est en lecture'. Utilisez /regex/ pour les modèles.",
+ "enter-tooltip": "Entrer l'infobulle",
+ "enter-width-pixels": "Entrez la largeur en pixels",
+ "profile-picture-path": "/home/user/.face",
+ "search": "Rechercher...",
+ "search-icons": "ex: noctalia, niri, batterie, nuage",
+ "search-launcher": "Rechercher des entrées... ou utilisez > pour les commandes",
+ "search-wallpapers": "Tapez pour filtrer les fonds d'écran...",
+ "select": "Sélectionner",
+ "test": "Tester"
},
"plugins": {
"applications": "Applications",
- "clipboard": "Historique du presse-papiers",
"calculator": "Calculatrice",
- "clipboard-search-description": "Rechercher dans l'historique du presse-papiers",
+ "calculator-description": "Calculatrice - évaluez des expressions mathématiques",
+ "calculator-enter-expression": "Entrez une expression mathématique",
+ "calculator-error": "Erreur",
+ "calculator-name": "Calculatrice",
+ "clipboard": "Historique du presse-papiers",
"clipboard-clear-description": "Effacer tout l'historique du presse-papiers",
+ "clipboard-clear-description-full": "Supprimer tous les éléments de l'historique du presse-papiers",
+ "clipboard-clear-history": "Effacer l'historique du presse-papiers",
"clipboard-history-disabled": "Historique du presse-papiers désactivé",
"clipboard-history-disabled-description": "Activez l'historique du presse-papiers dans les paramètres ou installez cliphist",
- "clipboard-clear-history": "Effacer l'historique du presse-papiers",
- "clipboard-clear-description-full": "Supprimer tous les éléments de l'historique du presse-papiers",
"clipboard-loading": "Chargement de l'historique du presse-papiers...",
"clipboard-loading-description": "Veuillez patienter",
- "calculator-description": "Calculatrice - évaluez des expressions mathématiques",
- "calculator-name": "Calculatrice",
- "calculator-enter-expression": "Entrez une expression mathématique",
- "calculator-error": "Erreur"
- },
- "system": {
- "welcome-back": "Bon retour,",
- "monitor-description": "{model} ({width}x{height} @ {scale}x)",
- "scaling-percentage": "{percentage}%",
- "location-display": "{name} ({coordinates})",
- "signal-strength": "{signal}%",
- "cpu-temperature": "{temp}°C",
- "disk-usage": "{percent}%",
- "widget-settings-title": "Paramètres de {widget}",
- "unknown-app": "Application inconnue",
- "no-media-player-detected": "Aucun lecteur multimédia détecté",
- "user-requested": "Demandé par l'utilisateur",
- "unknown": "Inconnu",
- "unknown-version": "Inconnue",
- "unknown-layout": "Inconnue",
- "uptime": "Activité : {uptime}"
- },
- "lock-screen": {
- "password": "Entrez votre mot de passe...",
- "welcome-back": "Bon retour,",
- "authenticating": "Authentification...",
- "authentication-failed": "Échec de l'authentification",
- "shut-down": "Éteindre",
- "restart": "Redémarrer",
- "suspend": "Mettre en veille"
+ "clipboard-search-description": "Rechercher dans l'historique du presse-papiers"
},
"quickSettings": {
+ "bluetooth": {
+ "label": {
+ "disabled": "Bluetooth",
+ "enabled": "Bluetooth"
+ },
+ "tooltip": {
+ "action": "Cliquer pour gérer les appareils Bluetooth"
+ }
+ },
+ "keepAwake": {
+ "label": {
+ "disabled": "Rester éveillé",
+ "enabled": "Rester éveillé"
+ },
+ "tooltip": {
+ "action": "Cliquer pour basculer le mode Rester éveillé"
+ }
+ },
+ "nightLight": {
+ "label": {
+ "disabled": "Lumière nocturne",
+ "enabled": "Lumière nocturne",
+ "forced": "Lumière nocturne"
+ },
+ "tooltip": {
+ "action": "Cliquer pour basculer le mode Lumière nocturne\nClic droit : Ouvrir les paramètres"
+ }
+ },
"notifications": {
"label": {
- "enabled": "Notifications",
- "disabled": "Ne pas déranger"
+ "disabled": "Ne pas déranger",
+ "enabled": "Notifications"
},
"tooltip": {
"action": "Clic gauche : Ouvrir l'historique des notifications\nClic droit : Basculer Ne pas déranger"
}
},
- "screenRecorder": {
- "label": {
- "recording": "Arrêter",
- "stopped": "Enregistrer"
- },
- "tooltip": {
- "action": "Cliquer pour démarrer/arrêter l'enregistrement d'écran"
- }
- },
"powerProfile": {
"label": {
"unavailable": "Profil d'alimentation"
@@ -1659,33 +616,13 @@
"disabled": "Installez power-profiles-daemon pour utiliser les profils d'alimentation"
}
},
- "wifi": {
+ "screenRecorder": {
"label": {
- "ethernet": "Ethernet",
- "wifi": "Wi-Fi",
- "disconnected": "Wi-Fi déconnecté"
+ "recording": "Arrêter",
+ "stopped": "Enregistrer"
},
"tooltip": {
- "action": "Cliquer pour gérer les connexions Wi-Fi"
- }
- },
- "bluetooth": {
- "label": {
- "enabled": "Bluetooth",
- "disabled": "Bluetooth"
- },
- "tooltip": {
- "action": "Cliquer pour gérer les appareils Bluetooth"
- }
- },
- "nightLight": {
- "label": {
- "enabled": "Lumière nocturne",
- "forced": "Lumière nocturne",
- "disabled": "Lumière nocturne"
- },
- "tooltip": {
- "action": "Cliquer pour basculer le mode Lumière nocturne\nClic droit : Ouvrir les paramètres"
+ "action": "Cliquer pour démarrer/arrêter l'enregistrement d'écran"
}
},
"wallpaperSelector": {
@@ -1694,177 +631,1386 @@
"action": "Clic gauche : Ouvrir le sélecteur de fond d'écran\nClic droit : Définir un fond d'écran aléatoire"
}
},
- "keepAwake": {
+ "wifi": {
"label": {
- "enabled": "Rester éveillé",
- "disabled": "Rester éveillé"
+ "disconnected": "Wi-Fi déconnecté",
+ "ethernet": "Ethernet",
+ "wifi": "Wi-Fi"
},
"tooltip": {
- "action": "Cliquer pour basculer le mode Rester éveillé"
+ "action": "Cliquer pour gérer les connexions Wi-Fi"
}
}
},
+ "session-menu": {
+ "action-in-seconds": "{action} dans {seconds} secondes...",
+ "lock": "Verrouiller",
+ "lock-and-suspend": "Verrouiller et Mettre en veille",
+ "logout": "Déconnexion",
+ "reboot": "Redémarrer",
+ "shutdown": "Éteindre",
+ "suspend": "Mettre en veille",
+ "title": "Menu de session"
+ },
+ "settings": {
+ "about": {
+ "contributors": {
+ "section": {
+ "description": "Un grand merci à notre {count} super contributeur !",
+ "description_plural": "Un grand merci à nos {count} super contributeurs !",
+ "label": "Contributeurs"
+ }
+ },
+ "noctalia": {
+ "download-latest": "Télécharger la dernière version",
+ "installed-version": "Version installée :",
+ "latest-version": "Dernière version :",
+ "section": {
+ "description": "Un shell de bureau élégant et minimaliste, conçu avec soin pour Wayland et bâti avec Quickshell.",
+ "label": "Shell Noctalia"
+ }
+ },
+ "support": "Soutenez-nous",
+ "title": "À propos"
+ },
+ "audio": {
+ "devices": {
+ "input-device": {
+ "description": "Sélectionnez le périphérique d'entrée audio souhaité.",
+ "label": "Périphérique d'entrée"
+ },
+ "output-device": {
+ "description": "Sélectionnez le périphérique de sortie audio souhaité.",
+ "label": "Périphérique de sortie"
+ },
+ "section": {
+ "description": "Configurez les périphériques d'entrée et de sortie audio disponibles.",
+ "label": "Périphériques audio"
+ }
+ },
+ "media": {
+ "excluded-player": {
+ "description": "Ajoutez des mots-clés pour les lecteurs que le système doit ignorer. Chaque mot-clé doit être sur une nouvelle ligne.",
+ "label": "Lecteur exclu",
+ "placeholder": "tapez la sous-chaîne et appuyez sur +"
+ },
+ "frame-rate": {
+ "description": "Des fréquences plus élevées sont plus fluides mais utilisent plus de ressources.",
+ "label": "Fréquence d'images"
+ },
+ "primary-player": {
+ "description": "Entrez un mot-clé pour identifier votre lecteur principal.",
+ "label": "Lecteur principal",
+ "placeholder": "ex. spotify, vlc, mpv"
+ },
+ "scrolling-speed": {
+ "description": "Temps en secondes pour que le titre défile du début à la fin",
+ "label": "Vitesse de défilement"
+ },
+ "scrolling-title": {
+ "description": "Activer le défilement continu pour les titres multimédias longs",
+ "label": "Titre défilant"
+ },
+ "section": {
+ "description": "Définissez vos applications multimédias préférées et ignorées.",
+ "label": "Lecteurs multimédias"
+ },
+ "visualizer-quality": {
+ "description": "L'utilisation de haute qualité nécessite plus de ressources GPU.",
+ "label": "Qualité de la visualisation"
+ },
+ "visualizer-type": {
+ "description": "Choisissez un type de visualisation pour la lecture multimédia.",
+ "label": "Type de visualisation"
+ }
+ },
+ "title": "Audio",
+ "volumes": {
+ "input-volume": {
+ "description": "Niveau du volume d'entrée du microphone.",
+ "label": "Volume d'entrée"
+ },
+ "mute-input": {
+ "description": "Couper l'entrée audio par défaut (microphone).",
+ "label": "Couper l'entrée audio"
+ },
+ "mute-output": {
+ "description": "Couper la sortie audio principale du système.",
+ "label": "Couper la sortie audio"
+ },
+ "output-volume": {
+ "description": "Niveau de volume de l'ensemble du système.",
+ "label": "Volume de sortie"
+ },
+ "section": {
+ "description": "Ajustez les contrôles de volume et les niveaux audio.",
+ "label": "Volumes"
+ },
+ "step-size": {
+ "description": "Ajustez l'incrément pour les changements de volume (molette de la souris, raccourcis clavier).",
+ "label": "Incrément de volume"
+ },
+ "volume-overdrive": {
+ "description": "Permet d'augmenter le volume au-delà de 100 %. Cette fonctionnalité peut ne pas être prise en charge par tout le matériel.",
+ "label": "Autoriser le dépassement du volume"
+ }
+ }
+ },
+ "bar": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Ajustez l'opacité de l'arrière-plan de la barre.",
+ "label": "Opacité de l'arrière-plan"
+ },
+ "density": {
+ "description": "Ajustez le remplissage de la barre pour un aspect compact ou spacieux.",
+ "label": "Densité de la barre"
+ },
+ "floating": {
+ "description": "Affiche la barre sous forme de 'pilule' flottante.",
+ "label": "Barre flottante"
+ },
+ "margins": {
+ "description": "Ajustez les marges autour de la barre flottante.",
+ "horizontal": "Horizontale",
+ "label": "Marges",
+ "vertical": "Verticale"
+ },
+ "outer-corners": {
+ "description": "Affiche des coins incurvés vers l'extérieur sur la barre.",
+ "label": "Coins extérieurs"
+ },
+ "position": {
+ "description": "Choisissez où placer la barre sur l'écran.",
+ "label": "Position de la barre"
+ },
+ "section": {
+ "description": "Personnalisez l'apparence et la position de la barre.",
+ "label": "Apparence"
+ },
+ "show-capsule": {
+ "description": "Afficher les arrière-plans des widgets.",
+ "label": "Afficher la capsule"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Afficher la barre sur des moniteurs spécifiques. Par défaut, sur tous si aucun n'est choisi.",
+ "label": "Affichage sur les moniteurs"
+ }
+ },
+ "title": "Barre",
+ "tray": {
+ "back": "Retour",
+ "blacklist": {
+ "description": "Ajouter des règles d'exclusion pour la boîte à miniatures, prend en charge les caractères génériques (*).",
+ "label": "Liste noire",
+ "placeholder": "ex: nm-applet, Fcitx*"
+ },
+ "pin-application": "Épingler l'Application",
+ "unpin-application": "Désépingler l'Application"
+ },
+ "widgets": {
+ "section": {
+ "description": "Faites glisser et déposez les widgets pour les réorganiser. Les badges indiquent l'utilisation : [G]auche, [C]entre, [D]roite.",
+ "label": "Positionnement des widgets"
+ }
+ }
+ },
+ "color-scheme": {
+ "color-source": {
+ "matugen-scheme-type": {
+ "description": {
+ "scheme-content": "Dérive des couleurs qui correspondent étroitement à l'image sous-jacente.",
+ "scheme-expressive": "Palette vibrante avec une saturation ludique.",
+ "scheme-fidelity": "Palette haute fidélité qui préserve les teintes sources.",
+ "scheme-fruit-salad": "Mélange coloré d'accents vifs et contrastés.",
+ "scheme-monochrome": "Palette minimaliste construite autour d'une seule teinte.",
+ "scheme-neutral": "Palette de couleurs atténuées aux tons doux et apaisants.",
+ "scheme-rainbow": "Palette diversifiée couvrant tout le spectre.",
+ "scheme-tonal-spot": "Palette équilibrée avec des accents ciblés."
+ },
+ "label": "Type de schéma Matugen"
+ },
+ "section": {
+ "description": "Paramètres principaux pour les couleurs de Noctalia.",
+ "label": "Source des couleurs"
+ },
+ "use-wallpaper-colors": {
+ "description": "Générer des schémas de couleurs à partir de votre fond d'écran avec Matugen. Extrait automatiquement les couleurs pour créer un thème cohérent.",
+ "label": "Utiliser les couleurs du fond d'écran"
+ }
+ },
+ "dark-mode": {
+ "mode": {
+ "description": "Active la commutation automatique entre le mode clair et le mode sombre.",
+ "label": "Programmation du mode sombre",
+ "location": "Emplacement",
+ "manual": "Manuel",
+ "off": "Éteint"
+ },
+ "switch": {
+ "description": "Passe à un thème plus sombre pour une visualisation plus facile la nuit.",
+ "label": "Mode sombre"
+ }
+ },
+ "predefined": {
+ "generate-templates": {
+ "description": "Génère des modèles Matugen (GTK, thèmes de terminal, etc.) lors de l'utilisation de schémas de couleurs prédéfinis.",
+ "label": "Générer des modèles pour les schémas prédéfinis"
+ },
+ "section": {
+ "description": "Choisissez parmi une collection de jeux de couleurs prédéfinis.",
+ "label": "Jeux de couleurs prédéfinis"
+ }
+ },
+ "templates": {
+ "misc": {
+ "description": "Options de configuration supplémentaires.",
+ "label": "Divers",
+ "user-templates": {
+ "description": "Activer la configuration Matugen définie par l'utilisateur. Un fichier modèle sera créé dans ~/.config/noctalia/user-templates.toml lors de la première activation",
+ "label": "Modèles utilisateur"
+ }
+ },
+ "programs": {
+ "code": {
+ "description": "Écrire {filepath}. Le thème Hyprluna doit être installé et activé manuellement.",
+ "description-missing": "Nécessite l'installation de {app}"
+ },
+ "spicetify": {
+ "description": "Écrire {filepath}. Le thème Comfy doit être installé et activé manuellement.",
+ "description-missing": "Nécessite l'installation de {app}"
+ },
+ "description": "Thématisation spécifique aux applications.",
+ "discord": {
+ "description": "Écrire {filepath} pour {client}",
+ "description-missing": "Aucun client Discord détecté. Installez vencord, vesktop, webcord, armcord, equibop, lightcord ou dorion."
+ },
+ "fuzzel": {
+ "description": "Écrire ~/.config/fuzzel/themes/noctalia et recharger",
+ "description-missing": "Nécessite que le lanceur fuzzel soit installé"
+ },
+ "label": "Programmes",
+ "pywalfox": {
+ "description": "Écrire ~/.cache/wal/colors.json et exécuter pywalfox update",
+ "description-missing": "Nécessite que le paquet pywalfox soit installé"
+ },
+ "vicinae": {
+ "description": "Écrire {filepath} et recharger",
+ "description-missing": "Nécessite que le lanceur {app} soit installé"
+ },
+ "walker": {
+ "description": "Écrire {filepath} et définir le thème sur noctalia",
+ "description-missing": "Nécessite que le lanceur {app} soit installé"
+ }
+ },
+ "section": {
+ "description": "Appliquer des couleurs aux applications externes.",
+ "label": "Modèles"
+ },
+ "terminal": {
+ "alacritty": {
+ "description": "Écrire {filepath} et recharger.",
+ "description-missing": "Nécessite l'installation de {app}"
+ },
+ "description": "Thématisation de l'émulateur de terminal.",
+ "foot": {
+ "description": "Écrire ~/.config/foot/themes/noctalia et recharger",
+ "description-missing": "Nécessite que le terminal foot soit installé"
+ },
+ "ghostty": {
+ "description": "Écrire ~/.config/ghostty/themes/noctalia et recharger",
+ "description-missing": "Nécessite que le terminal ghostty soit installé"
+ },
+ "kitty": {
+ "description": "Écrire ~/.config/kitty/themes/noctalia.conf et recharger",
+ "description-missing": "Nécessite que le terminal Kitty soit installé"
+ },
+ "label": "Terminal",
+ "wezterm": {
+ "description": "Écrire {filepath} et recharger.",
+ "description-missing": "Nécessite l'installation de {app}"
+ }
+ },
+ "ui": {
+ "description": "Thématisation de l'environnement de bureau et de la boîte à outils d'interface utilisateur.",
+ "gtk": {
+ "description": "Écrire {filepath}"
+ },
+ "kcolorscheme": {
+ "description": "Écrire {filepath}"
+ },
+ "label": "UI",
+ "qt": {
+ "description": "Écrire {filepath}"
+ }
+ }
+ },
+ "title": "Jeu de couleurs"
+ },
+ "control-center": {
+ "cards": {
+ "section": {
+ "description": "Personnalisez les commandes qui apparaissent dans le Centre de contrôle et leur ordre d'affichage.",
+ "label": "Cartes"
+ }
+ },
+ "position": {
+ "description": "Choisissez où le panneau du Centre de contrôle apparaît lorsqu'il est ouvert.",
+ "label": "Position"
+ },
+ "section": {
+ "description": "Configurer le positionnement et le comportement du panneau du Centre de contrôle.",
+ "label": "Apparence"
+ },
+ "shortcuts": {
+ "custom-button": {
+ "browse": "Parcourir",
+ "command": {
+ "description": "La commande à exécuter lorsque le bouton est cliqué.",
+ "label": "Commande"
+ },
+ "enable-on-state-logic": {
+ "description": "Active une deuxième icône et un état 'chaud' basés sur une commande de vérification.",
+ "label": "Activer la logique d'état activé"
+ },
+ "general-tooltip-text": {
+ "description": "Description générale de l'info-bulle du bouton.",
+ "label": "Texte de l'info-bulle générale"
+ },
+ "icon": {
+ "description": "Sélectionnez une icône dans la bibliothèque.",
+ "label": "Icône"
+ },
+ "on-clicked": {
+ "description": "Commande à exécuter lorsque le bouton est cliqué avec le bouton gauche.",
+ "label": "Commande de clic gauche"
+ },
+ "on-middle-clicked": {
+ "description": "Commande à exécuter lorsque le bouton est cliqué avec le bouton du milieu.",
+ "label": "Commande de clic central"
+ },
+ "on-right-clicked": {
+ "description": "Commande à exécuter lorsque le bouton est cliqué avec le bouton droit.",
+ "label": "Commande de clic droit"
+ },
+ "on-state-command": {
+ "description": "Commande à exécuter pour vérifier si le bouton doit être dans l'état 'activé'. Retourne 0 pour activé, non-zéro pour désactivé.",
+ "label": "Commande de vérification de l'état activé"
+ },
+ "on-state-icon": {
+ "description": "L'icône du bouton lorsqu'il est dans l'état 'activé'.",
+ "label": "Icône d'état activé"
+ },
+ "state-checks": {
+ "add": "Ajouter une vérification d'état",
+ "browse-icon": "Parcourir",
+ "command": "Commande à exécuter pour cette vérification d'état",
+ "label": "Vérifications d'état",
+ "remove": "Supprimer"
+ },
+ "tooltip": {
+ "description": "L'info-bulle à afficher lors du survol du bouton.",
+ "label": "Info-bulle"
+ }
+ },
+ "dialog": {
+ "apply": "Appliquer",
+ "cancel": "Annuler"
+ },
+ "section": {
+ "description": "Configurer et gérer les widgets de raccourcis.",
+ "label": "Widgets de raccourcis"
+ },
+ "sectionLeft": "Gauche",
+ "sectionRight": "Droite"
+ },
+ "title": "Centre de contrôle"
+ },
+ "display": {
+ "monitors": {
+ "brightness": "Luminosité",
+ "brightness-step": {
+ "description": "Ajustez l'incrément pour les changements de luminosité (molette de la souris et raccourcis clavier).",
+ "label": "Incrément de luminosité"
+ },
+ "brightness-unavailable": {
+ "ddc-disabled": "Contrôle de la luminosité indisponible. Activez \"Prise en charge de la luminosité externe\" pour contrôler la luminosité de cet écran.",
+ "generic": "Le contrôle de la luminosité n'est pas disponible pour cet écran."
+ },
+ "enforce-minimum": {
+ "description": "Résout le problème de l'extinction complète du rétroéclairage sur certains écrans à 0% de luminosité.",
+ "label": "Imposer une luminosité minimale (1%)"
+ },
+ "external-brightness": {
+ "description": "Activer le support DDCUtil pour contrôler la luminosité des écrans externes via le protocole DDC/CI.",
+ "label": "Prise en charge de la luminosité externe"
+ },
+ "section": {
+ "description": "Ajustez la luminosité pour chaque écran.",
+ "label": "Paramètres par moniteur"
+ }
+ },
+ "night-light": {
+ "auto-schedule": {
+ "description": "Basé sur l'heure du coucher et du lever du soleil à {location} - recommandé.",
+ "label": "Planification automatique"
+ },
+ "enable": {
+ "description": "Applique un filtre de couleur chaude pour réduire l'émission de lumière bleue.",
+ "label": "Activer l'éclairage nocturne"
+ },
+ "force-activation": {
+ "description": "Ignore la planification et applique le filtre nocturne immédiatement.",
+ "label": "Forcer l'activation"
+ },
+ "manual-schedule": {
+ "description": "Définissez des heures personnalisées pour le lever et le coucher du soleil.",
+ "label": "Planification manuelle",
+ "select-start": "Sélectionner l'heure de début",
+ "select-stop": "Sélectionner l'heure de fin",
+ "sunrise": "Heure du lever du soleil",
+ "sunset": "Heure du coucher du soleil"
+ },
+ "section": {
+ "description": "Réduit l'émission de lumière bleue pour vous aider à mieux dormir et réduire la fatigue oculaire.",
+ "label": "Éclairage nocturne"
+ },
+ "temperature": {
+ "day": "Jour",
+ "description": "Réglez la chaleur des couleurs pour la nuit et le jour.",
+ "label": "Température de couleur",
+ "night": "Nuit"
+ }
+ },
+ "title": "Affichage"
+ },
+ "dock": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Ajustez l'opacité de l'arrière-plan du dock.",
+ "label": "Opacité de l'arrière-plan"
+ },
+ "colorize-icons": {
+ "description": "Appliquer les couleurs du thème aux icônes d'applications du dock (applications non focalisées uniquement).",
+ "label": "Coloriser les icônes"
+ },
+ "display": {
+ "always-visible": "Toujours visible",
+ "auto-hide": "Masquer automatiquement",
+ "description": "Choisissez comment le dock se comporte.",
+ "exclusive": "Exclusif",
+ "label": "Affichage"
+ },
+ "floating-distance": {
+ "description": "Ajustez la distance de flottaison par rapport au bord de l'écran.",
+ "label": "Distance de flottaison du dock"
+ },
+ "icon-size": {
+ "description": "Ajuster la taille globale du Dock.",
+ "label": "Taille du Dock"
+ },
+ "section": {
+ "description": "Personnalisez le comportement et l'apparence du dock.",
+ "label": "Apparence"
+ }
+ },
+ "enabled": {
+ "description": "Afficher ou masquer complètement le dock",
+ "label": "Activer le dock"
+ },
+ "monitors": {
+ "only-same-output": {
+ "description": "Afficher uniquement les applications de la sortie où le dock est situé.",
+ "label": "Seulement les applications de la même sortie"
+ },
+ "section": {
+ "description": "Afficher le dock sur des écrans spécifiques. Par défaut, il s'affiche sur tous les écrans si aucun n'est sélectionné.",
+ "label": "Affichage sur les moniteur"
+ }
+ },
+ "title": "Dock"
+ },
+ "general": {
+ "fonts": {
+ "default": {
+ "description": "Police principale utilisée dans toute l'interface.",
+ "label": "Police par défaut",
+ "placeholder": "Sélectionner la police par défaut...",
+ "scale": {
+ "description": "Augmenter ou diminuer la taille du texte standard.",
+ "label": "Taille de police par défaut"
+ },
+ "search-placeholder": "Rechercher des polices..."
+ },
+ "monospace": {
+ "description": "Police à chasse fixe utilisée pour l'affichage des chiffres et des statistiques.",
+ "label": "Police à chasse fixe",
+ "placeholder": "Sélectionner la police à chasse fixe...",
+ "scale": {
+ "description": "Augmenter ou diminuer la taille du texte à chasse fixe.",
+ "label": "Taille de police à chasse fixe"
+ },
+ "search-placeholder": "Rechercher des polices à chasse fixe..."
+ },
+ "reset-scaling": "Réinitialiser l'échelle",
+ "section": {
+ "description": "Choisissez les polices utilisées dans toute l'interface.",
+ "label": "Polices"
+ }
+ },
+ "language": {
+ "section": {
+ "description": "Choisissez votre langue préférée pour l'application.",
+ "label": "Langue"
+ },
+ "select": {
+ "auto-detect": "Automatique",
+ "description": "Sélectionnez la langue utilisée dans l'interface de l'application.",
+ "label": "Langue de l'application"
+ }
+ },
+ "launch-setup-wizard": "Lancer l'assistant d'installation",
+ "profile": {
+ "picture": {
+ "description": "Votre photo de profil qui apparaît dans toute l'interface.",
+ "label": "Photo de profil de {user}"
+ },
+ "section": {
+ "description": "Modifiez vos informations utilisateur et votre avatar.",
+ "label": "Profil"
+ },
+ "select-avatar": "Sélectionner une image d'avatar"
+ },
+ "screen-corners": {
+ "radius": {
+ "description": "Ajustez l'arrondi des coins de l'écran.",
+ "label": "Rayon des coins de l'écran",
+ "reset": "Réinitialiser le rayon des coins de l'écran"
+ },
+ "section": {
+ "description": "Personnalisez l'arrondi des coins de l'écran et les effets visuels.",
+ "label": "Coins de l'écran"
+ },
+ "show-corners": {
+ "description": "Affichez des coins arrondis sur le bord de l'écran.",
+ "label": "Afficher les coins de l'écran"
+ },
+ "solid-black": {
+ "description": "Utilisez du noir uni au lieu de la couleur de fond de la barre.",
+ "label": "Coins noirs unis"
+ }
+ },
+ "title": "Général"
+ },
+ "hooks": {
+ "info": {
+ "command-info": {
+ "description": "• Les commandes sont exécutées via le shell (sh -c)\n• Les commandes s'exécutent en arrière-plan (détachées)\n• Les boutons de test s'exécutent avec les valeurs actuelles",
+ "label": "Informations sur la commande de hook"
+ },
+ "parameters": {
+ "description": "• Hook Fond d'écran : $1 = chemin du fond d'écran, $2 = nom de l'écran\n• Hook de bascule de thème : $1 = true/false (état du mode sombre)",
+ "label": "Paramètres disponibles"
+ }
+ },
+ "system-hooks": {
+ "enable": {
+ "description": "Activer ou désactiver toutes les commandes de hook.",
+ "label": "Activer les hooks"
+ },
+ "section": {
+ "description": "Configurez des commandes à exécuter lors d'événements système.",
+ "label": "Hooks système"
+ }
+ },
+ "theme-changed": {
+ "description": "Commande à exécuter lorsque le thème bascule entre le mode sombre et clair.",
+ "label": "Thème changé",
+ "placeholder": "ex: notify-send \"Thème\" \"Basculé\""
+ },
+ "title": "Hooks",
+ "wallpaper-changed": {
+ "description": "Commande à exécuter lorsque le fond d'écran change.",
+ "label": "Fond d'écran changé",
+ "placeholder": "ex: notify-send \"Fond d'écran\" \"Changé\""
+ }
+ },
+ "launcher": {
+ "settings": {
+ "background-opacity": {
+ "description": "Ajustez l'opacité de l'arrière-plan du lanceur.",
+ "label": "Opacité de l'arrière-plan"
+ },
+ "clipboard-history": {
+ "description": "Accédez aux éléments précédemment copiés depuis le lanceur.",
+ "label": "Activer l'historique du presse-papiers"
+ },
+ "custom-launch-prefix": {
+ "description": "Préfixer les commandes avec un lanceur personnalisé (ex. 'runapp' pour l'intégration systemd).",
+ "label": "Préfixe de lancement personnalisé"
+ },
+ "custom-launch-prefix-enabled": {
+ "description": "Utiliser un préfixe personnalisé pour lancer les applications au lieu de la méthode par défaut.",
+ "label": "Activer le préfixe de lancement personnalisé"
+ },
+ "position": {
+ "description": "Choisissez où le panneau du lanceur apparaît.",
+ "label": "Position"
+ },
+ "section": {
+ "description": "Personnalisez le comportement et l'apparence du lanceur.",
+ "label": "Apparence"
+ },
+ "sort-by-usage": {
+ "description": "Lorsque cette option est activée, les applications fréquemment lancées apparaissent en premier dans la liste.",
+ "label": "Trier par les plus utilisés"
+ },
+ "terminal-command": {
+ "description": "Commande pour lancer un terminal. Ex: 'kitty -e' ou 'gnome-terminal --'.",
+ "label": "Commande du terminal"
+ },
+ "use-app2unit": {
+ "description": "Utilise une méthode de lancement alternative pour mieux gérer les processus des applications et prévenir les problèmes.",
+ "label": "Utiliser App2Unit pour lancer les applications"
+ }
+ },
+ "title": "Lanceur"
+ },
+ "location": {
+ "date-time": {
+ "12hour-format": {
+ "description": "Affiche l'heure au format 12 heures sur l'écran de verrouillage et dans le calendrier. L'horloge de la barre possède ses propres paramètres.",
+ "label": "Utiliser le format horaire de 12 heures"
+ },
+ "first-day-of-week": {
+ "automatic": "Automatique (utiliser les paramètres régionaux du système)",
+ "description": "Choisissez quel jour commence la semaine dans le calendrier.",
+ "label": "Premier jour de la semaine"
+ },
+ "section": {
+ "description": "Personnalisez l'affichage de la date et de l'heure.",
+ "label": "Date & heure"
+ },
+ "show-events": {
+ "description": "Afficher les événements dans le panneau du calendrier.",
+ "label": "Afficher les événements du calendrier"
+ },
+ "use-analog": {
+ "description": "Afficher une horloge de style analogique dans la fenêtre du calendrier et sur l'écran de verrouillage.",
+ "label": "Utiliser une horloge de style analogique."
+ },
+ "week-numbers": {
+ "description": "Affiche la semaine de l'année (ex: Semaine 38) dans le calendrier.",
+ "label": "Afficher les numéros de semaine"
+ }
+ },
+ "location": {
+ "search": {
+ "description": "ex: Toronto, ON",
+ "label": "Rechercher un emplacement",
+ "placeholder": "Entrez le nom de l'emplacement"
+ },
+ "section": {
+ "description": "Obtenez une météo et une planification de l'éclairage nocturne précises en définissant votre emplacement.",
+ "label": "Votre emplacement"
+ }
+ },
+ "title": "Emplacement",
+ "weather": {
+ "enabled": {
+ "description": "Afficher les informations météorologiques dans toute l'interface et récupérer les données météo. Lorsque désactivé, tous les éléments météo seront masqués et aucune requête réseau ne sera effectuée.",
+ "label": "Activer la météo"
+ },
+ "fahrenheit": {
+ "description": "Afficher la température en Fahrenheit au lieu de Celsius.",
+ "label": "Afficher la température en Fahrenheit (°F)"
+ },
+ "section": {
+ "description": "Choisissez votre unité de température préférée.",
+ "label": "Météo"
+ },
+ "show-in-calendar": {
+ "description": "Afficher les prévisions météo quotidiennes directement dans votre vue calendrier.",
+ "label": "Afficher la météo dans le calendrier"
+ }
+ }
+ },
+ "lock-screen": {
+ "compact-lockscreen": {
+ "description": "Afficher uniquement le champ de saisie de connexion et les commandes système, en masquant les widgets météo et multimédia.",
+ "label": "Écran de verrouillage compact"
+ },
+ "lock-on-suspend": {
+ "description": "Verrouiller automatiquement l'écran lors de la mise en veille du système.",
+ "label": "Verrouiller à la suspension"
+ },
+ "title": "Écran de verrouillage"
+ },
+ "network": {
+ "bluetooth": {
+ "label": "Activer le Bluetooth"
+ },
+ "section": {
+ "description": "Gérez les connexions Wi-Fi et Bluetooth."
+ },
+ "title": "Réseau",
+ "wifi": {
+ "label": "Activer le Wi-Fi"
+ }
+ },
+ "notifications": {
+ "duration": {
+ "critical-urgency": {
+ "description": "Durée de visibilité des notifications de priorité critique.",
+ "label": "Urgence critique"
+ },
+ "low-urgency": {
+ "description": "Durée de visibilité des notifications de faible priorité.",
+ "label": "Urgence faible"
+ },
+ "normal-urgency": {
+ "description": "Durée de visibilité des notifications de priorité normale.",
+ "label": "Urgence normale"
+ },
+ "respect-expire": {
+ "description": "Utiliser le délai d'expiration défini dans la notification.",
+ "label": "Respecter le délai d'expiration"
+ },
+ "section": {
+ "description": "Configurer la durée d'affichage des notifications en fonction de leur niveau d'urgence.",
+ "label": "Durée des notifications"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Afficher les notifications sur des moniteurs spécifiques. Par défaut, sur tous si aucun n'est choisi.",
+ "label": "Affichage sur les moniteurs"
+ }
+ },
+ "settings": {
+ "always-on-top": {
+ "description": "Afficher les notifications au-dessus des fenêtres en plein écran et des autres applications.",
+ "label": "Toujours au premier plan"
+ },
+ "background-opacity": {
+ "description": "Ajuster l'opacité des arrière-plans de notifications.",
+ "label": "Opacité du fond"
+ },
+ "do-not-disturb": {
+ "description": "Désactive toutes les fenêtres de notification lorsque cette option est activée.",
+ "label": "Ne pas déranger"
+ },
+ "enable-osd": {
+ "description": "Afficher les changements de volume et de luminosité en temps réel.",
+ "label": "Activer l'affichage à l'écran (OSD)"
+ },
+ "enabled": {
+ "description": "Activer ou désactiver le démon de notifications, nécessite un redémarrage de noctalia-shell.",
+ "label": "Activer les notifications"
+ },
+ "location": {
+ "description": "Où les notifications apparaissent à l'écran.",
+ "label": "Emplacement"
+ },
+ "section": {
+ "description": "Configurez l'apparence et le comportement des notifications.",
+ "label": "Apparence"
+ }
+ },
+ "title": "Notifications"
+ },
+ "osd": {
+ "always-on-top": {
+ "description": "Afficher l'OSD au-dessus des fenêtres plein écran et autres couches.",
+ "label": "Toujours au premier plan"
+ },
+ "description": "Configurer les indicateurs à l'écran tels que le volume et la luminosité.",
+ "duration": {
+ "auto-hide": {
+ "description": "Ajuster le délai avant disparition de l'OSD.",
+ "label": "Masquer après"
+ },
+ "section": {
+ "description": "Temps d'affichage avant masquage automatique de l'OSD.",
+ "label": "Délai d'auto‑masquage"
+ }
+ },
+ "enabled": {
+ "description": "Afficher en temps réel les changements de volume et de luminosité.",
+ "label": "Activer l'affichage à l'écran"
+ },
+ "location": {
+ "description": "Emplacement des affichages à l'écran.",
+ "label": "Emplacement"
+ },
+ "monitors": {
+ "section": {
+ "description": "Afficher l'OSD sur des moniteurs spécifiques. Par défaut sur tous si aucun n'est choisi.",
+ "label": "Affichage sur les moniteurs"
+ }
+ },
+ "section": {
+ "general": {
+ "description": "Configurer la visibilité et le comportement de l'OSD.",
+ "label": "Général"
+ }
+ },
+ "title": "Affichage à l'écran"
+ },
+ "screen-recorder": {
+ "audio": {
+ "audio-codec": {
+ "description": "Opus est recommandé pour de meilleures performances et une taille audio minimale.",
+ "label": "Codec audio"
+ },
+ "audio-source": {
+ "description": "Source audio à capturer pendant l'enregistrement.",
+ "label": "Source audio"
+ },
+ "section": {
+ "description": "Configurez les options d'enregistrement audio.",
+ "label": "Paramètres audio"
+ }
+ },
+ "general": {
+ "output-folder": {
+ "description": "Dossier où les enregistrements d'écran seront sauvegardés.",
+ "label": "Dossier de sortie",
+ "tooltip": "Parcourir le dossier de sortie"
+ },
+ "section": {
+ "description": "Gérez la sortie et le contenu de l'enregistrement d'écran.",
+ "label": "Paramètres généraux"
+ },
+ "select-output-folder": "Sélectionner le dossier de sortie",
+ "show-cursor": {
+ "description": "Enregistrez le curseur de la souris dans la vidéo.",
+ "label": "Afficher le curseur"
+ }
+ },
+ "title": "Enregistreur d'écran",
+ "video": {
+ "color-range": {
+ "description": "Limitée est recommandée pour une meilleure compatibilité.",
+ "label": "Gamme de couleurs"
+ },
+ "frame-rate": {
+ "description": "Fréquence d'images cible pour les enregistrements d'écran.",
+ "label": "Fréquence d'images"
+ },
+ "section": {
+ "description": "Configurez les options d'enregistrement vidéo.",
+ "label": "Paramètres vidéo"
+ },
+ "video-codec": {
+ "description": "h264 est le codec le plus courant.",
+ "label": "Codec vidéo"
+ },
+ "video-quality": {
+ "description": "Une qualité supérieure entraîne des fichiers de plus grande taille.",
+ "label": "Qualité vidéo"
+ },
+ "video-source": {
+ "description": "Portail est recommandé, si vous obtenez des artefacts, essayez Écran.",
+ "label": "Source vidéo"
+ }
+ }
+ },
+ "user-interface": {
+ "animation-disable": {
+ "description": "Désactiver toutes les animations pour une expérience plus rapide et plus réactive.",
+ "label": "Désactiver les animations de l'interface utilisateur"
+ },
+ "animation-speed": {
+ "description": "Ajuster la vitesse globale de l'animation.",
+ "label": "Vitesse d'animation",
+ "reset": "Réinitialiser la vitesse de l'animation"
+ },
+ "border-radius": {
+ "description": "Contrôle l'arrondi des angles des fenêtres, des boutons et d'autres éléments.",
+ "label": "Rayon de bordure",
+ "reset": "Réinitialiser le rayon de la bordure"
+ },
+ "dim-desktop": {
+ "description": "Atténuer le bureau lorsque des panneaux ou des menus sont ouverts.",
+ "label": "Dim bureau"
+ },
+ "panels-attached-to-bar": {
+ "description": "Les panneaux se verrouillent sur la barre et les bords de l'écran, créant un aspect homogène avec d'élégants coins inversés.",
+ "label": "Aligner les panneaux aux bords"
+ },
+ "panels-overlay": {
+ "description": "Garantit que les panneaux et la barre restent visibles, même au-dessus des applications en plein écran.",
+ "label": "Garder les panneaux et la barre en haut."
+ },
+ "scaling": {
+ "description": "Modifie la taille de l'interface utilisateur générale, à l'exception de la barre.",
+ "label": "Mise à l'échelle de l'interface",
+ "reset-scaling": "Réinitialiser l'échelle de l'interface"
+ },
+ "section": {
+ "description": "Personnaliser l'apparence, l'ergonomie et le comportement de l'interface.",
+ "label": "Apparence"
+ },
+ "settings-panel-attached-to-bar": {
+ "description": "Alignez la fenêtre des paramètres sur la barre pour un rendu cohérent.",
+ "label": "Aligner la fenêtre des paramètres sur la barre"
+ },
+ "shadows": {
+ "description": "Active les ombres portées sous les barres et les panneaux.",
+ "direction": {
+ "description": "Choisissez d’où vient l’ombre.",
+ "label": "Direction de l’ombre"
+ },
+ "label": "Ombres portées"
+ },
+ "title": "Interface utilisateur",
+ "tooltips": {
+ "description": "Activer ou désactiver les info-bulles dans toute l'interface.",
+ "label": "Afficher les infobulles"
+ }
+ },
+ "wallpaper": {
+ "automation": {
+ "custom-interval": {
+ "description": "Entrez l'heure au format HH:MM (ex: 01:30).",
+ "label": "Intervalle personnalisé"
+ },
+ "interval": {
+ "description": "Fréquence de changement automatique des fonds d'écran.",
+ "label": "Intervalle du fond d'écran"
+ },
+ "random-wallpaper": {
+ "description": "Planifiez des changements de fond d'écran aléatoires à intervalles réguliers.",
+ "label": "Fond d'écran aléatoire"
+ },
+ "section": {
+ "label": "Automatisation"
+ }
+ },
+ "look-feel": {
+ "edge-smoothness": {
+ "description": "Applique un effet de flou progressif au bord des transitions.",
+ "label": "Adoucir le bord de la transition"
+ },
+ "fill-color": {
+ "description": "Choisissez une couleur de remplissage qui peut apparaître derrière le fond d'écran.",
+ "label": "Couleur de remplissage"
+ },
+ "fill-mode": {
+ "description": "Sélectionnez comment l'image doit être mise à l'échelle pour correspondre à la résolution de votre moniteur.",
+ "label": "Mode de remplissage"
+ },
+ "section": {
+ "label": "Apparence et ergonomie"
+ },
+ "transition-duration": {
+ "description": "Durée des animations de transition en secondes.",
+ "label": "Durée de la transition"
+ },
+ "transition-type": {
+ "description": "Type d'animation lors du changement de fond d'écran.",
+ "label": "Type de transition"
+ }
+ },
+ "settings": {
+ "enable-management": {
+ "description": "Gérez les fonds d'écran avec Noctalia. Décochez si vous préférez utiliser une autre application.",
+ "label": "Activer la gestion des fonds d'écran"
+ },
+ "enable-overview": {
+ "description": "Applique un fond d'écran flou et atténué à l'écran de présentation.",
+ "label": "Activer le fond d'écran de l'aperçu"
+ },
+ "folder": {
+ "description": "Chemin vers votre dossier principal de fonds d'écran.",
+ "label": "Dossier des fonds d'écran",
+ "tooltip": "Parcourir le dossier des fonds d'écran"
+ },
+ "monitor-specific": {
+ "description": "Définissez un dossier de fond d'écran différent pour chaque moniteur.",
+ "label": "Dossiers spécifiques au moniteur",
+ "tooltip": "Parcourir le dossier des fonds d'écran"
+ },
+ "recursive-search": {
+ "description": "Rechercher également des fonds d'écran dans les sous-dossiers du répertoire des fonds d'écran.",
+ "label": "Rechercher dans les sous-dossiers"
+ },
+ "section": {
+ "description": "Contrôlez comment les fonds d'écran sont gérés et affichés.",
+ "label": "Paramètres du fond d'écran"
+ },
+ "select-folder": "Sélectionner le dossier des fonds d'écran",
+ "select-monitor-folder": "Sélectionner le dossier des fonds d'écran du moniteur",
+ "selector-position": {
+ "description": "Choisissez l'emplacement d'affichage du panneau de sélection du fond d'écran.",
+ "label": "Position"
+ }
+ },
+ "title": "Fond d'écran"
+ }
+ },
+ "setup": {
+ "appearance": {
+ "header": "Apparence",
+ "subheader": "Choisissez le mode sombre et la source des couleurs (Matugen ou prédéfinie)."
+ },
+ "customize": {
+ "header": "Personnaliser votre expérience",
+ "subheader": "Ajustez la position de la barre, la densité, l'échelle et plus encore."
+ },
+ "wallpaper": {
+ "choose-dir": "Choisissez un répertoire contenant vos fonds d'écran",
+ "dir": {
+ "browse": "Parcourir le dossier",
+ "description": "Choisissez le dossier contenant vos fonds d'écran",
+ "label": "Répertoire des fonds d'écran",
+ "select-title": "Sélectionner le dossier des fonds d'écran"
+ },
+ "header": "Choisissez votre fond d'écran",
+ "no-dir": "Aucun répertoire de fonds d'écran sélectionné",
+ "no-valid": "Aucun fichier image valide trouvé dans : {dir}",
+ "none-in-dir": "Aucun fond d'écran trouvé dans le répertoire",
+ "preview-error": "Échec du chargement de l'image",
+ "select-prompt": "Sélectionnez un fond ci-dessous",
+ "subheader": "Définissez l'ambiance avec un joli fond."
+ },
+ "welcome": {
+ "note": "Quelques réglages de base pour démarrer — toutes les options sont dans Paramètres"
+ }
+ },
+ "system": {
+ "cpu-temperature": "{temp}°C",
+ "disk-usage": "{percent}%",
+ "location-display": "{name} ({coordinates})",
+ "monitor-description": "{model} ({width}x{height} @ {scale}x)",
+ "no-media-player-detected": "Aucun lecteur multimédia détecté",
+ "scaling-percentage": "{percentage}%",
+ "signal-strength": "{signal}%",
+ "unknown": "Inconnu",
+ "unknown-app": "Application inconnue",
+ "unknown-layout": "Inconnue",
+ "unknown-version": "Inconnue",
+ "uptime": "Activité : {uptime}",
+ "user-requested": "Demandé par l'utilisateur",
+ "welcome-back": "Bon retour,",
+ "widget-settings-title": "Paramètres de {widget}"
+ },
"toast": {
- "night-light": {
- "enabled": "Activé",
- "disabled": "Désactivé",
- "not-installed": "wlsunset non installé",
- "forced": "Activation forcée",
- "normal": "Mode normal"
- },
- "keep-awake": {
- "enabled": "Activé",
- "disabled": "Désactivé"
- },
- "wallpaper-colors": {
- "enabled": "Couleurs du fond d'écran activées",
- "disabled": "Couleurs du fond d'écran désactivées",
- "not-installed": "Matugen non installé - requis pour l'extraction des couleurs du fond d'écran"
- },
- "matugen": {
- "failed": "Échec du traitement Matugen",
- "failed-general": "Matugen a rencontré une erreur lors du traitement des modèles"
- },
- "recording": {
- "stopping": "Arrêt de l'enregistrement...",
- "started": "Enregistrement démarré",
- "saved": "Enregistrement sauvegardé",
- "failed-start": "Échec du démarrage de l'enregistrement",
- "failed-gpu": "gpu-screen-recorder s'est arrêté de manière inattendue.",
- "failed-general": "L'enregistreur s'est arrêté avec une erreur.",
- "no-portals": "Les portails de bureau ne sont pas en cours d'exécution",
- "no-portals-desc": "Démarrez xdg-desktop-portal et un portail de compositeur (wlr/hyprland/gnome/kde).",
- "not-installed": "gpu-screen-recorder non installé",
- "not-installed-desc": "Veuillez installer gpu-screen-recorder pour utiliser les fonctionnalités d'enregistrement d'écran."
- },
- "clipboard": {
- "unavailable": "Historique du presse-papiers indisponible",
- "unavailable-desc": "L'application 'cliphist' n'est pas installée. Veuillez l'installer pour utiliser les fonctionnalités d'historique du presse-papiers."
- },
- "ipc": {
- "powerpanel-deprecated": "PowerPanel a été renommé en SessionMenu, cet appel IPC sera bientôt obsolète. Veuillez utiliser \"ipc call sessionMenu toggle\" à la place.",
- "sidepanel-deprecated": "SidePanel a été renommé en ControlCenter, cet appel IPC sera bientôt obsolète. Veuillez utiliser \"ipc call controlCenter toggle\" à la place."
- },
- "wifi": {
- "enabled": "Activé",
- "disabled": "Désactivé",
- "connected": "Connecté à '{ssid}'",
- "disconnected": "Déconnecté de '{ssid}'"
- },
- "bluetooth": {
- "enabled": "Activé",
- "disabled": "Désactivé"
- },
"airplane-mode": {
- "title": "Mode avion",
+ "disabled": "Désactivé",
"enabled": "Activé",
- "disabled": "Désactivé"
- },
- "internet": {
- "limited": "Connecté sans Internet"
- },
- "kofi": {
- "opened": "Page Ko-fi ouverte dans votre navigateur"
- },
- "do-not-disturb": {
- "enabled": "'Ne pas déranger' activé",
- "disabled": "'Ne pas déranger' désactivé",
- "enabled-desc": "Vous trouverez ces notifications dans votre historique.",
- "disabled-desc": "Affichage de toutes les notifications."
- },
- "power-profile": {
- "changed": "Profil d'alimentation changé",
- "profile-name": "\"{profile}\""
+ "title": "Mode avion"
},
"battery": {
"low": "Batterie faible",
"low-desc": "La batterie est à {percent}%. Veuillez brancher le chargeur."
},
"battery-manager": {
- "title": "Seuil de batterie",
- "set-success-desc": "Seuil de batterie défini à {percent}%",
"initial-setup": "Configuration initiale requise",
- "set-failed": "Échec de la définition du seuil de batterie",
- "install-success": "Installation réussie",
- "install-missing": "Fichiers requis manquants",
- "install-unsupported": "Système non pris en charge",
"install-failed": "Échec de l'installation",
+ "install-missing": "Fichiers requis manquants",
+ "install-success": "Installation réussie",
+ "install-unsupported": "Système non pris en charge",
+ "set-failed": "Échec de la définition du seuil de batterie",
+ "set-success-desc": "Seuil de batterie défini à {percent}%",
+ "title": "Seuil de batterie",
+ "uninstall-failed": "Échec de la désinstallation",
"uninstall-setup": "Désinstallation, authentification requise",
- "uninstall-success": "Désinstallation réussie",
- "uninstall-failed": "Échec de la désinstallation"
+ "uninstall-success": "Désinstallation réussie"
+ },
+ "bluetooth": {
+ "disabled": "Désactivé",
+ "enabled": "Activé"
+ },
+ "clipboard": {
+ "unavailable": "Historique du presse-papiers indisponible",
+ "unavailable-desc": "L'application 'cliphist' n'est pas installée. Veuillez l'installer pour utiliser les fonctionnalités d'historique du presse-papiers."
+ },
+ "do-not-disturb": {
+ "disabled": "'Ne pas déranger' désactivé",
+ "disabled-desc": "Affichage de toutes les notifications.",
+ "enabled": "'Ne pas déranger' activé",
+ "enabled-desc": "Vous trouverez ces notifications dans votre historique."
+ },
+ "internet": {
+ "limited": "Connecté sans Internet"
+ },
+ "ipc": {
+ "powerpanel-deprecated": "PowerPanel a été renommé en SessionMenu, cet appel IPC sera bientôt obsolète. Veuillez utiliser \"ipc call sessionMenu toggle\" à la place.",
+ "sidepanel-deprecated": "SidePanel a été renommé en ControlCenter, cet appel IPC sera bientôt obsolète. Veuillez utiliser \"ipc call controlCenter toggle\" à la place."
+ },
+ "keep-awake": {
+ "disabled": "Désactivé",
+ "enabled": "Activé"
+ },
+ "kofi": {
+ "opened": "Page Ko-fi ouverte dans votre navigateur"
},
"missing-control-center": {
- "label": "Dernier widget du Centre de contrôle supprimé",
- "description": "Le widget du Centre de contrôle a été retiré de la barre. Pour y accéder à nouveau depuis la barre, veuillez ré‑ajouter le widget. Vous pouvez aussi l'ouvrir en cliquant avec le bouton droit sur la barre."
+ "description": "Le widget du Centre de contrôle a été retiré de la barre. Pour y accéder à nouveau depuis la barre, veuillez ré‑ajouter le widget. Vous pouvez aussi l'ouvrir en cliquant avec le bouton droit sur la barre.",
+ "label": "Dernier widget du Centre de contrôle supprimé"
+ },
+ "night-light": {
+ "disabled": "Désactivé",
+ "enabled": "Activé",
+ "forced": "Activation forcée",
+ "normal": "Mode normal",
+ "not-installed": "wlsunset non installé"
+ },
+ "noctalia-performance": {
+ "disabled": "Mode performance désactivé",
+ "enabled": "Mode performance activé",
+ "label": "Performance de Noctalia"
+ },
+ "power-profile": {
+ "changed": "Profil d'alimentation changé",
+ "profile-name": "\"{profile}\""
+ },
+ "recording": {
+ "failed-general": "L'enregistreur s'est arrêté avec une erreur.",
+ "failed-gpu": "gpu-screen-recorder s'est arrêté de manière inattendue.",
+ "failed-start": "Échec du démarrage de l'enregistrement",
+ "no-portals": "Les portails de bureau ne sont pas en cours d'exécution",
+ "no-portals-desc": "Démarrez xdg-desktop-portal et un portail de compositeur (wlr/hyprland/gnome/kde).",
+ "not-installed": "gpu-screen-recorder non installé",
+ "not-installed-desc": "Veuillez installer gpu-screen-recorder pour utiliser les fonctionnalités d'enregistrement d'écran.",
+ "saved": "Enregistrement sauvegardé",
+ "started": "Enregistrement démarré",
+ "stopping": "Arrêt de l'enregistrement..."
+ },
+ "theming-processor-failed": {
+ "desc-generic": "Une erreur s'est produite lors du traitement des modèles.",
+ "title-matugen": "Le traitement du modèle Matugen a échoué.",
+ "title-predefined": "Le traitement du schéma de couleurs prédéfini a échoué."
+ },
+ "wallpaper-colors": {
+ "disabled": "Couleurs du fond d'écran désactivées",
+ "enabled": "Couleurs du fond d'écran activées",
+ "label": "Génération de couleurs pour fonds d'écran",
+ "not-installed": "Matugen non installé - requis pour l'extraction des couleurs du fond d'écran"
+ },
+ "wifi": {
+ "connected": "Connecté à '{ssid}'",
+ "disabled": "Désactivé",
+ "disconnected": "Déconnecté de '{ssid}'",
+ "enabled": "Activé"
}
},
+ "tooltips": {
+ "add-widget": "Ajouter un widget",
+ "bluetooth-devices": "Appareils Bluetooth",
+ "brightness-at": "Luminosité : {brightness}%\nClic droit pour les paramètres.\nFaites défiler pour modifier la luminosité.",
+ "cancel-timer": "Annuler le minuteur",
+ "clear-history": "Effacer l'historique",
+ "click-to-start-recording": "Cliquez pour démarrer l'enregistrement",
+ "click-to-stop-recording": "Cliquez pour arrêter l'enregistrement",
+ "close": "Fermer",
+ "connect-disconnect-devices": "Clic gauche pour connecter. Clic droit pour oublier.",
+ "delete-notification": "Supprimer la notification",
+ "disable-keep-awake": "Désactiver le mode 'rester éveillé'",
+ "do-not-disturb-disabled": "'Ne pas déranger' désactivé",
+ "do-not-disturb-enabled": "'Ne pas déranger' activé",
+ "enable-keep-awake": "Activer le mode 'rester éveillé'",
+ "forget-network": "Oublier le réseau",
+ "home": "Accueil",
+ "input-muted": "Couper l'entrée audio",
+ "keep-awake": "Rester éveillé",
+ "keyboard-layout": "Disposition du clavier {layout}",
+ "manage-wifi": "Gérer le Wi-Fi",
+ "microphone-volume-at": "Volume du microphone à {volume}%.\nClic gauche pour les paramètres. Clic droit pour activer/désactiver le mode muet.\nFaites défiler pour modifier le volume.",
+ "move-to-center-section": "Déplacer vers la section centrale",
+ "move-to-left-section": "Déplacer vers la section de gauche",
+ "move-to-right-section": "Déplacer vers la section de droite",
+ "next-media": "Média suivant",
+ "next-month": "Mois suivant",
+ "night-light-disabled": "L'éclairage nocturne est désactivé.\nClic gauche pour changer de mode.\nClic droit pour accéder aux paramètres.",
+ "night-light-enabled": "L'éclairage nocturne est activé.\nClic gauche pour changer de mode.\nClic droit pour accéder aux paramètres.",
+ "night-light-forced": "L'éclairage nocturne est forcé.\nClic gauche pour changer de mode.\nClic droit pour accéder aux paramètres.",
+ "night-light-not-installed": "L'éclairage nocturne n'est pas disponible.\nwlsunset n'est pas installé.",
+ "noctalia-performance-disabled": "Le mode performance Noctalia est désactivé.\nClic gauche pour activer.",
+ "noctalia-performance-enabled": "Le mode performance Noctalia est activé.\nClic gauche pour désactiver.",
+ "open-control-center": "Ouvrir le centre de contrôle",
+ "open-notification-history-disable-dnd": "Ouvrir l'historique des notifications\nClic droit pour désactiver \"Ne pas déranger\".",
+ "open-notification-history-enable-dnd": "Ouvrir l'historique des notifications\nClic droit pour activer \"Ne pas déranger\".",
+ "open-settings": "Ouvrir les paramètres",
+ "open-tray-dropdown": "Ouvrir le menu déroulant de la barre d'état",
+ "open-wallpaper-selector": "Ouvrir le sélecteur de fond d'écran",
+ "output-muted": "Couper la sortie audio",
+ "pause": "Pause",
+ "play": "Lecture",
+ "power-profile": "Profil d'alimentation '{profile}'",
+ "previous-media": "Média précédent",
+ "previous-month": "Mois précédent",
+ "refresh": "Actualiser",
+ "refresh-devices": "Actualiser les appareils",
+ "refresh-wallpaper-list": "Actualiser la liste des fonds d'écran",
+ "remove-widget": "Supprimer le widget",
+ "screen-recorder-not-installed": "L'enregistreur d'écran n'est pas installé",
+ "session-menu": "Menu de session",
+ "set-power-profile": "Définir le profil d'alimentation \"{profile}\"",
+ "start-screen-recording": "Démarrer l'enregistrement d'écran",
+ "stop-screen-recording": "Arrêter l'enregistrement d'écran",
+ "switch-to-dark-mode": "Passer en mode sombre",
+ "switch-to-light-mode": "Passer en mode clair",
+ "up": "Remonter",
+ "volume-at": "Volume de sortie à {volume}%.\nClic gauche pour les paramètres. Clic droit pour activer/désactiver le mode muet.\nFaites défiler pour modifier le volume.",
+ "wallpaper-selector": "Clic gauche : Ouvrir le sélecteur de fond d'écran.\nClic droit : Définir un fond d'écran aléatoire.",
+ "widget-settings": "Paramètres du widget"
+ },
+ "wallpaper": {
+ "configure-directory": "Configurez votre répertoire de fonds d'écran avec des images.",
+ "fill-modes": {
+ "center": "Centrer",
+ "crop": "Recadrer (Remplir)",
+ "fit": "Ajuster (Contenir)",
+ "stretch": "Étirer"
+ },
+ "no-match": "Aucun résultat trouvé.",
+ "no-wallpaper": "Aucun fond d'écran trouvé.",
+ "panel": {
+ "apply-all-monitors": {
+ "description": "Appliquer le fond d'écran sélectionné à tous les moniteurs en même temps.",
+ "label": "Appliquer à tous les moniteurs"
+ },
+ "search": "Rechercher :",
+ "title": "Sélecteur de fond d'écran"
+ },
+ "transitions": {
+ "disc": "Disque",
+ "fade": "Fondu",
+ "none": "Aucune",
+ "random": "Aléatoire",
+ "stripes": "Rayures",
+ "wipe": "Balayage"
+ },
+ "try-different-search": "Essayez une autre requête de recherche."
+ },
"weather": {
"clear-sky": "Ciel dégagé",
- "mainly-clear": "Généralement dégagé",
- "partly-cloudy": "Partiellement nuageux",
- "overcast": "Couvert",
- "fog": "Brouillard",
"drizzle": "Bruine",
- "snow": "Neige",
+ "fog": "Brouillard",
+ "mainly-clear": "Généralement dégagé",
+ "overcast": "Couvert",
+ "partly-cloudy": "Partiellement nuageux",
"rain-showers": "Averses de pluie",
+ "snow": "Neige",
"thunderstorm": "Orage",
"unknown": "Inconnu"
},
- "authentication": {
- "failed": "Échec de l'authentification",
- "error": "Erreur d'authentification"
- },
- "general": {
- "no-results": "Aucun résultat",
- "no-summary": "Aucun résumé",
- "unknown": "Inconnu"
- },
- "battery": {
- "no-battery-detected": "Aucune batterie détectée.",
- "charging-rate": "Taux de charge : {rate} W.",
- "discharging-rate": "Taux de décharge : {rate} W.",
- "charging": "En charge.",
- "discharging": "En décharge.",
- "plugged-in": "Branché.",
- "idle": "Inactif.",
- "time-left": "Temps restant : {time}.",
- "time-until-full": "Temps jusqu'à charge complète : {time}.",
- "health": "État : {percent}%",
- "panel": {
- "title": "Seuil de charge",
- "full": "Capacité totale ({percent}%)",
- "balanced": "Équilibré ({percent}%)",
- "lifespan": "Durée de vie prolongée ({percent}%)",
- "disabled": "Gestionnaire de batterie désactivé"
- }
- },
- "setup": {
- "customize": {
- "header": "Personnaliser votre expérience",
- "subheader": "Ajustez la position de la barre, la densité, l'échelle et plus encore."
+ "widgets": {
+ "color-picker": {
+ "apply": "Appliquer",
+ "brightness": "Luminosité",
+ "cancel": "Annuler",
+ "hex": {
+ "description": "Entrez un code couleur hexadécimal.",
+ "label": "Couleur hexadécimale"
+ },
+ "palette": {
+ "description": "Choisissez parmi une large gamme de couleurs prédéfinies.",
+ "label": "Palette"
+ },
+ "rgb": {
+ "description": "Ajustez les valeurs de rouge, vert, bleu et de luminosité.",
+ "label": "Valeurs RVB"
+ },
+ "theme-colors": {
+ "description": "Accès rapide à la palette de couleurs de votre thème.",
+ "label": "Couleurs du thème"
+ },
+ "title": "Sélecteur de couleurs"
},
- "appearance": {
- "header": "Apparence",
- "subheader": "Choisissez le mode sombre et la source des couleurs (Matugen ou prédéfinie)."
- },
- "wallpaper": {
- "header": "Choisissez votre fond d'écran",
- "subheader": "Définissez l'ambiance avec un joli fond.",
- "select-prompt": "Sélectionnez un fond ci-dessous",
- "preview-error": "Échec du chargement de l'image",
- "none-in-dir": "Aucun fond d'écran trouvé dans le répertoire",
- "no-dir": "Aucun répertoire de fonds d'écran sélectionné",
- "no-valid": "Aucun fichier image valide trouvé dans : {dir}",
- "choose-dir": "Choisissez un répertoire contenant vos fonds d'écran",
- "dir": {
- "label": "Répertoire des fonds d'écran",
- "description": "Choisissez le dossier contenant vos fonds d'écran",
- "browse": "Parcourir le dossier",
- "select-title": "Sélectionner le dossier des fonds d'écran"
+ "datetime-tokens": {
+ "ampm": {
+ "lowercase": "am/pm en minuscules",
+ "uppercase": "AM/PM en majuscules"
+ },
+ "common": {
+ "12hour-time-minutes": "Heure sur 12 heures avec minutes",
+ "24hour-time-minutes": "Heure sur 24 heures avec minutes",
+ "24hour-time-seconds": "Heure sur 24 heures avec secondes",
+ "european-date": "Format de date européen",
+ "iso-date": "Format de date ISO",
+ "us-date": "Format de date américain",
+ "weekday-date": "Jour de la semaine avec date",
+ "weekday-month-day": "Jour de la semaine, mois et jour"
+ },
+ "day": {
+ "abbreviated": "Nom du jour abrégé",
+ "full": "Nom complet du jour",
+ "leading-zero": "Jour avec zéro initial (01-31)",
+ "no-leading-zero": "Jour sans zéro initial (1-31)"
+ },
+ "hour": {
+ "leading-zero": "Heure avec zéro initial (00-23) - format 24 heures",
+ "no-leading-zero": "Heure sans zéro initial (0-23) - format 24 heures"
+ },
+ "minute": {
+ "leading-zero": "Minute avec zéro initial (00-59)",
+ "no-leading-zero": "Minute sans zéro initial (0-59)"
+ },
+ "month": {
+ "abbreviated": "Nom du mois abrégé",
+ "full": "Nom complet du mois",
+ "number-leading-zero": "Mois en chiffre avec zéro initial (01-12)",
+ "number-no-zero": "Mois en chiffre sans zéro initial (1-12)"
+ },
+ "second": {
+ "leading-zero": "Seconde avec zéro initial (00-59)",
+ "no-leading-zero": "Seconde sans zéro initial (0-59)"
+ },
+ "timezone": {
+ "abbreviation": "Abréviation du fuseau horaire"
+ },
+ "year": {
+ "four-digit": "Année sur quatre chiffres",
+ "two-digit": "Année sur deux chiffres (00-99)"
}
},
- "welcome": {
- "note": "Quelques réglages de base pour démarrer — toutes les options sont dans Paramètres"
+ "file-picker": {
+ "cancel": "Annuler",
+ "search-placeholder": "Rechercher des fichiers et des dossiers...",
+ "select-current": "Sélectionner Actuel",
+ "select-file": "Sélectionner un fichier",
+ "select-folder": "Sélectionner un dossier",
+ "title": "Sélecteur de fichiers"
+ },
+ "icon-picker": {
+ "apply": "Appliquer",
+ "cancel": "Annuler",
+ "search": {
+ "label": "Rechercher"
+ },
+ "title": "Sélecteur d'icônes"
+ },
+ "text-input": {
+ "clear": "Effacer"
+ },
+ "tooltip": {
+ "placeholder": "Espace réservé"
+ }
+ },
+ "wifi": {
+ "panel": {
+ "connect": "Connecter",
+ "connected": "Connecté",
+ "disabled": "Le Wi-Fi est désactivé",
+ "disconnect": "Déconnecter",
+ "disconnecting": "Déconnexion en cours...",
+ "enable-message": "Activez le Wi-Fi pour voir les réseaux disponibles.",
+ "enter-password": "Entrez le mot de passe...",
+ "forget": "Oublier",
+ "forget-network": "Oublier ce réseau ?",
+ "forgetting": "Oubli en cours...",
+ "no-networks": "Aucun réseau trouvé",
+ "password": "Mot de passe",
+ "saved": "Enregistré",
+ "scan-again": "Analyser à nouveau",
+ "searching": "Recherche de réseaux à proximité en cours...",
+ "title": "Wi-Fi"
}
}
}
diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json
index 2825fb05..dd06ca77 100644
--- a/Assets/Translations/pt.json
+++ b/Assets/Translations/pt.json
@@ -1,1229 +1,278 @@
{
- "settings": {
- "general": {
- "title": "Geral",
- "profile": {
- "section": {
- "label": "Perfil",
- "description": "Edite os detalhes do seu usuário e avatar."
- },
- "picture": {
- "label": "Foto de perfil de {user}",
- "description": "Sua foto de perfil que aparece em toda a interface."
- },
- "select-avatar": "Selecionar imagem de avatar"
- },
- "screen-corners": {
- "section": {
- "label": "Cantos da tela",
- "description": "Personalize o arredondamento dos cantos da tela e os efeitos visuais."
- },
- "show-corners": {
- "label": "Mostrar cantos da tela",
- "description": "Exibe cantos arredondados na borda da tela."
- },
- "solid-black": {
- "label": "Cantos pretos sólidos",
- "description": "Use preto sólido em vez da cor de fundo da barra."
- },
- "radius": {
- "label": "Raio dos cantos da tela",
- "description": "Ajuste os cantos arredondados da tela.",
- "reset": "Redefinir raio dos cantos da tela"
- }
- },
- "fonts": {
- "section": {
- "label": "Fontes",
- "description": "Escolha as fontes usadas em toda a interface."
- },
- "default": {
- "label": "Fonte padrão",
- "description": "Fonte principal usada em toda a interface.",
- "placeholder": "Selecione a fonte padrão...",
- "search-placeholder": "Pesquisar fontes...",
- "scale": {
- "description": "Aumentar ou diminuir o tamanho do texto padrão.",
- "label": "Tamanho de fonte padrão"
- }
- },
- "monospace": {
- "label": "Fonte monoespaçada",
- "description": "Fonte monoespaçada usada para exibição de números e estatísticas.",
- "placeholder": "Selecione a fonte monoespaçada...",
- "search-placeholder": "Pesquisar fontes monoespaçadas...",
- "scale": {
- "description": "Aumentar ou diminuir o tamanho do texto monoespaçado.",
- "label": "Tamanho da fonte monoespaçada"
- }
- },
- "reset-scaling": "Redefinir escala"
- },
- "language": {
- "section": {
- "label": "Idioma",
- "description": "Escolha o seu idioma preferido para a aplicação."
- },
- "select": {
- "label": "Idioma da aplicação",
- "description": "Selecione o idioma usado na interface da aplicação.",
- "auto-detect": "Automático"
- }
- },
- "launch-setup-wizard": "Iniciar o assistente de configuração"
- },
- "audio": {
- "title": "Áudio",
- "volumes": {
- "section": {
- "label": "Volumes",
- "description": "Ajuste os controles de volume e os níveis de áudio."
- },
- "output-volume": {
- "label": "Volume de saída",
- "description": "Nível de volume de todo o sistema."
- },
- "mute-output": {
- "label": "Silenciar saída de áudio",
- "description": "Silencia a saída de áudio principal do sistema."
- },
- "input-volume": {
- "label": "Volume de entrada",
- "description": "Nível de volume de entrada do microfone."
- },
- "mute-input": {
- "label": "Silenciar entrada de áudio",
- "description": "Silencia a entrada de áudio padrão (microfone)."
- },
- "step-size": {
- "label": "Tamanho do passo do volume",
- "description": "Ajuste o tamanho do passo para alterações de volume (roda do mouse, atalhos de teclado)."
- },
- "volume-overdrive": {
- "label": "Permitir volume acima de 100%",
- "description": "Permite aumentar o volume para além dos 100%. Poderá não ser suportado por todo o hardware."
- }
- },
- "devices": {
- "section": {
- "label": "Dispositivos de áudio",
- "description": "Configure os dispositivos de entrada e saída de áudio disponíveis."
- },
- "output-device": {
- "label": "Dispositivo de saída",
- "description": "Selecione o dispositivo de saída de áudio desejado."
- },
- "input-device": {
- "label": "Dispositivo de entrada",
- "description": "Selecione o dispositivo de entrada de áudio desejado."
- }
- },
- "media": {
- "section": {
- "label": "Reprodutores de mídia",
- "description": "Defina seus aplicativos de mídia preferidos e ignorados."
- },
- "primary-player": {
- "label": "Reprodutor principal",
- "description": "Digite uma palavra-chave para identificar seu reprodutor principal.",
- "placeholder": "ex: spotify, vlc, mpv"
- },
- "excluded-player": {
- "label": "Reprodutor excluído",
- "description": "Adicione palavras-chave para os reprodutores que você deseja que o sistema ignore. Cada palavra-chave deve estar em uma nova linha.",
- "placeholder": "digite a substring e pressione +"
- },
- "visualizer-type": {
- "label": "Tipo de visualização",
- "description": "Escolha um tipo de visualização para a reprodução de mídia"
- },
- "frame-rate": {
- "label": "Taxa de quadros",
- "description": "Taxas mais altas são mais suaves, mas consomem mais recursos."
- },
- "scrolling-title": {
- "label": "Título com rolagem",
- "description": "Ativar rolagem contínua para títulos de mídia longos"
- },
- "scrolling-speed": {
- "label": "Velocidade de rolagem",
- "description": "Tempo em segundos para o título rolar do início ao fim"
- }
- }
- },
- "display": {
- "title": "Tela",
- "monitors": {
- "section": {
- "label": "Configurações por monitor",
- "description": "Ajustar o brilho das configurações para cada tela."
- },
- "brightness": "Brilho",
- "brightness-step": {
- "label": "Tamanho do passo do brilho",
- "description": "Ajuste o tamanho do passo para alterações de brilho (roda do mouse e atalhos de teclado)."
- },
- "enforce-minimum": {
- "label": "Forçar brilho mínimo (1%)",
- "description": "Resolve o problema da retroiluminação apagar completamente em alguns monitores com brilho em 0%."
- }
- },
- "night-light": {
- "section": {
- "label": "Luz noturna",
- "description": "Reduz a emissão de luz azul para ajudar você a dormir melhor e diminuir o cansaço visual."
- },
- "enable": {
- "label": "Ativar luz noturna",
- "description": "Aplica um filtro de cor quente para reduzir a emissão de luz azul."
- },
- "temperature": {
- "label": "Temperatura da cor",
- "description": "Defina o quão quente a cor será durante a noite e o dia.",
- "night": "Noite",
- "day": "Dia"
- },
- "auto-schedule": {
- "label": "Agendamento automático",
- "description": "Baseado no horário do pôr do sol e do nascer do sol em {location} - recomendado."
- },
- "manual-schedule": {
- "label": "Agendamento manual",
- "description": "Defina horários personalizados para o nascer e o pôr do sol.",
- "sunrise": "Horário do nascer do sol",
- "sunset": "Horário do pôr do sol",
- "select-start": "Selecionar hora de início",
- "select-stop": "Selecionar hora de término"
- },
- "force-activation": {
- "label": "Forçar ativação",
- "description": "Ignora o agendamento e aplica o filtro noturno imediatamente."
- }
- }
- },
- "bar": {
- "title": "Barra",
- "appearance": {
- "section": {
- "label": "Aparência",
- "description": "Personalize a aparência e a posição da barra."
- },
- "position": {
- "label": "Posição da barra",
- "description": "Escolha onde posicionar a barra na tela."
- },
- "density": {
- "label": "Densidade da barra",
- "description": "Ajuste o preenchimento da barra para um visual compacto ou espaçoso."
- },
- "background-opacity": {
- "label": "Opacidade do fundo",
- "description": "Ajuste a opacidade do fundo da barra."
- },
- "show-capsule": {
- "label": "Mostrar cápsula",
- "description": "Mostra o fundo dos widgets."
- },
- "floating": {
- "label": "Barra flutuante",
- "description": "Exibe a barra como uma 'pílula' flutuante. Nota: Isso moverá os cantos da tela para as bordas."
- },
- "margins": {
- "label": "Margens",
- "description": "Ajuste as margens ao redor da barra flutuante.",
- "vertical": "Vertical",
- "horizontal": "Horizontal"
- }
- },
- "widgets": {
- "section": {
- "label": "Posicionamento dos widgets",
- "description": "Arraste e solte widgets para reordená-los. Os emblemas indicam o uso: [E]squerda, [C]entro, [D]ireita."
- }
- },
- "monitors": {
- "section": {
- "label": "Exibição nos monitores",
- "description": "Mostra a barra em monitores específicos. O padrão é todos, se nenhum for escolhido."
- }
- },
- "tray": {
- "blacklist": {
- "label": "Lista Negra",
- "description": "Adicione regras de exclusão para a bandeja do sistema, suporta curingas (*).",
- "placeholder": "ex: nm-applet, Fcitx*"
- }
- }
- },
- "dock": {
- "title": "Dock",
- "enabled": {
- "label": "Ativar dock",
- "description": "Mostrar ou ocultar o dock completamente"
- },
- "appearance": {
- "section": {
- "label": "Aparência",
- "description": "Personalize o comportamento e a aparência da dock."
- },
- "display": {
- "label": "Exibição",
- "description": "Escolha como a dock se comporta.",
- "always-visible": "Sempre visível",
- "auto-hide": "Ocultar automaticamente",
- "exclusive": "Exclusivo"
- },
- "background-opacity": {
- "label": "Opacidade do fundo",
- "description": "Ajuste a opacidade do fundo da dock."
- },
- "floating-distance": {
- "label": "Distância de flutuação da dock",
- "description": "Ajuste a distância de flutuação da borda da tela."
- },
- "icon-size": {
- "label": "Tamanho do Dock",
- "description": "Ajustar o tamanho geral do Dock."
- },
- "colorize-icons": {
- "label": "Colorir ícones",
- "description": "Aplicar cores do tema aos ícones de aplicativos da dock (apenas aplicativos não focados)."
- }
- },
- "monitors": {
- "section": {
- "label": "Exibição no monitor",
- "description": "Mostrar dock em monitores específicos. O padrão é todos se nenhum for escolhido."
- },
- "only-same-output": {
- "description": "Mostrar apenas aplicativos da saída onde o dock está localizado.",
- "label": "Apenas aplicativos da mesma saída"
- }
- }
- },
- "launcher": {
- "title": "Lançador",
- "settings": {
- "section": {
- "label": "Aparência",
- "description": "Personalize o comportamento e a aparência do lançador."
- },
- "position": {
- "label": "Posição",
- "description": "Escolha onde o painel do lançador aparece."
- },
- "background-opacity": {
- "label": "Opacidade do fundo",
- "description": "Ajuste a opacidade do fundo do lançador."
- },
- "clipboard-history": {
- "label": "Ativar histórico da área de transferência",
- "description": "Acesse itens copiados anteriormente a partir do lançador."
- },
- "sort-by-usage": {
- "label": "Ordenar por mais usados",
- "description": "Quando ativado, os aplicativos mais usados aparecem primeiro na lista."
- },
- "use-app2unit": {
- "label": "Usar App2Unit para iniciar aplicativos",
- "description": "Usa um método de inicialização alternativo para gerenciar melhor os processos de aplicativos e evitar problemas."
- },
- "terminal-command": {
- "label": "Comando do terminal",
- "description": "Comando para iniciar um terminal. Ex: 'kitty -e' ou 'gnome-terminal --'."
- },
- "custom-launch-prefix": {
- "label": "Prefixo de inicialização personalizado",
- "description": "Prefixar comandos com um inicializador personalizado (ex. 'runapp' para integração systemd)."
- },
- "custom-launch-prefix-enabled": {
- "label": "Habilitar prefixo de inicialização personalizado",
- "description": "Usar um prefixo personalizado para inicializar aplicativos em vez do método padrão."
- }
- }
- },
- "notifications": {
- "title": "Notificações",
- "settings": {
- "section": {
- "label": "Aparência",
- "description": "Configure a aparência e o comportamento das notificações."
- },
- "do-not-disturb": {
- "label": "Não perturbe",
- "description": "Desativa todos os pop-ups de notificação quando ativado."
- },
- "enable-osd": {
- "label": "Ativar exibição na tela",
- "description": "Mostra alterações de volume e brilho em tempo real."
- },
- "location": {
- "label": "Localização",
- "description": "Onde as notificações aparecem na tela."
- },
- "always-on-top": {
- "label": "Sempre no topo",
- "description": "Exibe notificações acima de janelas em tela cheia e outras camadas."
- },
- "background-opacity": {
- "label": "Opacidade do fundo",
- "description": "Ajustar a opacidade dos fundos das notificações."
- }
- },
- "duration": {
- "section": {
- "label": "Duração da notificação",
- "description": "Configure por quanto tempo as notificações permanecem visíveis com base no nível de urgência."
- },
- "respect-expire": {
- "label": "Respeitar tempo de expiração",
- "description": "Usa o tempo de expiração definido na notificação."
- },
- "low-urgency": {
- "label": "Urgência baixa",
- "description": "Por quanto tempo as notificações de baixa prioridade permanecem visíveis."
- },
- "normal-urgency": {
- "label": "Urgência normal",
- "description": "Por quanto tempo as notificações de prioridade normal permanecem visíveis."
- },
- "critical-urgency": {
- "label": "Urgência crítica",
- "description": "Por quanto tempo as notificações de prioridade crítica permanecem visíveis."
- }
- },
- "monitors": {
- "section": {
- "label": "Exibição nos monitores",
- "description": "Mostra a notificação em monitores específicos. O padrão é todos, se nenhum for escolhido."
- }
- }
- },
- "wallpaper": {
- "title": "Papel de parede",
- "settings": {
- "section": {
- "label": "Configurações de papel de parede",
- "description": "Controle como os papéis de parede são gerenciados e exibidos."
- },
- "enable-management": {
- "label": "Ativar gerenciamento de papel de parede",
- "description": "Gerencie papéis de parede com o Noctalia. Desmarque se preferir usar outro aplicativo."
- },
- "folder": {
- "label": "Pasta de papéis de parede",
- "description": "Caminho para a sua pasta principal de papéis de parede.",
- "tooltip": "Procurar pasta de papéis de parede"
- },
- "recursive-search": {
- "label": "Buscar em subpastas",
- "description": "Também buscar papéis de parede em subpastas do diretório de papéis de parede."
- },
- "monitor-specific": {
- "label": "Diretórios específicos por monitor",
- "description": "Defina uma pasta de papel de parede diferente para cada monitor.",
- "tooltip": "Procurar pasta de papéis de parede"
- },
- "select-folder": "Selecionar pasta de papéis de parede",
- "select-monitor-folder": "Selecionar pasta de papéis de parede do monitor"
- },
- "look-feel": {
- "section": {
- "label": "Aparência e estilo"
- },
- "fill-mode": {
- "label": "Modo de preenchimento",
- "description": "Selecione como a imagem deve ser dimensionada para corresponder à resolução do seu monitor."
- },
- "fill-color": {
- "label": "Cor de preenchimento",
- "description": "Escolha uma cor de preenchimento que pode aparecer atrás do papel de parede."
- },
- "transition-type": {
- "label": "Tipo de transição",
- "description": "Tipo de animação ao alternar entre papéis de parede."
- },
- "transition-duration": {
- "label": "Duração da transição",
- "description": "Duração das animações de transição em segundos."
- },
- "edge-smoothness": {
- "label": "Suavizar borda da transição",
- "description": "Aplica um efeito suave e esfumaçado à borda das transições."
- }
- },
- "automation": {
- "section": {
- "label": "Automação"
- },
- "random-wallpaper": {
- "label": "Papel de parede aleatório",
- "description": "Agende mudanças aleatórias de papel de parede em intervalos regulares."
- },
- "interval": {
- "label": "Intervalo do papel de parede",
- "description": "Com que frequência alterar os papéis de parede automaticamente."
- },
- "custom-interval": {
- "label": "Intervalo personalizado",
- "description": "Digite o tempo como HH:MM (ex., 01:30)."
- }
- }
- },
- "color-scheme": {
- "title": "Esquema de cores",
- "color-source": {
- "section": {
- "label": "Fonte de cor",
- "description": "Configurações principais para as cores do Noctalia."
- },
- "use-wallpaper-colors": {
- "label": "Usar cores do papel de parede",
- "description": "Gerar esquemas de cores do seu papel de parede usando Matugen. Extrai automaticamente cores para criar um tema coeso."
- },
- "matugen-scheme-type": {
- "label": "Tipo de esquema Matugen",
- "description": {
- "scheme-content": "Deriva cores que correspondem de perto à imagem subjacente.",
- "scheme-expressive": "Paleta vibrante com saturação divertida.",
- "scheme-fidelity": "Paleta de alta fidelidade que preserva as tonalidades originais.",
- "scheme-fruit-salad": "Mistura colorida de detalhes contrastantes e vibrantes.",
- "scheme-monochrome": "Paleta minimalista construída em torno de uma única tonalidade.",
- "scheme-neutral": "Paleta de cores neutras com tons suaves e calmantes.",
- "scheme-rainbow": "Paleta diversificada abrangendo todo o espectro.",
- "scheme-tonal-spot": "Paleta equilibrada com toques focais."
- }
- }
- },
- "dark-mode": {
- "switch": {
- "label": "Modo escuro",
- "description": "Muda para um tema mais escuro para facilitar a visualização à noite."
- },
- "mode": {
- "description": "Ativa a mudança automática entre o modo claro e o modo escuro.",
- "label": "Agendamento do modo escuro",
- "location": "Localização",
- "manual": "Manual",
- "off": "Desligado"
- }
- },
- "predefined": {
- "section": {
- "label": "Esquemas de cores predefinidos",
- "description": "Escolha entre uma coleção de esquemas de cores predefinidos."
- },
- "generate-templates": {
- "label": "Gerar modelos para esquemas predefinidos",
- "description": "Gera modelos Matugen (GTK, temas de terminal, etc.) ao usar esquemas de cores predefinidos."
- }
- },
- "templates": {
- "ui": {
- "label": "UI",
- "description": "Tematização do ambiente de desktop e kit de ferramentas de UI.",
- "gtk": {
- "description": "Escrever {filepath}"
- },
- "qt": {
- "description": "Escrever {filepath}"
- },
- "kcolorscheme": {
- "description": "Escrever {filepath}"
- }
- },
- "terminal": {
- "label": "Terminal",
- "description": "Tematização do emulador de terminal.",
- "kitty": {
- "description": "Escrever {filepath} e recarregar",
- "description-missing": "Requer que o {app} esteja instalado"
- },
- "ghostty": {
- "description": "Escrever {filepath} e recarregar",
- "description-missing": "Requer que o {app} esteja instalado"
- },
- "foot": {
- "description": "Escrever {filepath} e recarregar",
- "description-missing": "Requer que o {app} esteja instalado"
- }
- },
- "programs": {
- "label": "Programas",
- "description": "Tematização específica de aplicativos.",
- "fuzzel": {
- "description": "Escrever {filepath} e recarregar",
- "description-missing": "Requer que o {app} esteja instalado"
- },
- "vicinae": {
- "description": "Escrever {filepath} e recarregar",
- "description-missing": "Requer que o {app} esteja instalado"
- },
- "walker": {
- "description": "Escrever {filepath} e definir tema como noctalia",
- "description-missing": "Requer que o {app} esteja instalado"
- },
- "discord": {
- "description": "Escrever {filepath} para {client}",
- "description-missing": "Nenhum cliente Discord detectado. Instale vencord, vesktop, webcord, armcord, equibop, lightcord ou dorion."
- },
- "pywalfox": {
- "description": "Escrever {filepath} e executar pywalfox update",
- "description-missing": "Requer que o {app} esteja instalado"
- },
- "code": {
- "description": "Escreva em {filepath}. O tema Hyprluna precisa ser instalado e ativado manualmente.",
- "description-missing": "Requer que o {app} esteja instalado."
- }
- },
- "misc": {
- "label": "Diversos",
- "description": "Opções de configuração adicionais.",
- "user-templates": {
- "label": "Modelos do usuário",
- "description": "Ativa a configuração do Matugen definida pelo usuário. Um arquivo de modelo será criado em ~/.config/noctalia/user-templates.toml na primeira ativação"
- }
- },
- "section": {
- "description": "Aplicar cores a aplicações externas.",
- "label": "Modelos"
- }
- }
- },
- "location": {
- "title": "Localização",
- "location": {
- "section": {
- "label": "Sua localização",
- "description": "Obtenha previsão do tempo e agendamento de luz noturna precisos definindo sua localização."
- },
- "search": {
- "label": "Procurar por uma localização",
- "description": "ex., São Paulo, SP",
- "placeholder": "Digite o nome da localização"
- }
- },
- "weather": {
- "section": {
- "label": "Clima",
- "description": "Escolha sua unidade de temperatura preferida."
- },
- "enabled": {
- "label": "Ativar clima",
- "description": "Mostrar informações meteorológicas em toda a interface e buscar dados do clima. Quando desativado, todos os elementos do clima serão ocultados e nenhuma solicitação de rede será feita."
- },
- "fahrenheit": {
- "label": "Exibir temperatura em Fahrenheit (°F)",
- "description": "Exibe a temperatura em Fahrenheit em vez de Celsius."
- },
- "show-in-calendar": {
- "description": "Mostre a previsão do tempo diária diretamente na sua visualização de calendário.",
- "label": "Exibir clima no calendário"
- }
- },
- "date-time": {
- "section": {
- "label": "Data e hora",
- "description": "Personalize como a data e a hora aparecem."
- },
- "12hour-format": {
- "label": "Use o formato de hora de 12 horas",
- "description": "Exibe a hora no formato de 12 horas na tela de bloqueio e no calendário. O relógio da barra tem suas próprias configurações."
- },
- "week-numbers": {
- "label": "Mostrar números da semana",
- "description": "Exibe a semana do ano (ex., Semana 38) no calendário."
- },
- "show-events": {
- "label": "Mostrar eventos do calendário",
- "description": "Exibir eventos no painel do calendário."
- },
- "use-analog": {
- "description": "Mostrar um relógio estilo analógico na tela do calendário.",
- "label": "Use um relógio de estilo analógico."
- },
- "first-day-of-week": {
- "automatic": "Automático (usar localização do sistema)",
- "description": "Escolha qual dia começa a semana no calendário.",
- "label": "Primeiro dia da semana"
- }
- }
- },
- "network": {
- "title": "Rede",
- "section": {
- "description": "Gerencie conexões Wi-Fi e Bluetooth."
- },
- "wifi": {
- "label": "Ativar Wi-Fi"
- },
- "bluetooth": {
- "label": "Ativar Bluetooth"
- }
- },
- "screen-recorder": {
- "title": "Gravador de tela",
- "general": {
- "section": {
- "label": "Configurações gerais",
- "description": "Gerencie a saída e o conteúdo da gravação de tela."
- },
- "output-folder": {
- "label": "Pasta de saída",
- "description": "Pasta onde as gravações de tela serão salvas.",
- "tooltip": "Procurar pasta de saída"
- },
- "show-cursor": {
- "label": "Mostrar cursor",
- "description": "Grava o cursor do mouse no vídeo."
- },
- "select-output-folder": "Selecionar pasta de saída"
- },
- "video": {
- "section": {
- "label": "Configurações de vídeo",
- "description": "Configure as opções de gravação de vídeo."
- },
- "video-source": {
- "label": "Fonte de vídeo",
- "description": "Portal é recomendado, se você tiver artefatos, tente Screen."
- },
- "frame-rate": {
- "label": "Taxa de quadros",
- "description": "Taxa de quadros alvo para gravações de tela."
- },
- "video-quality": {
- "label": "Qualidade de vídeo",
- "description": "Qualidade mais alta resulta em arquivos maiores."
- },
- "video-codec": {
- "label": "Codec de vídeo",
- "description": "h264 é o codec mais comum."
- },
- "color-range": {
- "label": "Gama de cores",
- "description": "Limitada é recomendada para melhor compatibilidade."
- }
- },
- "audio": {
- "section": {
- "label": "Configurações de áudio",
- "description": "Configure as opções de gravação de áudio."
- },
- "audio-source": {
- "label": "Fonte de áudio",
- "description": "Fonte de áudio a ser capturada durante a gravação."
- },
- "audio-codec": {
- "label": "Codec de áudio",
- "description": "Opus é recomendado para melhor desempenho e menor tamanho de áudio."
- }
- }
- },
- "about": {
- "title": "Sobre",
- "noctalia": {
- "section": {
- "label": "Shell Noctalia",
- "description": "Um shell de desktop elegante e minimalista, cuidadosamente criado para Wayland, construído com Quickshell."
- },
- "latest-version": "Última versão:",
- "installed-version": "Versão instalada:",
- "download-latest": "Baixar a versão mais recente"
- },
- "contributors": {
- "section": {
- "label": "Contribuidores",
- "description": "Agradecimentos ao nosso incrível colaborador!",
- "description_plural": "Agradecimentos aos nossos {count} incríveis colaboradores!"
- }
- },
- "support": "Apoie-nos"
- },
- "hooks": {
- "title": "Hooks",
- "system-hooks": {
- "section": {
- "label": "Ganchos do sistema",
- "description": "Configure comandos para serem executados quando eventos do sistema ocorrerem."
- },
- "enable": {
- "label": "Ativar hooks",
- "description": "Ativa ou desativa todos os comandos de hook."
- }
- },
- "wallpaper-changed": {
- "label": "Papel de parede alterado",
- "description": "Comando a ser executado quando o papel de parede muda.",
- "placeholder": "ex., notify-send \"Papel de parede\" \"Alterado\""
- },
- "theme-changed": {
- "label": "Tema alterado",
- "description": "Comando a ser executado quando o tema alterna entre modo escuro e claro.",
- "placeholder": "ex., notify-send \"Tema\" \"Alternado\""
- },
- "info": {
- "command-info": {
- "label": "Informações de Comando do Hook",
- "description": "• Comandos são executados via shell (sh -c)\n• Comandos rodam em segundo plano (desanexados)\n• Botões de teste executam com os valores atuais"
- },
- "parameters": {
- "label": "Parâmetros Disponíveis",
- "description": "• Hook de Papel de Parede: $1 = caminho do papel de parede, $2 = nome da tela\n• Hook de Alternância de Tema: $1 = true/false (estado do modo escuro)"
- }
- }
- },
- "osd": {
- "title": "Exibição na tela",
- "description": "Configure indicadores na tela, como volume e brilho.",
- "section": {
- "general": {
- "label": "Geral",
- "description": "Configurar visibilidade e comportamento da OSD."
- }
- },
- "enabled": {
- "label": "Ativar exibição na tela",
- "description": "Mostrar alterações de volume e brilho em tempo real."
- },
- "always-on-top": {
- "label": "Sempre no topo",
- "description": "Exibir OSD acima de janelas em tela cheia e outras camadas."
- },
- "location": {
- "label": "Localização",
- "description": "Onde a exibição na tela aparece."
- },
- "duration": {
- "section": {
- "label": "Tempo para ocultar",
- "description": "Quanto tempo a OSD permanece visível antes de ocultar automaticamente."
- },
- "auto-hide": {
- "label": "Ocultar após",
- "description": "Ajusta o tempo antes da OSD desaparecer."
- }
- },
- "monitors": {
- "section": {
- "label": "Exibição nos monitores",
- "description": "Mostrar a OSD em monitores específicos. Padrão é todos se nenhum for escolhido."
- }
- }
- },
- "control-center": {
- "position": {
- "description": "Escolha onde o painel da Central de Controle aparece quando aberto.",
- "label": "Posição"
- },
- "section": {
- "description": "Configurar o posicionamento e o comportamento do painel da Central de Controle.",
- "label": "Aparência"
- },
- "title": "Central de Controle",
- "cards": {
- "section": {
- "description": "Personalize quais controles aparecem na Central de Controle e em que ordem.",
- "label": "Cartas"
- }
- },
- "shortcuts": {
- "section": {
- "description": "Configure e gerencie os widgets de atalhos.",
- "label": "Widgets de atalhos"
- },
- "sectionLeft": "Esquerda",
- "sectionRight": "Direito/Certo/À direita"
- }
- },
- "user-interface": {
- "animation-disable": {
- "description": "Desative todas as animações para uma experiência mais rápida e responsiva.",
- "label": "Desativar animações da interface do usuário"
- },
- "panels-overlay": {
- "label": "Abrir painéis na camada de sobreposição",
- "description": "Os painéis aparecerão sobre janelas em tela cheia"
- },
- "animation-speed": {
- "description": "Ajustar a velocidade global da animação.",
- "label": "Velocidade da animação",
- "reset": "Redefinir velocidade da animação"
- },
- "border-radius": {
- "description": "Controla o arredondamento dos cantos de janelas, botões e outros elementos.",
- "label": "Raio da borda",
- "reset": "Redefinir raio da borda"
- },
- "scaling": {
- "description": "Altera o tamanho da interface geral do usuário, excluindo a barra.",
- "label": "Escalonamento da interface",
- "reset-scaling": "Redefinir escala da interface"
- },
- "section": {
- "description": "Personalize a aparência, a sensação e o comportamento da interface.",
- "label": "Aparência"
- },
- "title": "Interface do usuário",
- "tooltips": {
- "description": "Ativar ou desativar dicas de ferramentas em toda a interface.",
- "label": "Mostrar dicas de ferramenta"
- },
- "panels-attached-to-bar": {
- "description": "Quando ativado, os painéis serão anexados à barra com um belo design de canto invertido.",
- "label": "Anexar painéis à barra"
- }
- },
- "lock-screen": {
- "compact-lockscreen": {
- "description": "Mostrar apenas a entrada de login e os controles do sistema, ocultando os widgets de clima e mídia.",
- "label": "Tela de bloqueio compacta"
- },
- "lock-on-suspend": {
- "description": "Bloquear a tela automaticamente ao suspender o sistema.",
- "label": "Bloquear ao suspender"
- },
- "title": "Tela de bloqueio"
- }
- },
- "widgets": {
- "tooltip": {
- "placeholder": "Espaço reservado"
- },
- "file-picker": {
- "select-folder": "Selecionar Pasta",
- "select-file": "Selecionar Arquivo",
- "cancel": "Cancelar",
- "search-placeholder": "Pesquisar arquivos e pastas...",
- "select-current": "Selecionar Atual",
- "title": "Seletor de Arquivos"
- },
- "datetime-tokens": {
- "common": {
- "12hour-time-minutes": "Horário de 12 horas com minutos",
- "24hour-time-minutes": "Horário de 24 horas com minutos",
- "24hour-time-seconds": "Horário de 24 horas com segundos",
- "weekday-month-day": "Dia da semana, mês e dia",
- "iso-date": "Formato de data ISO",
- "us-date": "Formato de data dos EUA",
- "european-date": "Formato de data europeu",
- "weekday-date": "Dia da semana com data"
- },
- "hour": {
- "no-leading-zero": "Hora sem zero à esquerda (0-23) - formato de 24 horas",
- "leading-zero": "Hora com zero à esquerda (00-23) - formato de 24 horas"
- },
- "minute": {
- "no-leading-zero": "Minuto sem zero à esquerda (0-59)",
- "leading-zero": "Minuto com zero à esquerda (00-59)"
- },
- "second": {
- "no-leading-zero": "Segundo sem zero à esquerda (0-59)",
- "leading-zero": "Segundo com zero à esquerda (00-59)"
- },
- "ampm": {
- "uppercase": "AM/PM em maiúsculas",
- "lowercase": "am/pm em minúsculas"
- },
- "timezone": {
- "abbreviation": "Abreviação do fuso horário"
- },
- "year": {
- "two-digit": "Ano com dois dígitos (00-99)",
- "four-digit": "Ano com quatro dígitos"
- },
- "month": {
- "number-no-zero": "Mês como número sem zero à esquerda (1-12)",
- "number-leading-zero": "Mês como número com zero à esquerda (01-12)",
- "abbreviated": "Nome do mês abreviado",
- "full": "Nome completo do mês"
- },
- "day": {
- "no-leading-zero": "Dia sem zero à esquerda (1-31)",
- "leading-zero": "Dia com zero à esquerda (01-31)",
- "abbreviated": "Nome do dia abreviado",
- "full": "Nome completo do dia"
- }
- },
- "icon-picker": {
- "title": "Seletor de ícones",
- "search": {
- "label": "Pesquisar"
- },
- "cancel": "Cancelar",
- "apply": "Aplicar"
- },
- "color-picker": {
- "title": "Seletor de cores",
- "hex": {
- "label": "Cor hexadecimal",
- "description": "Digite um código de cor hexadecimal."
- },
- "rgb": {
- "label": "Valores RGB",
- "description": "Ajuste os valores de vermelho, verde, azul e brilho."
- },
- "brightness": "Brilho",
- "theme-colors": {
- "label": "Cores do tema",
- "description": "Acesso rápido à paleta de cores do seu tema."
- },
- "palette": {
- "label": "Paleta",
- "description": "Escolha entre uma vasta gama de cores predefinidas."
- },
- "cancel": "Cancelar",
- "apply": "Aplicar"
- },
- "text-input": {
- "clear": "Limpar"
- }
+ "authentication": {
+ "error": "Erro de autenticação",
+ "failed": "Falha na autenticação"
},
"bar": {
"widget-settings": {
- "dialog": {
- "cancel": "Cancelar",
- "apply": "Aplicar"
- },
- "section-editor": {
- "placeholder": "Selecione um widget para adicionar...",
- "search-placeholder": "Pesquisar widgets..."
- },
"active-window": {
+ "colorize-icons": {
+ "description": "Aplicar cores do tema ao ícone da janela ativa.",
+ "label": "Colorir ícones"
+ },
"hide-mode": {
- "label": "Modo de ocultação",
- "description": "Controla o comportamento do widget quando nenhuma janela está ativa."
- },
- "show-app-icon": {
- "label": "Mostrar ícone do aplicativo",
- "description": "Exibir o ícone do aplicativo ao lado do título da janela."
- },
- "scrolling-mode": {
- "label": "Modo de rolagem",
- "description": "Controlar quando a rolagem de texto está habilitada para títulos de janela longos."
+ "description": "Controla o comportamento do widget quando nenhuma janela está ativa.",
+ "label": "Modo de ocultação"
},
"max-width": {
- "label": "Largura Máxima",
- "description": "Define o tamanho horizontal máximo do widget. O widget será reduzido para se adequar a conteúdo mais curto."
+ "description": "Define o tamanho horizontal máximo do widget. O widget será reduzido para se adequar a conteúdo mais curto.",
+ "label": "Largura Máxima"
+ },
+ "scrolling-mode": {
+ "description": "Controlar quando a rolagem de texto está habilitada para títulos de janela longos.",
+ "label": "Modo de rolagem"
+ },
+ "show-app-icon": {
+ "description": "Exibir o ícone do aplicativo ao lado do título da janela.",
+ "label": "Mostrar ícone do aplicativo"
},
"use-fixed-width": {
- "label": "Usar Largura Fixa",
- "description": "Quando ativado, o widget sempre usará a largura máxima em vez de ajustar dinamicamente ao conteúdo."
- },
- "colorize-icons": {
- "label": "Colorir ícones",
- "description": "Aplicar cores do tema ao ícone da janela ativa."
+ "description": "Quando ativado, o widget sempre usará a largura máxima em vez de ajustar dinamicamente ao conteúdo.",
+ "label": "Usar Largura Fixa"
}
},
- "system-monitor": {
- "cpu-usage": {
- "label": "Uso da CPU",
- "description": "Exibir o percentual atual de uso da CPU."
+ "audio-visualizer": {
+ "color-name": {
+ "description": "Selecione a cor para o visualizador.",
+ "label": "Cor de preenchimento"
},
- "cpu-temperature": {
- "label": "Temperatura da CPU",
- "description": "Mostrar leituras de temperatura da CPU se disponíveis."
+ "hide-when-idle": {
+ "description": "Quando ativado, o visualizador fica oculto a menos que haja reprodução ativa.",
+ "label": "Ocultar quando não houver reprodução"
},
- "memory-usage": {
- "label": "Uso de memória",
- "description": "Exibir informações atuais de uso da RAM."
- },
- "memory-percentage": {
- "label": "Memória como percentual",
- "description": "Mostrar o uso de memória como percentual em vez de valores absolutos."
- },
- "network-traffic": {
- "label": "Tráfego de rede",
- "description": "Exibir velocidades de upload e download da rede."
- },
- "storage-usage": {
- "label": "Uso de armazenamento",
- "description": "Mostrar informações de uso do espaço em disco."
- }
- },
- "notification-history": {
- "show-unread-badge": {
- "label": "Mostrar distintivo de não lidos",
- "description": "Exibir um distintivo mostrando o número de notificações não lidas."
- },
- "hide-badge-when-zero": {
- "label": "Ocultar distintivo quando zero",
- "description": "Ocultar o distintivo de notificação quando não há notificações não lidas."
+ "width": {
+ "description": "Largura do componente personalizado.",
+ "label": "Largura"
}
},
"battery": {
"display-mode": {
- "label": "Modo de exibição",
- "description": "Escolha como você gostaria que este valor aparecesse."
+ "description": "Escolha como você gostaria que este valor aparecesse.",
+ "label": "Modo de exibição"
},
"low-battery-threshold": {
- "label": "Limite de aviso de bateria fraca",
- "description": "Mostrar um aviso quando a bateria cair abaixo desta porcentagem."
- }
- },
- "control-center": {
- "use-distro-logo": {
- "label": "Usar logo da distribuição em vez de ícone",
- "description": "Usar o logo da sua distribuição em vez de um ícone personalizado."
- },
- "icon": {
- "label": "Ícone",
- "description": "Selecione um ícone da biblioteca ou um arquivo personalizado."
- },
- "browse-library": "Navegar na Biblioteca",
- "browse-file": "Navegar por Arquivo",
- "select-custom-icon": "Selecione um ícone personalizado"
- },
- "keyboard-layout": {
- "display-mode": {
- "label": "Modo de exibição",
- "description": "Escolha como você gostaria que este valor aparecesse."
- }
- },
- "volume": {
- "display-mode": {
- "label": "Modo de exibição",
- "description": "Escolha como você gostaria que este valor aparecesse."
- }
- },
- "workspace": {
- "label-mode": {
- "label": "Modo de rótulo",
- "description": "Escolher como os rótulos de espaço de trabalho são exibidos."
- },
- "hide-unoccupied": {
- "label": "Ocultar desocupados",
- "description": "Não exibir áreas de trabalho sem janelas."
- },
- "character-count": {
- "label": "Número de caracteres",
- "description": "Número de caracteres a exibir dos nomes de espaços de trabalho (1-10)."
- }
- },
- "microphone": {
- "display-mode": {
- "label": "Modo de exibição",
- "description": "Escolha como você gostaria que este valor aparecesse."
+ "description": "Mostrar um aviso quando a bateria cair abaixo desta porcentagem.",
+ "label": "Limite de aviso de bateria fraca"
}
},
"brightness": {
"display-mode": {
- "label": "Modo de exibição",
- "description": "Escolha como você gostaria que este valor aparecesse."
+ "description": "Escolha como você gostaria que este valor aparecesse.",
+ "label": "Modo de exibição"
}
},
- "spacer": {
- "width": {
- "label": "Largura",
- "description": "Largura do espaçamento em pixels"
- }
- },
- "custom-button": {
- "icon": {
- "label": "Ícone",
- "description": "Selecione um ícone da biblioteca."
- },
- "browse": "Navegar",
- "left-click": {
- "label": "Clique esquerdo",
- "description": "Comando a executar quando o botão é clicado com o botão esquerdo."
- },
- "right-click": {
- "label": "Clique direito",
- "description": "Comando a executar quando o botão é clicado com o botão direito."
- },
- "middle-click": {
- "label": "Clique do meio",
- "description": "Comando a executar quando o botão é clicado com o botão do meio."
- },
- "dynamic-text": "Texto dinâmico",
- "display-command-output": {
- "label": "Exibir Saída de Comando",
- "description": "Digite um comando para ser executado em um intervalo regular. A primeira linha da sua saída será exibida como texto.",
- "stream-description": "Insira um comando para executar continuamente."
- },
- "refresh-interval": {
- "label": "Intervalo de atualização",
- "description": "Intervalo em milissegundos."
- },
- "text-stream": {
- "label": "Transmissão",
- "description": "As linhas transmitidas do comando serão exibidas como texto no botão."
- },
- "collapse-condition": {
- "label": "Condição de recolhimento",
- "description": "Se o texto de saída corresponder a este valor, o botão será recolhido."
- },
- "parse-json": {
- "label": "Analisar saída como JSON",
- "description": "Analisa a saída do comando como um objeto JSON para definir dinamicamente o texto e o ícone."
- }
- },
- "media-mini": {
- "hide-mode": {
- "label": "Modo de ocultação",
- "description": "Controla o comportamento do widget quando nenhuma mídia está sendo reproduzida."
- },
- "show-album-art": {
- "label": "Mostrar arte do álbum",
- "description": "Exibir a arte do álbum da faixa atualmente sendo reproduzida."
- },
- "show-visualizer": {
- "label": "Mostrar visualizador",
- "description": "Exibir um visualizador de áudio quando música está sendo reproduzida."
- },
- "visualizer-type": {
- "label": "Tipo de visualizador",
- "description": "Escolher o estilo de visualizador de áudio a exibir."
- },
- "max-width": {
- "label": "Largura Máxima",
- "description": "Define o tamanho horizontal máximo do widget. O widget será reduzido para se adequar a conteúdo mais curto."
- },
- "use-fixed-width": {
- "label": "Usar Largura Fixa",
- "description": "Quando ativado, o widget sempre usará a largura máxima em vez de ajustar dinamicamente ao conteúdo."
- },
- "scrolling-mode": {
- "label": "Modo de rolagem",
- "description": "Controlar quando a rolagem de texto está habilitada para títulos de faixa longos."
- },
- "no-active-player": "Nenhum player ativo"
- },
"clock": {
- "use-primary-color": {
- "label": "Usar cor primária",
- "description": "Quando ativado, aplica a cor primária para dar ênfase."
- },
- "use-monospaced-font": {
- "label": "Usar fonte monoespaçada",
- "description": "Quando ativado, o relógio usará a fonte monoespaçada."
- },
"clock-display": {
- "label": "Exibição do relógio",
- "description": "Personalize a exibição do seu relógio adicionando tokens da lista abaixo. Para usar o formato de 12 horas, você deve incluir o token 'AP'."
+ "description": "Personalize a exibição do seu relógio adicionando tokens da lista abaixo. Para usar o formato de 12 horas, você deve incluir o token 'AP'.",
+ "label": "Exibição do relógio"
},
- "horizontal-bar": {
- "label": "Barra horizontal",
- "description": "Dica: Use \\n para criar uma quebra de linha."
- },
- "vertical-bar": {
- "label": "Barra vertical",
- "description": "Use um espaço para separar cada parte em uma nova linha."
- },
- "preview": "Pré-visualização",
"custom-font": {
"description": "Selecione uma fonte personalizada para a exibição do relógio.",
"label": "Fonte personalizada",
"placeholder": "Selecionar fonte personalizada...",
"search-placeholder": "Procurar fontes..."
},
+ "horizontal-bar": {
+ "description": "Dica: Use \\n para criar uma quebra de linha.",
+ "label": "Barra horizontal"
+ },
+ "preview": "Pré-visualização",
"use-custom-font": {
"description": "Substitua a fonte padrão por uma fonte personalizada para o relógio.",
"label": "Usar fonte personalizada"
+ },
+ "use-monospaced-font": {
+ "description": "Quando ativado, o relógio usará a fonte monoespaçada.",
+ "label": "Usar fonte monoespaçada"
+ },
+ "use-primary-color": {
+ "description": "Quando ativado, aplica a cor primária para dar ênfase.",
+ "label": "Usar cor primária"
+ },
+ "vertical-bar": {
+ "description": "Use um espaço para separar cada parte em uma nova linha.",
+ "label": "Barra vertical"
+ }
+ },
+ "control-center": {
+ "browse-file": "Navegar por Arquivo",
+ "browse-library": "Navegar na Biblioteca",
+ "icon": {
+ "description": "Selecione um ícone da biblioteca ou um arquivo personalizado.",
+ "label": "Ícone"
+ },
+ "select-custom-icon": "Selecione um ícone personalizado",
+ "use-distro-logo": {
+ "description": "Usar o logo da sua distribuição em vez de um ícone personalizado.",
+ "label": "Usar logo da distribuição em vez de ícone"
+ },
+ "colorize-distro-logo": {
+ "description": "Aplicar as cores do tema ao logotipo da sua distribuição.",
+ "label": "Colorir logo da distribuição"
+ }
+ },
+ "custom-button": {
+ "browse": "Navegar",
+ "collapse-condition": {
+ "description": "Se o texto de saída corresponder a este valor, o botão será recolhido.",
+ "label": "Condição de recolhimento"
+ },
+ "display-command-output": {
+ "description": "Digite um comando para ser executado em um intervalo regular. A primeira linha da sua saída será exibida como texto.",
+ "label": "Exibir Saída de Comando",
+ "stream-description": "Insira um comando para executar continuamente."
+ },
+ "dynamic-text": "Texto dinâmico",
+ "hide-vertical": {
+ "description": "Se ativado, o texto da saída do comando não será exibido quando a barra estiver em um layout vertical (esquerda ou direita).",
+ "label": "Ocultar texto na barra vertical"
+ },
+ "icon": {
+ "description": "Selecione um ícone da biblioteca.",
+ "label": "Ícone"
+ },
+ "left-click": {
+ "description": "Comando a executar quando o botão é clicado com o botão esquerdo.",
+ "label": "Clique esquerdo"
+ },
+ "middle-click": {
+ "description": "Comando a executar quando o botão é clicado com o botão do meio.",
+ "label": "Clique do meio"
+ },
+ "parse-json": {
+ "description": "Analisa a saída do comando como um objeto JSON para definir dinamicamente o texto e o ícone.",
+ "label": "Analisar saída como JSON"
+ },
+ "refresh-interval": {
+ "description": "Intervalo em milissegundos.",
+ "label": "Intervalo de atualização"
+ },
+ "right-click": {
+ "description": "Comando a executar quando o botão é clicado com o botão direito.",
+ "label": "Clique direito"
+ },
+ "text-stream": {
+ "description": "As linhas transmitidas do comando serão exibidas como texto no botão.",
+ "label": "Transmissão"
+ }
+ },
+ "dialog": {
+ "apply": "Aplicar",
+ "cancel": "Cancelar"
+ },
+ "keyboard-layout": {
+ "display-mode": {
+ "description": "Escolha como você gostaria que este valor aparecesse.",
+ "label": "Modo de exibição"
+ }
+ },
+ "lock-keys": {
+ "browse": "Navegar",
+ "show-caps-lock": {
+ "description": "Exibir o status do Caps Lock.",
+ "label": "Caps Lock"
+ },
+ "show-num-lock": {
+ "description": "Exibir o status do Num Lock.",
+ "label": "Bloq Num"
+ },
+ "show-scroll-lock": {
+ "description": "Exibir o status do Scroll Lock.",
+ "label": "Scroll Lock"
+ }
+ },
+ "media-mini": {
+ "hide-mode": {
+ "description": "Controla o comportamento do widget quando nenhuma mídia está sendo reproduzida.",
+ "label": "Modo de ocultação"
+ },
+ "max-width": {
+ "description": "Define o tamanho horizontal máximo do widget. O widget será reduzido para se adequar a conteúdo mais curto.",
+ "label": "Largura Máxima"
+ },
+ "no-active-player": "Nenhum player ativo",
+ "scrolling-mode": {
+ "description": "Controlar quando a rolagem de texto está habilitada para títulos de faixa longos.",
+ "label": "Modo de rolagem"
+ },
+ "show-album-art": {
+ "description": "Exibir a arte do álbum da faixa atualmente sendo reproduzida.",
+ "label": "Mostrar arte do álbum"
+ },
+ "show-visualizer": {
+ "description": "Exibir um visualizador de áudio quando música está sendo reproduzida.",
+ "label": "Mostrar visualizador"
+ },
+ "use-fixed-width": {
+ "description": "Quando ativado, o widget sempre usará a largura máxima em vez de ajustar dinamicamente ao conteúdo.",
+ "label": "Usar Largura Fixa"
+ },
+ "visualizer-type": {
+ "description": "Escolher o estilo de visualizador de áudio a exibir.",
+ "label": "Tipo de visualizador"
+ }
+ },
+ "microphone": {
+ "display-mode": {
+ "description": "Escolha como você gostaria que este valor aparecesse.",
+ "label": "Modo de exibição"
+ }
+ },
+ "notification-history": {
+ "hide-badge-when-zero": {
+ "description": "Ocultar o distintivo de notificação quando não há notificações não lidas.",
+ "label": "Ocultar distintivo quando zero"
+ },
+ "show-unread-badge": {
+ "description": "Exibir um distintivo mostrando o número de notificações não lidas.",
+ "label": "Mostrar distintivo de não lidos"
+ }
+ },
+ "section-editor": {
+ "placeholder": "Selecione um widget para adicionar...",
+ "search-placeholder": "Pesquisar widgets..."
+ },
+ "spacer": {
+ "width": {
+ "description": "Largura do espaçamento em pixels",
+ "label": "Largura"
+ }
+ },
+ "system-monitor": {
+ "cpu-temperature": {
+ "description": "Mostrar leituras de temperatura da CPU se disponíveis.",
+ "label": "Temperatura da CPU"
+ },
+ "cpu-usage": {
+ "description": "Exibir o percentual atual de uso da CPU.",
+ "label": "Uso da CPU"
+ },
+ "memory-percentage": {
+ "description": "Mostrar o uso de memória como percentual em vez de valores absolutos.",
+ "label": "Memória como percentual"
+ },
+ "memory-usage": {
+ "description": "Exibir informações atuais de uso da RAM.",
+ "label": "Uso de memória"
+ },
+ "network-traffic": {
+ "description": "Exibir velocidades de upload e download da rede.",
+ "label": "Tráfego de rede"
+ },
+ "storage-usage": {
+ "description": "Mostrar informações de uso do espaço em disco.",
+ "label": "Uso de armazenamento"
}
},
"taskbar": {
+ "colorize-icons": {
+ "description": "Aplicar cores do tema aos ícones da barra de tarefas.",
+ "label": "Colorir ícones"
+ },
+ "hide-mode": {
+ "description": "Controla como o widget se comporta quando não há janelas correspondentes.",
+ "label": "Modo de ocultação"
+ },
"only-active-workspaces": {
"description": "Mostrar apenas aplicativos de áreas de trabalho ativas.",
"label": "Apenas de espaços de trabalho ativos"
@@ -1231,14 +280,6 @@
"only-same-output": {
"description": "Mostrar apenas os aplicativos da saída onde a barra está localizada.",
"label": "Apenas da mesma saída"
- },
- "colorize-icons": {
- "label": "Colorir ícones",
- "description": "Aplicar cores do tema aos ícones da barra de tarefas."
- },
- "hide-mode": {
- "label": "Modo de ocultação",
- "description": "Controla como o widget se comporta quando não há janelas correspondentes."
}
},
"taskbar-grouped": {
@@ -1253,118 +294,69 @@
},
"tray": {
"colorize-icons": {
- "label": "Colorir ícones",
- "description": "Aplicar cores do tema aos ícones da bandeja do sistema."
+ "description": "Aplicar cores do tema aos ícones da bandeja do sistema.",
+ "label": "Colorir ícones"
+ },
+ "drawer-enabled": {
+ "description": "Quando habilitado, os itens da bandeja não fixados são exibidos em um painel gaveta. Quando desabilitado, todos os itens da bandeja são exibidos inline.",
+ "label": "Habilitar gaveta"
}
},
- "audio-visualizer": {
- "width": {
- "label": "Largura",
- "description": "Largura do componente personalizado."
- },
- "hide-when-idle": {
- "label": "Ocultar quando não houver reprodução",
- "description": "Quando ativado, o visualizador fica oculto a menos que haja reprodução ativa."
- },
- "color-name": {
- "description": "Selecione a cor para o visualizador.",
- "label": "Cor de preenchimento"
+ "volume": {
+ "display-mode": {
+ "description": "Escolha como você gostaria que este valor aparecesse.",
+ "label": "Modo de exibição"
}
},
- "lock-keys": {
- "show-caps-lock": {
- "description": "Exibir o status do Caps Lock.",
- "label": "Caps Lock"
+ "workspace": {
+ "character-count": {
+ "description": "Número de caracteres a exibir dos nomes de espaços de trabalho (1-10).",
+ "label": "Número de caracteres"
},
- "show-num-lock": {
- "description": "Exibir o status do Num Lock.",
- "label": "Bloq Num"
+ "hide-unoccupied": {
+ "description": "Não exibir áreas de trabalho sem janelas.",
+ "label": "Ocultar desocupados"
},
- "show-scroll-lock": {
- "description": "Exibir o status do Scroll Lock.",
- "label": "Scroll Lock"
- },
- "indicator-style": {
- "circle": "Letras Circulares",
- "circle-dash": "Letras em Círculo Tracejadas",
- "circle-dot": "Letras de Círculo Pontilhado",
- "description": "Estilo de ícone para os indicadores da tecla de bloqueio.",
- "hex": "Letras Hexagonais",
- "label": "Estilo do Indicador",
- "large": "Letras grandes",
- "small": "Letras minúsculas",
- "square": "Letras Quadradas",
- "square-round": "Letras Quadradas Arredondadas"
+ "label-mode": {
+ "description": "Escolher como os rótulos de espaço de trabalho são exibidos.",
+ "label": "Modo de rótulo"
}
}
}
},
- "notifications": {
+ "battery": {
+ "charging": "Carregando.",
+ "charging-rate": "Taxa de carregamento: {rate} W.",
+ "discharging": "Descarregando.",
+ "discharging-rate": "Taxa de descarregamento: {rate} W.",
+ "health": "Saúde: {percent}%",
+ "idle": "Ocioso.",
+ "no-battery-detected": "Nenhuma bateria detectada.",
"panel": {
- "title": "Notificações",
- "no-notifications": "Nenhuma notificação",
- "description": "Suas notificações aparecerão aqui assim que chegarem.",
- "click-to-expand": "Clique para expandir"
- }
- },
- "wallpaper": {
- "panel": {
- "title": "Seletor de papel de parede",
- "apply-all-monitors": {
- "label": "Aplicar a todos os monitores",
- "description": "Aplica o papel de parede selecionado a todos os monitores de uma só vez."
- },
- "search": "Pesquisar:"
+ "balanced": "Balanceado ({percent}%)",
+ "disabled": "Gerenciador de bateria desativado",
+ "full": "Capacidade máxima ({percent}%)",
+ "lifespan": "Vida útil prolongada ({percent}%)",
+ "title": "Limite de carga"
},
- "transitions": {
- "none": "Nenhuma",
- "random": "Aleatória",
- "fade": "Esmaecer",
- "disc": "Disco",
- "stripes": "Listras",
- "wipe": "Limpar"
- },
- "fill-modes": {
- "center": "Centralizar",
- "crop": "Cortar (Preencher)",
- "fit": "Ajustar (Conter)",
- "stretch": "Esticar"
- },
- "no-match": "Nenhuma correspondência encontrada.",
- "no-wallpaper": "Nenhum papel de parede encontrado.",
- "try-different-search": "Tente uma busca diferente.",
- "configure-directory": "Configure seu diretório de papéis de parede com imagens."
+ "plugged-in": "Conectado.",
+ "time-left": "Tempo restante: {time}.",
+ "time-until-full": "Tempo até carga completa: {time}."
},
"bluetooth": {
"panel": {
- "title": "Bluetooth",
- "disabled": "O Bluetooth está desativado",
- "enable-message": "Ative o Bluetooth para ver os dispositivos disponíveis.",
- "connected-devices": "Dispositivos conectados",
- "known-devices": "Dispositivos conhecidos",
"available-devices": "Dispositivos disponíveis",
- "scanning": "Procurando por dispositivos...",
- "pairing-mode": "Certifique-se de que seu dispositivo está em modo de pareamento."
- }
- },
- "wifi": {
- "panel": {
- "title": "Wi-Fi",
- "disabled": "O Wi-Fi está desativado",
- "enable-message": "Ative o Wi-Fi para ver as redes disponíveis.",
- "searching": "Procurando redes próximas...",
- "connected": "Conectado",
- "disconnecting": "Desconectando...",
- "forgetting": "Esquecendo...",
- "saved": "Salva",
- "disconnect": "Desconectar",
- "enter-password": "Digite a senha...",
+ "blocked": "Bloqueado",
"connect": "Conectar",
- "password": "Senha",
- "forget-network": "Esquecer esta rede?",
- "forget": "Esquecer",
- "no-networks": "Nenhuma rede encontrada",
- "scan-again": "Escanear novamente"
+ "connected-devices": "Dispositivos conectados",
+ "disabled": "O Bluetooth está desativado",
+ "disconnect": "Desconectar",
+ "enable-message": "Ative o Bluetooth para ver os dispositivos disponíveis.",
+ "known-devices": "Dispositivos conhecidos",
+ "pairing": "Emparelhamento...",
+ "pairing-mode": "Certifique-se de que seu dispositivo está em modo de pareamento.",
+ "scanning": "Procurando por dispositivos...",
+ "title": "Bluetooth"
}
},
"calendar": {
@@ -1375,281 +367,246 @@
"loading": "Carregando o clima…"
}
},
- "tooltips": {
- "refresh": "Atualizar",
- "close": "Fechar",
- "up": "Acima",
- "home": "Início",
- "refresh-wallpaper-list": "Atualizar lista de papéis de parede",
- "refresh-devices": "Atualizar dispositivos",
- "forget-network": "Esquecer rede",
- "clear-history": "Limpar histórico",
- "delete-notification": "Excluir notificação",
- "previous-month": "Mês anterior",
- "next-month": "Próximo mês",
- "add-widget": "Adicionar widget",
- "widget-settings": "Configurações do widget",
- "remove-widget": "Remover widget",
- "move-to-left-section": "Mover para a seção esquerda",
- "move-to-center-section": "Mover para a seção central",
- "move-to-right-section": "Mover para a seção direita",
- "open-settings": "Abrir configurações",
- "session-menu": "Menu da Sessão",
- "cancel-timer": "Cancelar temporizador",
- "start-screen-recording": "Iniciar gravação de tela",
- "stop-screen-recording": "Parar gravação de tela",
- "screen-recorder-not-installed": "O gravador de tela não está instalado",
- "keep-awake": "Manter acordado",
- "enable-keep-awake": "Ativar 'manter acordado'",
- "disable-keep-awake": "Desativar 'manter acordado'",
- "wallpaper-selector": "Clique esquerdo: Abrir seletor de papel de parede.\nClique direito: Definir papel de parede aleatório.",
- "do-not-disturb-enabled": "'Não perturbe' ativado",
- "do-not-disturb-disabled": "'Não perturbe' desativado",
- "connect-disconnect-devices": "Clique esquerdo para conectar. Clique direito para esquecer.",
- "set-power-profile": "Definir perfil de energia \"{profile}\"",
- "switch-to-light-mode": "Mudar para o modo claro",
- "switch-to-dark-mode": "Mudar para o modo escuro",
- "night-light-disabled": "Luz noturna desativada.\nClique esquerdo para alternar o modo.\nClique direito para acessar as configurações.",
- "night-light-enabled": "Luz noturna ativada.\nClique esquerdo para alternar o modo.\nClique direito para acessar as configurações.",
- "night-light-forced": "Luz noturna forçada.\nClique esquerdo para alternar o modo.\nClique direito para acessar as configurações.",
- "night-light-not-installed": "Luz noturna não disponível.\nwlsunset não está instalado.",
- "click-to-start-recording": "Clique para iniciar a gravação",
- "click-to-stop-recording": "Clique para parar a gravação",
- "open-control-center": "Abrir central de controle",
- "brightness-at": "Brilho: {brightness}%\nClique direito para configurações.\nRole para modificar o brilho.",
- "manage-wifi": "Gerenciar Wi-Fi",
- "bluetooth-devices": "Dispositivos Bluetooth",
- "open-notification-history-enable-dnd": "Abrir histórico de notificações\nClique direito para ativar \"Não perturbe\".",
- "open-notification-history-disable-dnd": "Abrir histórico de notificações\nClique direito para desativar \"Não perturbe\".",
- "open-wallpaper-selector": "Abrir seletor de papel de parede",
- "previous-media": "Mídia anterior",
- "pause": "Pausar",
- "play": "Reproduzir",
- "next-media": "Próxima mídia",
- "power-profile": "Perfil de energia '{profile}'",
- "keyboard-layout": "Layout de teclado {layout}",
- "output-muted": "Silenciar saída de áudio",
- "input-muted": "Silenciar entrada de áudio",
- "microphone-volume-at": "Volume do microfone em {volume}%.\nClique esquerdo para configurações. Clique direito para ativar/desativar o mudo.\nRole para modificar o volume.",
- "volume-at": "Volume de saída em {volume}%.\nClique esquerdo para configurações. Clique direito para alternar o mudo.\nRole para modificar o volume."
- },
"clock": {
"tooltip": "Abrir calendário"
},
"dock": {
"menu": {
+ "close": "Fechar",
"focus": "Focar",
"pin": "Fixar",
- "unpin": "Desafixar",
- "close": "Fechar"
+ "unpin": "Desafixar"
}
},
+ "general": {
+ "no-results": "Nenhum resultado",
+ "no-summary": "Sem resumo",
+ "unknown": "Desconhecido"
+ },
"launcher": {
"pin": "Fixar no dock",
"unpin": "Desafixar do dock"
},
- "placeholders": {
- "search-icons": "ex., noctalia, niri, battery, cloud",
- "profile-picture-path": "/home/usuario/.face",
- "enter-width-pixels": "Digite a largura em pixels",
- "enter-command": "Digite o comando para executar (app ou script personalizado)",
- "command-example": "echo \"Olá Mundo\"",
- "enter-text-to-collapse": "por exemplo, 'nada está sendo reproduzido'. Use /regex/ para padrões.",
- "search-wallpapers": "Digite para filtrar papéis de parede...",
- "search-launcher": "Pesquisar entradas... ou use > para comandos",
- "search": "Pesquisar...",
- "select": "Selecionar",
- "cancel": "Cancelar",
- "test": "Testar"
+ "lock-screen": {
+ "authenticating": "Autenticando...",
+ "authentication-failed": "Falha na autenticação",
+ "password": "Digite sua senha...",
+ "restart": "Reiniciar",
+ "shut-down": "Desligar",
+ "suspend": "Suspender",
+ "welcome-back": "Bem-vindo de volta,"
+ },
+ "notifications": {
+ "panel": {
+ "click-to-expand": "Clique para expandir",
+ "description": "Suas notificações aparecerão aqui assim que chegarem.",
+ "no-notifications": "Nenhuma notificação",
+ "title": "Notificações"
+ }
},
"options": {
- "osd": {
+ "bar": {
+ "density": {
+ "comfortable": "Confortável",
+ "compact": "Compacta",
+ "default": "Padrão",
+ "mini": "Mini"
+ },
"position": {
- "top_left": "Superior esquerdo",
- "top_right": "Superior direito",
- "top_center": "Centro superior",
- "bottom_left": "Inferior esquerdo",
- "bottom_right": "Inferior direito",
- "bottom_center": "Centro inferior",
- "center_left": "Esquerda central",
- "center_right": "Direita central"
+ "bottom": "Inferior",
+ "left": "Esquerda",
+ "right": "Direita",
+ "top": "Topo"
}
},
- "launcher": {
- "position": {
- "center": "Centro (padrão)",
- "top_left": "Superior esquerdo",
- "top_right": "Superior direito",
- "bottom_left": "Inferior esquerdo",
- "bottom_right": "Inferior direito",
- "bottom_center": "Centro inferior",
- "top_center": "Centro superior",
- "center_left": "Centro à esquerda",
- "center_right": "Centro à direita"
- }
+ "colors": {
+ "error": "Erro",
+ "onSurface": "Na Superfície",
+ "primary": "Primário",
+ "secondary": "Secundário",
+ "tertiary": "Terciário"
},
"control-center": {
"position": {
- "close_to_bar_button": "Perto do botão da barra",
- "top_left": "Superior esquerdo",
- "top_right": "Superior direito",
+ "bottom_center": "Centro inferior",
"bottom_left": "Inferior esquerdo",
"bottom_right": "Inferior direito",
- "bottom_center": "Centro inferior",
+ "center": "Centro",
+ "close_to_bar_button": "Perto do botão da barra",
"top_center": "Centro superior",
- "center": "Centro"
+ "top_left": "Superior esquerdo",
+ "top_right": "Superior direito"
},
"quickSettingsStyle": {
- "modern": "Moderno",
"classic": "Clássico",
- "compact": "Compacto"
- }
- },
- "bar": {
- "position": {
- "top": "Topo",
- "bottom": "Inferior",
- "left": "Esquerda",
- "right": "Direita"
- },
- "density": {
- "compact": "Compacta",
- "default": "Padrão",
- "comfortable": "Confortável",
- "mini": "Mini"
+ "compact": "Compacto",
+ "modern": "Moderno"
}
},
"display-mode": {
- "on-hover": "Ao passar o mouse",
- "always-show": "Sempre mostrar",
"always-hide": "Sempre ocultar",
- "force-open": "Forçar Abertura"
+ "always-show": "Sempre mostrar",
+ "force-open": "Forçar Abertura",
+ "on-hover": "Ao passar o mouse"
},
- "workspace-labels": {
- "none": "Nenhum",
- "index": "Índice",
- "name": "Nome"
+ "frame-rates": {
+ "fps": "{fps} FPS"
},
- "visualizer-types": {
- "none": "Nenhum",
- "linear": "Linear",
- "mirrored": "Espelhado",
- "wave": "Onda"
+ "hide-modes": {
+ "hidden": "Ocultar Quando Vazio",
+ "idle": "Ocultar quando inativo",
+ "transparent": "Transparente quando vazio",
+ "visible": "Sempre Visível"
+ },
+ "launcher": {
+ "position": {
+ "bottom_center": "Centro inferior",
+ "bottom_left": "Inferior esquerdo",
+ "bottom_right": "Inferior direito",
+ "center": "Centro (padrão)",
+ "center_left": "Centro à esquerda",
+ "center_right": "Centro à direita",
+ "follow_bar": "Seguir barra (padrão)",
+ "top_center": "Centro superior",
+ "top_left": "Superior esquerdo",
+ "top_right": "Superior direito"
+ }
+ },
+ "osd": {
+ "position": {
+ "bottom_center": "Centro inferior",
+ "bottom_left": "Inferior esquerdo",
+ "bottom_right": "Inferior direito",
+ "center_left": "Esquerda central",
+ "center_right": "Direita central",
+ "top_center": "Centro superior",
+ "top_left": "Superior esquerdo",
+ "top_right": "Superior direito"
+ }
+ },
+ "screen-recording": {
+ "audio-sources": {
+ "both": "Saída do sistema + entrada do microfone",
+ "microphone-input": "Entrada do microfone",
+ "system-output": "Saída do sistema"
+ },
+ "color-range": {
+ "full": "Completa",
+ "limited": "Limitada"
+ },
+ "quality": {
+ "high": "Alta",
+ "medium": "Média",
+ "ultra": "Ultra",
+ "very-high": "Muito alta"
+ },
+ "sources": {
+ "portal": "Portal",
+ "screen": "Tela"
+ }
},
"scrolling-modes": {
"always": "Rolar Sempre",
"hover": "Rolar ao Passar o Mouse",
"never": "Nunca Rolar"
},
- "frame-rates": {
- "fps": "{fps} FPS"
+ "shadow-direction": {
+ "bottom": "Inferior",
+ "bottom_left": "Inferior esquerda",
+ "bottom_right": "Inferior direita",
+ "center": "Centro",
+ "left": "Esquerda",
+ "right": "Direita",
+ "top": "Superior",
+ "top_left": "Superior esquerda",
+ "top_right": "Superior direita"
},
- "screen-recording": {
- "sources": {
- "portal": "Portal",
- "screen": "Tela"
- },
- "quality": {
- "medium": "Média",
- "high": "Alta",
- "very-high": "Muito alta",
- "ultra": "Ultra"
- },
- "color-range": {
- "limited": "Limitada",
- "full": "Completa"
- },
- "audio-sources": {
- "system-output": "Saída do sistema",
- "microphone-input": "Entrada do microfone",
- "both": "Saída do sistema + entrada do microfone"
- }
+ "visualizer-quality": {
+ "high": "Alto(a)",
+ "low": "Baixo(a)"
},
- "hide-modes": {
- "hidden": "Ocultar Quando Vazio",
- "transparent": "Transparente quando vazio",
- "visible": "Sempre Visível"
+ "visualizer-types": {
+ "linear": "Linear",
+ "mirrored": "Espelhado",
+ "none": "Nenhum",
+ "wave": "Onda"
},
- "colors": {
- "error": "Erro",
- "primary": "Primário",
- "secondary": "Secundário",
- "tertiary": "Terciário",
- "onSurface": "Na Superfície"
+ "workspace-labels": {
+ "index": "Índice",
+ "name": "Nome",
+ "none": "Nenhum"
}
},
- "session-menu": {
- "title": "Menu da Sessão",
- "action-in-seconds": "{action} em {seconds} segundos...",
- "lock": "Bloquear",
- "lock-and-suspend": "Bloquear e Suspender",
- "suspend": "Suspender",
- "reboot": "Reiniciar",
- "logout": "Sair",
- "shutdown": "Desligar"
+ "placeholders": {
+ "cancel": "Cancelar",
+ "command-example": "echo \"Olá Mundo\"",
+ "enter-command": "Digite o comando para executar (app ou script personalizado)",
+ "enter-text-to-collapse": "por exemplo, 'nada está sendo reproduzido'. Use /regex/ para padrões.",
+ "enter-tooltip": "Inserir dica de ferramenta",
+ "enter-width-pixels": "Digite a largura em pixels",
+ "profile-picture-path": "/home/usuario/.face",
+ "search": "Pesquisar...",
+ "search-icons": "ex., noctalia, niri, battery, cloud",
+ "search-launcher": "Pesquisar entradas... ou use > para comandos",
+ "search-wallpapers": "Digite para filtrar papéis de parede...",
+ "select": "Selecionar",
+ "test": "Testar"
},
"plugins": {
"applications": "Aplicativos",
- "clipboard": "Histórico da área de transferência",
"calculator": "Calculadora",
- "clipboard-search-description": "Pesquisar no histórico da área de transferência",
+ "calculator-description": "Calculadora - avalie expressões matemáticas",
+ "calculator-enter-expression": "Digite uma expressão matemática",
+ "calculator-error": "Erro",
+ "calculator-name": "Calculadora",
+ "clipboard": "Histórico da área de transferência",
"clipboard-clear-description": "Limpar todo o histórico da área de transferência",
+ "clipboard-clear-description-full": "Remover todos os itens do histórico da área de transferência",
+ "clipboard-clear-history": "Limpar histórico da área de transferência",
"clipboard-history-disabled": "Histórico da área de transferência desativado",
"clipboard-history-disabled-description": "Ative o histórico da área de transferência nas configurações ou instale o cliphist",
- "clipboard-clear-history": "Limpar histórico da área de transferência",
- "clipboard-clear-description-full": "Remover todos os itens do histórico da área de transferência",
"clipboard-loading": "Carregando histórico da área de transferência...",
"clipboard-loading-description": "Por favor, aguarde",
- "calculator-description": "Calculadora - avalie expressões matemáticas",
- "calculator-name": "Calculadora",
- "calculator-enter-expression": "Digite uma expressão matemática",
- "calculator-error": "Erro"
- },
- "system": {
- "uptime": "Atividade: {uptime}",
- "welcome-back": "Bem-vindo(a) de volta, {user}!",
- "monitor-description": "{model} ({width}x{height} @ {scale}x)",
- "scaling-percentage": "{percentage}%",
- "location-display": "{name} ({coordinates})",
- "signal-strength": "{signal}%",
- "cpu-temperature": "{temp}°C",
- "disk-usage": "{percent}%",
- "widget-settings-title": "Configurações de {widget}",
- "unknown-app": "Aplicativo Desconhecido",
- "no-media-player-detected": "Nenhum reprodutor de mídia detectado",
- "user-requested": "Solicitado pelo usuário",
- "unknown": "Desconhecido",
- "unknown-version": "Desconhecida",
- "unknown-layout": "Desconhecido"
- },
- "lock-screen": {
- "password": "Digite sua senha...",
- "welcome-back": "Bem-vindo de volta,",
- "authenticating": "Autenticando...",
- "authentication-failed": "Falha na autenticação",
- "shut-down": "Desligar",
- "restart": "Reiniciar",
- "suspend": "Suspender"
+ "clipboard-search-description": "Pesquisar no histórico da área de transferência"
},
"quickSettings": {
+ "bluetooth": {
+ "label": {
+ "disabled": "Bluetooth",
+ "enabled": "Bluetooth"
+ },
+ "tooltip": {
+ "action": "Clique para gerenciar dispositivos Bluetooth"
+ }
+ },
+ "keepAwake": {
+ "label": {
+ "disabled": "Manter acordado",
+ "enabled": "Manter acordado"
+ },
+ "tooltip": {
+ "action": "Clique para alternar o modo Manter acordado"
+ }
+ },
+ "nightLight": {
+ "label": {
+ "disabled": "Luz noturna",
+ "enabled": "Luz noturna",
+ "forced": "Luz noturna"
+ },
+ "tooltip": {
+ "action": "Clique para alternar o modo Luz noturna\nClique direito: Abrir configurações"
+ }
+ },
"notifications": {
"label": {
- "enabled": "Notificações",
- "disabled": "Não perturbar"
+ "disabled": "Não perturbar",
+ "enabled": "Notificações"
},
"tooltip": {
"action": "Clique esquerdo: Abrir histórico de notificações\nClique direito: Alternar Não perturbar"
}
},
- "screenRecorder": {
- "label": {
- "recording": "Parar",
- "stopped": "Gravar"
- },
- "tooltip": {
- "action": "Clique para iniciar/parar a gravação da tela"
- }
- },
"powerProfile": {
"label": {
"unavailable": "Perfil de energia"
@@ -1659,33 +616,13 @@
"disabled": "Instale power-profiles-daemon para usar perfis de energia"
}
},
- "wifi": {
+ "screenRecorder": {
"label": {
- "ethernet": "Ethernet",
- "wifi": "Wi-Fi",
- "disconnected": "Wi-Fi desconectado"
+ "recording": "Parar",
+ "stopped": "Gravar"
},
"tooltip": {
- "action": "Clique para gerenciar conexões Wi-Fi"
- }
- },
- "bluetooth": {
- "label": {
- "enabled": "Bluetooth",
- "disabled": "Bluetooth"
- },
- "tooltip": {
- "action": "Clique para gerenciar dispositivos Bluetooth"
- }
- },
- "nightLight": {
- "label": {
- "enabled": "Luz noturna",
- "forced": "Luz noturna",
- "disabled": "Luz noturna"
- },
- "tooltip": {
- "action": "Clique para alternar o modo Luz noturna\nClique direito: Abrir configurações"
+ "action": "Clique para iniciar/parar a gravação da tela"
}
},
"wallpaperSelector": {
@@ -1694,177 +631,1386 @@
"action": "Clique esquerdo: Abrir seletor de papel de parede\nClique direito: Definir papel de parede aleatório"
}
},
- "keepAwake": {
+ "wifi": {
"label": {
- "enabled": "Manter acordado",
- "disabled": "Manter acordado"
+ "disconnected": "Wi-Fi desconectado",
+ "ethernet": "Ethernet",
+ "wifi": "Wi-Fi"
},
"tooltip": {
- "action": "Clique para alternar o modo Manter acordado"
+ "action": "Clique para gerenciar conexões Wi-Fi"
}
}
},
+ "session-menu": {
+ "action-in-seconds": "{action} em {seconds} segundos...",
+ "lock": "Bloquear",
+ "lock-and-suspend": "Bloquear e Suspender",
+ "logout": "Sair",
+ "reboot": "Reiniciar",
+ "shutdown": "Desligar",
+ "suspend": "Suspender",
+ "title": "Menu da Sessão"
+ },
+ "settings": {
+ "about": {
+ "contributors": {
+ "section": {
+ "description": "Agradecimentos ao nosso incrível colaborador!",
+ "description_plural": "Agradecimentos aos nossos {count} incríveis colaboradores!",
+ "label": "Contribuidores"
+ }
+ },
+ "noctalia": {
+ "download-latest": "Baixar a versão mais recente",
+ "installed-version": "Versão instalada:",
+ "latest-version": "Última versão:",
+ "section": {
+ "description": "Um shell de desktop elegante e minimalista, cuidadosamente criado para Wayland, construído com Quickshell.",
+ "label": "Shell Noctalia"
+ }
+ },
+ "support": "Apoie-nos",
+ "title": "Sobre"
+ },
+ "audio": {
+ "devices": {
+ "input-device": {
+ "description": "Selecione o dispositivo de entrada de áudio desejado.",
+ "label": "Dispositivo de entrada"
+ },
+ "output-device": {
+ "description": "Selecione o dispositivo de saída de áudio desejado.",
+ "label": "Dispositivo de saída"
+ },
+ "section": {
+ "description": "Configure os dispositivos de entrada e saída de áudio disponíveis.",
+ "label": "Dispositivos de áudio"
+ }
+ },
+ "media": {
+ "excluded-player": {
+ "description": "Adicione palavras-chave para os reprodutores que você deseja que o sistema ignore. Cada palavra-chave deve estar em uma nova linha.",
+ "label": "Reprodutor excluído",
+ "placeholder": "digite a substring e pressione +"
+ },
+ "frame-rate": {
+ "description": "Taxas mais altas são mais suaves, mas consomem mais recursos.",
+ "label": "Taxa de quadros"
+ },
+ "primary-player": {
+ "description": "Digite uma palavra-chave para identificar seu reprodutor principal.",
+ "label": "Reprodutor principal",
+ "placeholder": "ex: spotify, vlc, mpv"
+ },
+ "scrolling-speed": {
+ "description": "Tempo em segundos para o título rolar do início ao fim",
+ "label": "Velocidade de rolagem"
+ },
+ "scrolling-title": {
+ "description": "Ativar rolagem contínua para títulos de mídia longos",
+ "label": "Título com rolagem"
+ },
+ "section": {
+ "description": "Defina seus aplicativos de mídia preferidos e ignorados.",
+ "label": "Reprodutores de mídia"
+ },
+ "visualizer-quality": {
+ "description": "Alta qualidade usa mais recursos da GPU.",
+ "label": "Qualidade da visualização"
+ },
+ "visualizer-type": {
+ "description": "Escolha um tipo de visualização para a reprodução de mídia",
+ "label": "Tipo de visualização"
+ }
+ },
+ "title": "Áudio",
+ "volumes": {
+ "input-volume": {
+ "description": "Nível de volume de entrada do microfone.",
+ "label": "Volume de entrada"
+ },
+ "mute-input": {
+ "description": "Silencia a entrada de áudio padrão (microfone).",
+ "label": "Silenciar entrada de áudio"
+ },
+ "mute-output": {
+ "description": "Silencia a saída de áudio principal do sistema.",
+ "label": "Silenciar saída de áudio"
+ },
+ "output-volume": {
+ "description": "Nível de volume de todo o sistema.",
+ "label": "Volume de saída"
+ },
+ "section": {
+ "description": "Ajuste os controles de volume e os níveis de áudio.",
+ "label": "Volumes"
+ },
+ "step-size": {
+ "description": "Ajuste o tamanho do passo para alterações de volume (roda do mouse, atalhos de teclado).",
+ "label": "Tamanho do passo do volume"
+ },
+ "volume-overdrive": {
+ "description": "Permite aumentar o volume para além dos 100%. Poderá não ser suportado por todo o hardware.",
+ "label": "Permitir volume acima de 100%"
+ }
+ }
+ },
+ "bar": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Ajuste a opacidade do fundo da barra.",
+ "label": "Opacidade do fundo"
+ },
+ "density": {
+ "description": "Ajuste o preenchimento da barra para um visual compacto ou espaçoso.",
+ "label": "Densidade da barra"
+ },
+ "floating": {
+ "description": "Exibe a barra como uma 'pílula' flutuante.",
+ "label": "Barra flutuante"
+ },
+ "margins": {
+ "description": "Ajuste as margens ao redor da barra flutuante.",
+ "horizontal": "Horizontal",
+ "label": "Margens",
+ "vertical": "Vertical"
+ },
+ "outer-corners": {
+ "description": "Exibe cantos curvados para fora na barra.",
+ "label": "Cantos externos"
+ },
+ "position": {
+ "description": "Escolha onde posicionar a barra na tela.",
+ "label": "Posição da barra"
+ },
+ "section": {
+ "description": "Personalize a aparência e a posição da barra.",
+ "label": "Aparência"
+ },
+ "show-capsule": {
+ "description": "Mostra o fundo dos widgets.",
+ "label": "Mostrar cápsula"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Mostra a barra em monitores específicos. O padrão é todos, se nenhum for escolhido.",
+ "label": "Exibição nos monitores"
+ }
+ },
+ "title": "Barra",
+ "tray": {
+ "back": "Voltar",
+ "blacklist": {
+ "description": "Adicione regras de exclusão para a bandeja do sistema, suporta curingas (*).",
+ "label": "Lista Negra",
+ "placeholder": "ex: nm-applet, Fcitx*"
+ },
+ "pin-application": "Fixar Aplicativo",
+ "unpin-application": "Desfixar Aplicativo"
+ },
+ "widgets": {
+ "section": {
+ "description": "Arraste e solte widgets para reordená-los. Os emblemas indicam o uso: [E]squerda, [C]entro, [D]ireita.",
+ "label": "Posicionamento dos widgets"
+ }
+ }
+ },
+ "color-scheme": {
+ "color-source": {
+ "matugen-scheme-type": {
+ "description": {
+ "scheme-content": "Deriva cores que correspondem de perto à imagem subjacente.",
+ "scheme-expressive": "Paleta vibrante com saturação divertida.",
+ "scheme-fidelity": "Paleta de alta fidelidade que preserva as tonalidades originais.",
+ "scheme-fruit-salad": "Mistura colorida de detalhes contrastantes e vibrantes.",
+ "scheme-monochrome": "Paleta minimalista construída em torno de uma única tonalidade.",
+ "scheme-neutral": "Paleta de cores neutras com tons suaves e calmantes.",
+ "scheme-rainbow": "Paleta diversificada abrangendo todo o espectro.",
+ "scheme-tonal-spot": "Paleta equilibrada com toques focais."
+ },
+ "label": "Tipo de esquema Matugen"
+ },
+ "section": {
+ "description": "Configurações principais para as cores do Noctalia.",
+ "label": "Fonte de cor"
+ },
+ "use-wallpaper-colors": {
+ "description": "Gerar esquemas de cores do seu papel de parede usando Matugen. Extrai automaticamente cores para criar um tema coeso.",
+ "label": "Usar cores do papel de parede"
+ }
+ },
+ "dark-mode": {
+ "mode": {
+ "description": "Ativa a mudança automática entre o modo claro e o modo escuro.",
+ "label": "Agendamento do modo escuro",
+ "location": "Localização",
+ "manual": "Manual",
+ "off": "Desligado"
+ },
+ "switch": {
+ "description": "Muda para um tema mais escuro para facilitar a visualização à noite.",
+ "label": "Modo escuro"
+ }
+ },
+ "predefined": {
+ "generate-templates": {
+ "description": "Gera modelos Matugen (GTK, temas de terminal, etc.) ao usar esquemas de cores predefinidos.",
+ "label": "Gerar modelos para esquemas predefinidos"
+ },
+ "section": {
+ "description": "Escolha entre uma coleção de esquemas de cores predefinidos.",
+ "label": "Esquemas de cores predefinidos"
+ }
+ },
+ "templates": {
+ "misc": {
+ "description": "Opções de configuração adicionais.",
+ "label": "Diversos",
+ "user-templates": {
+ "description": "Ativa a configuração do Matugen definida pelo usuário. Um arquivo de modelo será criado em ~/.config/noctalia/user-templates.toml na primeira ativação",
+ "label": "Modelos do usuário"
+ }
+ },
+ "programs": {
+ "code": {
+ "description": "Escreva em {filepath}. O tema Hyprluna precisa ser instalado e ativado manualmente.",
+ "description-missing": "Requer que o {app} esteja instalado."
+ },
+ "spicetify": {
+ "description": "Escreva em {filepath}. O tema Comfy precisa ser instalado e ativado manualmente.",
+ "description-missing": "Requer que o {app} esteja instalado."
+ },
+ "description": "Tematização específica de aplicativos.",
+ "discord": {
+ "description": "Escrever {filepath} para {client}",
+ "description-missing": "Nenhum cliente Discord detectado. Instale vencord, vesktop, webcord, armcord, equibop, lightcord ou dorion."
+ },
+ "fuzzel": {
+ "description": "Escrever {filepath} e recarregar",
+ "description-missing": "Requer que o {app} esteja instalado"
+ },
+ "label": "Programas",
+ "pywalfox": {
+ "description": "Escrever {filepath} e executar pywalfox update",
+ "description-missing": "Requer que o {app} esteja instalado"
+ },
+ "vicinae": {
+ "description": "Escrever {filepath} e recarregar",
+ "description-missing": "Requer que o {app} esteja instalado"
+ },
+ "walker": {
+ "description": "Escrever {filepath} e definir tema como noctalia",
+ "description-missing": "Requer que o {app} esteja instalado"
+ }
+ },
+ "section": {
+ "description": "Aplicar cores a aplicações externas.",
+ "label": "Modelos"
+ },
+ "terminal": {
+ "alacritty": {
+ "description": "Escreva {filepath} e recarregue.",
+ "description-missing": "Requer que o {app} esteja instalado."
+ },
+ "description": "Tematização do emulador de terminal.",
+ "foot": {
+ "description": "Escrever {filepath} e recarregar",
+ "description-missing": "Requer que o {app} esteja instalado"
+ },
+ "ghostty": {
+ "description": "Escrever {filepath} e recarregar",
+ "description-missing": "Requer que o {app} esteja instalado"
+ },
+ "kitty": {
+ "description": "Escrever {filepath} e recarregar",
+ "description-missing": "Requer que o {app} esteja instalado"
+ },
+ "label": "Terminal",
+ "wezterm": {
+ "description": "Escreva {filepath} e recarregue.",
+ "description-missing": "Requer que o {app} esteja instalado."
+ }
+ },
+ "ui": {
+ "description": "Tematização do ambiente de desktop e kit de ferramentas de UI.",
+ "gtk": {
+ "description": "Escrever {filepath}"
+ },
+ "kcolorscheme": {
+ "description": "Escrever {filepath}"
+ },
+ "label": "UI",
+ "qt": {
+ "description": "Escrever {filepath}"
+ }
+ }
+ },
+ "title": "Esquema de cores"
+ },
+ "control-center": {
+ "cards": {
+ "section": {
+ "description": "Personalize quais controles aparecem na Central de Controle e em que ordem.",
+ "label": "Cartas"
+ }
+ },
+ "position": {
+ "description": "Escolha onde o painel da Central de Controle aparece quando aberto.",
+ "label": "Posição"
+ },
+ "section": {
+ "description": "Configurar o posicionamento e o comportamento do painel da Central de Controle.",
+ "label": "Aparência"
+ },
+ "shortcuts": {
+ "custom-button": {
+ "browse": "Procurar",
+ "command": {
+ "description": "O comando a ser executado quando o botão é clicado.",
+ "label": "Comando"
+ },
+ "enable-on-state-logic": {
+ "description": "Ativa um segundo ícone e um estado 'quente' com base em um comando de verificação.",
+ "label": "Ativar lógica de estado ativado"
+ },
+ "general-tooltip-text": {
+ "description": "Descrição geral para a dica de ferramenta do botão.",
+ "label": "Texto geral da dica de ferramenta"
+ },
+ "icon": {
+ "description": "Selecione um ícone da biblioteca.",
+ "label": "Ícone"
+ },
+ "on-clicked": {
+ "description": "Comando a ser executado quando o botão é clicado com o botão esquerdo.",
+ "label": "Comando de clique esquerdo"
+ },
+ "on-middle-clicked": {
+ "description": "Comando a ser executado quando o botão é clicado com o botão do meio.",
+ "label": "Comando de clique do meio"
+ },
+ "on-right-clicked": {
+ "description": "Comando a ser executado quando o botão é clicado com o botão direito.",
+ "label": "Comando de clique direito"
+ },
+ "on-state-command": {
+ "description": "Comando a ser executado para verificar se o botão deve estar no estado 'ligado'. Retorna 0 para ligado, diferente de zero para desligado.",
+ "label": "Comando de verificação de estado ativado"
+ },
+ "on-state-icon": {
+ "description": "O ícone para o botão quando ele está no estado 'ligado'.",
+ "label": "Ícone de estado ativado"
+ },
+ "state-checks": {
+ "add": "Adicionar verificação de estado",
+ "browse-icon": "Procurar",
+ "command": "Comando a ser executado para esta verificação de estado",
+ "label": "Verificações de estado",
+ "remove": "Remover"
+ },
+ "tooltip": {
+ "description": "A dica de ferramenta a ser exibida ao passar o mouse sobre o botão.",
+ "label": "Dica de ferramenta"
+ }
+ },
+ "dialog": {
+ "apply": "Aplicar",
+ "cancel": "Cancelar"
+ },
+ "section": {
+ "description": "Configure e gerencie os widgets de atalhos.",
+ "label": "Widgets de atalhos"
+ },
+ "sectionLeft": "Esquerda",
+ "sectionRight": "Direito/Certo/À direita"
+ },
+ "title": "Central de Controle"
+ },
+ "display": {
+ "monitors": {
+ "brightness": "Brilho",
+ "brightness-step": {
+ "description": "Ajuste o tamanho do passo para alterações de brilho (roda do mouse e atalhos de teclado).",
+ "label": "Tamanho do passo do brilho"
+ },
+ "brightness-unavailable": {
+ "ddc-disabled": "Controle de brilho indisponível. Ative \"Suporte de brilho externo\" para controlar o brilho desta tela.",
+ "generic": "O controle de brilho não está disponível para esta tela."
+ },
+ "enforce-minimum": {
+ "description": "Resolve o problema da retroiluminação apagar completamente em alguns monitores com brilho em 0%.",
+ "label": "Forçar brilho mínimo (1%)"
+ },
+ "external-brightness": {
+ "description": "Ativar suporte DDCUtil para controlar o brilho em monitores externos através do protocolo DDC/CI.",
+ "label": "Suporte de brilho externo"
+ },
+ "section": {
+ "description": "Ajustar o brilho das configurações para cada tela.",
+ "label": "Configurações por monitor"
+ }
+ },
+ "night-light": {
+ "auto-schedule": {
+ "description": "Baseado no horário do pôr do sol e do nascer do sol em {location} - recomendado.",
+ "label": "Agendamento automático"
+ },
+ "enable": {
+ "description": "Aplica um filtro de cor quente para reduzir a emissão de luz azul.",
+ "label": "Ativar luz noturna"
+ },
+ "force-activation": {
+ "description": "Ignora o agendamento e aplica o filtro noturno imediatamente.",
+ "label": "Forçar ativação"
+ },
+ "manual-schedule": {
+ "description": "Defina horários personalizados para o nascer e o pôr do sol.",
+ "label": "Agendamento manual",
+ "select-start": "Selecionar hora de início",
+ "select-stop": "Selecionar hora de término",
+ "sunrise": "Horário do nascer do sol",
+ "sunset": "Horário do pôr do sol"
+ },
+ "section": {
+ "description": "Reduz a emissão de luz azul para ajudar você a dormir melhor e diminuir o cansaço visual.",
+ "label": "Luz noturna"
+ },
+ "temperature": {
+ "day": "Dia",
+ "description": "Defina o quão quente a cor será durante a noite e o dia.",
+ "label": "Temperatura da cor",
+ "night": "Noite"
+ }
+ },
+ "title": "Tela"
+ },
+ "dock": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Ajuste a opacidade do fundo da dock.",
+ "label": "Opacidade do fundo"
+ },
+ "colorize-icons": {
+ "description": "Aplicar cores do tema aos ícones de aplicativos da dock (apenas aplicativos não focados).",
+ "label": "Colorir ícones"
+ },
+ "display": {
+ "always-visible": "Sempre visível",
+ "auto-hide": "Ocultar automaticamente",
+ "description": "Escolha como a dock se comporta.",
+ "exclusive": "Exclusivo",
+ "label": "Exibição"
+ },
+ "floating-distance": {
+ "description": "Ajuste a distância de flutuação da borda da tela.",
+ "label": "Distância de flutuação da dock"
+ },
+ "icon-size": {
+ "description": "Ajustar o tamanho geral do Dock.",
+ "label": "Tamanho do Dock"
+ },
+ "section": {
+ "description": "Personalize o comportamento e a aparência da dock.",
+ "label": "Aparência"
+ }
+ },
+ "enabled": {
+ "description": "Mostrar ou ocultar o dock completamente",
+ "label": "Ativar dock"
+ },
+ "monitors": {
+ "only-same-output": {
+ "description": "Mostrar apenas aplicativos da saída onde o dock está localizado.",
+ "label": "Apenas aplicativos da mesma saída"
+ },
+ "section": {
+ "description": "Mostrar dock em monitores específicos. O padrão é todos se nenhum for escolhido.",
+ "label": "Exibição no monitor"
+ }
+ },
+ "title": "Dock"
+ },
+ "general": {
+ "fonts": {
+ "default": {
+ "description": "Fonte principal usada em toda a interface.",
+ "label": "Fonte padrão",
+ "placeholder": "Selecione a fonte padrão...",
+ "scale": {
+ "description": "Aumentar ou diminuir o tamanho do texto padrão.",
+ "label": "Tamanho de fonte padrão"
+ },
+ "search-placeholder": "Pesquisar fontes..."
+ },
+ "monospace": {
+ "description": "Fonte monoespaçada usada para exibição de números e estatísticas.",
+ "label": "Fonte monoespaçada",
+ "placeholder": "Selecione a fonte monoespaçada...",
+ "scale": {
+ "description": "Aumentar ou diminuir o tamanho do texto monoespaçado.",
+ "label": "Tamanho da fonte monoespaçada"
+ },
+ "search-placeholder": "Pesquisar fontes monoespaçadas..."
+ },
+ "reset-scaling": "Redefinir escala",
+ "section": {
+ "description": "Escolha as fontes usadas em toda a interface.",
+ "label": "Fontes"
+ }
+ },
+ "language": {
+ "section": {
+ "description": "Escolha o seu idioma preferido para a aplicação.",
+ "label": "Idioma"
+ },
+ "select": {
+ "auto-detect": "Automático",
+ "description": "Selecione o idioma usado na interface da aplicação.",
+ "label": "Idioma da aplicação"
+ }
+ },
+ "launch-setup-wizard": "Iniciar o assistente de configuração",
+ "profile": {
+ "picture": {
+ "description": "Sua foto de perfil que aparece em toda a interface.",
+ "label": "Foto de perfil de {user}"
+ },
+ "section": {
+ "description": "Edite os detalhes do seu usuário e avatar.",
+ "label": "Perfil"
+ },
+ "select-avatar": "Selecionar imagem de avatar"
+ },
+ "screen-corners": {
+ "radius": {
+ "description": "Ajuste os cantos arredondados da tela.",
+ "label": "Raio dos cantos da tela",
+ "reset": "Redefinir raio dos cantos da tela"
+ },
+ "section": {
+ "description": "Personalize o arredondamento dos cantos da tela e os efeitos visuais.",
+ "label": "Cantos da tela"
+ },
+ "show-corners": {
+ "description": "Exibe cantos arredondados na borda da tela.",
+ "label": "Mostrar cantos da tela"
+ },
+ "solid-black": {
+ "description": "Use preto sólido em vez da cor de fundo da barra.",
+ "label": "Cantos pretos sólidos"
+ }
+ },
+ "title": "Geral"
+ },
+ "hooks": {
+ "info": {
+ "command-info": {
+ "description": "• Comandos são executados via shell (sh -c)\n• Comandos rodam em segundo plano (desanexados)\n• Botões de teste executam com os valores atuais",
+ "label": "Informações de Comando do Hook"
+ },
+ "parameters": {
+ "description": "• Hook de Papel de Parede: $1 = caminho do papel de parede, $2 = nome da tela\n• Hook de Alternância de Tema: $1 = true/false (estado do modo escuro)",
+ "label": "Parâmetros Disponíveis"
+ }
+ },
+ "system-hooks": {
+ "enable": {
+ "description": "Ativa ou desativa todos os comandos de hook.",
+ "label": "Ativar hooks"
+ },
+ "section": {
+ "description": "Configure comandos para serem executados quando eventos do sistema ocorrerem.",
+ "label": "Ganchos do sistema"
+ }
+ },
+ "theme-changed": {
+ "description": "Comando a ser executado quando o tema alterna entre modo escuro e claro.",
+ "label": "Tema alterado",
+ "placeholder": "ex., notify-send \"Tema\" \"Alternado\""
+ },
+ "title": "Hooks",
+ "wallpaper-changed": {
+ "description": "Comando a ser executado quando o papel de parede muda.",
+ "label": "Papel de parede alterado",
+ "placeholder": "ex., notify-send \"Papel de parede\" \"Alterado\""
+ }
+ },
+ "launcher": {
+ "settings": {
+ "background-opacity": {
+ "description": "Ajuste a opacidade do fundo do lançador.",
+ "label": "Opacidade do fundo"
+ },
+ "clipboard-history": {
+ "description": "Acesse itens copiados anteriormente a partir do lançador.",
+ "label": "Ativar histórico da área de transferência"
+ },
+ "custom-launch-prefix": {
+ "description": "Prefixar comandos com um inicializador personalizado (ex. 'runapp' para integração systemd).",
+ "label": "Prefixo de inicialização personalizado"
+ },
+ "custom-launch-prefix-enabled": {
+ "description": "Usar um prefixo personalizado para inicializar aplicativos em vez do método padrão.",
+ "label": "Habilitar prefixo de inicialização personalizado"
+ },
+ "position": {
+ "description": "Escolha onde o painel do lançador aparece.",
+ "label": "Posição"
+ },
+ "section": {
+ "description": "Personalize o comportamento e a aparência do lançador.",
+ "label": "Aparência"
+ },
+ "sort-by-usage": {
+ "description": "Quando ativado, os aplicativos mais usados aparecem primeiro na lista.",
+ "label": "Ordenar por mais usados"
+ },
+ "terminal-command": {
+ "description": "Comando para iniciar um terminal. Ex: 'kitty -e' ou 'gnome-terminal --'.",
+ "label": "Comando do terminal"
+ },
+ "use-app2unit": {
+ "description": "Usa um método de inicialização alternativo para gerenciar melhor os processos de aplicativos e evitar problemas.",
+ "label": "Usar App2Unit para iniciar aplicativos"
+ }
+ },
+ "title": "Lançador"
+ },
+ "location": {
+ "date-time": {
+ "12hour-format": {
+ "description": "Exibe a hora no formato de 12 horas na tela de bloqueio e no calendário. O relógio da barra tem suas próprias configurações.",
+ "label": "Use o formato de hora de 12 horas"
+ },
+ "first-day-of-week": {
+ "automatic": "Automático (usar localização do sistema)",
+ "description": "Escolha qual dia começa a semana no calendário.",
+ "label": "Primeiro dia da semana"
+ },
+ "section": {
+ "description": "Personalize como a data e a hora aparecem.",
+ "label": "Data e hora"
+ },
+ "show-events": {
+ "description": "Exibir eventos no painel do calendário.",
+ "label": "Mostrar eventos do calendário"
+ },
+ "use-analog": {
+ "description": "Mostrar um relógio estilo analógico na janela do calendário e na tela de bloqueio.",
+ "label": "Use um relógio de estilo analógico."
+ },
+ "week-numbers": {
+ "description": "Exibe a semana do ano (ex., Semana 38) no calendário.",
+ "label": "Mostrar números da semana"
+ }
+ },
+ "location": {
+ "search": {
+ "description": "ex., São Paulo, SP",
+ "label": "Procurar por uma localização",
+ "placeholder": "Digite o nome da localização"
+ },
+ "section": {
+ "description": "Obtenha previsão do tempo e agendamento de luz noturna precisos definindo sua localização.",
+ "label": "Sua localização"
+ }
+ },
+ "title": "Localização",
+ "weather": {
+ "enabled": {
+ "description": "Mostrar informações meteorológicas em toda a interface e buscar dados do clima. Quando desativado, todos os elementos do clima serão ocultados e nenhuma solicitação de rede será feita.",
+ "label": "Ativar clima"
+ },
+ "fahrenheit": {
+ "description": "Exibe a temperatura em Fahrenheit em vez de Celsius.",
+ "label": "Exibir temperatura em Fahrenheit (°F)"
+ },
+ "section": {
+ "description": "Escolha sua unidade de temperatura preferida.",
+ "label": "Clima"
+ },
+ "show-in-calendar": {
+ "description": "Mostre a previsão do tempo diária diretamente na sua visualização de calendário.",
+ "label": "Exibir clima no calendário"
+ }
+ }
+ },
+ "lock-screen": {
+ "compact-lockscreen": {
+ "description": "Mostrar apenas a entrada de login e os controles do sistema, ocultando os widgets de clima e mídia.",
+ "label": "Tela de bloqueio compacta"
+ },
+ "lock-on-suspend": {
+ "description": "Bloquear a tela automaticamente ao suspender o sistema.",
+ "label": "Bloquear ao suspender"
+ },
+ "title": "Tela de bloqueio"
+ },
+ "network": {
+ "bluetooth": {
+ "label": "Ativar Bluetooth"
+ },
+ "section": {
+ "description": "Gerencie conexões Wi-Fi e Bluetooth."
+ },
+ "title": "Rede",
+ "wifi": {
+ "label": "Ativar Wi-Fi"
+ }
+ },
+ "notifications": {
+ "duration": {
+ "critical-urgency": {
+ "description": "Por quanto tempo as notificações de prioridade crítica permanecem visíveis.",
+ "label": "Urgência crítica"
+ },
+ "low-urgency": {
+ "description": "Por quanto tempo as notificações de baixa prioridade permanecem visíveis.",
+ "label": "Urgência baixa"
+ },
+ "normal-urgency": {
+ "description": "Por quanto tempo as notificações de prioridade normal permanecem visíveis.",
+ "label": "Urgência normal"
+ },
+ "respect-expire": {
+ "description": "Usa o tempo de expiração definido na notificação.",
+ "label": "Respeitar tempo de expiração"
+ },
+ "section": {
+ "description": "Configure por quanto tempo as notificações permanecem visíveis com base no nível de urgência.",
+ "label": "Duração da notificação"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Mostra a notificação em monitores específicos. O padrão é todos, se nenhum for escolhido.",
+ "label": "Exibição nos monitores"
+ }
+ },
+ "settings": {
+ "always-on-top": {
+ "description": "Exibe notificações acima de janelas em tela cheia e outras camadas.",
+ "label": "Sempre no topo"
+ },
+ "background-opacity": {
+ "description": "Ajustar a opacidade dos fundos das notificações.",
+ "label": "Opacidade do fundo"
+ },
+ "do-not-disturb": {
+ "description": "Desativa todos os pop-ups de notificação quando ativado.",
+ "label": "Não perturbe"
+ },
+ "enable-osd": {
+ "description": "Mostra alterações de volume e brilho em tempo real.",
+ "label": "Ativar exibição na tela"
+ },
+ "enabled": {
+ "description": "Ativar ou desativar o daemon de notificações, requer reiniciar o noctalia-shell.",
+ "label": "Ativar notificações"
+ },
+ "location": {
+ "description": "Onde as notificações aparecem na tela.",
+ "label": "Localização"
+ },
+ "section": {
+ "description": "Configure a aparência e o comportamento das notificações.",
+ "label": "Aparência"
+ }
+ },
+ "title": "Notificações"
+ },
+ "osd": {
+ "always-on-top": {
+ "description": "Exibir OSD acima de janelas em tela cheia e outras camadas.",
+ "label": "Sempre no topo"
+ },
+ "description": "Configure indicadores na tela, como volume e brilho.",
+ "duration": {
+ "auto-hide": {
+ "description": "Ajusta o tempo antes da OSD desaparecer.",
+ "label": "Ocultar após"
+ },
+ "section": {
+ "description": "Quanto tempo a OSD permanece visível antes de ocultar automaticamente.",
+ "label": "Tempo para ocultar"
+ }
+ },
+ "enabled": {
+ "description": "Mostrar alterações de volume e brilho em tempo real.",
+ "label": "Ativar exibição na tela"
+ },
+ "location": {
+ "description": "Onde a exibição na tela aparece.",
+ "label": "Localização"
+ },
+ "monitors": {
+ "section": {
+ "description": "Mostrar a OSD em monitores específicos. Padrão é todos se nenhum for escolhido.",
+ "label": "Exibição nos monitores"
+ }
+ },
+ "section": {
+ "general": {
+ "description": "Configurar visibilidade e comportamento da OSD.",
+ "label": "Geral"
+ }
+ },
+ "title": "Exibição na tela"
+ },
+ "screen-recorder": {
+ "audio": {
+ "audio-codec": {
+ "description": "Opus é recomendado para melhor desempenho e menor tamanho de áudio.",
+ "label": "Codec de áudio"
+ },
+ "audio-source": {
+ "description": "Fonte de áudio a ser capturada durante a gravação.",
+ "label": "Fonte de áudio"
+ },
+ "section": {
+ "description": "Configure as opções de gravação de áudio.",
+ "label": "Configurações de áudio"
+ }
+ },
+ "general": {
+ "output-folder": {
+ "description": "Pasta onde as gravações de tela serão salvas.",
+ "label": "Pasta de saída",
+ "tooltip": "Procurar pasta de saída"
+ },
+ "section": {
+ "description": "Gerencie a saída e o conteúdo da gravação de tela.",
+ "label": "Configurações gerais"
+ },
+ "select-output-folder": "Selecionar pasta de saída",
+ "show-cursor": {
+ "description": "Grava o cursor do mouse no vídeo.",
+ "label": "Mostrar cursor"
+ }
+ },
+ "title": "Gravador de tela",
+ "video": {
+ "color-range": {
+ "description": "Limitada é recomendada para melhor compatibilidade.",
+ "label": "Gama de cores"
+ },
+ "frame-rate": {
+ "description": "Taxa de quadros alvo para gravações de tela.",
+ "label": "Taxa de quadros"
+ },
+ "section": {
+ "description": "Configure as opções de gravação de vídeo.",
+ "label": "Configurações de vídeo"
+ },
+ "video-codec": {
+ "description": "h264 é o codec mais comum.",
+ "label": "Codec de vídeo"
+ },
+ "video-quality": {
+ "description": "Qualidade mais alta resulta em arquivos maiores.",
+ "label": "Qualidade de vídeo"
+ },
+ "video-source": {
+ "description": "Portal é recomendado, se você tiver artefatos, tente Screen.",
+ "label": "Fonte de vídeo"
+ }
+ }
+ },
+ "user-interface": {
+ "animation-disable": {
+ "description": "Desative todas as animações para uma experiência mais rápida e responsiva.",
+ "label": "Desativar animações da interface do usuário"
+ },
+ "animation-speed": {
+ "description": "Ajustar a velocidade global da animação.",
+ "label": "Velocidade da animação",
+ "reset": "Redefinir velocidade da animação"
+ },
+ "border-radius": {
+ "description": "Controla o arredondamento dos cantos de janelas, botões e outros elementos.",
+ "label": "Raio da borda",
+ "reset": "Redefinir raio da borda"
+ },
+ "dim-desktop": {
+ "description": "Escurecer a área de trabalho quando painéis ou menus estiverem abertos.",
+ "label": "Dim área de trabalho"
+ },
+ "panels-attached-to-bar": {
+ "description": "Os painéis travam na barra e nas bordas da tela, criando uma aparência perfeita com cantos invertidos elegantes.",
+ "label": "Ajustar Painéis às Bordas"
+ },
+ "panels-overlay": {
+ "description": "Garante que os painéis e a barra permaneçam visíveis, mesmo sobre aplicativos em tela cheia.",
+ "label": "Manter Painéis e Barra no Topo"
+ },
+ "scaling": {
+ "description": "Altera o tamanho da interface geral do usuário, excluindo a barra.",
+ "label": "Escalonamento da interface",
+ "reset-scaling": "Redefinir escala da interface"
+ },
+ "section": {
+ "description": "Personalize a aparência, a sensação e o comportamento da interface.",
+ "label": "Aparência"
+ },
+ "settings-panel-attached-to-bar": {
+ "description": "Mantenha a janela de Configurações alinhada com a barra para um visual uniforme.",
+ "label": "Ajustar Configurações à barra"
+ },
+ "shadows": {
+ "description": "Ativa sombras projetadas sob barras e painéis.",
+ "direction": {
+ "description": "Escolha de onde a sombra é projetada.",
+ "label": "Direção da sombra"
+ },
+ "label": "Sombras projetadas"
+ },
+ "title": "Interface do usuário",
+ "tooltips": {
+ "description": "Ativar ou desativar dicas de ferramentas em toda a interface.",
+ "label": "Mostrar dicas de ferramenta"
+ }
+ },
+ "wallpaper": {
+ "automation": {
+ "custom-interval": {
+ "description": "Digite o tempo como HH:MM (ex., 01:30).",
+ "label": "Intervalo personalizado"
+ },
+ "interval": {
+ "description": "Com que frequência alterar os papéis de parede automaticamente.",
+ "label": "Intervalo do papel de parede"
+ },
+ "random-wallpaper": {
+ "description": "Agende mudanças aleatórias de papel de parede em intervalos regulares.",
+ "label": "Papel de parede aleatório"
+ },
+ "section": {
+ "label": "Automação"
+ }
+ },
+ "look-feel": {
+ "edge-smoothness": {
+ "description": "Aplica um efeito suave e esfumaçado à borda das transições.",
+ "label": "Suavizar borda da transição"
+ },
+ "fill-color": {
+ "description": "Escolha uma cor de preenchimento que pode aparecer atrás do papel de parede.",
+ "label": "Cor de preenchimento"
+ },
+ "fill-mode": {
+ "description": "Selecione como a imagem deve ser dimensionada para corresponder à resolução do seu monitor.",
+ "label": "Modo de preenchimento"
+ },
+ "section": {
+ "label": "Aparência e estilo"
+ },
+ "transition-duration": {
+ "description": "Duração das animações de transição em segundos.",
+ "label": "Duração da transição"
+ },
+ "transition-type": {
+ "description": "Tipo de animação ao alternar entre papéis de parede.",
+ "label": "Tipo de transição"
+ }
+ },
+ "settings": {
+ "enable-management": {
+ "description": "Gerencie papéis de parede com o Noctalia. Desmarque se preferir usar outro aplicativo.",
+ "label": "Ativar gerenciamento de papel de parede"
+ },
+ "enable-overview": {
+ "description": "Aplica um papel de parede desfocado e escurecido à tela de visão geral.",
+ "label": "Ativar papel de parede de visão geral"
+ },
+ "folder": {
+ "description": "Caminho para a sua pasta principal de papéis de parede.",
+ "label": "Pasta de papéis de parede",
+ "tooltip": "Procurar pasta de papéis de parede"
+ },
+ "monitor-specific": {
+ "description": "Defina uma pasta de papel de parede diferente para cada monitor.",
+ "label": "Diretórios específicos por monitor",
+ "tooltip": "Procurar pasta de papéis de parede"
+ },
+ "recursive-search": {
+ "description": "Também buscar papéis de parede em subpastas do diretório de papéis de parede.",
+ "label": "Buscar em subpastas"
+ },
+ "section": {
+ "description": "Controle como os papéis de parede são gerenciados e exibidos.",
+ "label": "Configurações de papel de parede"
+ },
+ "select-folder": "Selecionar pasta de papéis de parede",
+ "select-monitor-folder": "Selecionar pasta de papéis de parede do monitor",
+ "selector-position": {
+ "description": "Escolha onde o painel do seletor de papel de parede aparece.",
+ "label": "Posição"
+ }
+ },
+ "title": "Papel de parede"
+ }
+ },
+ "setup": {
+ "appearance": {
+ "header": "Aparência",
+ "subheader": "Escolha o modo escuro e as fontes de cores (Matugen ou predefinidas)."
+ },
+ "customize": {
+ "header": "Personalizar a sua experiência",
+ "subheader": "Ajuste a posição da barra, densidade, escala e mais."
+ },
+ "wallpaper": {
+ "choose-dir": "Escolha um diretório contendo seus papéis de parede",
+ "dir": {
+ "browse": "Procurar pasta",
+ "description": "Escolha a pasta que contém seus papéis de parede",
+ "label": "Diretório de papéis de parede",
+ "select-title": "Selecionar pasta de papéis de parede"
+ },
+ "header": "Escolha o seu papel de parede",
+ "no-dir": "Nenhum diretório de papéis de parede selecionado",
+ "no-valid": "Nenhuma imagem válida encontrada em: {dir}",
+ "none-in-dir": "Nenhum papel de parede encontrado no diretório",
+ "preview-error": "Falha ao carregar a imagem",
+ "select-prompt": "Selecione um papel de parede abaixo",
+ "subheader": "Defina o ambiente com um belo fundo."
+ },
+ "welcome": {
+ "note": "Apenas alguns ajustes básicos para começar - o restante está em Configurações"
+ }
+ },
+ "system": {
+ "cpu-temperature": "{temp}°C",
+ "disk-usage": "{percent}%",
+ "location-display": "{name} ({coordinates})",
+ "monitor-description": "{model} ({width}x{height} @ {scale}x)",
+ "no-media-player-detected": "Nenhum reprodutor de mídia detectado",
+ "scaling-percentage": "{percentage}%",
+ "signal-strength": "{signal}%",
+ "unknown": "Desconhecido",
+ "unknown-app": "Aplicativo Desconhecido",
+ "unknown-layout": "Desconhecido",
+ "unknown-version": "Desconhecida",
+ "uptime": "Atividade: {uptime}",
+ "user-requested": "Solicitado pelo usuário",
+ "welcome-back": "Bem-vindo(a) de volta, {user}!",
+ "widget-settings-title": "Configurações de {widget}"
+ },
"toast": {
- "night-light": {
- "enabled": "Ativada",
- "disabled": "Desativada",
- "not-installed": "wlsunset não está instalado",
- "forced": "Ativação forçada",
- "normal": "Modo normal"
- },
- "keep-awake": {
- "enabled": "Ativado",
- "disabled": "Desativado"
- },
- "wallpaper-colors": {
- "enabled": "Cores do papel de parede ativadas",
- "disabled": "Cores do papel de parede desativadas",
- "not-installed": "Matugen não instalado - necessário para extração de cores do papel de parede"
- },
- "matugen": {
- "failed": "Falha no processamento do Matugen",
- "failed-general": "Matugen encontrou um erro ao processar os modelos"
- },
- "recording": {
- "stopping": "Parando gravação…",
- "started": "Gravação iniciada",
- "saved": "Gravação salva",
- "failed-start": "Falha ao iniciar a gravação",
- "failed-gpu": "gpu-screen-recorder encerrou inesperadamente.",
- "failed-general": "O gravador encerrou com um erro.",
- "no-portals": "Portais de desktop não estão em execução",
- "no-portals-desc": "Inicie o xdg-desktop-portal e um portal de compositor (wlr/hyprland/gnome/kde).",
- "not-installed": "gpu-screen-recorder não instalado",
- "not-installed-desc": "Por favor instale o gpu-screen-recorder para usar as funcionalidades de gravação de tela."
- },
- "clipboard": {
- "unavailable": "Histórico da área de transferência indisponível",
- "unavailable-desc": "O aplicativo 'cliphist' não está instalado. Por favor, instale-o para usar os recursos do histórico da área de transferência."
- },
- "ipc": {
- "powerpanel-deprecated": "PowerPanel foi renomeado para SessionMenu, esta chamada de IPC será descontinuada em breve. Por favor, use \"ipc call sessionMenu toggle\".",
- "sidepanel-deprecated": "SidePanel foi renomeado para ControlCenter, esta chamada de IPC será descontinuada em breve. Por favor, use \"ipc call controlCenter toggle\"."
- },
- "wifi": {
- "enabled": "Ativado",
- "disabled": "Desativado",
- "connected": "Conectado a '{ssid}'",
- "disconnected": "Desconectado de '{ssid}'"
- },
- "bluetooth": {
- "enabled": "Ativado",
- "disabled": "Desativado"
- },
"airplane-mode": {
- "title": "Modo avião",
+ "disabled": "Desativado",
"enabled": "Ativado",
- "disabled": "Desativado"
- },
- "internet": {
- "limited": "Conectado sem Internet"
- },
- "kofi": {
- "opened": "Página do Ko-fi aberta no seu navegador"
- },
- "do-not-disturb": {
- "enabled": "'Não perturbe' ativado",
- "disabled": "'Não perturbe' desativado",
- "enabled-desc": "Você encontrará essas notificações no seu histórico.",
- "disabled-desc": "Mostrando todas as notificações."
- },
- "power-profile": {
- "changed": "Perfil de energia alterado",
- "profile-name": "\"{profile}\""
+ "title": "Modo avião"
},
"battery": {
"low": "Bateria Fraca",
"low-desc": "A bateria está em {percent}%. Por favor, conecte o carregador."
},
"battery-manager": {
- "title": "Limite da bateria",
- "set-success-desc": "Limite da bateria definido para {percent}%",
"initial-setup": "Configuração inicial necessária",
- "set-failed": "Falha ao definir o limite da bateria",
- "install-success": "Instalado com sucesso",
- "install-missing": "Arquivos necessários ausentes",
- "install-unsupported": "Sistema não suportado",
"install-failed": "Falha na instalação",
+ "install-missing": "Arquivos necessários ausentes",
+ "install-success": "Instalado com sucesso",
+ "install-unsupported": "Sistema não suportado",
+ "set-failed": "Falha ao definir o limite da bateria",
+ "set-success-desc": "Limite da bateria definido para {percent}%",
+ "title": "Limite da bateria",
+ "uninstall-failed": "Falha na desinstalação",
"uninstall-setup": "Desinstalando, autenticação necessária",
- "uninstall-success": "Desinstalado com sucesso",
- "uninstall-failed": "Falha na desinstalação"
+ "uninstall-success": "Desinstalado com sucesso"
+ },
+ "bluetooth": {
+ "disabled": "Desativado",
+ "enabled": "Ativado"
+ },
+ "clipboard": {
+ "unavailable": "Histórico da área de transferência indisponível",
+ "unavailable-desc": "O aplicativo 'cliphist' não está instalado. Por favor, instale-o para usar os recursos do histórico da área de transferência."
+ },
+ "do-not-disturb": {
+ "disabled": "'Não perturbe' desativado",
+ "disabled-desc": "Mostrando todas as notificações.",
+ "enabled": "'Não perturbe' ativado",
+ "enabled-desc": "Você encontrará essas notificações no seu histórico."
+ },
+ "internet": {
+ "limited": "Conectado sem Internet"
+ },
+ "ipc": {
+ "powerpanel-deprecated": "PowerPanel foi renomeado para SessionMenu, esta chamada de IPC será descontinuada em breve. Por favor, use \"ipc call sessionMenu toggle\".",
+ "sidepanel-deprecated": "SidePanel foi renomeado para ControlCenter, esta chamada de IPC será descontinuada em breve. Por favor, use \"ipc call controlCenter toggle\"."
+ },
+ "keep-awake": {
+ "disabled": "Desativado",
+ "enabled": "Ativado"
+ },
+ "kofi": {
+ "opened": "Página do Ko-fi aberta no seu navegador"
},
"missing-control-center": {
- "label": "Último widget da Central de Controle removido",
- "description": "O widget da Central de Controle foi removido da barra. Para acessá-lo novamente pela barra, adicione o widget novamente. Você também pode abri-lo clicando com o botão direito na barra."
+ "description": "O widget da Central de Controle foi removido da barra. Para acessá-lo novamente pela barra, adicione o widget novamente. Você também pode abri-lo clicando com o botão direito na barra.",
+ "label": "Último widget da Central de Controle removido"
+ },
+ "night-light": {
+ "disabled": "Desativada",
+ "enabled": "Ativada",
+ "forced": "Ativação forçada",
+ "normal": "Modo normal",
+ "not-installed": "wlsunset não está instalado"
+ },
+ "noctalia-performance": {
+ "disabled": "Modo de desempenho desativado",
+ "enabled": "Modo de desempenho ativado",
+ "label": "Apresentação de Noctalia"
+ },
+ "power-profile": {
+ "changed": "Perfil de energia alterado",
+ "profile-name": "\"{profile}\""
+ },
+ "recording": {
+ "failed-general": "O gravador encerrou com um erro.",
+ "failed-gpu": "gpu-screen-recorder encerrou inesperadamente.",
+ "failed-start": "Falha ao iniciar a gravação",
+ "no-portals": "Portais de desktop não estão em execução",
+ "no-portals-desc": "Inicie o xdg-desktop-portal e um portal de compositor (wlr/hyprland/gnome/kde).",
+ "not-installed": "gpu-screen-recorder não instalado",
+ "not-installed-desc": "Por favor instale o gpu-screen-recorder para usar as funcionalidades de gravação de tela.",
+ "saved": "Gravação salva",
+ "started": "Gravação iniciada",
+ "stopping": "Parando gravação…"
+ },
+ "theming-processor-failed": {
+ "desc-generic": "Ocorreu um erro ao processar os templates.",
+ "title-matugen": "Falha no processamento do template Matugen",
+ "title-predefined": "O processamento do esquema de cores predefinido falhou."
+ },
+ "wallpaper-colors": {
+ "disabled": "Cores do papel de parede desativadas",
+ "enabled": "Cores do papel de parede ativadas",
+ "label": "Geração de cores para papel de parede",
+ "not-installed": "Matugen não instalado - necessário para extração de cores do papel de parede"
+ },
+ "wifi": {
+ "connected": "Conectado a '{ssid}'",
+ "disabled": "Desativado",
+ "disconnected": "Desconectado de '{ssid}'",
+ "enabled": "Ativado"
}
},
+ "tooltips": {
+ "add-widget": "Adicionar widget",
+ "bluetooth-devices": "Dispositivos Bluetooth",
+ "brightness-at": "Brilho: {brightness}%\nClique direito para configurações.\nRole para modificar o brilho.",
+ "cancel-timer": "Cancelar temporizador",
+ "clear-history": "Limpar histórico",
+ "click-to-start-recording": "Clique para iniciar a gravação",
+ "click-to-stop-recording": "Clique para parar a gravação",
+ "close": "Fechar",
+ "connect-disconnect-devices": "Clique esquerdo para conectar. Clique direito para esquecer.",
+ "delete-notification": "Excluir notificação",
+ "disable-keep-awake": "Desativar 'manter acordado'",
+ "do-not-disturb-disabled": "'Não perturbe' desativado",
+ "do-not-disturb-enabled": "'Não perturbe' ativado",
+ "enable-keep-awake": "Ativar 'manter acordado'",
+ "forget-network": "Esquecer rede",
+ "home": "Início",
+ "input-muted": "Silenciar entrada de áudio",
+ "keep-awake": "Manter acordado",
+ "keyboard-layout": "Layout de teclado {layout}",
+ "manage-wifi": "Gerenciar Wi-Fi",
+ "microphone-volume-at": "Volume do microfone em {volume}%.\nClique esquerdo para configurações. Clique direito para ativar/desativar o mudo.\nRole para modificar o volume.",
+ "move-to-center-section": "Mover para a seção central",
+ "move-to-left-section": "Mover para a seção esquerda",
+ "move-to-right-section": "Mover para a seção direita",
+ "next-media": "Próxima mídia",
+ "next-month": "Próximo mês",
+ "night-light-disabled": "Luz noturna desativada.\nClique esquerdo para alternar o modo.\nClique direito para acessar as configurações.",
+ "night-light-enabled": "Luz noturna ativada.\nClique esquerdo para alternar o modo.\nClique direito para acessar as configurações.",
+ "night-light-forced": "Luz noturna forçada.\nClique esquerdo para alternar o modo.\nClique direito para acessar as configurações.",
+ "night-light-not-installed": "Luz noturna não disponível.\nwlsunset não está instalado.",
+ "noctalia-performance-disabled": "O modo de desempenho Noctalia está desativado.\nClique esquerdo para ativar.",
+ "noctalia-performance-enabled": "O modo de desempenho Noctalia está ativado.\nClique esquerdo para desativar.",
+ "open-control-center": "Abrir central de controle",
+ "open-notification-history-disable-dnd": "Abrir histórico de notificações\nClique direito para desativar \"Não perturbe\".",
+ "open-notification-history-enable-dnd": "Abrir histórico de notificações\nClique direito para ativar \"Não perturbe\".",
+ "open-settings": "Abrir configurações",
+ "open-tray-dropdown": "Abrir menu suspenso da bandeja",
+ "open-wallpaper-selector": "Abrir seletor de papel de parede",
+ "output-muted": "Silenciar saída de áudio",
+ "pause": "Pausar",
+ "play": "Reproduzir",
+ "power-profile": "Perfil de energia '{profile}'",
+ "previous-media": "Mídia anterior",
+ "previous-month": "Mês anterior",
+ "refresh": "Atualizar",
+ "refresh-devices": "Atualizar dispositivos",
+ "refresh-wallpaper-list": "Atualizar lista de papéis de parede",
+ "remove-widget": "Remover widget",
+ "screen-recorder-not-installed": "O gravador de tela não está instalado",
+ "session-menu": "Menu da Sessão",
+ "set-power-profile": "Definir perfil de energia \"{profile}\"",
+ "start-screen-recording": "Iniciar gravação de tela",
+ "stop-screen-recording": "Parar gravação de tela",
+ "switch-to-dark-mode": "Mudar para o modo escuro",
+ "switch-to-light-mode": "Mudar para o modo claro",
+ "up": "Acima",
+ "volume-at": "Volume de saída em {volume}%.\nClique esquerdo para configurações. Clique direito para alternar o mudo.\nRole para modificar o volume.",
+ "wallpaper-selector": "Clique esquerdo: Abrir seletor de papel de parede.\nClique direito: Definir papel de parede aleatório.",
+ "widget-settings": "Configurações do widget"
+ },
+ "wallpaper": {
+ "configure-directory": "Configure seu diretório de papéis de parede com imagens.",
+ "fill-modes": {
+ "center": "Centralizar",
+ "crop": "Cortar (Preencher)",
+ "fit": "Ajustar (Conter)",
+ "stretch": "Esticar"
+ },
+ "no-match": "Nenhuma correspondência encontrada.",
+ "no-wallpaper": "Nenhum papel de parede encontrado.",
+ "panel": {
+ "apply-all-monitors": {
+ "description": "Aplica o papel de parede selecionado a todos os monitores de uma só vez.",
+ "label": "Aplicar a todos os monitores"
+ },
+ "search": "Pesquisar:",
+ "title": "Seletor de papel de parede"
+ },
+ "transitions": {
+ "disc": "Disco",
+ "fade": "Esmaecer",
+ "none": "Nenhuma",
+ "random": "Aleatória",
+ "stripes": "Listras",
+ "wipe": "Limpar"
+ },
+ "try-different-search": "Tente uma busca diferente."
+ },
"weather": {
"clear-sky": "Céu limpo",
- "mainly-clear": "Predominantemente limpo",
- "partly-cloudy": "Parcialmente nublado",
- "overcast": "Nublado",
- "fog": "Névoa",
"drizzle": "Garoa",
- "snow": "Neve",
+ "fog": "Névoa",
+ "mainly-clear": "Predominantemente limpo",
+ "overcast": "Nublado",
+ "partly-cloudy": "Parcialmente nublado",
"rain-showers": "Pancadas de chuva",
+ "snow": "Neve",
"thunderstorm": "Tempestade",
"unknown": "Desconhecido"
},
- "authentication": {
- "failed": "Falha na autenticação",
- "error": "Erro de autenticação"
- },
- "general": {
- "no-results": "Nenhum resultado",
- "no-summary": "Sem resumo",
- "unknown": "Desconhecido"
- },
- "battery": {
- "no-battery-detected": "Nenhuma bateria detectada.",
- "charging-rate": "Taxa de carregamento: {rate} W.",
- "discharging-rate": "Taxa de descarregamento: {rate} W.",
- "charging": "Carregando.",
- "discharging": "Descarregando.",
- "plugged-in": "Conectado.",
- "idle": "Ocioso.",
- "time-left": "Tempo restante: {time}.",
- "time-until-full": "Tempo até carga completa: {time}.",
- "health": "Saúde: {percent}%",
- "panel": {
- "balanced": "Balanceado ({percent}%)",
- "disabled": "Gerenciador de bateria desativado",
- "full": "Capacidade máxima ({percent}%)",
- "lifespan": "Vida útil prolongada ({percent}%)",
- "title": "Limite de carga"
- }
- },
- "setup": {
- "customize": {
- "header": "Personalizar a sua experiência",
- "subheader": "Ajuste a posição da barra, densidade, escala e mais."
+ "widgets": {
+ "color-picker": {
+ "apply": "Aplicar",
+ "brightness": "Brilho",
+ "cancel": "Cancelar",
+ "hex": {
+ "description": "Digite um código de cor hexadecimal.",
+ "label": "Cor hexadecimal"
+ },
+ "palette": {
+ "description": "Escolha entre uma vasta gama de cores predefinidas.",
+ "label": "Paleta"
+ },
+ "rgb": {
+ "description": "Ajuste os valores de vermelho, verde, azul e brilho.",
+ "label": "Valores RGB"
+ },
+ "theme-colors": {
+ "description": "Acesso rápido à paleta de cores do seu tema.",
+ "label": "Cores do tema"
+ },
+ "title": "Seletor de cores"
},
- "appearance": {
- "header": "Aparência",
- "subheader": "Escolha o modo escuro e as fontes de cores (Matugen ou predefinidas)."
- },
- "wallpaper": {
- "header": "Escolha o seu papel de parede",
- "subheader": "Defina o ambiente com um belo fundo.",
- "select-prompt": "Selecione um papel de parede abaixo",
- "preview-error": "Falha ao carregar a imagem",
- "none-in-dir": "Nenhum papel de parede encontrado no diretório",
- "no-dir": "Nenhum diretório de papéis de parede selecionado",
- "no-valid": "Nenhuma imagem válida encontrada em: {dir}",
- "choose-dir": "Escolha um diretório contendo seus papéis de parede",
- "dir": {
- "label": "Diretório de papéis de parede",
- "description": "Escolha a pasta que contém seus papéis de parede",
- "browse": "Procurar pasta",
- "select-title": "Selecionar pasta de papéis de parede"
+ "datetime-tokens": {
+ "ampm": {
+ "lowercase": "am/pm em minúsculas",
+ "uppercase": "AM/PM em maiúsculas"
+ },
+ "common": {
+ "12hour-time-minutes": "Horário de 12 horas com minutos",
+ "24hour-time-minutes": "Horário de 24 horas com minutos",
+ "24hour-time-seconds": "Horário de 24 horas com segundos",
+ "european-date": "Formato de data europeu",
+ "iso-date": "Formato de data ISO",
+ "us-date": "Formato de data dos EUA",
+ "weekday-date": "Dia da semana com data",
+ "weekday-month-day": "Dia da semana, mês e dia"
+ },
+ "day": {
+ "abbreviated": "Nome do dia abreviado",
+ "full": "Nome completo do dia",
+ "leading-zero": "Dia com zero à esquerda (01-31)",
+ "no-leading-zero": "Dia sem zero à esquerda (1-31)"
+ },
+ "hour": {
+ "leading-zero": "Hora com zero à esquerda (00-23) - formato de 24 horas",
+ "no-leading-zero": "Hora sem zero à esquerda (0-23) - formato de 24 horas"
+ },
+ "minute": {
+ "leading-zero": "Minuto com zero à esquerda (00-59)",
+ "no-leading-zero": "Minuto sem zero à esquerda (0-59)"
+ },
+ "month": {
+ "abbreviated": "Nome do mês abreviado",
+ "full": "Nome completo do mês",
+ "number-leading-zero": "Mês como número com zero à esquerda (01-12)",
+ "number-no-zero": "Mês como número sem zero à esquerda (1-12)"
+ },
+ "second": {
+ "leading-zero": "Segundo com zero à esquerda (00-59)",
+ "no-leading-zero": "Segundo sem zero à esquerda (0-59)"
+ },
+ "timezone": {
+ "abbreviation": "Abreviação do fuso horário"
+ },
+ "year": {
+ "four-digit": "Ano com quatro dígitos",
+ "two-digit": "Ano com dois dígitos (00-99)"
}
},
- "welcome": {
- "note": "Apenas alguns ajustes básicos para começar - o restante está em Configurações"
+ "file-picker": {
+ "cancel": "Cancelar",
+ "search-placeholder": "Pesquisar arquivos e pastas...",
+ "select-current": "Selecionar Atual",
+ "select-file": "Selecionar Arquivo",
+ "select-folder": "Selecionar Pasta",
+ "title": "Seletor de Arquivos"
+ },
+ "icon-picker": {
+ "apply": "Aplicar",
+ "cancel": "Cancelar",
+ "search": {
+ "label": "Pesquisar"
+ },
+ "title": "Seletor de ícones"
+ },
+ "text-input": {
+ "clear": "Limpar"
+ },
+ "tooltip": {
+ "placeholder": "Espaço reservado"
+ }
+ },
+ "wifi": {
+ "panel": {
+ "connect": "Conectar",
+ "connected": "Conectado",
+ "disabled": "O Wi-Fi está desativado",
+ "disconnect": "Desconectar",
+ "disconnecting": "Desconectando...",
+ "enable-message": "Ative o Wi-Fi para ver as redes disponíveis.",
+ "enter-password": "Digite a senha...",
+ "forget": "Esquecer",
+ "forget-network": "Esquecer esta rede?",
+ "forgetting": "Esquecendo...",
+ "no-networks": "Nenhuma rede encontrada",
+ "password": "Senha",
+ "saved": "Salva",
+ "scan-again": "Escanear novamente",
+ "searching": "Procurando redes próximas...",
+ "title": "Wi-Fi"
}
}
}
diff --git a/Assets/Translations/tr.json b/Assets/Translations/tr.json
new file mode 100644
index 00000000..01e237db
--- /dev/null
+++ b/Assets/Translations/tr.json
@@ -0,0 +1,2006 @@
+{
+ "authentication": {
+ "error": "Kimlik doğrulama hatası",
+ "failed": "Kimlik doğrulama başarısız"
+ },
+ "bar": {
+ "widget-settings": {
+ "active-window": {
+ "colorize-icons": {
+ "description": "Aktif pencere ikonuna tema renklerini uygula.",
+ "label": "İkonları Renklendir"
+ },
+ "hide-mode": {
+ "description": "Pencere aktif değilken widget'in nasıl davrandığını kontrol eder.",
+ "label": "Gizleme modu"
+ },
+ "max-width": {
+ "description": "Widget'in maksimum yatay boyutunu ayarlar. Widget daha kısa içerik için küçülecektir.",
+ "label": "Maksimum Genişlik"
+ },
+ "scrolling-mode": {
+ "description": "Uzun pencere başlıkları için metin kaydırmanın ne zaman etkinleştirileceğini kontrol et.",
+ "label": "Kaydırma modu"
+ },
+ "show-app-icon": {
+ "description": "Pencere başlığı yanında uygulama ikonunu göster.",
+ "label": "Uygulama ikonunu göster"
+ },
+ "use-fixed-width": {
+ "description": "Etkinleştirildiğinde, widget dinamik olarak içerik göre ayarlamak yerine her zaman maksimum genişliği kullanır.",
+ "label": "Sabit Genişlik Kullan"
+ }
+ },
+ "audio-visualizer": {
+ "color-name": {
+ "description": "Görselleştirici için renk seçin.",
+ "label": "Dolgu Rengi"
+ },
+ "hide-when-idle": {
+ "description": "Etkinleştirildiğinde, çalar aktif bir şekilde çalmadığı sürece görselleştirici gizlidir.",
+ "label": "Boşken gizle"
+ },
+ "width": {
+ "description": "Özel bileşen genişliği.",
+ "label": "Genişlik"
+ }
+ },
+ "battery": {
+ "display-mode": {
+ "description": "Bu değerin nasıl görünmesini istediğinizi seçin.",
+ "label": "Görüntüleme modu"
+ },
+ "low-battery-threshold": {
+ "description": "Batarya bu yüzdeye düştüğünde uyarı göster.",
+ "label": "Düşük batarya eşiği"
+ }
+ },
+ "brightness": {
+ "display-mode": {
+ "description": "Bu değerin nasıl görünmesini istediğinizi seçin.",
+ "label": "Görüntüleme modu"
+ }
+ },
+ "clock": {
+ "clock-display": {
+ "description": "Saatinizin görüntüsünü özelleştirmek için aşağıdaki listeden jetonları ekleyin. 12 saat formatını kullanmak için 'AP' jetonunu eklemelisiniz.",
+ "label": "Saat görüntüleme"
+ },
+ "custom-font": {
+ "description": "Saat görüntüleme için özel bir yazı tipi seçin.",
+ "label": "Özel yazı tipi",
+ "placeholder": "Özel yazı tipi seçin...",
+ "search-placeholder": "Yazı tiplerini ara..."
+ },
+ "horizontal-bar": {
+ "description": "İpucu: Bir satır sonu oluşturmak için \\n kullanın.",
+ "label": "Yatay çubuk"
+ },
+ "preview": "Önizleme",
+ "use-custom-font": {
+ "description": "Saat için varsayılan yazı tipi seçimini özel bir yazı tipiyle geçersiz kılın.",
+ "label": "Özel yazı tipi kullan"
+ },
+ "use-monospaced-font": {
+ "description": "Etkinleştirildiğinde, saat tek aralıklı yazı tipini kullanır.",
+ "label": "Tek aralıklı yazı tipi kullan"
+ },
+ "use-primary-color": {
+ "description": "Etkinleştirildiğinde, bu vurgu için birincil rengi uygular.",
+ "label": "Birincil rengi kullan"
+ },
+ "vertical-bar": {
+ "description": "Her parçayı yeni bir satıra ayırmak için boşluk kullanın.",
+ "label": "Dikey çubuk"
+ }
+ },
+ "control-center": {
+ "browse-file": "Dosyaya Göz At",
+ "browse-library": "Kütüphaneye Göz At",
+ "icon": {
+ "description": "Kütüphaneden veya özel bir dosyadan bir ikon seçin.",
+ "label": "İkon"
+ },
+ "select-custom-icon": "Özel bir ikon seç",
+ "use-distro-logo": {
+ "description": "Özel ikon yerine dağıtımınızın logosunu kullanın.",
+ "label": "Dağıtım logosunu ikon yerine kullan"
+ },
+ "colorize-distro-logo": {
+ "description": "Dağıtım logonuza tema renklerini uygula.",
+ "label": "Dağıtım logosunu renklendir"
+ }
+ },
+ "custom-button": {
+ "browse": "Göz At",
+ "collapse-condition": {
+ "description": "Çıktı metni bu değerle eşleşirse buton daralacak.",
+ "label": "Daralma durumu"
+ },
+ "display-command-output": {
+ "description": "Düzenli aralıklarla çalıştırılacak bir komut girin. Çıktısının ilk satırı metin olarak gösterilecektir.",
+ "label": "Komut Çıktısını Göster",
+ "stream-description": "Sürekli çalıştırılacak bir komut girin."
+ },
+ "dynamic-text": "Dinamik metin",
+ "hide-vertical": {
+ "description": "Etkinleştirilirse, komut çıktısındaki metin, çubuk dikey düzende (sol veya sağ) olduğunda gösterilmeyecektir.",
+ "label": "Dikey çubukta metni gizle"
+ },
+ "icon": {
+ "description": "Kütüphaneden bir ikon seçin.",
+ "label": "İkon"
+ },
+ "left-click": {
+ "description": "Butona sol tıklandığında yürütülecek komut.",
+ "label": "Sol tıklama"
+ },
+ "middle-click": {
+ "description": "Butona orta tıklandığında yürütülecek komut.",
+ "label": "Orta tıklama"
+ },
+ "parse-json": {
+ "description": "Komut çıktısını metin ve ikon dinamik olarak ayarlamak için bir JSON nesnesi olarak ayrıştırın.",
+ "label": "Çıktıyı JSON olarak ayrıştır"
+ },
+ "refresh-interval": {
+ "description": "milisaniye cinsinden aralık.",
+ "label": "Yenileme aralığı"
+ },
+ "right-click": {
+ "description": "Butona sağ tıklandığında yürütülecek komut.",
+ "label": "Sağ tıklama"
+ },
+ "text-stream": {
+ "description": "Komuttan gelen akış satırları butonda metin olarak gösterilecektir.",
+ "label": "Akış"
+ }
+ },
+ "dialog": {
+ "apply": "Uygula",
+ "cancel": "İptal"
+ },
+ "keyboard-layout": {
+ "display-mode": {
+ "description": "Bu değerin nasıl görünmesini istediğinizi seçin.",
+ "label": "Görüntüleme modu"
+ }
+ },
+ "lock-keys": {
+ "browse": "Göz at",
+ "show-caps-lock": {
+ "description": "Caps Lock durumunu göster.",
+ "label": "Caps Lock"
+ },
+ "show-num-lock": {
+ "description": "Num Lock durumunu göster.",
+ "label": "Num Lock"
+ },
+ "show-scroll-lock": {
+ "description": "Scroll Lock durumunu göster.",
+ "label": "Scroll Lock"
+ }
+ },
+ "media-mini": {
+ "hide-mode": {
+ "description": "Medya çalınmadığında widget'in nasıl davrandığını kontrol eder.",
+ "label": "Gizleme modu"
+ },
+ "max-width": {
+ "description": "Widget'in maksimum yatay boyutunu ayarlar. Widget daha kısa içerik için küçülecektir.",
+ "label": "Maksimum Genişlik"
+ },
+ "no-active-player": "Etkin çalar yok",
+ "scrolling-mode": {
+ "description": "Uzun parça başlıkları için metin kaydırmanın ne zaman etkinleştirileceğini kontrol et.",
+ "label": "Kaydırma modu"
+ },
+ "show-album-art": {
+ "description": "Şu anda çalan parçanın albüm kapak sanatını göster.",
+ "label": "Albüm kapağını göster"
+ },
+ "show-visualizer": {
+ "description": "Müzik çalarken bir ses görselleştirici göster.",
+ "label": "Görselleştiriciyi göster"
+ },
+ "use-fixed-width": {
+ "description": "Etkinleştirildiğinde, widget dinamik olarak içerik göre ayarlamak yerine her zaman maksimum genişliği kullanır.",
+ "label": "Sabit Genişlik Kullan"
+ },
+ "visualizer-type": {
+ "description": "Gösterilecek ses görselleştirici stilini seçin.",
+ "label": "Görselleştirici türü"
+ }
+ },
+ "microphone": {
+ "display-mode": {
+ "description": "Bu değerin nasıl görünmesini istediğinizi seçin.",
+ "label": "Görüntüleme modu"
+ }
+ },
+ "notification-history": {
+ "hide-badge-when-zero": {
+ "description": "Okunmamış bildirim olmadığında bildirim rozetini gizle.",
+ "label": "Sıfırken rozeti gizle"
+ },
+ "show-unread-badge": {
+ "description": "Okunmamış bildirim sayısını gösteren bir rozet göster.",
+ "label": "Okunmamayakutu göster"
+ }
+ },
+ "section-editor": {
+ "placeholder": "Eklenecek bir widget seçin...",
+ "search-placeholder": "Widget ara..."
+ },
+ "spacer": {
+ "width": {
+ "description": " piksel olarak aralık genişliği",
+ "label": "Genişlik"
+ }
+ },
+ "system-monitor": {
+ "cpu-temperature": {
+ "description": "Mevcut CPU sıcaklık okumalarını gösterilir.",
+ "label": "CPU sıcaklığı"
+ },
+ "cpu-usage": {
+ "description": "Mevcut CPU kullanım yüzdesini göster.",
+ "label": "CPU kullanımı"
+ },
+ "memory-percentage": {
+ "description": "Mutlak değerler yerine bellek kullanımını yüzde olarak göster.",
+ "label": "Bellek yüzde olarak"
+ },
+ "memory-usage": {
+ "description": "Mevcut RAM kullanım bilgilerini göster.",
+ "label": "Bellek kullanımı"
+ },
+ "network-traffic": {
+ "description": "Ağ yükleme ve indirme hızlarını göster.",
+ "label": "Ağ trafiği"
+ },
+ "storage-usage": {
+ "description": "Disk alanı kullanım bilgilerini göster.",
+ "label": "Depolama kullanımı"
+ }
+ },
+ "taskbar": {
+ "colorize-icons": {
+ "description": "Görev çubuğu ikonlarına tema renklerini uygula.",
+ "label": "İkonları Renklendir"
+ },
+ "hide-mode": {
+ "description": "Eşleşen pencere olmadığında widget'in nasıl davrandığını kontrol eder.",
+ "label": "Gizleme modu"
+ },
+ "only-active-workspaces": {
+ "description": "Sadece aktif çalışma alanlarından uygulamaları göster.",
+ "label": "Sadece aktif çalışma alanlarından"
+ },
+ "only-same-output": {
+ "description": "Sadece çubuğun bulunduğu çıktıdan uygulamaları göster.",
+ "label": "Sadece aynı çıktıdan"
+ }
+ },
+ "tray": {
+ "colorize-icons": {
+ "description": "Tepsi ikonlarına tema renklerini uygula.",
+ "label": "İkonları Renklendir"
+ },
+ "drawer-enabled": {
+ "description": "Etkinleştirildiğinde, sabitlenmemiş tepsi öğeleri bir çekmece panelinde gösterilir. Devre dışı bırakıldığında, tüm tepsi öğeleri satır içi gösterilir.",
+ "label": "Çekmeceyi Etkinleştir"
+ }
+ },
+ "volume": {
+ "display-mode": {
+ "description": "Bu değerin nasıl görünmesini istediğinizi seçin.",
+ "label": "Görüntüleme modu"
+ }
+ },
+ "workspace": {
+ "character-count": {
+ "description": "Çalışma alanı adlarından gösterilecek karakter sayısı (1-10).",
+ "label": "Karakter sayısı"
+ },
+ "hide-unoccupied": {
+ "description": "Penceresi olmayan çalışma alanlarını gösterme.",
+ "label": "Dolu olmayanları gizle"
+ },
+ "label-mode": {
+ "description": "Çalışma alanı etiketlerinin nasıl gösterileceğini seçin.",
+ "label": "Etiket Modu"
+ }
+ }
+ }
+ },
+ "battery": {
+ "charging": "Şarj oluyor.",
+ "charging-rate": "Şarj oranı: {rate} W.",
+ "discharging": "Deşarj oluyor.",
+ "discharging-rate": "Deşarj oranı: {rate} W.",
+ "health": "Sağlık: {percent}%",
+ "idle": "Boşta.",
+ "no-battery-detected": "Pil tespit edilmedi.",
+ "panel": {
+ "balanced": "Dengeli ({percent}%)",
+ "disabled": "Pil yöneticisi devre dışı",
+ "full": "Tam kapasite ({percent}%)",
+ "lifespan": "Uzatılmış ömür ({percent}%)",
+ "title": "Şarj eşiği"
+ },
+ "plugged-in": "Prize takılı.",
+ "time-left": "Kalan süre: {time}.",
+ "time-until-full": "Dolma süresi: {time}."
+ },
+ "bluetooth": {
+ "panel": {
+ "available-devices": "Mevcut cihazlar",
+ "blocked": "Engellendi",
+ "connect": "Bağlan",
+ "connected-devices": "Bağlı cihazlar",
+ "disabled": "Bluetooth devre dışı",
+ "disconnect": "Bağlantıyı Kes",
+ "enable-message": "Mevcut cihazları görmek için Bluetooth'u etkinleştirin.",
+ "known-devices": "Bilinen cihazlar",
+ "pairing": "Eşleştiriliyor...",
+ "pairing-mode": "Cihazınızın eşleştirme modunda olduğundan emin olun.",
+ "scanning": "Cihazlar tarınıyor...",
+ "title": "Bluetooth"
+ }
+ },
+ "calendar": {
+ "panel": {
+ "week": "Hafta"
+ },
+ "weather": {
+ "loading": "Hava durumu yükleniyor..."
+ }
+ },
+ "clock": {
+ "tooltip": "Takvimi aç"
+ },
+ "dock": {
+ "menu": {
+ "close": "Kapat",
+ "focus": "Odaklan",
+ "pin": "Sabitle",
+ "unpin": "Sabitlemeyi kaldır"
+ }
+ },
+ "general": {
+ "no-results": "Sonuç yok",
+ "no-summary": "Özet yok",
+ "unknown": "Bilinmiyor"
+ },
+ "launcher": {
+ "pin": "Dock'a sabitle",
+ "unpin": "Dock'dan sabitlemeyi kaldır"
+ },
+ "lock-screen": {
+ "authenticating": "Doğrulanıyor...",
+ "authentication-failed": "Kimlik doğrulama başarısız",
+ "password": "Parolanızı girin...",
+ "restart": "Yeniden başlat",
+ "shut-down": "Kapat",
+ "suspend": "Askıya al",
+ "welcome-back": "Hoş geldiniz,"
+ },
+ "notifications": {
+ "panel": {
+ "click-to-expand": "Genişletmek için tıklayın",
+ "description": "Bildirimler geldikçe burada görünecek.",
+ "no-notifications": "Bildirim yok",
+ "title": "Bildirimler"
+ }
+ },
+ "options": {
+ "bar": {
+ "density": {
+ "comfortable": "Rahat",
+ "compact": "Sıkı",
+ "default": "Varsayılan",
+ "mini": "Mini"
+ },
+ "position": {
+ "bottom": "Alt",
+ "left": "Sol",
+ "right": "Sağ",
+ "top": "Üst"
+ }
+ },
+ "colors": {
+ "error": "Hata",
+ "onSurface": "Üstünde",
+ "primary": "Birincil",
+ "secondary": "İkincil",
+ "tertiary": "Üçüncül"
+ },
+ "control-center": {
+ "position": {
+ "bottom_center": "Alt merkez",
+ "bottom_left": "Sol alt",
+ "bottom_right": "Sağ alt",
+ "center": "Merkez",
+ "close_to_bar_button": "Çubuğa yakın kapat",
+ "top_center": "Üst merkez",
+ "top_left": "Sol üst",
+ "top_right": "Sağ üst"
+ },
+ "quickSettingsStyle": {
+ "classic": "Klasik",
+ "compact": "Sıkı",
+ "modern": "Modern"
+ }
+ },
+ "display-mode": {
+ "always-hide": "Her zaman gizle",
+ "always-show": "Her zaman göster",
+ "force-open": "Zorla Aç",
+ "on-hover": "Üzerine gelince"
+ },
+ "frame-rates": {
+ "fps": "{fps} FPS"
+ },
+ "hide-modes": {
+ "hidden": "Boşken Gizle",
+ "idle": "Boştayken gizle",
+ "transparent": "Boşken Şeffaf",
+ "visible": "Her zaman Görünür"
+ },
+ "launcher": {
+ "position": {
+ "bottom_center": "Alt merkez",
+ "bottom_left": "Sol alt",
+ "bottom_right": "Sağ alt",
+ "center": "Merkez (varsayılan)",
+ "center_left": "Sol merkez",
+ "center_right": "Sağ merkez",
+ "follow_bar": "Çubuğu takip et (varsayılan)",
+ "top_center": "Üst merkez",
+ "top_left": "Sol üst",
+ "top_right": "Sağ üst"
+ }
+ },
+ "osd": {
+ "position": {
+ "bottom_center": "Alt merkez",
+ "bottom_left": "Sol alt",
+ "bottom_right": "Sağ alt",
+ "center_left": "Sol merkez",
+ "center_right": "Sağ merkez",
+ "top_center": "Üst merkez",
+ "top_left": "Sol üst",
+ "top_right": "Sağ üst"
+ }
+ },
+ "screen-recording": {
+ "audio-sources": {
+ "both": "Sistem çıktısı + mikrofon girişi",
+ "microphone-input": "Mikrofon girişi",
+ "system-output": "Sistem çıktısı"
+ },
+ "color-range": {
+ "full": "Tam",
+ "limited": "Sınırlı"
+ },
+ "quality": {
+ "high": "Yüksek",
+ "medium": "Orta",
+ "ultra": "Ultra",
+ "very-high": "Çok yüksek"
+ },
+ "sources": {
+ "portal": "Portal",
+ "screen": "Ekran"
+ }
+ },
+ "scrolling-modes": {
+ "always": "Her Kaydır",
+ "hover": "Üzerine Gelince Kaydır",
+ "never": "Asla Kaydırma"
+ },
+ "shadow-direction": {
+ "bottom": "Alt",
+ "bottom_left": "Sol alt",
+ "bottom_right": "Sağ alt",
+ "center": "Merkez",
+ "left": "Sol",
+ "right": "Sağ",
+ "top": "Üst",
+ "top_left": "Sol üst",
+ "top_right": "Sağ üst"
+ },
+ "visualizer-quality": {
+ "high": "Yüksek",
+ "low": "Düşük"
+ },
+ "visualizer-types": {
+ "linear": "Doğrusal",
+ "mirrored": "Yansıtılmış",
+ "none": "Hiçbiri",
+ "wave": "Dalga"
+ },
+ "workspace-labels": {
+ "index": "İndeks",
+ "name": "İsim",
+ "none": "Hiçbiri"
+ }
+ },
+ "placeholders": {
+ "cancel": "İptal",
+ "command-example": "echo \"Hello World\"",
+ "enter-command": "Çalıştırılacak komutu girin (uygulama veya özel komut)",
+ "enter-text-to-collapse": "örn., 'nothing is playing'. Desenler için /regex/ kullanın.",
+ "enter-tooltip": "Araç ipucunu girin",
+ "enter-width-pixels": "Genişliği piksel olarak girin",
+ "profile-picture-path": "/home/user/.face",
+ "search": "Ara...",
+ "search-icons": "örn., noctalia, niri, battery, cloud",
+ "search-launcher": "Girişleri arayın... veya > komutları için kullanın",
+ "search-wallpapers": "Duvar kağıtlarını filtrelemek için yazın...",
+ "select": "Seç",
+ "test": "Test"
+ },
+ "plugins": {
+ "applications": "Uygulamalar",
+ "calculator": "Hesap makinesi",
+ "calculator-description": "Hesap makinesi - matematiksel ifadeleri hesapla",
+ "calculator-enter-expression": "Matematiksel bir ifade girin",
+ "calculator-error": "Hata",
+ "calculator-name": "Hesap makinesi",
+ "clipboard": "Panoya geçmiş",
+ "clipboard-clear-description": "Tüm panoya geçmişini temizle",
+ "clipboard-clear-description-full": "Tüm öğeleri panoya geçmişinden kaldır",
+ "clipboard-clear-history": "Panoya geçmişini temizle",
+ "clipboard-history-disabled": "Panoya geçmişi devre dışı",
+ "clipboard-history-disabled-description": "Panoya geçmişini ayarlarda etkinleştir veya cliphist kur",
+ "clipboard-loading": "Panoya geçmişi yükleniyor...",
+ "clipboard-loading-description": "Lütfen bekleyin",
+ "clipboard-search-description": "Panoya geçmişini ara"
+ },
+ "quickSettings": {
+ "bluetooth": {
+ "label": {
+ "disabled": "Bluetooth",
+ "enabled": "Bluetooth"
+ },
+ "tooltip": {
+ "action": "Tıklayarak Bluetooth cihazlarını yönet"
+ }
+ },
+ "keepAwake": {
+ "label": {
+ "disabled": "Uyanık Tut",
+ "enabled": "Uyanık Tut"
+ },
+ "tooltip": {
+ "action": "Tıklayarak uyanık kalma modunu aç/kapat"
+ }
+ },
+ "nightLight": {
+ "label": {
+ "disabled": "Gece Işığı",
+ "enabled": "Gece Işığı",
+ "forced": "Gece Işığı"
+ },
+ "tooltip": {
+ "action": "Tıklayarak gece ışığı modunu değiştir\nSağ tık: Ayarları aç"
+ }
+ },
+ "notifications": {
+ "label": {
+ "disabled": "Rahatsız Etme",
+ "enabled": "Bildirimler"
+ },
+ "tooltip": {
+ "action": "Sol tık: Bildirim geçmişini aç\nSağ tık: Rahatsız etmeyi aç/kapat"
+ }
+ },
+ "powerProfile": {
+ "label": {
+ "unavailable": "Güç Profili"
+ },
+ "tooltip": {
+ "action": "Tıklayarak güç profilini değiştir",
+ "disabled": "Güç profillerini kullanmak için power-profiles-daemon kur"
+ }
+ },
+ "screenRecorder": {
+ "label": {
+ "recording": "Durdur",
+ "stopped": "Kaydet"
+ },
+ "tooltip": {
+ "action": "Tıklayarak ekran kaydını başlat/durdur"
+ }
+ },
+ "wallpaperSelector": {
+ "label": "Duvar Kağıdı",
+ "tooltip": {
+ "action": "Sol tık: Duvar kağıdı seçiciyi aç\nSağ tık: Rastgele duvar kağıdı ayarla"
+ }
+ },
+ "wifi": {
+ "label": {
+ "disconnected": "Wi-Fi Bağlantısı Kesik",
+ "ethernet": "Ethernet",
+ "wifi": "Wi-Fi"
+ },
+ "tooltip": {
+ "action": "Tıklayarak Wi-Fi bağlantılarını yönet"
+ }
+ }
+ },
+ "session-menu": {
+ "action-in-seconds": "{action} in {seconds} seconds...",
+ "lock": "Kilitle",
+ "lock-and-suspend": "Kilitle ve Askıya Al",
+ "logout": "Oturumu kapat",
+ "reboot": "Yeniden başlat",
+ "shutdown": "Bilgisayarı kapat",
+ "suspend": "Askıya al",
+ "title": "Oturum Menüsü"
+ },
+ "settings": {
+ "about": {
+ "contributors": {
+ "section": {
+ "description": "{count} harika katılımcımıza teşekkürler!",
+ "description_plural": "{count} harika katılımcımıza teşekkürler!",
+ "label": "Katkıda Bulunanlar"
+ }
+ },
+ "noctalia": {
+ "download-latest": "En son sürümü indir",
+ "installed-version": "Yüklü sürüm:",
+ "latest-version": "En son sürüm:",
+ "section": {
+ "description": "Wayland için düşünülmüş, Quickshell ile inşa edilmiş şık ve minimal masaüstü kabuğu.",
+ "label": "Noctalia kabuğu"
+ }
+ },
+ "support": "Bizi destekleyin",
+ "title": "Hakkında"
+ },
+ "audio": {
+ "devices": {
+ "input-device": {
+ "description": "İstenilen ses giriş cihazını seçin.",
+ "label": "Giriş cihazı"
+ },
+ "output-device": {
+ "description": "İstenilen ses çıkış cihazını seçin.",
+ "label": "Çıkış cihazı"
+ },
+ "section": {
+ "description": "Mevcut ses giriş ve çıkış cihazlarını yapılandırın.",
+ "label": "Ses cihazları"
+ }
+ },
+ "media": {
+ "excluded-player": {
+ "description": "Sistemin yoksaymasını istediğiniz oynatıcılar için anahtar kelimeler ekleyin. Her anahtar kelime ayrı bir satırda olmalıdır.",
+ "label": "Dışlanan oynatıcı",
+ "placeholder": "alt dize girin ve + tuşuna basın"
+ },
+ "frame-rate": {
+ "description": "Daha yüksek hızlar daha yumuşaktır ancak daha fazla kaynak kullanır.",
+ "label": "Kare hızı"
+ },
+ "primary-player": {
+ "description": "Ana oynatıcınızı belirlemek için bir anahtar kelime girin.",
+ "label": "Ana oynatıcı",
+ "placeholder": "örn. spotify, vlc, mpv"
+ },
+ "scrolling-speed": {
+ "description": "Başlığın baştan sona kayması için saniye cinsinden süre",
+ "label": "Kaydırma hızı"
+ },
+ "scrolling-title": {
+ "description": "Uzun medya başlıkları için sürekli kaydırmayı etkinleştirin",
+ "label": "Kayan başlık"
+ },
+ "section": {
+ "description": "Tercih ettiğiniz ve yoksayacağınız medya uygulamalarını ayarlayın.",
+ "label": "Medya oynatıcılar"
+ },
+ "visualizer-quality": {
+ "description": "Yüksek kalite daha fazla GPU kaynağı kullanır.",
+ "label": "Görselleştirme kalitesi"
+ },
+ "visualizer-type": {
+ "description": "Medya oynatımı için bir görselleştirme türü seçin",
+ "label": "Görselleştirme türü"
+ }
+ },
+ "title": "Ses",
+ "volumes": {
+ "input-volume": {
+ "description": "Mikrofon giriş ses seviyesi.",
+ "label": "Giriş sesi"
+ },
+ "mute-input": {
+ "description": "Varsayılan ses girişini (mikrofon) sessize al.",
+ "label": "Ses girişini sessize al"
+ },
+ "mute-output": {
+ "description": "Sistemin ana ses çıkışını sessize al.",
+ "label": "Ses çıkışını sessize al"
+ },
+ "output-volume": {
+ "description": "Sistem genelindeki ses seviyesi.",
+ "label": "Çıkış sesi"
+ },
+ "section": {
+ "description": "Ses kontrollerini ve ses seviyelerini ayarlayın.",
+ "label": "Ses seviyeleri"
+ },
+ "step-size": {
+ "description": "Ses değişimleri için adım boyutunu ayarlayın (tekerlek, klavye kısayolları).",
+ "label": "Ses adım boyutu"
+ },
+ "volume-overdrive": {
+ "description": "Sesi %100'in üzerine çıkarmaya izin ver. Tüm donanımlar desteklemeyebilir.",
+ "label": "Ses aşımına izin ver"
+ }
+ }
+ },
+ "bar": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Barın arka plan opaklığını ayarlayın.",
+ "label": "Arka plan opaklığı"
+ },
+ "density": {
+ "description": "Sıkı veya geniş bir görünüm için barın dolgusunu ayarlayın.",
+ "label": "Bar yoğunluğu"
+ },
+ "floating": {
+ "description": "Barı yüzen bir 'hap' olarak görüntüler. Not: Bu, ekran köşelerini kenarlara taşıyacaktır.",
+ "label": "Yüzen bar"
+ },
+ "margins": {
+ "description": "Yüzen barın etrafındaki kenar boşluklarını ayarlayın.",
+ "horizontal": "Yatay",
+ "label": "Kenar boşlukları",
+ "vertical": "Dikey"
+ },
+ "outer-corners": {
+ "description": "Çubuğun dışa doğru kavisli köşelerini görüntüler.",
+ "label": "Dış köşeler"
+ },
+ "position": {
+ "description": "Barı ekranda nereye yerleştireceğinizi seçin.",
+ "label": "Bar konumu"
+ },
+ "section": {
+ "description": "Barın görünümünü ve konumunu özelleştirin.",
+ "label": "Görünüm"
+ },
+ "show-capsule": {
+ "description": "Widget arka planlarını göster.",
+ "label": "Kapsülü göster"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Barı belirli ekranlarda gösterin. Hiçbiri seçilmezse varsayılan olarak tümünde gösterilir.",
+ "label": "Ekran görüntüsü"
+ }
+ },
+ "title": "Bar",
+ "tray": {
+ "back": "Geri",
+ "blacklist": {
+ "description": "Tepsi hariç tutma kuralları ekleyin, joker karakterleri destekler (*).",
+ "label": "Kara liste",
+ "placeholder": "örn., nm-applet, Fcitx*"
+ },
+ "pin-application": "Uygulamayı Sabitle",
+ "unpin-application": "Sabitlemeyi Kaldır"
+ },
+ "widgets": {
+ "section": {
+ "description": "Widget'ları yeniden sıralamak için sürükleyip bırakın. Rozetler kullanımı gösterir: [S]ol, [O]rta, [S]ağ.",
+ "label": "Widget konumlandırma"
+ }
+ }
+ },
+ "color-scheme": {
+ "color-source": {
+ "matugen-scheme-type": {
+ "description": {
+ "scheme-content": "Temel görüntüye yakın renkler türetir",
+ "scheme-expressive": "Oyuncak doygunlukla canlı palet",
+ "scheme-fidelity": "Kaynak tonlarını koruyan yüksek sadakatli palet",
+ "scheme-fruit-salad": "Canlı kontrast aksanların renkli karışımı",
+ "scheme-monochrome": "Tek bir ton etrafında inşa edilmiş minimal palet",
+ "scheme-neutral": "Sakinleştirici ve bastırılmış tonlarla yumuşak palet",
+ "scheme-rainbow": "Tam spektrumu kapsayan çeşitli palet",
+ "scheme-tonal-spot": "Odaklanmış aksanlarla dengeli palet"
+ },
+ "label": "Matugen şema türü"
+ },
+ "section": {
+ "description": "Noctalia'nın renkleri için ana ayarlar.",
+ "label": "Renk kaynağı"
+ },
+ "use-wallpaper-colors": {
+ "description": "Matugen kullanarak duvar kağıdınızdan renk şemaları oluşturun. Bütünleşik bir tema oluşturmak için renkleri otomatik olarak çıkarır.",
+ "label": "Duvar kağıdı renklerini kullan"
+ }
+ },
+ "dark-mode": {
+ "mode": {
+ "description": "Işık ve koyu mod arasında otomatik geçişi etkinleştirir.",
+ "label": "Koyu mod zamanlaması",
+ "location": "Konum",
+ "manual": "Manuel",
+ "off": "Kapalı"
+ },
+ "switch": {
+ "description": "Gece daha kolay görüntülemek için daha koyu bir tema geçiş yapar.",
+ "label": "Koyu mod"
+ }
+ },
+ "predefined": {
+ "generate-templates": {
+ "description": "Önceden tanımlanmış renk şemaları kullanırken Matugen şablonları (GTK, terminal temaları vb.) oluşturun.",
+ "label": "Önceden tanımlanmış şemalar için şablonlar oluşturun"
+ },
+ "section": {
+ "description": "Önceden tanımlanmış renk şemaları koleksiyonundan seçim yapın.",
+ "label": "Önceden tanımlanmış renk şemaları"
+ }
+ },
+ "templates": {
+ "misc": {
+ "description": "Ek yapılandırma seçenekleri.",
+ "label": "Çeşitli",
+ "user-templates": {
+ "description": "Kullanıcı tanımlı Matugen yapılandırmasını etkinleştir. İlk etkinleştirmede ~/.config/noctalia/user-templates.toml dosyası oluşturulacaktır",
+ "label": "Kullanıcı şablonları"
+ }
+ },
+ "programs": {
+ "code": {
+ "description": "{filepath} dosyasına yaz. Hyprluna temasının kurulu ve manuel olarak etkinleştirilmiş olması gerekir.",
+ "description-missing": "Kurulum için {app} gereklidir"
+ },
+ "spicetify": {
+ "description": "{filepath} dosyasına yaz. Comfy temasının kurulu ve manuel olarak etkinleştirilmiş olması gerekir.",
+ "description-missing": "Kurulum için {app} gereklidir"
+ },
+ "description": "Uygulamaya özel temalandırma.",
+ "discord": {
+ "description": "{client} için {filepath} dosyasına yaz. Hyprluna temasının manuel olarak etkinleştirilmesi gerekir.",
+ "description-missing": "Discord istemcisi tespit edilmedi. Vencord, vesktop, webcord, armcord, equibop, lightcord veya dorion kurun."
+ },
+ "fuzzel": {
+ "description": "{filepath} dosyasına yaz ve yeniden yükle",
+ "description-missing": "Kurulum için {app} gereklidir"
+ },
+ "label": "Programlar",
+ "pywalfox": {
+ "description": "{filepath} dosyasına yaz ve pywalfox güncellemesini çalıştır",
+ "description-missing": "Kurulum için {app} gereklidir"
+ },
+ "vicinae": {
+ "description": "{filepath} dosyasına yaz ve yeniden yükle",
+ "description-missing": "Kurulum için {app} gereklidir"
+ },
+ "walker": {
+ "description": "{filepath} dosyasına yaz ve temayı noctalia olarak ayarla",
+ "description-missing": "Kurulum için {app} gereklidir"
+ }
+ },
+ "section": {
+ "description": "Renkleri harici uygulamalara uygula.",
+ "label": "Şablonlar"
+ },
+ "terminal": {
+ "alacritty": {
+ "description": "{filepath} yaz ve yeniden yükle",
+ "description-missing": "{app} uygulamasının yüklü olmasını gerektirir"
+ },
+ "description": "Terminal emülatörü temalandırma.",
+ "foot": {
+ "description": "{filepath} dosyasına yaz ve yeniden yükle",
+ "description-missing": "Kurulum için {app} gereklidir"
+ },
+ "ghostty": {
+ "description": "{filepath} dosyasına yaz ve yeniden yükle",
+ "description-missing": "Kurulum için {app} gereklidir"
+ },
+ "kitty": {
+ "description": "{filepath} dosyasına yaz ve yeniden yükle",
+ "description-missing": "Kurulum için {app} gereklidir"
+ },
+ "label": "Terminal",
+ "wezterm": {
+ "description": "{filepath} yaz ve yeniden yükle",
+ "description-missing": "{app} uygulamasının yüklü olmasını gerektirir"
+ }
+ },
+ "ui": {
+ "description": "Masaüstü ortamı ve UI araç seti temalandırma.",
+ "gtk": {
+ "description": "{filepath} dosyasına yaz"
+ },
+ "kcolorscheme": {
+ "description": "{filepath} dosyasına yaz"
+ },
+ "label": "Arayüz",
+ "qt": {
+ "description": "{filepath} dosyasına yaz"
+ }
+ }
+ },
+ "title": "Renk şeması"
+ },
+ "control-center": {
+ "cards": {
+ "section": {
+ "description": "Kontrol Merkezi'nde hangi kontrollerin ve hangi sırada görüneceğini özelleştirin.",
+ "label": "Kartlar"
+ }
+ },
+ "position": {
+ "description": "Kontrol Merkezi paneli açıldığında nerede görüneceğini seçin.",
+ "label": "Konum"
+ },
+ "section": {
+ "description": "Kontrol Merkezi paneli konumunu ve davranışını yapılandırın.",
+ "label": "Görünüm"
+ },
+ "shortcuts": {
+ "custom-button": {
+ "browse": "Göz at",
+ "command": {
+ "description": "Düğmeye tıklandığında çalıştırılacak komut.",
+ "label": "Komut"
+ },
+ "enable-on-state-logic": {
+ "description": "Bir kontrol komutuna bağlı olarak ikinci bir simgeyi ve 'etkin' durumunu etkinleştir.",
+ "label": "Açık Durum Mantığını Etkinleştir"
+ },
+ "general-tooltip-text": {
+ "description": "Düğme araç ipucu için genel açıklama.",
+ "label": "Genel Araç İpucu Metni"
+ },
+ "icon": {
+ "description": "Kütüphaneden bir simge seçin.",
+ "label": "Simge"
+ },
+ "on-clicked": {
+ "description": "Düğmeye sol tıklandığında yürütülecek komut.",
+ "label": "Sol Tık Komutu"
+ },
+ "on-middle-clicked": {
+ "description": "Orta tuşa tıklandığında çalıştırılacak komut.",
+ "label": "Orta Tıklama Komutu"
+ },
+ "on-right-clicked": {
+ "description": "Düğmeye sağ tıklandığında çalıştırılacak komut.",
+ "label": "Sağ Tık Komutu"
+ },
+ "on-state-command": {
+ "description": "Düğmenin 'açık' durumda olup olmadığını kontrol etmek için çalıştırılacak komut. Açık için 0, kapalı için sıfır olmayan bir değer döndürür.",
+ "label": "Durum Kontrol Komutu"
+ },
+ "on-state-icon": {
+ "description": "Düğme 'açık' durumdayken kullanılan simge.",
+ "label": "Durum Simgesinde"
+ },
+ "state-checks": {
+ "add": "Durum Kontrolü Ekle",
+ "browse-icon": "Göz at",
+ "command": "Bu durum kontrolü için yürütülecek komut",
+ "label": "Durum Kontrolleri",
+ "remove": "Kaldır"
+ },
+ "tooltip": {
+ "description": "Düğme üzerine gelindiğinde gösterilecek araç ipucu.",
+ "label": "Araç ipucu"
+ }
+ },
+ "dialog": {
+ "apply": "Uygula",
+ "cancel": "İptal"
+ },
+ "section": {
+ "description": "Kısayut widget'larını yapılandırın ve yönetin.",
+ "label": "Kısayut widget'ları"
+ },
+ "sectionLeft": "Sol",
+ "sectionRight": "Sağ"
+ },
+ "title": "Kontrol Merkezi"
+ },
+ "display": {
+ "monitors": {
+ "brightness": "Parlaklık",
+ "brightness-step": {
+ "description": "Parlaklık değişimleri için adım boyutunu ayarlayın (tekerlek ve klavye kısayolları).",
+ "label": "Parlaklık adım boyutu"
+ },
+ "brightness-unavailable": {
+ "ddc-disabled": "Parlaklık kontrolü kullanılamıyor. Bu ekranın parlaklığını kontrol etmek için \"Harici parlaklık desteği\"ni etkinleştirin.",
+ "generic": "Bu ekran için parlaklık kontrolü mevcut değil."
+ },
+ "enforce-minimum": {
+ "description": "Bazı ekranlarda %0 parlaklıkta arka ışığın tamamen kapanması sorununu çözer.",
+ "label": "Minimum parlaklığı zorla (%1)"
+ },
+ "external-brightness": {
+ "description": "Harici ekranlarda parlaklığı DDC/CI protokolü üzerinden kontrol etmek için DDCUtil desteğini etkinleştir.",
+ "label": "Harici parlaklık desteği"
+ },
+ "section": {
+ "description": "Her ekran için parlaklık ayarlarını yapın.",
+ "label": "Ekran başına ayarlar"
+ }
+ },
+ "night-light": {
+ "auto-schedule": {
+ "description": "{location} konumunda gün batımı ve gün doğumu zamanına göre - önerilen.",
+ "label": "Otomatik zamanlama"
+ },
+ "enable": {
+ "description": "Mavi ışık yayılımını azaltmak için sıcak renk filtresi uygulayın.",
+ "label": "Gece ışığını etkinleştir"
+ },
+ "force-activation": {
+ "description": "Zamanlamayı yoksayar ve gece filtresini hemen uygular.",
+ "label": "Zorla etkinleştir"
+ },
+ "manual-schedule": {
+ "description": "Gün doğumu ve gün batımı için özel zamanlar ayarlayın.",
+ "label": "Manuel zamanlama",
+ "select-start": "Başlangıç zamanını seç",
+ "select-stop": "Bitiş zamanını seç",
+ "sunrise": "Gün doğumu zamanı",
+ "sunset": "Gün batımı zamanı"
+ },
+ "section": {
+ "description": "Daha iyi uyumanıza ve göz yorgunluğunu azaltmanıza yardımcı olmak için mavi ışık yayılımını azaltın.",
+ "label": "Gece ışığı"
+ },
+ "temperature": {
+ "day": "Gündüz",
+ "description": "Gece ve gündüz için renk sıcaklığını ayarlayın.",
+ "label": "Renk sıcaklığı",
+ "night": "Gece"
+ }
+ },
+ "title": "Görüntü"
+ },
+ "dock": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Dock'un arka plan opaklığını ayarlayın.",
+ "label": "Arka plan opaklığı"
+ },
+ "colorize-icons": {
+ "description": "Dock uygulama simgelerine tema renklerini uygulayın (sadece odaklanılmamış uygulamalar).",
+ "label": "Simgeleri Renklendir"
+ },
+ "display": {
+ "always-visible": "Her zaman görünür",
+ "auto-hide": "Otomatik gizle",
+ "description": "Dock'un nasıl davranacağını seçin.",
+ "exclusive": "Özel",
+ "label": "Görüntüleme"
+ },
+ "floating-distance": {
+ "description": "Dock ile ekran kenarı arasındaki mesafeyi ayarlayın.",
+ "label": "Dock yüzen mesafesi"
+ },
+ "icon-size": {
+ "description": "Dock'un genel boyutunu ayarlayın.",
+ "label": "Dock boyutu"
+ },
+ "section": {
+ "description": "Dock'un davranışını ve görünümünü özelleştirin.",
+ "label": "Görünüm"
+ }
+ },
+ "enabled": {
+ "description": "Dock'u tamamen gösterin veya gizleyin",
+ "label": "Dock'u etkinleştir"
+ },
+ "monitors": {
+ "only-same-output": {
+ "description": "Sadece dock'un bulunduğu çıktıdaki uygulamaları gösterin.",
+ "label": "Sadece aynı çıktıdaki uygulamalar"
+ },
+ "section": {
+ "description": "Dock'u belirli ekranlarda gösterin. Hiçbiri seçilmezse varsayılan olarak tümünde gösterilir.",
+ "label": "Ekran görüntüsü"
+ }
+ },
+ "title": "Dock"
+ },
+ "general": {
+ "fonts": {
+ "default": {
+ "description": "Arayüz boyunca kullanılan ana yazı tipi.",
+ "label": "Varsayılan yazı tipi",
+ "placeholder": "Varsayılan yazı tipi seçin...",
+ "scale": {
+ "description": "Standart metin boyutunu artırın veya azaltın.",
+ "label": "Varsayılan yazı tipi boyutu"
+ },
+ "search-placeholder": "Yazı tipi ara..."
+ },
+ "monospace": {
+ "description": "Sayılar ve istatistikler için kullanılan eş aralıklı yazı tipi.",
+ "label": "Eş aralıklı yazı tipi",
+ "placeholder": "Eş aralıklı yazı tipi seçin...",
+ "scale": {
+ "description": "Eş aralıklı metin boyutunu artırın veya azaltın.",
+ "label": "Eş aralıklı yazı tipi boyutu"
+ },
+ "search-placeholder": "Eş aralıklı yazı tipi ara..."
+ },
+ "reset-scaling": "Ölçeklemeyi sıfırla",
+ "section": {
+ "description": "Arayüz boyunca kullanılacak yazı tiplerini seçin.",
+ "label": "Yazı tipleri"
+ }
+ },
+ "language": {
+ "section": {
+ "description": "Uygulama için tercih ettiğiniz dili seçin.",
+ "label": "Dil"
+ },
+ "select": {
+ "auto-detect": "Otomatik",
+ "description": "Uygulama arayüzünde kullanılacak dili seçin.",
+ "label": "Uygulama Dili"
+ }
+ },
+ "launch-setup-wizard": "Kurulum sihirbazını başlat",
+ "profile": {
+ "picture": {
+ "description": "Arayüzün tamamında görünen profil fotoğrafınız.",
+ "label": "{user} profil fotoğrafı"
+ },
+ "section": {
+ "description": "Kullanıcı detaylarınızı ve avatarınızı düzenleyin.",
+ "label": "Profil"
+ },
+ "select-avatar": "Avatar görüntüsü seç"
+ },
+ "screen-corners": {
+ "radius": {
+ "description": "Ekranın yuvarlatılmış köşelerini ayarlayın.",
+ "label": "Ekran köşesi yarıçapı",
+ "reset": "Ekran köşesi yarıçapını sıfırla"
+ },
+ "section": {
+ "description": "Ekran köşesi yuvarlatma ve görsel efektleri özelleştirin.",
+ "label": "Ekran köşeleri"
+ },
+ "show-corners": {
+ "description": "Ekran kenarlarında yuvarlatılmış köşeler görüntüle.",
+ "label": "Ekran köşelerini göster"
+ },
+ "solid-black": {
+ "description": "Bar arka plan rengi yerine tam siyah kullan.",
+ "label": "Siyah köşeler"
+ }
+ },
+ "title": "Genel"
+ },
+ "hooks": {
+ "info": {
+ "command-info": {
+ "description": "• Komutlar kabuk (sh -c) ile yürütülür\n• Komutlar arka planda yürütülür (ayrık)\n• Test düğmeleri mevcut değerlerle yürütülür",
+ "label": "Komut Bilgisi"
+ },
+ "parameters": {
+ "description": "• Duvar Kağıdı Kancası: $1 = duvar kağıdı yolu, $2 = ekran adı\n• Tema Geçiş Kancası: $1 = true/false (koyu mod durumu)",
+ "label": "Kullanılabilir Parametreler"
+ }
+ },
+ "system-hooks": {
+ "enable": {
+ "description": "Tüm kanca komutlarını etkinleştirin veya devre dışı bırakın.",
+ "label": "Kancaları etkinleştir"
+ },
+ "section": {
+ "description": "Sistem olayları gerçekleştiğinde çalıştırılacak komutları yapılandırın.",
+ "label": "Sistem kancaları"
+ }
+ },
+ "theme-changed": {
+ "description": "Tema koyu ve aydınlık mod arasında geçişte çalıştırılacak komut.",
+ "label": "Tema değişti",
+ "placeholder": "örn., notify-send \"Tema\" \"Geçiş Yapıldı\""
+ },
+ "title": "Kancalar",
+ "wallpaper-changed": {
+ "description": "Duvar kağıdı değiştiğinde çalıştırılacak komut.",
+ "label": "Duvar kağıdı değişti",
+ "placeholder": "örn., notify-send \"Duvar Kağıdı\" \"Değişti\""
+ }
+ },
+ "launcher": {
+ "settings": {
+ "background-opacity": {
+ "description": "Başlatıcının arka plan opaklığını ayarlayın.",
+ "label": "Arka plan opaklığı"
+ },
+ "clipboard-history": {
+ "description": "Başlatıcıdan daha önce kopyalanan öğelere erişin.",
+ "label": "Pano geçmişini etkinleştir"
+ },
+ "custom-launch-prefix": {
+ "description": "Komutlara özel bir başlatıcı ile ön ek ekleyin (örn., systemd entegrasyonu için 'runapp').",
+ "label": "Özel başlatma ön eki"
+ },
+ "custom-launch-prefix-enabled": {
+ "description": "Uygulamaları başlatmak için varsayılan yöntem yerine özel bir ön ek kullanın.",
+ "label": "Özel başlatma önekini etkinleştir"
+ },
+ "position": {
+ "description": "Başlatıcı panelinin nerede görüneceğini seçin.",
+ "label": "Konum"
+ },
+ "section": {
+ "description": "Başlatıcının davranışını ve görünümünü özelleştirin.",
+ "label": "Görünüm"
+ },
+ "sort-by-usage": {
+ "description": "Etkinleştirildiğinde, sıkça başlatılan uygulamalar listede ilk olarak görünür.",
+ "label": "En çok kullanılana göre sırala"
+ },
+ "terminal-command": {
+ "description": "Bir terminal başlatmak için komut. Örn., 'kitty -e' veya 'gnome-terminal --'.",
+ "label": "Terminal komutu"
+ },
+ "use-app2unit": {
+ "description": "Uygulama süreçlerini daha iyi yönetmek ve sorunları önlemek için alternatif bir başlatma yöntemi kullanır.",
+ "label": "Uygulamaları başlatmak için App2Unit kullan"
+ }
+ },
+ "title": "Başlatıcı"
+ },
+ "location": {
+ "date-time": {
+ "12hour-format": {
+ "description": "Zamanı kilit ekranında ve takvimde 12 saatlik formatta gösterir. Çubuk saatinin kendi ayarları vardır.",
+ "label": "12 saatlik zaman formatını kullan"
+ },
+ "first-day-of-week": {
+ "automatic": "Otomatik (sistem yerel ayarını kullan)",
+ "description": "Takvimde haftanın hangi günü başladığını seçin.",
+ "label": "Haftanın ilk günü"
+ },
+ "section": {
+ "description": "Tarih ve saatin nasıl görüneceğini özelleştirin.",
+ "label": "Tarih & Saat"
+ },
+ "show-events": {
+ "description": "Takvim panelinde etkinlikleri göster.",
+ "label": "Takvim etkinliklerini göster"
+ },
+ "use-analog": {
+ "description": "Takvim ekranında analog tarzı bir saat göster.",
+ "label": "Analog tarzı saat kullan"
+ },
+ "week-numbers": {
+ "description": "Takvimde yılın haftasını (örn., 38. Hafta) gösterir.",
+ "label": "Hafta numaralarını göster"
+ }
+ },
+ "location": {
+ "search": {
+ "description": "örn., İstanbul, TR",
+ "label": "Konum ara",
+ "placeholder": "Konum adını girin"
+ },
+ "section": {
+ "description": "Konumunuzu ayarlayarak hassas hava durumu ve gece ışığı zamanlaması alın.",
+ "label": "Konumunuz"
+ }
+ },
+ "title": "Konum",
+ "weather": {
+ "enabled": {
+ "description": "Arayüz boyunca hava durumu bilgilerini gösterin ve hava durumu verileri alın. Devre dışı bırakıldığında, tüm hava durumu öğeleri gizlenecek ve ağ istekleri yapılmayacaktır.",
+ "label": "Hava durumunu etkinleştir"
+ },
+ "fahrenheit": {
+ "description": "Sıcaklığı Celsius yerine Fahrenheit cinsinden gösterin.",
+ "label": "Sıcaklığı Fahrenheit (°F) cinsinden göster"
+ },
+ "section": {
+ "description": "Tercih ettiğiniz sıcaklık birimini seçin.",
+ "label": "Hava durumu"
+ },
+ "show-in-calendar": {
+ "description": "Günlük hava durumu tahminini doğrudan takvim görünümünüzde gösterin.",
+ "label": "Takvimde hava durumunu göster"
+ }
+ }
+ },
+ "lock-screen": {
+ "compact-lockscreen": {
+ "description": "Sadece giriş girdisini ve sistem kontrollerini göster, hava durumu ve ortam medya widget'larını gizle.",
+ "label": "Kompakt ekran kilidi"
+ },
+ "lock-on-suspend": {
+ "description": "Sistemi askıya alırken otomatik olarak ekranı kilitler.",
+ "label": "Askıya alırken kilitle"
+ },
+ "title": "Ekran Kilit"
+ },
+ "network": {
+ "bluetooth": {
+ "label": "Bluetooth'u Etkinleştir"
+ },
+ "section": {
+ "description": "Wi-Fi ve Bluetooth bağlantılarını yönetin."
+ },
+ "title": "Ağ",
+ "wifi": {
+ "label": "Wi-Fi'yi Etkinleştir"
+ }
+ },
+ "notifications": {
+ "duration": {
+ "critical-urgency": {
+ "description": "Kritik öncelikli bildirimlerin ne kadar süreyle görüneceği.",
+ "label": "Kritik öncelikli"
+ },
+ "low-urgency": {
+ "description": "Düşük öncelikli bildirimlerin ne kadar süreyle görüneceği.",
+ "label": "Düşük öncelikli"
+ },
+ "normal-urgency": {
+ "description": "Normal öncelikli bildirimlerin ne kadar süreyle görüneceği.",
+ "label": "Normal öncelikli"
+ },
+ "respect-expire": {
+ "description": "Bildirimde ayarlanan sona erme zaman aşımını kullanın.",
+ "label": "Sona erme zaman aşımına uyar"
+ },
+ "section": {
+ "description": "Bildirimlerin aciliyet seviyelerine göre ne kadar süreyle görüneceğini yapılandırın.",
+ "label": "Bildirim süresi"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Bildirimleri belirli ekranlarda gösterin. Hiçbiri seçilmezse varsayılan olarak tümünde gösterilir.",
+ "label": "Ekran görüntüsü"
+ }
+ },
+ "settings": {
+ "always-on-top": {
+ "description": "Bildirimleri tam ekran pencerelerinin ve diğer katmanların üzerinde görüntüle.",
+ "label": "Her zaman üstte"
+ },
+ "background-opacity": {
+ "description": "Bildirim arka planlarının opaklığını ayarlayın.",
+ "label": "Arka plan opaklığı"
+ },
+ "do-not-disturb": {
+ "description": "Etkinleştirildiğinde tüm bildirim açılır pencerelerini devre dışı bırakır.",
+ "label": "Rahatsız etme"
+ },
+ "enable-osd": {
+ "description": "Ses ve parlaklık değişikliklerini gerçek zamanlı olarak gösterin.",
+ "label": "Ekran görüntüsünü etkinleştir"
+ },
+ "enabled": {
+ "description": "Bildirim arka plan programını etkinleştir veya devre dışı bırak, noctalia-shell'in yeniden başlatılmasını gerektirir.",
+ "label": "Bildirimleri etkinleştir"
+ },
+ "location": {
+ "description": "Bildirimlerin ekranda nerede görüneceği.",
+ "label": "Konum"
+ },
+ "section": {
+ "description": "Bildirimlerin görünümünü ve davranışını yapılandırın.",
+ "label": "Görünüm"
+ }
+ },
+ "title": "Bildirimler"
+ },
+ "osd": {
+ "always-on-top": {
+ "description": "OSD'yi tam ekran pencerelerinin ve diğer katmanların üzerinde görüntüle.",
+ "label": "Her zaman üstte"
+ },
+ "description": "Ses ve parlaklık kaplamaları gibi ekran göstergelerini yapılandırın.",
+ "duration": {
+ "auto-hide": {
+ "description": "OSD'nin kaybolmadan önceki süreyi ayarlayın.",
+ "label": "Şu kadar sonra otomatik gizle"
+ },
+ "section": {
+ "description": "OSD'nin otomatik olarak gizlenmeden önce ne kadar süreyle görüneceği.",
+ "label": "Otomatik gizleme zaman aşımı"
+ }
+ },
+ "enabled": {
+ "description": "Ses ve parlaklık değişikliklerini gerçek zamanlı olarak gösterin.",
+ "label": "Ekran görüntüsünü etkinleştir"
+ },
+ "location": {
+ "description": "Ekran görüntülerinin nerede görüneceği.",
+ "label": "Konum"
+ },
+ "monitors": {
+ "section": {
+ "description": "OSD'yi belirli ekranlarda gösterin. Hiçbiri seçilmezse varsayılan olarak tümünde gösterilir.",
+ "label": "Ekran görüntüsü"
+ }
+ },
+ "section": {
+ "general": {
+ "description": "OSD'nin görünürlüğünü ve davranışını yapılandırın.",
+ "label": "Genel"
+ }
+ },
+ "title": "Ekran Görüntüsü"
+ },
+ "screen-recorder": {
+ "audio": {
+ "audio-codec": {
+ "description": "En iyi performans ve en küçük ses boyutu için Opus önerilir.",
+ "label": "Ses codec"
+ },
+ "audio-source": {
+ "description": "Kayıt sırasında yakalanacak ses kaynağı.",
+ "label": "Ses kaynağı"
+ },
+ "section": {
+ "description": "Ses kaydı seçeneklerini yapılandırın.",
+ "label": "Ses ayarları"
+ }
+ },
+ "general": {
+ "output-folder": {
+ "description": "Ekran kayıtlarının kaydedileceği klasör.",
+ "label": "Çıktı klasörü",
+ "tooltip": "Çıktı klasörüne göz at"
+ },
+ "section": {
+ "description": "Ekran kaydı çıktısını ve içeriğini yönetin.",
+ "label": "Genel ayarlar"
+ },
+ "select-output-folder": "Çıktı klasörünü seç",
+ "show-cursor": {
+ "description": "Video fare imlecini kaydedin.",
+ "label": "İmleci göster"
+ }
+ },
+ "title": "Ekran kaydedici",
+ "video": {
+ "color-range": {
+ "description": "Daha iyi uyumluluk için Sınırlı önerilir.",
+ "label": "Renk aralığı"
+ },
+ "frame-rate": {
+ "description": "Ekran kayıtları için hedef kare hızı.",
+ "label": "Kare hızı"
+ },
+ "section": {
+ "description": "Video kaydı seçeneklerini yapılandırın.",
+ "label": "Video ayarları"
+ },
+ "video-codec": {
+ "description": "h264 en yaygın codec'tir.",
+ "label": "Video codec"
+ },
+ "video-quality": {
+ "description": "Daha yüksek kalite daha büyük dosya boyutları ile sonuçlanır.",
+ "label": "Video kalitesi"
+ },
+ "video-source": {
+ "description": "Portal önerilir, eğer artefaktlar alırsanız Ekranı deneyin.",
+ "label": "Video kaynağı"
+ }
+ }
+ },
+ "user-interface": {
+ "animation-disable": {
+ "description": "Daha hızlı ve daha duyarlı bir deneyim için tüm animasyonları devre dışı bırakın.",
+ "label": "UI Animasyonlarını Devre Dışı Bırak"
+ },
+ "animation-speed": {
+ "description": "Genel animasyon hızını ayarlar.",
+ "label": "Animasyon hızı",
+ "reset": "Animasyon hızını sıfırla"
+ },
+ "border-radius": {
+ "description": "Pencerelerin, düğmelerin ve diğer elemanların köşe yuvarlaklığını kontrol eder.",
+ "label": "Kenar yarıçapı",
+ "reset": "Kenar yarıçapını sıfırla"
+ },
+ "dim-desktop": {
+ "description": "Paneller veya menüler açıkken masaüstünü karart.",
+ "label": "Masaüstünü karart"
+ },
+ "panels-attached-to-bar": {
+ "description": "Paneller çubuğa ve ekran kenarlarına kilitlenerek şık ters köşelerle kusursuz bir görünüm oluşturur.",
+ "label": "Kenarları Yakala"
+ },
+ "panels-overlay": {
+ "description": "Panellerin ve çubuğun, tam ekran uygulamalarda bile görünür kalmasını sağlar.",
+ "label": "Panelleri ve Çubuğu Üstte Tut"
+ },
+ "scaling": {
+ "description": "Çubuğu hariç tutarak genel kullanıcı arayüzünün boyutunu değiştirir.",
+ "label": "Arayüz ölçekleme",
+ "reset-scaling": "Arayüz ölçeklemesini sıfırla"
+ },
+ "section": {
+ "description": "Arayüzün görünümünü, hissini ve davranışını özelleştirin.",
+ "label": "Görünüm"
+ },
+ "settings-panel-attached-to-bar": {
+ "description": "Ayarlar penceresini çubukla hizalayarak tutarlı bir görünüm sağlayın.",
+ "label": "Ayarlar penceresini çubuğa hizala"
+ },
+ "shadows": {
+ "description": "Çubukların ve panellerin altında gölgelerin etkinleştirilmesini sağlar.",
+ "direction": {
+ "description": "Gölgenin hangi yönden verileceğini seçin.",
+ "label": "Gölge yönü"
+ },
+ "label": "Gölge efektleri"
+ },
+ "title": "Kullanıcı arayüzü",
+ "tooltips": {
+ "description": "Arayüz genelindeki ipuçlarını etkinleştirin veya devre dışı bırakın.",
+ "label": "İpuçlarını göster"
+ }
+ },
+ "wallpaper": {
+ "automation": {
+ "custom-interval": {
+ "description": "Zamanı SS:DD olarak girin (örn., 01:30).",
+ "label": "Özel aralık"
+ },
+ "interval": {
+ "description": "Duvar kağıtlarını otomatik olarak ne sıklıkla değiştireceğiniz.",
+ "label": "Duvar kağıdı aralığı"
+ },
+ "random-wallpaper": {
+ "description": "Düzenli aralıklarla rastgele duvar kağıdı değişiklikleri planlayın.",
+ "label": "Rastgele duvar kağıdı"
+ },
+ "section": {
+ "label": "Otomasyon"
+ }
+ },
+ "look-feel": {
+ "edge-smoothness": {
+ "description": "Geçişlerin kenarına yumuşak, tüylü bir efekt uygular.",
+ "label": "Geçiş kenarını yumuşat"
+ },
+ "fill-color": {
+ "description": "Duvar kağıdının arkasında görünebilecek bir dolgu rengi seçin.",
+ "label": "Doldurma rengi"
+ },
+ "fill-mode": {
+ "description": "Görüntünün ekran çözünürlüğünüzle eşleşmesi için nasıl ölçekleneceğini seçin.",
+ "label": "Doldurma modu"
+ },
+ "section": {
+ "label": "Görünüm ve his"
+ },
+ "transition-duration": {
+ "description": "Geçiş animasyonlarının saniye cinsinden süresi.",
+ "label": "Geçiş süresi"
+ },
+ "transition-type": {
+ "description": "Duvar kağıtları arasında geçiş yaparken animasyon türü.",
+ "label": "Geçiş türü"
+ }
+ },
+ "settings": {
+ "enable-management": {
+ "description": "Duvar kağıtlarını Noctalia ile yönetin. Başka bir uygulamayı kullanmayı tercih ediyorsanız işareti kaldırın.",
+ "label": "Duvar kağıdı yönetimini etkinleştir"
+ },
+ "enable-overview": {
+ "description": "Genel bakış ekranına bulanık ve karartılmış bir duvar kağıdı uygular.",
+ "label": "Genel bakış duvar kağıdını etkinleştir"
+ },
+ "folder": {
+ "description": "Ana duvar kağıdı klasörünüzün yolu.",
+ "label": "Duvar kağıdı klasörü",
+ "tooltip": "Duvar kağıdı klasörüne göz atın"
+ },
+ "monitor-specific": {
+ "description": "Her ekran için farklı bir duvar kağıdı klasörü ayarlayın.",
+ "label": "Ekran özgü dizinler",
+ "tooltip": "Duvar kağıdı klasörüne göz atın"
+ },
+ "recursive-search": {
+ "description": "Duvar kağıdı dizinin alt klasörlerinde de duvar kağıdı arayın.",
+ "label": "Alt klasörleri ara"
+ },
+ "section": {
+ "description": "Duvar kağıtlarının nasıl yönetileceğini ve görüntüleneceğini kontrol edin.",
+ "label": "Duvar kağıdı ayarları"
+ },
+ "select-folder": "Duvar kağıdı klasörünü seç",
+ "select-monitor-folder": "Ekran duvar kağıdı klasörünü seç",
+ "selector-position": {
+ "description": "Duvar kağıdı seçici panelinin nerede görüneceğini seçin.",
+ "label": "Pozisyon"
+ }
+ },
+ "title": "Duvar kağıdı"
+ }
+ },
+ "setup": {
+ "appearance": {
+ "header": "Görünüm",
+ "subheader": "Karanlık modu ve renk kaynaklarını seçin (Matugen veya önceden tanımlanmış)."
+ },
+ "customize": {
+ "header": "Deneyiminizi özelleştirin",
+ "subheader": "Bar konumunu, yoğunluğunu, ölçeğini ve daha fazlasını ayarlayın."
+ },
+ "wallpaper": {
+ "choose-dir": "Duvar kağıdı görüntülerinizi içeren bir dizin seçin",
+ "dir": {
+ "browse": "Duvar kağıdı klasörüne göz at",
+ "description": "Duvar kağıtlarınızı içeren klasörü seçin",
+ "label": "Duvar kağıdı dizini",
+ "select-title": "Duvar kağıdı klasörünü seç"
+ },
+ "header": "Duvar kağıdınızı seçin",
+ "no-dir": "Duvar kağıdı dizini seçilmedi",
+ "no-valid": "Şurada geçerli görüntü dosyası bulunamadı: {dir}",
+ "none-in-dir": "Dizinde duvar kağıdı bulunamadı",
+ "preview-error": "Görüntü yüklenemedi",
+ "select-prompt": "Aşağıdan bir duvar kağıdı seçin",
+ "subheader": "Güzel bir arka planla ortamı ayarlayın."
+ },
+ "welcome": {
+ "note": "Başlamanız için birkaç temel ayar - tam seçenekler Ayarlar'da"
+ }
+ },
+ "system": {
+ "cpu-temperature": "{temp}°C",
+ "disk-usage": "{percent}%",
+ "location-display": "{name} ({koordinatlar})",
+ "monitor-description": "{model} ({width}x{height} @ {scale}x)",
+ "no-media-player-detected": "Hiçbir medya oynatıcı algılanmadı",
+ "scaling-percentage": "{percentage}%",
+ "signal-strength": "{signal}%",
+ "unknown": "Bilinmeyen",
+ "unknown-app": "Bilinmeyen Uygulama",
+ "unknown-layout": "Bilinmeyen düzen",
+ "unknown-version": "Bilinmeyen sürüm",
+ "uptime": "Çalışma süresi: {uptime}",
+ "user-requested": "Kullanıcı istedi",
+ "welcome-back": "Hoş geldiniz,",
+ "widget-settings-title": "{widget} Ayarları"
+ },
+ "toast": {
+ "airplane-mode": {
+ "disabled": "Devre dışı",
+ "enabled": "Etkin",
+ "title": "Uçak modu"
+ },
+ "battery": {
+ "low": "Düşük batarya",
+ "low-desc": "Batarya % {percent}. Lütfen şarj bağlantısını yapın."
+ },
+ "battery-manager": {
+ "initial-setup": "Başlangıç kurulumu gerekli",
+ "install-failed": "Kurulum başarısız oldu",
+ "install-missing": "Gerekli dosyalar eksik",
+ "install-success": "Başarıyla kuruldu",
+ "install-unsupported": "Sistem desteklenmiyor",
+ "set-failed": "Batarya eşiği ayarlanamadı",
+ "set-success-desc": "Batarya eşiği % {percent} olarak ayarlandı",
+ "title": "Batarya eşiği",
+ "uninstall-failed": "Kaldırma başarısız oldu",
+ "uninstall-setup": "Kaldırılıyor, kimlik doğrulama gerekli",
+ "uninstall-success": "Başarıyla kaldırıldı"
+ },
+ "bluetooth": {
+ "disabled": "Devre dışı",
+ "enabled": "Etkin"
+ },
+ "clipboard": {
+ "unavailable": "Panoya geçmişi kullanılamıyor",
+ "unavailable-desc": "'cliphist' uygulaması kurulu değil. Lütfen panoya geçmişi özelliklerini kullanmak için kurun."
+ },
+ "do-not-disturb": {
+ "disabled": "'Rahatsız etme' devre dışı",
+ "disabled-desc": "Tüm bildirimler gösteriliyor.",
+ "enabled": "'Rahatsız etme' etin",
+ "enabled-desc": "Bu bildirimleri geçmişinizde bulacaksınız."
+ },
+ "internet": {
+ "limited": "İnternetsiz bağlandı"
+ },
+ "ipc": {
+ "powerpanel-deprecated": "PowerPanel artık SessionMenu olarak adlandırılıyor, bu IPC çağrısı yakında kullanımdan kaldırılacak. Lütfen \"ipc call sessionMenu toggle\" kullanın.",
+ "sidepanel-deprecated": "SidePanel artık ControlCenter olarak adlandırılıyor, bu IPC çağrısı yakında kullanımdan kaldırılacak. Lütfen \"ipc call controlCenter toggle\" kullanın."
+ },
+ "keep-awake": {
+ "disabled": "Devre dışı",
+ "enabled": "Etkin"
+ },
+ "kofi": {
+ "opened": "Ko-fi sayfası tarayıcınızda açıldı"
+ },
+ "missing-control-center": {
+ "description": "Kontrol Merkezi widget'ı çubuktan kaldırıldı. Çubuktan tekrar erişmek için widget'ı yeniden eklemeniz gerekecek. Çubuğa sağ tıklayarak da açabilirsiniz.",
+ "label": "Son Kontrol Merkezi widget'ı kaldırıldı"
+ },
+ "night-light": {
+ "disabled": "Devre dışı",
+ "enabled": "Etkin",
+ "forced": "Zorla etkinleştirme",
+ "normal": "Normal mod",
+ "not-installed": "wlsunset kurulu değil"
+ },
+ "noctalia-performance": {
+ "disabled": "Performans modu devre dışı bırakıldı",
+ "enabled": "Performans modu etkinleştirildi",
+ "label": "Noctalia performansı"
+ },
+ "power-profile": {
+ "changed": "Güç profili değişti",
+ "profile-name": "\"{profile}\""
+ },
+ "recording": {
+ "failed-general": "Kayıtçısı bir hata ile kapandı.",
+ "failed-gpu": "gpu-screen-recorder beklenmedik şekilde kapandı.",
+ "failed-start": "Kayıt başlatma başarısız oldu",
+ "no-portals": "Masaüstü portal çalışmıyor",
+ "no-portals-desc": "xdg-desktop-portal ve bir kompozitör portalı (wlr/hyprland/gnome/kde) başlatın.",
+ "not-installed": "gpu-screen-recorder kurulu değil",
+ "not-installed-desc": "Ekran kaydı özelliklerini kullanmak için lütfen gpu-screen-recorder kurun.",
+ "saved": "Kayıt kaydedildi",
+ "started": "Kayıt başladı",
+ "stopping": "Kayıt durduruluyor..."
+ },
+ "theming-processor-failed": {
+ "desc-generic": "Şablonlar işlenirken bir hatayla karşılaşıldı.",
+ "title-matugen": "Matugen şablon işleme başarısız oldu.",
+ "title-predefined": "Önceden tanımlanmış renk şeması işleme başarısız oldu."
+ },
+ "wallpaper-colors": {
+ "disabled": "Duvar kağıdı renkleri devre dışı",
+ "enabled": "Duvar kağıdı renkleri etkin",
+ "label": "Duvar kağıdı renkleri oluşturma",
+ "not-installed": "Matugen kurulu değil - duvar kağıdı renk çıkarmak için gerekli"
+ },
+ "wifi": {
+ "connected": "'{ssid}' ile bağlandı",
+ "disabled": "Devre dışı",
+ "disconnected": "'{ssid}' bağlantısı kesildi",
+ "enabled": "Etkin"
+ }
+ },
+ "tooltips": {
+ "add-widget": "Widget ekle",
+ "bluetooth-devices": "Bluetooth cihazları",
+ "brightness-at": "Parlaklık: %{brightness}\nAyarlar için sağ tık.\nParlaklığı değiştirmek için kaydırın.",
+ "cancel-timer": "Zamanlayıcıyı iptal et",
+ "clear-history": "Geçmişi temizle",
+ "click-to-start-recording": "Kaydı başlatmak için tıklayın",
+ "click-to-stop-recording": "Kaydı durdurmak için tıklayın",
+ "close": "Kapat",
+ "connect-disconnect-devices": "Bağlanmak için sol tık. Unutmak için sağ tık.",
+ "delete-notification": "Bildiriyi sil",
+ "disable-keep-awake": "Uyanık kalmayı devre dışı bırak",
+ "do-not-disturb-disabled": "'Rahatsız etme' devre dışı",
+ "do-not-disturb-enabled": "'Rahatsız etme' etkin",
+ "enable-keep-awake": "Uyanık kalmayı etkinleştir",
+ "forget-network": "Ağı unut",
+ "home": "Ana Sayfa",
+ "input-muted": "Giriş sessizliğini değiştir",
+ "keep-awake": "Uyanık kal",
+ "keyboard-layout": "{layout} klavye düzeni",
+ "manage-wifi": "Wi-Fi yönet",
+ "microphone-volume-at": "Mikrofon sesi %{volume}\nAyarlar için sol tık. Sessize almak için sağ tık.\nSesi değiştirmek için kaydırın.",
+ "move-to-center-section": "Orta bölüme taşı",
+ "move-to-left-section": "Sol bölüme taşı",
+ "move-to-right-section": "Sağ bölüme taşı",
+ "next-media": "Sonraki medya",
+ "next-month": "Sonraki ay",
+ "night-light-disabled": "Gece ışığı devre dışı.\nMod arasında geçiş yapmak için sol tık.\nAyarlara erişmek için sağ tık.",
+ "night-light-enabled": "Gece ışığı etkin.\nMod arasında geçiş yapmak için sol tık.\nAyarlara erişmek için sağ tık.",
+ "night-light-forced": "Gece ışığı zorla.\nMod arasında geçiş yapmak için sol tık.\nAyarlara erişmek için sağ tık.",
+ "night-light-not-installed": "Gece ışığı mevcut değil.\nwlsunset yüklü değil.",
+ "noctalia-performance-disabled": "Noctalia performans modu devre dışı.\nEtkinleştirmek için sol tıklayın.",
+ "noctalia-performance-enabled": "Noctalia performans modu etkin.\nDevre dışı bırakmak için sol tıklayın.",
+ "open-control-center": "Kontrol merkezini aç",
+ "open-notification-history-disable-dnd": "Bildirim geçmişini aç\n\"Rahatsız etmeyi\" devre dışı bırakmak için sağ tık.",
+ "open-notification-history-enable-dnd": "Bildirim geçmişini aç\n\"Rahatsız etmeyi\" etkinleştirmek için sağ tık.",
+ "open-settings": "Ayarları aç",
+ "open-tray-dropdown": "Tepsi açılır menüsünü aç",
+ "open-wallpaper-selector": "Duvar kağıdı seçiciyi aç",
+ "output-muted": "Çıkış sessizliğini değiştir",
+ "pause": "Duraklat",
+ "play": "Oynat",
+ "power-profile": "'{profile}' güç profili",
+ "previous-media": "Önceki medya",
+ "previous-month": "Önceki ay",
+ "refresh": "Yenile",
+ "refresh-devices": "Cihazları yenile",
+ "refresh-wallpaper-list": "Duvar kağıdı listesini yenile",
+ "remove-widget": "Widget kaldır",
+ "screen-recorder-not-installed": "Ekran kaydedici yüklü değil",
+ "session-menu": "Oturum Menüsü",
+ "set-power-profile": "\"{profile}\" güç profilini ayarla",
+ "start-screen-recording": "Ekran kaydını başlat",
+ "stop-screen-recording": "Ekran kaydını durdur",
+ "switch-to-dark-mode": "Koyu moda geç",
+ "switch-to-light-mode": "Açık moda geç",
+ "up": "Yukarı",
+ "volume-at": "Çıkış sesi %{volume}\nAyarlar için sol tık. Sessize almak için sağ tık.\nSesi değiştirmek için kaydırın.",
+ "wallpaper-selector": "Sol tık: Duvar kağıdı seçiciyi aç.\nSağ tık: Rastgele duvar kağıdı ayarla.",
+ "widget-settings": "Widget ayarları"
+ },
+ "wallpaper": {
+ "configure-directory": "Resimler içeren duvar kağıdı dizininizi yapılandırın.",
+ "fill-modes": {
+ "center": "Merkez",
+ "crop": "Kırp (Doldur)",
+ "fit": "Sığ (İçer)",
+ "stretch": "Ger"
+ },
+ "no-match": "Eşleşme bulunamadı.",
+ "no-wallpaper": "Duvar kağıdı bulunamadı.",
+ "panel": {
+ "apply-all-monitors": {
+ "description": "Seçilen duvar kağıdını tüm monitörlere aynı anda uygulayın.",
+ "label": "Tüm monitörler için uygula"
+ },
+ "search": "Ara:",
+ "title": "Duvar kağıdı seçici"
+ },
+ "transitions": {
+ "disc": "Disk",
+ "fade": "Belirsizleşme",
+ "none": "Hiçbiri",
+ "random": "Rastgele",
+ "stripes": "Çizgiler",
+ "wipe": "Silme"
+ },
+ "try-different-search": "Farklı bir arama sorgusu deneyin."
+ },
+ "weather": {
+ "clear-sky": "Açık hava",
+ "drizzle": "Yağmurlu",
+ "fog": "Sisli",
+ "mainly-clear": "Genellikle açık",
+ "overcast": "Bulutlu",
+ "partly-cloudy": "Parçalı bulutlu",
+ "rain-showers": "Sağanak yağmur",
+ "snow": "Karlı",
+ "thunderstorm": "Fırtına",
+ "unknown": "Bilinmiyor"
+ },
+ "widgets": {
+ "color-picker": {
+ "apply": "Uygula",
+ "brightness": "Parlaklık",
+ "cancel": "İptal",
+ "hex": {
+ "description": "Onaltılık renk kodu girin.",
+ "label": "Hex renk"
+ },
+ "palette": {
+ "description": "Önceden tanımlı renkler geniş yelpazesinden seçin.",
+ "label": "Palet"
+ },
+ "rgb": {
+ "description": "Kırmızı, yeşil, mavi ve parlaklık değerlerini ayarlayın.",
+ "label": "RGB değerleri"
+ },
+ "theme-colors": {
+ "description": "Temanızın renk paletine hızlı erişim.",
+ "label": "Tema renkleri"
+ },
+ "title": "Renk seçici"
+ },
+ "datetime-tokens": {
+ "ampm": {
+ "lowercase": "Küçük harfli am/pm",
+ "uppercase": "Büyük harfli AM/PM"
+ },
+ "common": {
+ "12hour-time-minutes": "12 saatlik zaman ile dakika",
+ "24hour-time-minutes": "24 saatlik zaman ile dakika",
+ "24hour-time-seconds": "24 saatlik zaman ile saniye",
+ "european-date": "Avrupa tarih formatı",
+ "iso-date": "ISO tarih formatı",
+ "us-date": "ABD tarih formatı",
+ "weekday-date": "Tarih ile hafta günü",
+ "weekday-month-day": "Hafta günü, ay ve gün"
+ },
+ "day": {
+ "abbreviated": "Kısaltılmış gün adı",
+ "full": "Tam gün adı",
+ "leading-zero": "Sıfır ile başlayan gün (01-31)",
+ "no-leading-zero": "Sıfır olmadan gün (1-31)"
+ },
+ "hour": {
+ "leading-zero": "Sıfır ile başlayan saat (00-23) - 24 saat formatı",
+ "no-leading-zero": "Sıfır olmadan saat (0-23) - 24 saat formatı"
+ },
+ "minute": {
+ "leading-zero": "Sıfır ile başlayan dakika (00-59)",
+ "no-leading-zero": "Sıfır olmadan dakika (0-59)"
+ },
+ "month": {
+ "abbreviated": "Kısaltılmış ay adı",
+ "full": "Tam ay adı",
+ "number-leading-zero": "Sıfır ile başlayan sayı olarak ay (01-12)",
+ "number-no-zero": "Sıfır olmadan sayı olarak ay (1-12)"
+ },
+ "second": {
+ "leading-zero": "Sıfır ile başlayan saniye (00-59)",
+ "no-leading-zero": "Sıfır olmadan saniye (0-59)"
+ },
+ "timezone": {
+ "abbreviation": "Saat dilimi kısaltması"
+ },
+ "year": {
+ "four-digit": "Dört haneli sayı olarak yıl",
+ "two-digit": "İki haneli sayı olarak yıl (00-99)"
+ }
+ },
+ "file-picker": {
+ "cancel": "İptal",
+ "search-placeholder": "Dosya ve klasörlerde ara...",
+ "select-current": "Mevcutı Seç",
+ "select-file": "Dosya Seç",
+ "select-folder": "Klasör Seç",
+ "title": "Dosya seçici"
+ },
+ "icon-picker": {
+ "apply": "Uygula",
+ "cancel": "İptal",
+ "search": {
+ "label": "Ara"
+ },
+ "title": "Simge seçici"
+ },
+ "text-input": {
+ "clear": "Temizle"
+ },
+ "tooltip": {
+ "placeholder": "Yer tutucu"
+ }
+ },
+ "wifi": {
+ "panel": {
+ "connect": "Bağlan",
+ "connected": "Bağlı",
+ "disabled": "Wi-Fi devre dışı",
+ "disconnect": "Bağlantıyı kes",
+ "disconnecting": "Bağlantı kesiliyor...",
+ "enable-message": "Mevcut ağları görmek için Wi-Fi'yi etkinleştirin.",
+ "enter-password": "Şifre girin...",
+ "forget": "Unut",
+ "forget-network": "Bu ağı unut?",
+ "forgetting": "Unutuluyor...",
+ "no-networks": "Ağ bulunamadı",
+ "password": "Şifre",
+ "saved": "Kaydedildi",
+ "scan-again": "Yeniden tara",
+ "searching": "Yakındaki ağlar aranıyor...",
+ "title": "Wi-Fi"
+ }
+ }
+}
diff --git a/Assets/Translations/uk-UA.json b/Assets/Translations/uk-UA.json
index babb0f4b..0ae53914 100644
--- a/Assets/Translations/uk-UA.json
+++ b/Assets/Translations/uk-UA.json
@@ -1,1244 +1,285 @@
{
- "settings": {
- "general": {
- "title": "Загальні",
- "launch-setup-wizard": "Запустити майстер налаштування",
- "profile": {
- "section": {
- "label": "Профіль",
- "description": "Редагуйте дані користувача та аватар."
- },
- "picture": {
- "label": "Фото профілю {user}",
- "description": "Ваше фото профілю, що відображається в інтерфейсі."
- },
- "select-avatar": "Вибрати зображення аватара"
- },
- "screen-corners": {
- "section": {
- "label": "Кути екрана",
- "description": "Налаштуйте заокруглення кутів екрана та візуальні ефекти."
- },
- "show-corners": {
- "label": "Показувати кути екрана",
- "description": "Відображати заокруглені кути на краях екрана."
- },
- "solid-black": {
- "label": "Суцільно чорні кути",
- "description": "Використовувати суцільний чорний колір замість кольору фону панелі."
- },
- "radius": {
- "label": "Радіус кутів екрана",
- "description": "Налаштуйте заокруглення кутів екрана.",
- "reset": "Скинути радіус кутів екрана"
- }
- },
- "fonts": {
- "reset-scaling": "Скинути масштаб",
- "section": {
- "label": "Шрифти",
- "description": "Виберіть шрифти, що використовуються в інтерфейсі."
- },
- "default": {
- "label": "Стандартний шрифт",
- "description": "Основний шрифт, що використовується в інтерфейсі.",
- "placeholder": "Виберіть стандартний шрифт...",
- "search-placeholder": "Пошук шрифту...",
- "scale": {
- "label": "Розмір стандартного шрифту",
- "description": "Збільшити або зменшити розмір звичайного тексту."
- }
- },
- "monospace": {
- "label": "Моноширинний шрифт",
- "description": "Моноширинний шрифт для відображення чисел та статистики.",
- "placeholder": "Виберіть моноширинний шрифт...",
- "search-placeholder": "Пошук моноширинного шрифту...",
- "scale": {
- "label": "Розмір моноширинного шрифту",
- "description": "Збільшити або зменшити розмір моноширинного тексту."
- }
- }
- },
- "language": {
- "section": {
- "label": "Мова",
- "description": "Виберіть бажану мову програми."
- },
- "select": {
- "label": "Мова програми",
- "description": "Виберіть мову інтерфейсу програми.",
- "auto-detect": "Автоматично"
- }
- }
- },
- "audio": {
- "title": "Аудіо",
- "volumes": {
- "section": {
- "label": "Гучність",
- "description": "Налаштуйте регулятори гучності та рівні звуку."
- },
- "output-volume": {
- "label": "Вихідна гучність",
- "description": "Системний рівень гучності."
- },
- "mute-output": {
- "label": "Вимкнути вихідний звук",
- "description": "Вимкнути основний аудіовихід системи."
- },
- "input-volume": {
- "label": "Вхідна гучність",
- "description": "Рівень гучності мікрофона."
- },
- "mute-input": {
- "label": "Вимкнути вхідний звук",
- "description": "Вимкнути стандартний аудіовхід (мікрофон)."
- },
- "step-size": {
- "label": "Крок зміни гучності",
- "description": "Налаштуйте крок зміни гучності (колесо миші, гарячі клавіші)."
- },
- "volume-overdrive": {
- "label": "Дозволити перегучність",
- "description": "Дозволити підвищення гучності понад 100%. Може не підтримуватись усім обладнанням."
- }
- },
- "devices": {
- "section": {
- "label": "Аудіопристрої",
- "description": "Налаштуйте доступні пристрої введення та виведення звуку."
- },
- "output-device": {
- "label": "Пристрій виведення",
- "description": "Виберіть бажаний пристрій виведення звуку."
- },
- "input-device": {
- "label": "Пристрій введення",
- "description": "Виберіть бажаний пристрій введення звуку."
- }
- },
- "media": {
- "section": {
- "label": "Медіаплеєри",
- "description": "Встановіть бажані та ігноровані медіапрограми."
- },
- "primary-player": {
- "label": "Основний плеєр",
- "description": "Введіть ключове слово для ідентифікації основного плеєра.",
- "placeholder": "напр. spotify, vlc, mpv"
- },
- "excluded-player": {
- "label": "Виключений плеєр",
- "description": "Додайте ключові слова для плеєрів, які система має ігнорувати. Кожне ключове слово на новому рядку.",
- "placeholder": "введіть підрядок та натисніть +"
- },
- "visualizer-type": {
- "label": "Тип візуалізації",
- "description": "Виберіть тип візуалізації для відтворення медіа"
- },
- "frame-rate": {
- "label": "Частота кадрів",
- "description": "Вищі значення плавніші, але споживають більше ресурсів."
- },
- "scrolling-title": {
- "label": "Прокрутка назви",
- "description": "Увімкнути безперервну прокрутку для довгих назв медіа"
- },
- "scrolling-speed": {
- "label": "Швидкість прокрутки",
- "description": "Час у секундах для прокрутки назви від початку до кінця"
- }
- }
- },
- "display": {
- "title": "Дисплей",
- "monitors": {
- "section": {
- "label": "Налаштування для кожного монітора",
- "description": "Налаштуйте яскравість для кожного дисплея."
- },
- "brightness": "Яскравість",
- "brightness-step": {
- "label": "Крок зміни яскравості",
- "description": "Налаштуйте крок зміни яскравості (колесо миші та гарячі клавіші)."
- },
- "enforce-minimum": {
- "label": "Примусова мінімальна яскравість (1%)",
- "description": "Вирішує проблему повного вимкнення підсвітки на деяких дисплеях при яскравості 0%."
- }
- },
- "night-light": {
- "section": {
- "label": "Нічне світло",
- "description": "Зменшіть випромінювання синього світла для кращого сну та зменшення втоми очей."
- },
- "enable": {
- "label": "Увімкнути нічне світло",
- "description": "Застосувати теплий колірний фільтр для зменшення випромінювання синього світла."
- },
- "temperature": {
- "label": "Колірна температура",
- "description": "Встановіть теплоту кольору для нічного та денного часу.",
- "night": "Ніч",
- "day": "День"
- },
- "auto-schedule": {
- "label": "Автоматичне планування",
- "description": "На основі часу заходу та сходу сонця в {location} - рекомендовано."
- },
- "manual-schedule": {
- "label": "Ручне планування",
- "description": "Встановіть власний час сходу та заходу сонця.",
- "sunrise": "Час сходу сонця",
- "sunset": "Час заходу сонця",
- "select-start": "Виберіть час початку",
- "select-stop": "Виберіть час зупинки"
- },
- "force-activation": {
- "label": "Примусова активація",
- "description": "Ігнорує розклад і негайно застосовує нічний фільтр."
- }
- }
- },
- "bar": {
- "title": "Панель",
- "appearance": {
- "section": {
- "label": "Зовнішній вигляд",
- "description": "Налаштуйте зовнішній вигляд та положення панелі."
- },
- "position": {
- "label": "Положення панелі",
- "description": "Виберіть, де розмістити панель на екрані."
- },
- "density": {
- "label": "Щільність панелі",
- "description": "Налаштуйте відступи панелі для компактного або просторого вигляду."
- },
- "background-opacity": {
- "label": "Непрозорість фону",
- "description": "Налаштуйте непрозорість фону панелі."
- },
- "show-capsule": {
- "label": "Показувати капсулу",
- "description": "Показувати фон віджетів."
- },
- "floating": {
- "label": "Плаваюча панель",
- "description": "Відображати панель як плаваючу 'таблетку'. Примітка: Це перемістить кути екрана до країв."
- },
- "margins": {
- "label": "Поля",
- "description": "Налаштуйте поля навколо плаваючої панелі.",
- "vertical": "Вертикальні",
- "horizontal": "Горизонтальні"
- }
- },
- "widgets": {
- "section": {
- "label": "Розміщення віджетів",
- "description": "Перетягуйте віджети для зміни порядку. Позначки вказують використання: [L]івий, [C]ентр, [R]правий."
- }
- },
- "monitors": {
- "section": {
- "label": "Відображення на моніторах",
- "description": "Показувати панель на певних моніторах. За замовчуванням на всіх, якщо не вибрано."
- }
- },
- "tray": {
- "blacklist": {
- "label": "Чорний список",
- "description": "Додайте правила виключення з трея, підтримує шаблони (*).",
- "placeholder": "напр., nm-applet, Fcitx*"
- }
- }
- },
- "dock": {
- "title": "Док",
- "enabled": {
- "label": "Увімкнути док",
- "description": "Показати або сховати док повністю"
- },
- "appearance": {
- "section": {
- "label": "Зовнішній вигляд",
- "description": "Налаштуйте поведінку та зовнішній вигляд дока."
- },
- "display": {
- "label": "Відображення",
- "description": "Виберіть, як поводиться док.",
- "always-visible": "Завжди видимий",
- "auto-hide": "Автоприховування",
- "exclusive": "Ексклюзивний"
- },
- "background-opacity": {
- "label": "Непрозорість фону",
- "description": "Налаштуйте непрозорість фону дока."
- },
- "floating-distance": {
- "label": "Відстань плавання дока",
- "description": "Встановіть відстань між доком і краєм екрана."
- },
- "icon-size": {
- "label": "Розмір дока",
- "description": "Налаштуйте загальний розмір дока."
- },
- "colorize-icons": {
- "label": "Розфарбувати значки",
- "description": "Застосувати кольори теми до значків програм у доці (тільки неактивні програми)."
- }
- },
- "monitors": {
- "section": {
- "label": "Відображення на моніторах",
- "description": "Показувати док на певних моніторах. За замовчуванням на всіх, якщо не вибрано."
- },
- "only-same-output": {
- "label": "Тільки програми з того ж виходу",
- "description": "Показувати тільки програми з виходу, де розташований док."
- }
- }
- },
- "launcher": {
- "title": "Запускач",
- "settings": {
- "section": {
- "label": "Зовнішній вигляд",
- "description": "Налаштуйте поведінку та зовнішній вигляд запускача."
- },
- "position": {
- "label": "Положення",
- "description": "Виберіть, де з'являється панель запускача."
- },
- "background-opacity": {
- "label": "Непрозорість фону",
- "description": "Налаштуйте непрозорість фону запускача."
- },
- "clipboard-history": {
- "label": "Увімкнути історію буфера обміну",
- "description": "Отримати доступ до раніше скопійованих елементів із запускача."
- },
- "sort-by-usage": {
- "label": "Сортувати за використанням",
- "description": "Коли увімкнено, часто запускані програми з'являються першими в списку."
- },
- "use-app2unit": {
- "label": "Використовувати App2Unit для запуску програм",
- "description": "Використовує альтернативний метод запуску для кращого управління процесами програм і запобігання проблемам."
- },
- "terminal-command": {
- "label": "Команда терміналу",
- "description": "Команда для запуску терміналу. Напр., 'kitty -e' або 'gnome-terminal --'."
- },
- "custom-launch-prefix": {
- "label": "Власний префікс запуску",
- "description": "Додати префікс до команд власним запускачем (напр., 'runapp' для інтеграції з systemd)."
- },
- "custom-launch-prefix-enabled": {
- "label": "Увімкнути власний префікс запуску",
- "description": "Використовувати власний префікс для запуску програм замість стандартного методу."
- }
- }
- },
- "notifications": {
- "title": "Сповіщення",
- "settings": {
- "section": {
- "label": "Зовнішній вигляд",
- "description": "Налаштуйте зовнішній вигляд та поведінку сповіщень."
- },
- "do-not-disturb": {
- "label": "Не турбувати",
- "description": "Вимкнути всі спливаючі сповіщення, коли увімкнено."
- },
- "enable-osd": {
- "label": "Увімкнути екранну індикацію",
- "description": "Показувати зміни гучності та яскравості в реальному часі."
- },
- "location": {
- "label": "Розташування",
- "description": "Де з'являються сповіщення на екрані."
- },
- "always-on-top": {
- "label": "Завжди зверху",
- "description": "Відображати сповіщення поверх повноекранних вікон та інших шарів."
- },
- "background-opacity": {
- "label": "Непрозорість фону",
- "description": "Налаштуйте непрозорість фону сповіщень."
- }
- },
- "duration": {
- "section": {
- "label": "Тривалість сповіщень",
- "description": "Налаштуйте, як довго сповіщення залишаються видимими залежно від рівня терміновості."
- },
- "respect-expire": {
- "label": "Дотримуватись часу закінчення",
- "description": "Використовувати час закінчення, встановлений у сповіщенні."
- },
- "low-urgency": {
- "label": "Низька терміновість",
- "description": "Як довго залишаються видимими сповіщення низького пріоритету."
- },
- "normal-urgency": {
- "label": "Звичайна терміновість",
- "description": "Як довго залишаються видимими сповіщення звичайного пріоритету."
- },
- "critical-urgency": {
- "label": "Критична терміновість",
- "description": "Як довго залишаються видимими критичні сповіщення."
- }
- },
- "monitors": {
- "section": {
- "label": "Відображення на моніторах",
- "description": "Показувати сповіщення на певних моніторах. За замовчуванням на всіх, якщо не вибрано."
- }
- }
- },
- "osd": {
- "title": "Екранна індикація",
- "description": "Налаштуйте екранні індикатори, такі як накладки гучності та яскравості.",
- "section": {
- "general": {
- "label": "Загальні",
- "description": "Налаштуйте видимість та поведінку екранної індикації."
- }
- },
- "enabled": {
- "label": "Увімкнути екранну індикацію",
- "description": "Показувати зміни гучності та яскравості в реальному часі."
- },
- "always-on-top": {
- "label": "Завжди зверху",
- "description": "Відображати екранну індикацію поверх повноекранних вікон та інших шарів."
- },
- "location": {
- "label": "Розташування",
- "description": "Де з'являються екранні індикації."
- },
- "duration": {
- "section": {
- "label": "Час автоприховування",
- "description": "Як довго екранна індикація залишається видимою перед автоматичним приховуванням."
- },
- "auto-hide": {
- "label": "Автоприховування через",
- "description": "Налаштуйте час до зникнення екранної індикації."
- }
- },
- "monitors": {
- "section": {
- "label": "Відображення на моніторах",
- "description": "Показувати екранну індикацію на певних моніторах. За замовчуванням на всіх, якщо не вибрано."
- }
- }
- },
- "wallpaper": {
- "title": "Шпалери",
- "settings": {
- "section": {
- "label": "Налаштування шпалер",
- "description": "Керуйте управлінням та відображенням шпалер."
- },
- "enable-management": {
- "label": "Увімкнути управління шпалерами",
- "description": "Керувати шпалерами за допомогою Noctalia. Вимкніть, якщо надаєте перевагу іншій програмі."
- },
- "folder": {
- "label": "Тека шпалер",
- "description": "Шлях до основної теки шпалер.",
- "tooltip": "Огляд теки шпалер"
- },
- "recursive-search": {
- "label": "Шукати в підтеках",
- "description": "Також шукати шпалери в підтеках директорії шпалер."
- },
- "monitor-specific": {
- "label": "Окремі теки для моніторів",
- "description": "Встановити різні теки шпалер для кожного монітора.",
- "tooltip": "Огляд теки шпалер"
- },
- "select-folder": "Вибрати теку шпалер",
- "select-monitor-folder": "Вибрати теку шпалер монітора"
- },
- "look-feel": {
- "section": {
- "label": "Вигляд і відчуття"
- },
- "fill-mode": {
- "label": "Режим заповнення",
- "description": "Виберіть, як зображення має масштабуватися відповідно до роздільної здатності монітора."
- },
- "fill-color": {
- "label": "Колір заповнення",
- "description": "Виберіть колір заповнення, що може з'являтися за шпалерами."
- },
- "transition-type": {
- "label": "Тип переходу",
- "description": "Тип анімації при перемиканні між шпалерами."
- },
- "transition-duration": {
- "label": "Тривалість переходу",
- "description": "Тривалість анімацій переходу в секундах."
- },
- "edge-smoothness": {
- "label": "Пом'якшення краю переходу",
- "description": "Застосовує м'який, розмитий ефект до краю переходів."
- }
- },
- "automation": {
- "section": {
- "label": "Автоматизація"
- },
- "random-wallpaper": {
- "label": "Випадкові шпалери",
- "description": "Планувати випадкову зміну шпалер через регулярні інтервали."
- },
- "interval": {
- "label": "Інтервал шпалер",
- "description": "Як часто автоматично змінювати шпалери."
- },
- "custom-interval": {
- "label": "Власний інтервал",
- "description": "Введіть час як ГГ:ХХ (напр., 01:30)."
- }
- }
- },
- "color-scheme": {
- "title": "Колірна схема",
- "color-source": {
- "section": {
- "label": "Джерело кольорів",
- "description": "Основні налаштування для кольорів Noctalia."
- },
- "use-wallpaper-colors": {
- "label": "Використовувати кольори шпалер",
- "description": "Генерувати колірні схеми зі шпалер за допомогою Matugen. Автоматично витягує кольори для створення цілісної теми."
- },
- "matugen-scheme-type": {
- "label": "Тип схеми Matugen",
- "description": {
- "scheme-content": "Виводить кольори, що тісно збігаються з базовим зображенням",
- "scheme-expressive": "Яскрава палітра з грайливою насиченістю",
- "scheme-fidelity": "Високоточна палітра, що зберігає вихідні відтінки",
- "scheme-fruit-salad": "Барвистий мікс яскравих контрастних акцентів",
- "scheme-monochrome": "Мінімальна палітра на основі одного відтінку",
- "scheme-neutral": "Приглушена палітра з стриманими та заспокійливими тонами",
- "scheme-rainbow": "Різноманітна палітра, що охоплює весь спектр",
- "scheme-tonal-spot": "Збалансована палітра з фокусованими акцентами"
- }
- }
- },
- "dark-mode": {
- "switch": {
- "label": "Темний режим",
- "description": "Перемикає на темнішу тему для зручного перегляду вночі."
- },
- "mode": {
- "label": "Розклад темного режиму",
- "description": "Увімкнути автоматичне перемикання між світлим і темним режимом.",
- "off": "Вимкнено",
- "manual": "Вручну",
- "location": "Розташування"
- }
- },
- "predefined": {
- "section": {
- "label": "Попередньо визначені колірні схеми",
- "description": "Виберіть із колекції попередньо визначених колірних схем."
- },
- "generate-templates": {
- "label": "Генерувати шаблони для попередньо визначених схем",
- "description": "Генерувати шаблони Matugen (GTK, теми терміналу тощо) при використанні попередньо визначених колірних схем."
- }
- },
- "templates": {
- "section": {
- "label": "Шаблони",
- "description": "Застосовувати кольори до зовнішніх програм."
- },
- "ui": {
- "label": "Інтерфейс",
- "description": "Оформлення робочого середовища та інструментарію інтерфейсу.",
- "gtk": {
- "description": "Записати {filepath}"
- },
- "qt": {
- "description": "Записати {filepath}"
- },
- "kcolorscheme": {
- "description": "Записати {filepath}"
- }
- },
- "terminal": {
- "label": "Термінал",
- "description": "Оформлення емулятора терміналу.",
- "kitty": {
- "description": "Записати {filepath} та перезавантажити",
- "description-missing": "Потрібна установка {app}"
- },
- "ghostty": {
- "description": "Записати {filepath} та перезавантажити",
- "description-missing": "Потрібна установка {app}"
- },
- "foot": {
- "description": "Записати {filepath} та перезавантажити",
- "description-missing": "Потрібна установка {app}"
- }
- },
- "programs": {
- "label": "Програми",
- "description": "Оформлення окремих програм.",
- "fuzzel": {
- "description": "Записати {filepath} та перезавантажити",
- "description-missing": "Потрібна установка {app}"
- },
- "vicinae": {
- "description": "Записати {filepath} та перезавантажити",
- "description-missing": "Потрібна установка {app}"
- },
- "walker": {
- "description": "Записати {filepath} та встановити тему noctalia",
- "description-missing": "Потрібна установка {app}"
- },
- "discord": {
- "description": "Записати {filepath} для {client}. Тему Hyprluna потрібно активувати вручну.",
- "description-missing": "Клієнт Discord не виявлено. Встановіть vencord, vesktop, webcord, armcord, equibop, lightcord або dorion."
- },
- "pywalfox": {
- "description": "Записати {filepath} та запустити pywalfox update",
- "description-missing": "Потрібна установка {app}"
- },
- "code": {
- "description": "Записати {filepath}. Тему Hyprluna потрібно встановити та активувати вручну.",
- "description-missing": "Потрібна установка {app}"
- }
- },
- "misc": {
- "label": "Різне",
- "description": "Додаткові параметри конфігурації.",
- "user-templates": {
- "label": "Користувацькі шаблони",
- "description": "Увімкнути визначений користувачем конфіг Matugen. Файл шаблону буде створено за адресою ~/.config/noctalia/user-templates.toml при першому увімкненні"
- }
- }
- }
- },
- "location": {
- "title": "Розташування",
- "location": {
- "section": {
- "label": "Ваше розташування",
- "description": "Отримуйте точну погоду та планування нічного світла, встановивши ваше розташування."
- },
- "search": {
- "label": "Шукати розташування",
- "description": "напр., Торонто, ОН",
- "placeholder": "Введіть назву місця"
- }
- },
- "weather": {
- "section": {
- "label": "Погода",
- "description": "Виберіть бажану одиницю температури."
- },
- "enabled": {
- "label": "Увімкнути погоду",
- "description": "Показувати інформацію про погоду в інтерфейсі та отримувати дані про погоду. Коли вимкнено, всі елементи погоди будуть приховані та мережеві запити не виконуватимуться."
- },
- "fahrenheit": {
- "label": "Відображати температуру в Фаренгейтах (°F)",
- "description": "Відображати температуру в Фаренгейтах замість Цельсія."
- },
- "show-in-calendar": {
- "label": "Відображати погоду в календарі",
- "description": "Показувати щоденний прогноз погоди безпосередньо в календарі."
- }
- },
- "date-time": {
- "section": {
- "label": "Дата і час",
- "description": "Налаштуйте, як відображаються дата та час."
- },
- "12hour-format": {
- "label": "Використовувати 12-годинний формат часу",
- "description": "Відображати час у 12-годинному форматі на екрані блокування та в календарі. Годинник на панелі має власні налаштування."
- },
- "week-numbers": {
- "label": "Показувати номери тижнів",
- "description": "Відображати номер тижня року (напр., Тиждень 38) у календарі."
- },
- "first-day-of-week": {
- "label": "Перший день тижня",
- "description": "Виберіть, який день починає тиждень у календарі.",
- "automatic": "Автоматично (використовувати системну локаль)"
- },
- "show-events": {
- "label": "Показувати події календаря",
- "description": "Відображати події на панелі календаря."
- },
- "use-analog": {
- "label": "Використовувати аналоговий годинник",
- "description": "Показувати аналоговий годинник на екрані календаря."
- }
- }
- },
- "network": {
- "title": "Мережа",
- "section": {
- "description": "Керувати підключеннями Wi-Fi та Bluetooth."
- },
- "wifi": {
- "label": "Увімкнути Wi-Fi"
- },
- "bluetooth": {
- "label": "Увімкнути Bluetooth"
- }
- },
- "screen-recorder": {
- "title": "Запис екрана",
- "general": {
- "section": {
- "label": "Загальні налаштування",
- "description": "Керуйте виведенням та вмістом запису екрана."
- },
- "output-folder": {
- "label": "Тека виведення",
- "description": "Тека, де зберігатимуться записи екрана.",
- "tooltip": "Огляд теки виведення"
- },
- "show-cursor": {
- "label": "Показувати курсор",
- "description": "Записувати курсор миші у відео."
- },
- "select-output-folder": "Вибрати теку виведення"
- },
- "video": {
- "section": {
- "label": "Налаштування відео",
- "description": "Налаштуйте параметри запису відео."
- },
- "video-source": {
- "label": "Джерело відео",
- "description": "Рекомендується Portal, якщо виникають артефакти, спробуйте Screen."
- },
- "frame-rate": {
- "label": "Частота кадрів",
- "description": "Цільова частота кадрів для записів екрана."
- },
- "video-quality": {
- "label": "Якість відео",
- "description": "Вища якість призводить до більшого розміру файлів."
- },
- "video-codec": {
- "label": "Кодек відео",
- "description": "h264 є найпоширенішим кодеком."
- },
- "color-range": {
- "label": "Діапазон кольорів",
- "description": "Обмежений рекомендується для кращої сумісності."
- }
- },
- "audio": {
- "section": {
- "label": "Налаштування аудіо",
- "description": "Налаштуйте параметри запису аудіо."
- },
- "audio-source": {
- "label": "Джерело аудіо",
- "description": "Джерело аудіо для захоплення під час запису."
- },
- "audio-codec": {
- "label": "Кодек аудіо",
- "description": "Opus рекомендується для найкращої продуктивності та найменшого розміру аудіо."
- }
- }
- },
- "about": {
- "title": "Про програму",
- "noctalia": {
- "section": {
- "label": "Оболонка Noctalia",
- "description": "Елегантна та мінімалістична оболонка робочого столу, ретельно створена для Wayland, побудована на Quickshell."
- },
- "latest-version": "Остання версія:",
- "installed-version": "Встановлена версія:",
- "download-latest": "Завантажити останній реліз"
- },
- "contributors": {
- "section": {
- "label": "Учасники",
- "description": "Вітаємо нашого {count} чудового учасника!",
- "description_plural": "Вітаємо наших {count} чудових учасників!"
- }
- },
- "support": "Підтримати нас"
- },
- "hooks": {
- "title": "Хуки",
- "system-hooks": {
- "section": {
- "label": "Системні хуки",
- "description": "Налаштуйте команди для виконання при системних подіях."
- },
- "enable": {
- "label": "Увімкнути хуки",
- "description": "Увімкнути або вимкнути всі команди хуків."
- }
- },
- "wallpaper-changed": {
- "label": "Шпалери змінено",
- "description": "Команда для виконання при зміні шпалер.",
- "placeholder": "напр., notify-send \"Шпалери\" \"Змінено\""
- },
- "theme-changed": {
- "label": "Тему змінено",
- "description": "Команда для виконання при перемиканні теми між темним і світлим режимом.",
- "placeholder": "напр., notify-send \"Тема\" \"Перемкнуто\""
- },
- "info": {
- "command-info": {
- "label": "Інформація про команди хуків",
- "description": "• Команди виконуються через оболонку (sh -c)\n• Команди запускаються у фоні (відокремлено)\n• Кнопки тестування виконують з поточними значеннями"
- },
- "parameters": {
- "label": "Доступні параметри",
- "description": "• Хук шпалер: $1 = шлях до шпалер, $2 = назва екрана\n• Хук перемикання теми: $1 = true/false (стан темного режиму)"
- }
- }
- },
- "control-center": {
- "title": "Центр керування",
- "section": {
- "label": "Зовнішній вигляд",
- "description": "Налаштуйте положення та поведінку панелі Центру керування."
- },
- "position": {
- "label": "Положення",
- "description": "Виберіть, де з'являється панель Центру керування при відкритті."
- },
- "cards": {
- "section": {
- "label": "Картки",
- "description": "Налаштуйте, які елементи керування з'являються в Центрі керування та в якому порядку."
- }
- },
- "shortcuts": {
- "section": {
- "label": "Віджети швидкого доступу",
- "description": "Налаштуйте та керуйте віджетами швидкого доступу."
- },
- "sectionLeft": "Лівий",
- "sectionRight": "Правий"
- }
- },
- "user-interface": {
- "title": "Користувацький інтерфейс",
- "section": {
- "label": "Зовнішній вигляд",
- "description": "Налаштуйте вигляд, відчуття та поведінку інтерфейсу."
- },
- "tooltips": {
- "label": "Показувати підказки",
- "description": "Увімкнути або вимкнути підказки в інтерфейсі."
- },
- "scaling": {
- "label": "Масштабування інтерфейсу",
- "description": "Змінює розмір загального користувацького інтерфейсу, окрім панелі.",
- "reset-scaling": "Скинути масштабування інтерфейсу"
- },
- "border-radius": {
- "label": "Радіус меж",
- "description": "Керує заокругленістю кутів вікон, кнопок та інших елементів.",
- "reset": "Скинути радіус меж"
- },
- "animation-speed": {
- "label": "Швидкість анімації",
- "description": "Налаштувати глобальну швидкість анімації.",
- "reset": "Скинути швидкість анімації"
- },
- "animation-disable": {
- "label": "Вимкнути анімації інтерфейсу",
- "description": "Вимкнути всі анімації для швидшого, більш відгукового досвіду."
- },
- "panels-attached-to-bar": {
- "label": "Прикріпити панелі до панелі",
- "description": "Коли увімкнено, панелі будуть прикріплені до панелі з красивим дизайном інвертованих кутів"
- },
- "panels-overlay": {
- "label": "Відкривати панелі на шарі накладення",
- "description": "Панелі з'являтимуться поверх повноекранних вікон"
- }
- },
- "lock-screen": {
- "title": "Екран блокування",
- "compact-lockscreen": {
- "label": "Компактний екран блокування",
- "description": "Показувати тільки поле входу та системні елементи керування, приховуючи віджети погоди та медіа."
- },
- "lock-on-suspend": {
- "label": "Блокувати при призупиненні",
- "description": "Автоматично блокувати екран при призупиненні системи."
- }
- }
- },
- "widgets": {
- "tooltip": {
- "placeholder": "Заповнювач"
- },
- "file-picker": {
- "title": "Вибір файлу",
- "select-folder": "Вибрати теку",
- "select-file": "Вибрати файл",
- "search-placeholder": "Пошук файлів та тек...",
- "select-current": "Вибрати поточне",
- "cancel": "Скасувати"
- },
- "datetime-tokens": {
- "common": {
- "12hour-time-minutes": "12-годинний час з хвилинами",
- "24hour-time-minutes": "24-годинний час з хвилинами",
- "24hour-time-seconds": "24-годинний час з секундами",
- "weekday-month-day": "День тижня, місяць і день",
- "iso-date": "Формат дати ISO",
- "us-date": "Формат дати США",
- "european-date": "Європейський формат дати",
- "weekday-date": "День тижня з датою"
- },
- "hour": {
- "no-leading-zero": "Година без нуля попереду (0-23) - 24-годинний формат",
- "leading-zero": "Година з нулем попереду (00-23) - 24-годинний формат"
- },
- "minute": {
- "no-leading-zero": "Хвилина без нуля попереду (0-59)",
- "leading-zero": "Хвилина з нулем попереду (00-59)"
- },
- "second": {
- "no-leading-zero": "Секунда без нуля попереду (0-59)",
- "leading-zero": "Секунда з нулем попереду (00-59)"
- },
- "ampm": {
- "uppercase": "AM/PM великими літерами",
- "lowercase": "am/pm малими літерами"
- },
- "timezone": {
- "abbreviation": "Абревіатура часового поясу"
- },
- "year": {
- "two-digit": "Рік як двозначне число (00-99)",
- "four-digit": "Рік як чотиризначне число"
- },
- "month": {
- "number-no-zero": "Місяць як число без нуля попереду (1-12)",
- "number-leading-zero": "Місяць як число з нулем попереду (01-12)",
- "abbreviated": "Скорочена назва місяця",
- "full": "Повна назва місяця"
- },
- "day": {
- "no-leading-zero": "День без нуля попереду (1-31)",
- "leading-zero": "День з нулем попереду (01-31)",
- "abbreviated": "Скорочена назва дня",
- "full": "Повна назва дня"
- }
- },
- "icon-picker": {
- "title": "Вибір значка",
- "search": {
- "label": "Пошук"
- },
- "cancel": "Скасувати",
- "apply": "Застосувати"
- },
- "color-picker": {
- "title": "Вибір кольору",
- "hex": {
- "label": "Hex-колір",
- "description": "Введіть шістнадцятковий код кольору."
- },
- "rgb": {
- "label": "Значення RGB",
- "description": "Налаштуйте значення червоного, зеленого, синього та яскравості."
- },
- "brightness": "Яскравість",
- "theme-colors": {
- "label": "Кольори теми",
- "description": "Швидкий доступ до колірної палітри вашої теми."
- },
- "palette": {
- "label": "Палітра",
- "description": "Виберіть із широкого діапазону попередньо визначених кольорів."
- },
- "cancel": "Скасувати",
- "apply": "Застосувати"
- },
- "text-input": {
- "clear": "Очистити"
- }
+ "authentication": {
+ "error": "Помилка автентифікації",
+ "failed": "Помилка автентифікації"
},
"bar": {
"widget-settings": {
+ "active-window": {
+ "colorize-icons": {
+ "description": "Застосувати кольори теми до значка активного вікна.",
+ "label": "Розфарбувати значки"
+ },
+ "hide-mode": {
+ "description": "Керує поведінкою віджета, коли немає активного вікна.",
+ "label": "Режим приховування"
+ },
+ "max-width": {
+ "description": "Встановлює максимальний горизонтальний розмір віджета. Віджет зменшується для коротшого вмісту.",
+ "label": "Максимальна ширина"
+ },
+ "scrolling-mode": {
+ "description": "Керування, коли увімкнено прокрутку тексту для довгих заголовків вікон.",
+ "label": "Режим прокрутки"
+ },
+ "show-app-icon": {
+ "description": "Відображати значок програми біля заголовка вікна.",
+ "label": "Показувати значок програми"
+ },
+ "use-fixed-width": {
+ "description": "Коли увімкнено, віджет завжди використовуватиме максимальну ширину замість динамічного налаштування до вмісту.",
+ "label": "Використовувати фіксовану ширину"
+ }
+ },
+ "audio-visualizer": {
+ "color-name": {
+ "description": "Вибрати колір для візуалізатора.",
+ "label": "Колір заповнення"
+ },
+ "hide-when-idle": {
+ "description": "Коли увімкнено, візуалізатор приховано, якщо плеєр активно не відтворює.",
+ "label": "Приховати, коли медіа не відтворюється"
+ },
+ "width": {
+ "description": "Власна ширина компонента.",
+ "label": "Ширина"
+ }
+ },
+ "battery": {
+ "display-mode": {
+ "description": "Виберіть, як ви хочете, щоб це значення відображалося.",
+ "label": "Режим відображення"
+ },
+ "low-battery-threshold": {
+ "description": "Показувати попередження, коли батарея падає нижче цього відсотка.",
+ "label": "Поріг попередження про низький заряд батареї"
+ }
+ },
+ "brightness": {
+ "display-mode": {
+ "description": "Виберіть, як ви хочете, щоб це значення відображалося.",
+ "label": "Режим відображення"
+ }
+ },
+ "clock": {
+ "clock-display": {
+ "description": "Налаштуйте відображення годинника, додавши токени зі списку нижче. Для використання 12-годинного формату ви повинні включити токен 'AP'.",
+ "label": "Відображення годинника"
+ },
+ "custom-font": {
+ "description": "Вибрати власний шрифт для відображення годинника.",
+ "label": "Власний шрифт",
+ "placeholder": "Вибрати власний шрифт...",
+ "search-placeholder": "Пошук шрифтів..."
+ },
+ "horizontal-bar": {
+ "description": "Порада: Використовуйте \\n для створення розриву рядка.",
+ "label": "Горизонтальна панель"
+ },
+ "preview": "Попередній перегляд",
+ "use-custom-font": {
+ "description": "Замінити стандартний вибір шрифту власним шрифтом для годинника.",
+ "label": "Використовувати власний шрифт"
+ },
+ "use-monospaced-font": {
+ "description": "Коли увімкнено, годинник використовуватиме моноширинний шрифт.",
+ "label": "Використовувати моноширинний шрифт"
+ },
+ "use-primary-color": {
+ "description": "Коли увімкнено, застосовується основний колір для акценту.",
+ "label": "Використовувати основний колір"
+ },
+ "vertical-bar": {
+ "description": "Використовуйте пробіл для розділення кожної частини на новий рядок.",
+ "label": "Вертикальна панель"
+ }
+ },
+ "control-center": {
+ "browse-file": "Огляд файлу",
+ "browse-library": "Огляд бібліотеки",
+ "icon": {
+ "description": "Вибрати значок з бібліотеки або власний файл.",
+ "label": "Значок"
+ },
+ "select-custom-icon": "Вибрати власний значок",
+ "use-distro-logo": {
+ "description": "Використовувати логотип вашого дистрибутива замість власного значка.",
+ "label": "Використовувати логотип дистрибутива замість значка"
+ },
+ "colorize-distro-logo": {
+ "description": "Застосувати кольори теми до логотипа вашого дистрибутива.",
+ "label": "Розфарбовувати логотип дистрибутива"
+ }
+ },
+ "custom-button": {
+ "browse": "Огляд",
+ "collapse-condition": {
+ "description": "Якщо вихідний текст збігається з цим значенням, кнопка згорнеться.",
+ "label": "Умова згортання"
+ },
+ "display-command-output": {
+ "description": "Введіть команду для запуску з регулярним інтервалом. Перший рядок її виведення відображатиметься як текст.",
+ "label": "Відображати виведення команди",
+ "stream-description": "Введіть команду для безперервного запуску."
+ },
+ "dynamic-text": "Динамічний текст",
+ "hide-vertical": {
+ "description": "Якщо увімкнено, текст з виводу команди не відображатиметься, коли панель знаходиться у вертикальному розташуванні (ліворуч або праворуч).",
+ "label": "Приховати текст у вертикальній панелі"
+ },
+ "icon": {
+ "description": "Вибрати значок з бібліотеки.",
+ "label": "Значок"
+ },
+ "left-click": {
+ "description": "Команда для виконання при лівому кліку на кнопку.",
+ "label": "Лівий клік"
+ },
+ "middle-click": {
+ "description": "Команда для виконання при середньому кліку на кнопку.",
+ "label": "Середній клік"
+ },
+ "parse-json": {
+ "description": "Розбирати виведення команди як JSON-об'єкт для динамічного встановлення тексту та значка.",
+ "label": "Розбирати виведення як JSON"
+ },
+ "refresh-interval": {
+ "description": "Інтервал у мілісекундах.",
+ "label": "Інтервал оновлення"
+ },
+ "right-click": {
+ "description": "Команда для виконання при правому кліку на кнопку.",
+ "label": "Правий клік"
+ },
+ "text-stream": {
+ "description": "Потокові рядки з команди відображатимуться як текст на кнопці.",
+ "label": "Потік"
+ }
+ },
"dialog": {
- "cancel": "Скасувати",
- "apply": "Застосувати"
+ "apply": "Застосувати",
+ "cancel": "Скасувати"
+ },
+ "keyboard-layout": {
+ "display-mode": {
+ "description": "Виберіть, як ви хочете, щоб це значення відображалося.",
+ "label": "Режим відображення"
+ }
+ },
+ "lock-keys": {
+ "browse": "Перегляд",
+ "show-caps-lock": {
+ "description": "Відображати стан caps lock.",
+ "label": "Caps Lock"
+ },
+ "show-num-lock": {
+ "description": "Відображати стан num lock.",
+ "label": "Num Lock"
+ },
+ "show-scroll-lock": {
+ "description": "Відображати стан scroll lock.",
+ "label": "Scroll Lock"
+ }
+ },
+ "media-mini": {
+ "hide-mode": {
+ "description": "Керує поведінкою віджета, коли медіа не відтворюється.",
+ "label": "Режим приховування"
+ },
+ "max-width": {
+ "description": "Встановлює максимальний горизонтальний розмір віджета. Віджет зменшується для коротшого вмісту.",
+ "label": "Максимальна ширина"
+ },
+ "no-active-player": "Немає активного плеєра",
+ "scrolling-mode": {
+ "description": "Керування, коли увімкнено прокрутку тексту для довгих назв треків.",
+ "label": "Режим прокрутки"
+ },
+ "show-album-art": {
+ "description": "Відображати обкладинку альбому для поточного треку.",
+ "label": "Показувати обкладинку альбому"
+ },
+ "show-visualizer": {
+ "description": "Відображати аудіовізуалізатор під час відтворення музики.",
+ "label": "Показувати візуалізатор"
+ },
+ "use-fixed-width": {
+ "description": "Коли увімкнено, віджет завжди використовуватиме максимальну ширину замість динамічного налаштування до вмісту.",
+ "label": "Використовувати фіксовану ширину"
+ },
+ "visualizer-type": {
+ "description": "Виберіть стиль аудіовізуалізатора для відображення.",
+ "label": "Тип візуалізатора"
+ }
+ },
+ "microphone": {
+ "display-mode": {
+ "description": "Виберіть, як ви хочете, щоб це значення відображалося.",
+ "label": "Режим відображення"
+ }
+ },
+ "notification-history": {
+ "hide-badge-when-zero": {
+ "description": "Приховувати значок сповіщень, коли немає непрочитаних сповіщень.",
+ "label": "Приховувати значок при нулі"
+ },
+ "show-unread-badge": {
+ "description": "Відображати значок з кількістю непрочитаних сповіщень.",
+ "label": "Показувати значок непрочитаних"
+ }
},
"section-editor": {
"placeholder": "Виберіть віджет для додавання...",
"search-placeholder": "Пошук віджета..."
},
- "active-window": {
- "hide-mode": {
- "label": "Режим приховування",
- "description": "Керує поведінкою віджета, коли немає активного вікна."
- },
- "show-app-icon": {
- "label": "Показувати значок програми",
- "description": "Відображати значок програми біля заголовка вікна."
- },
- "scrolling-mode": {
- "label": "Режим прокрутки",
- "description": "Керування, коли увімкнено прокрутку тексту для довгих заголовків вікон."
- },
- "max-width": {
- "label": "Максимальна ширина",
- "description": "Встановлює максимальний горизонтальний розмір віджета. Віджет зменшується для коротшого вмісту."
- },
- "use-fixed-width": {
- "label": "Використовувати фіксовану ширину",
- "description": "Коли увімкнено, віджет завжди використовуватиме максимальну ширину замість динамічного налаштування до вмісту."
- },
- "colorize-icons": {
- "label": "Розфарбувати значки",
- "description": "Застосувати кольори теми до значка активного вікна."
+ "spacer": {
+ "width": {
+ "description": "Ширина відступу в пікселях",
+ "label": "Ширина"
}
},
"system-monitor": {
- "cpu-usage": {
- "label": "Використання ЦП",
- "description": "Відображати поточний відсоток використання ЦП."
- },
"cpu-temperature": {
- "label": "Температура ЦП",
- "description": "Показувати показники температури ЦП, якщо доступно."
+ "description": "Показувати показники температури ЦП, якщо доступно.",
+ "label": "Температура ЦП"
},
- "memory-usage": {
- "label": "Використання пам'яті",
- "description": "Відображати інформацію про поточне використання оперативної пам'яті."
+ "cpu-usage": {
+ "description": "Відображати поточний відсоток використання ЦП.",
+ "label": "Використання ЦП"
},
"memory-percentage": {
- "label": "Пам'ять у відсотках",
- "description": "Показувати використання пам'яті у відсотках замість абсолютних значень."
+ "description": "Показувати використання пам'яті у відсотках замість абсолютних значень.",
+ "label": "Пам'ять у відсотках"
+ },
+ "memory-usage": {
+ "description": "Відображати інформацію про поточне використання оперативної пам'яті.",
+ "label": "Використання пам'яті"
},
"network-traffic": {
- "label": "Мережевий трафік",
- "description": "Відображати швидкість завантаження та вивантаження в мережі."
+ "description": "Відображати швидкість завантаження та вивантаження в мережі.",
+ "label": "Мережевий трафік"
},
"storage-usage": {
- "label": "Використання сховища",
- "description": "Показувати інформацію про використання дискового простору."
+ "description": "Показувати інформацію про використання дискового простору.",
+ "label": "Використання сховища"
}
},
- "notification-history": {
- "show-unread-badge": {
- "label": "Показувати значок непрочитаних",
- "description": "Відображати значок з кількістю непрочитаних сповіщень."
- },
- "hide-badge-when-zero": {
- "label": "Приховувати значок при нулі",
- "description": "Приховувати значок сповіщень, коли немає непрочитаних сповіщень."
- }
- },
- "battery": {
- "display-mode": {
- "label": "Режим відображення",
- "description": "Виберіть, як ви хочете, щоб це значення відображалося."
- },
- "low-battery-threshold": {
- "label": "Поріг попередження про низький заряд батареї",
- "description": "Показувати попередження, коли батарея падає нижче цього відсотка."
- }
- },
- "control-center": {
- "use-distro-logo": {
- "label": "Використовувати логотип дистрибутива замість значка",
- "description": "Використовувати логотип вашого дистрибутива замість власного значка."
- },
- "icon": {
- "label": "Значок",
- "description": "Вибрати значок з бібліотеки або власний файл."
- },
- "browse-library": "Огляд бібліотеки",
- "browse-file": "Огляд файлу",
- "select-custom-icon": "Вибрати власний значок"
- },
- "keyboard-layout": {
- "display-mode": {
- "label": "Режим відображення",
- "description": "Виберіть, як ви хочете, щоб це значення відображалося."
- }
- },
- "volume": {
- "display-mode": {
- "label": "Режим відображення",
- "description": "Виберіть, як ви хочете, щоб це значення відображалося."
- }
- },
- "workspace": {
- "label-mode": {
- "label": "Режим міток",
- "description": "Виберіть, як відображаються мітки робочих просторів."
- },
- "hide-unoccupied": {
- "label": "Приховати незайняті",
- "description": "Не відображати робочі простори без вікон."
- },
- "character-count": {
- "label": "Кількість символів",
- "description": "Кількість символів для відображення з назв робочих просторів (1-10)."
- }
- },
- "microphone": {
- "display-mode": {
- "label": "Режим відображення",
- "description": "Виберіть, як ви хочете, щоб це значення відображалося."
- }
- },
- "brightness": {
- "display-mode": {
- "label": "Режим відображення",
- "description": "Виберіть, як ви хочете, щоб це значення відображалося."
- }
- },
- "spacer": {
- "width": {
- "label": "Ширина",
- "description": "Ширина відступу в пікселях"
- }
- },
- "custom-button": {
- "icon": {
- "label": "Значок",
- "description": "Вибрати значок з бібліотеки."
- },
- "browse": "Огляд",
- "left-click": {
- "label": "Лівий клік",
- "description": "Команда для виконання при лівому кліку на кнопку."
- },
- "right-click": {
- "label": "Правий клік",
- "description": "Команда для виконання при правому кліку на кнопку."
- },
- "middle-click": {
- "label": "Середній клік",
- "description": "Команда для виконання при середньому кліку на кнопку."
- },
- "dynamic-text": "Динамічний текст",
- "text-stream": {
- "label": "Потік",
- "description": "Потокові рядки з команди відображатимуться як текст на кнопці."
- },
- "display-command-output": {
- "label": "Відображати виведення команди",
- "description": "Введіть команду для запуску з регулярним інтервалом. Перший рядок її виведення відображатиметься як текст.",
- "stream-description": "Введіть команду для безперервного запуску."
- },
- "refresh-interval": {
- "label": "Інтервал оновлення",
- "description": "Інтервал у мілісекундах."
- },
- "collapse-condition": {
- "label": "Умова згортання",
- "description": "Якщо вихідний текст збігається з цим значенням, кнопка згорнеться."
- },
- "parse-json": {
- "label": "Розбирати виведення як JSON",
- "description": "Розбирати виведення команди як JSON-об'єкт для динамічного встановлення тексту та значка."
- }
- },
- "media-mini": {
- "hide-mode": {
- "label": "Режим приховування",
- "description": "Керує поведінкою віджета, коли медіа не відтворюється."
- },
- "show-album-art": {
- "label": "Показувати обкладинку альбому",
- "description": "Відображати обкладинку альбому для поточного треку."
- },
- "show-visualizer": {
- "label": "Показувати візуалізатор",
- "description": "Відображати аудіовізуалізатор під час відтворення музики."
- },
- "visualizer-type": {
- "label": "Тип візуалізатора",
- "description": "Виберіть стиль аудіовізуалізатора для відображення."
- },
- "max-width": {
- "label": "Максимальна ширина",
- "description": "Встановлює максимальний горизонтальний розмір віджета. Віджет зменшується для коротшого вмісту."
- },
- "use-fixed-width": {
- "label": "Використовувати фіксовану ширину",
- "description": "Коли увімкнено, віджет завжди використовуватиме максимальну ширину замість динамічного налаштування до вмісту."
- },
- "scrolling-mode": {
- "label": "Режим прокрутки",
- "description": "Керування, коли увімкнено прокрутку тексту для довгих назв треків."
- },
- "no-active-player": "Немає активного плеєра"
- },
- "clock": {
- "use-primary-color": {
- "label": "Використовувати основний колір",
- "description": "Коли увімкнено, застосовується основний колір для акценту."
- },
- "use-monospaced-font": {
- "label": "Використовувати моноширинний шрифт",
- "description": "Коли увімкнено, годинник використовуватиме моноширинний шрифт."
- },
- "use-custom-font": {
- "label": "Використовувати власний шрифт",
- "description": "Замінити стандартний вибір шрифту власним шрифтом для годинника."
- },
- "custom-font": {
- "label": "Власний шрифт",
- "description": "Вибрати власний шрифт для відображення годинника.",
- "placeholder": "Вибрати власний шрифт...",
- "search-placeholder": "Пошук шрифтів..."
- },
- "clock-display": {
- "label": "Відображення годинника",
- "description": "Налаштуйте відображення годинника, додавши токени зі списку нижче. Для використання 12-годинного формату ви повинні включити токен 'AP'."
- },
- "horizontal-bar": {
- "label": "Горизонтальна панель",
- "description": "Порада: Використовуйте \\n для створення розриву рядка."
- },
- "vertical-bar": {
- "label": "Вертикальна панель",
- "description": "Використовуйте пробіл для розділення кожної частини на новий рядок."
- },
- "preview": "Попередній перегляд"
- },
"taskbar": {
+ "colorize-icons": {
+ "description": "Застосувати кольори теми до значків панелі завдань.",
+ "label": "Розфарбувати значки"
+ },
"hide-mode": {
- "label": "Режим приховування",
- "description": "Керує поведінкою віджета, коли немає відповідних вікон."
+ "description": "Керує поведінкою віджета, коли немає відповідних вікон.",
+ "label": "Режим приховування"
},
"only-active-workspaces": {
- "label": "Тільки з активних робочих просторів",
- "description": "Показувати тільки програми з активних робочих просторів."
+ "description": "Показувати тільки програми з активних робочих просторів.",
+ "label": "Тільки з активних робочих просторів"
},
"only-same-output": {
- "label": "Тільки з того ж виходу",
- "description": "Показувати тільки програми з виходу, де розташована панель."
- },
- "colorize-icons": {
- "label": "Розфарбувати значки",
- "description": "Застосувати кольори теми до значків панелі завдань."
+ "description": "Показувати тільки програми з виходу, де розташована панель.",
+ "label": "Тільки з того ж виходу"
}
},
"taskbar-grouped": {
@@ -1253,181 +294,71 @@
},
"tray": {
"colorize-icons": {
- "label": "Розфарбувати значки",
- "description": "Застосувати кольори теми до значків трея."
+ "description": "Застосувати кольори теми до значків трея.",
+ "label": "Розфарбувати значки"
+ },
+ "drawer-enabled": {
+ "description": "Коли увімкнено, не закріплені елементи трея відображаються на панелі ящика. Коли вимкнено, всі елементи трея відображаються в рядку.",
+ "label": "Увімкнути ящик"
}
},
- "audio-visualizer": {
- "width": {
- "label": "Ширина",
- "description": "Власна ширина компонента."
- },
- "color-name": {
- "label": "Колір заповнення",
- "description": "Вибрати колір для візуалізатора."
- },
- "hide-when-idle": {
- "label": "Приховати, коли медіа не відтворюється",
- "description": "Коли увімкнено, візуалізатор приховано, якщо плеєр активно не відтворює."
+ "volume": {
+ "display-mode": {
+ "description": "Виберіть, як ви хочете, щоб це значення відображалося.",
+ "label": "Режим відображення"
}
},
- "lock-keys": {
- "indicator-style": {
- "label": "Стиль індикатора",
- "description": "Стиль значків для індикаторів клавіш блокування",
- "large": "Великі літери",
- "small": "Малі літери",
- "square": "Квадратні літери",
- "square-round": "Заокруглені квадратні літери",
- "circle": "Круглі літери",
- "circle-dash": "Пунктирні круглі літери",
- "circle-dot": "Точкові круглі літери",
- "hex": "Шестикутні літери"
+ "workspace": {
+ "character-count": {
+ "description": "Кількість символів для відображення з назв робочих просторів (1-10).",
+ "label": "Кількість символів"
},
- "show-caps-lock": {
- "label": "Caps Lock",
- "description": "Відображати стан caps lock."
+ "hide-unoccupied": {
+ "description": "Не відображати робочі простори без вікон.",
+ "label": "Приховати незайняті"
},
- "show-num-lock": {
- "label": "Num Lock",
- "description": "Відображати стан num lock."
- },
- "show-scroll-lock": {
- "label": "Scroll Lock",
- "description": "Відображати стан scroll lock."
+ "label-mode": {
+ "description": "Виберіть, як відображаються мітки робочих просторів.",
+ "label": "Режим міток"
}
}
}
},
- "notifications": {
+ "battery": {
+ "charging": "Зарядка.",
+ "charging-rate": "Швидкість зарядки: {rate} Вт.",
+ "discharging": "Розрядка.",
+ "discharging-rate": "Швидкість розрядки: {rate} Вт.",
+ "health": "Здоров'я: {percent}%",
+ "idle": "Бездіяльність.",
+ "no-battery-detected": "Батарею не виявлено.",
"panel": {
- "title": "Сповіщення",
- "no-notifications": "Немає сповіщень",
- "description": "Ваші сповіщення з'являтимуться тут по мірі надходження.",
- "click-to-expand": "Клацніть для розгортання"
- }
- },
- "wallpaper": {
- "panel": {
- "title": "Вибір шпалер",
- "apply-all-monitors": {
- "label": "Застосувати до всіх моніторів",
- "description": "Застосувати вибрані шпалери до всіх моніторів одночасно."
- },
- "search": "Пошук:"
+ "balanced": "Збалансований ({percent}%)",
+ "disabled": "Менеджер батареї вимкнено",
+ "full": "Повна ємність ({percent}%)",
+ "lifespan": "Подовжений термін служби ({percent}%)",
+ "title": "Поріг зарядки"
},
- "transitions": {
- "none": "Немає",
- "random": "Випадковий",
- "fade": "Затухання",
- "disc": "Диск",
- "stripes": "Смуги",
- "wipe": "Змітання"
- },
- "fill-modes": {
- "center": "Центр",
- "crop": "Обрізати (Заповнити)",
- "fit": "Вмістити (Містити)",
- "stretch": "Розтягнути"
- },
- "no-match": "Збігів не знайдено.",
- "no-wallpaper": "Шпалери не знайдено.",
- "try-different-search": "Спробуйте інший пошуковий запит.",
- "configure-directory": "Налаштуйте вашу теку шпалер зі зображеннями."
+ "plugged-in": "Підключено.",
+ "time-left": "Залишилось часу: {time}.",
+ "time-until-full": "Час до повного заряду: {time}."
},
"bluetooth": {
"panel": {
- "title": "Bluetooth",
- "disabled": "Bluetooth вимкнено",
- "enable-message": "Увімкніть Bluetooth, щоб побачити доступні пристрої.",
- "connected-devices": "Підключені пристрої",
- "known-devices": "Відомі пристрої",
"available-devices": "Доступні пристрої",
- "scanning": "Сканування пристроїв...",
- "pairing-mode": "Переконайтеся, що ваш пристрій у режимі з'єднання."
- }
- },
- "wifi": {
- "panel": {
- "title": "Wi-Fi",
- "disabled": "Wi-Fi вимкнено",
- "enable-message": "Увімкніть Wi-Fi, щоб побачити доступні мережі.",
- "searching": "Пошук близьких мереж...",
- "connected": "Підключено",
- "disconnecting": "Відключення...",
- "forgetting": "Забування...",
- "saved": "Збережено",
- "disconnect": "Відключити",
- "enter-password": "Введіть пароль...",
+ "blocked": "Заблоковано",
"connect": "Підключити",
- "password": "Пароль",
- "forget-network": "Забути цю мережу?",
- "forget": "Забути",
- "no-networks": "Мереж не знайдено",
- "scan-again": "Сканувати знову"
+ "connected-devices": "Підключені пристрої",
+ "disabled": "Bluetooth вимкнено",
+ "disconnect": "Відключити",
+ "enable-message": "Увімкніть Bluetooth, щоб побачити доступні пристрої.",
+ "known-devices": "Відомі пристрої",
+ "pairing": "Спарювання...",
+ "pairing-mode": "Переконайтеся, що ваш пристрій у режимі з'єднання.",
+ "scanning": "Сканування пристроїв...",
+ "title": "Bluetooth"
}
},
- "tooltips": {
- "refresh": "Оновити",
- "close": "Закрити",
- "up": "Вгору",
- "home": "Додому",
- "refresh-wallpaper-list": "Оновити список шпалер",
- "refresh-devices": "Оновити пристрої",
- "forget-network": "Забути мережу",
- "clear-history": "Очистити історію",
- "delete-notification": "Видалити сповіщення",
- "previous-month": "Попередній місяць",
- "next-month": "Наступний місяць",
- "add-widget": "Додати віджет",
- "widget-settings": "Налаштування віджета",
- "remove-widget": "Видалити віджет",
- "move-to-left-section": "Перемістити в ліву секцію",
- "move-to-center-section": "Перемістити в центральну секцію",
- "move-to-right-section": "Перемістити в праву секцію",
- "open-settings": "Відкрити налаштування",
- "session-menu": "Меню сеансу",
- "cancel-timer": "Скасувати таймер",
- "start-screen-recording": "Почати запис екрана",
- "stop-screen-recording": "Зупинити запис екрана",
- "screen-recorder-not-installed": "Запис екрана не встановлено",
- "keep-awake": "Не спати",
- "enable-keep-awake": "Увімкнути режим неспання",
- "disable-keep-awake": "Вимкнути режим неспання",
- "wallpaper-selector": "Лівий клік: Відкрити вибір шпалер.\nПравий клік: Встановити випадкові шпалери.",
- "do-not-disturb-enabled": "'Не турбувати' увімкнено",
- "do-not-disturb-disabled": "'Не турбувати' вимкнено",
- "connect-disconnect-devices": "Лівий клік для підключення. Правий клік для забування.",
- "set-power-profile": "Встановити профіль живлення \"{profile}\"",
- "switch-to-light-mode": "Перемкнутися на світлий режим",
- "switch-to-dark-mode": "Перемкнутися на темний режим",
- "night-light-disabled": "Нічне світло вимкнено.\nЛівий клік для циклічного режиму.\nПравий клік для доступу до налаштувань.",
- "night-light-enabled": "Нічне світло увімкнено.\nЛівий клік для циклічного режиму.\nПравий клік для доступу до налаштувань.",
- "night-light-forced": "Нічне світло примусово.\nЛівий клік для циклічного режиму.\nПравий клік для доступу до налаштувань.",
- "night-light-not-installed": "Нічне світло недоступне.\nwlsunset не встановлено.",
- "click-to-start-recording": "Клацніть для початку запису",
- "click-to-stop-recording": "Клацніть для зупинки запису",
- "open-control-center": "Відкрити центр керування",
- "volume-at": "Вихідна гучність на {volume}%\nЛівий клік для налаштувань. Правий клік для вимкнення звуку.\nПрокрутка для зміни гучності.",
- "microphone-volume-at": "Гучність мікрофона на {volume}%\nЛівий клік для налаштувань. Правий клік для вимкнення звуку.\nПрокрутка для зміни гучності.",
- "brightness-at": "Яскравість: {brightness}%\nПравий клік для налаштувань.\nПрокрутка для зміни яскравості.",
- "manage-wifi": "Керувати Wi-Fi",
- "bluetooth-devices": "Пристрої Bluetooth",
- "open-notification-history-enable-dnd": "Відкрити історію сповіщень\nПравий клік для увімкнення \"Не турбувати\".",
- "open-notification-history-disable-dnd": "Відкрити історію сповіщень\nПравий клік для вимкнення \"Не турбувати\".",
- "open-wallpaper-selector": "Відкрити вибір шпалер",
- "previous-media": "Попереднє медіа",
- "pause": "Пауза",
- "play": "Відтворити",
- "next-media": "Наступне медіа",
- "power-profile": "Профіль живлення '{profile}'",
- "keyboard-layout": "Розкладка клавіатури {layout}",
- "output-muted": "Перемкнути вимкнення виходу",
- "input-muted": "Перемкнути вимкнення входу"
- },
- "clock": {
- "tooltip": "Відкрити календар"
- },
"calendar": {
"panel": {
"week": "Тиждень"
@@ -1436,220 +367,246 @@
"loading": "Завантаження погоди…"
}
},
+ "clock": {
+ "tooltip": "Відкрити календар"
+ },
"dock": {
"menu": {
+ "close": "Закрити",
"focus": "Фокус",
"pin": "Закріпити",
- "unpin": "Відкріпити",
- "close": "Закрити"
+ "unpin": "Відкріпити"
}
},
+ "general": {
+ "no-results": "Немає результатів",
+ "no-summary": "Немає резюме",
+ "unknown": "Невідомо"
+ },
"launcher": {
"pin": "Закріпити в доці",
"unpin": "Відкріпити з доку"
},
- "placeholders": {
- "search-icons": "напр., noctalia, niri, battery, cloud",
- "profile-picture-path": "/home/user/.face",
- "enter-width-pixels": "Введіть ширину в пікселях",
- "enter-command": "Введіть команду для виконання (програма або власний скрипт)",
- "command-example": "echo \"Привіт, Світ\"",
- "enter-text-to-collapse": "напр., 'нічого не відтворюється'. Використовуйте /regex/ для шаблонів.",
- "search-wallpapers": "Введіть для фільтрації шпалер...",
- "search-launcher": "Пошук записів... або використовуйте > для команд",
- "search": "Пошук...",
- "select": "Вибрати",
- "cancel": "Скасувати",
- "test": "Тест"
+ "lock-screen": {
+ "authenticating": "Автентифікація...",
+ "authentication-failed": "Помилка автентифікації",
+ "password": "Введіть ваш пароль...",
+ "restart": "Перезапустити",
+ "shut-down": "Вимкнути",
+ "suspend": "Призупинити",
+ "welcome-back": "З поверненням,"
+ },
+ "notifications": {
+ "panel": {
+ "click-to-expand": "Клацніть для розгортання",
+ "description": "Ваші сповіщення з'являтимуться тут по мірі надходження.",
+ "no-notifications": "Немає сповіщень",
+ "title": "Сповіщення"
+ }
},
"options": {
- "colors": {
- "primary": "Основний",
- "secondary": "Вторинний",
- "tertiary": "Третинний",
- "error": "Помилка",
- "onSurface": "На поверхні"
- },
"bar": {
- "position": {
- "top": "Зверху",
- "bottom": "Знизу",
- "left": "Зліва",
- "right": "Справа"
- },
"density": {
- "mini": "Міні",
+ "comfortable": "Зручний",
"compact": "Компактний",
"default": "Стандартний",
- "comfortable": "Зручний"
+ "mini": "Міні"
+ },
+ "position": {
+ "bottom": "Знизу",
+ "left": "Зліва",
+ "right": "Справа",
+ "top": "Зверху"
}
},
- "launcher": {
- "position": {
- "center": "Центр (за замовчуванням)",
- "top_left": "Зверху зліва",
- "top_right": "Зверху справа",
- "bottom_left": "Знизу зліва",
- "bottom_right": "Знизу справа",
- "bottom_center": "Знизу по центру",
- "top_center": "Зверху по центру",
- "center_left": "Зліва по центру",
- "center_right": "Справа по центру"
- }
+ "colors": {
+ "error": "Помилка",
+ "onSurface": "На поверхні",
+ "primary": "Основний",
+ "secondary": "Вторинний",
+ "tertiary": "Третинний"
},
"control-center": {
"position": {
- "close_to_bar_button": "Біля кнопки панелі",
- "top_left": "Зверху зліва",
- "top_right": "Зверху справа",
+ "bottom_center": "Знизу по центру",
"bottom_left": "Знизу зліва",
"bottom_right": "Знизу справа",
- "bottom_center": "Знизу по центру",
+ "center": "Центр",
+ "close_to_bar_button": "Біля кнопки панелі",
"top_center": "Зверху по центру",
- "center": "Центр"
+ "top_left": "Зверху зліва",
+ "top_right": "Зверху справа"
},
"quickSettingsStyle": {
- "modern": "Сучасний",
"classic": "Класичний",
- "compact": "Компактний"
+ "compact": "Компактний",
+ "modern": "Сучасний"
+ }
+ },
+ "display-mode": {
+ "always-hide": "Завжди приховувати",
+ "always-show": "Завжди показувати",
+ "force-open": "Примусово відкрити",
+ "on-hover": "При наведенні"
+ },
+ "frame-rates": {
+ "fps": "{fps} к/с"
+ },
+ "hide-modes": {
+ "hidden": "Приховати, коли порожньо",
+ "idle": "Приховувати під час простою",
+ "transparent": "Прозорий, коли порожньо",
+ "visible": "Завжди видимий"
+ },
+ "launcher": {
+ "position": {
+ "bottom_center": "Знизу по центру",
+ "bottom_left": "Знизу зліва",
+ "bottom_right": "Знизу справа",
+ "center": "Центр (за замовчуванням)",
+ "center_left": "Зліва по центру",
+ "center_right": "Справа по центру",
+ "follow_bar": "Слідувати за панеллю (за замовчуванням)",
+ "top_center": "Зверху по центру",
+ "top_left": "Зверху зліва",
+ "top_right": "Зверху справа"
}
},
"osd": {
"position": {
- "top_left": "Зверху зліва",
- "top_right": "Зверху справа",
- "top_center": "Зверху по центру",
+ "bottom_center": "Знизу по центру",
"bottom_left": "Знизу зліва",
"bottom_right": "Знизу справа",
- "bottom_center": "Знизу по центру",
"center_left": "Зліва по центру",
- "center_right": "Справа по центру"
+ "center_right": "Справа по центру",
+ "top_center": "Зверху по центру",
+ "top_left": "Зверху зліва",
+ "top_right": "Зверху справа"
}
},
- "display-mode": {
- "on-hover": "При наведенні",
- "always-show": "Завжди показувати",
- "always-hide": "Завжди приховувати",
- "force-open": "Примусово відкрити"
- },
- "workspace-labels": {
- "none": "Немає",
- "index": "Індекс",
- "name": "Назва"
- },
- "visualizer-types": {
- "none": "Немає",
- "linear": "Лінійний",
- "mirrored": "Дзеркальний",
- "wave": "Хвиля"
+ "screen-recording": {
+ "audio-sources": {
+ "both": "Системний вихід + вхід мікрофона",
+ "microphone-input": "Вхід мікрофона",
+ "system-output": "Системний вихід"
+ },
+ "color-range": {
+ "full": "Повний",
+ "limited": "Обмежений"
+ },
+ "quality": {
+ "high": "Висока",
+ "medium": "Середня",
+ "ultra": "Ультра",
+ "very-high": "Дуже висока"
+ },
+ "sources": {
+ "portal": "Портал",
+ "screen": "Екран"
+ }
},
"scrolling-modes": {
"always": "Завжди прокручувати",
"hover": "Прокручувати при наведенні",
"never": "Ніколи не прокручувати"
},
- "hide-modes": {
- "visible": "Завжди видимий",
- "hidden": "Приховати, коли порожньо",
- "transparent": "Прозорий, коли порожньо"
+ "shadow-direction": {
+ "bottom": "Знизу",
+ "bottom_left": "Знизу ліворуч",
+ "bottom_right": "Знизу праворуч",
+ "center": "По центру",
+ "left": "Ліворуч",
+ "right": "Праворуч",
+ "top": "Згори",
+ "top_left": "Згори ліворуч",
+ "top_right": "Згори праворуч"
},
- "frame-rates": {
- "fps": "{fps} к/с"
+ "visualizer-quality": {
+ "high": "Високий",
+ "low": "Низький"
},
- "screen-recording": {
- "sources": {
- "portal": "Портал",
- "screen": "Екран"
- },
- "quality": {
- "medium": "Середня",
- "high": "Висока",
- "very-high": "Дуже висока",
- "ultra": "Ультра"
- },
- "color-range": {
- "limited": "Обмежений",
- "full": "Повний"
- },
- "audio-sources": {
- "system-output": "Системний вихід",
- "microphone-input": "Вхід мікрофона",
- "both": "Системний вихід + вхід мікрофона"
- }
+ "visualizer-types": {
+ "linear": "Лінійний",
+ "mirrored": "Дзеркальний",
+ "none": "Немає",
+ "wave": "Хвиля"
+ },
+ "workspace-labels": {
+ "index": "Індекс",
+ "name": "Назва",
+ "none": "Немає"
}
},
- "session-menu": {
- "title": "Меню сеансу",
- "action-in-seconds": "{action} через {seconds} секунд...",
- "lock": "Заблокувати",
- "lock-and-suspend": "Заблокувати та призупинити",
- "suspend": "Призупинити",
- "reboot": "Перезавантажити",
- "logout": "Вийти",
- "shutdown": "Вимкнути"
+ "placeholders": {
+ "cancel": "Скасувати",
+ "command-example": "echo \"Привіт, Світ\"",
+ "enter-command": "Введіть команду для виконання (програма або власний скрипт)",
+ "enter-text-to-collapse": "напр., 'нічого не відтворюється'. Використовуйте /regex/ для шаблонів.",
+ "enter-tooltip": "Введіть підказку",
+ "enter-width-pixels": "Введіть ширину в пікселях",
+ "profile-picture-path": "/home/user/.face",
+ "search": "Пошук...",
+ "search-icons": "напр., noctalia, niri, battery, cloud",
+ "search-launcher": "Пошук записів... або використовуйте > для команд",
+ "search-wallpapers": "Введіть для фільтрації шпалер...",
+ "select": "Вибрати",
+ "test": "Тест"
},
"plugins": {
"applications": "Програми",
- "clipboard": "Історія буфера обміну",
"calculator": "Калькулятор",
- "clipboard-search-description": "Пошук в історії буфера обміну",
+ "calculator-description": "Калькулятор - обчислення математичних виразів",
+ "calculator-enter-expression": "Введіть математичний вираз",
+ "calculator-error": "Помилка",
+ "calculator-name": "Калькулятор",
+ "clipboard": "Історія буфера обміну",
"clipboard-clear-description": "Очистити всю історію буфера обміну",
+ "clipboard-clear-description-full": "Видалити всі елементи з історії буфера обміну",
+ "clipboard-clear-history": "Очистити історію буфера обміну",
"clipboard-history-disabled": "Історія буфера обміну вимкнена",
"clipboard-history-disabled-description": "Увімкніть історію буфера обміну в налаштуваннях або встановіть cliphist",
- "clipboard-clear-history": "Очистити історію буфера обміну",
- "clipboard-clear-description-full": "Видалити всі елементи з історії буфера обміну",
"clipboard-loading": "Завантаження історії буфера обміну...",
"clipboard-loading-description": "Зачекайте, будь ласка",
- "calculator-description": "Калькулятор - обчислення математичних виразів",
- "calculator-name": "Калькулятор",
- "calculator-enter-expression": "Введіть математичний вираз",
- "calculator-error": "Помилка"
- },
- "system": {
- "uptime": "Час роботи: {uptime}",
- "welcome-back": "З поверненням,",
- "monitor-description": "{model} ({width}x{height} @ {scale}x)",
- "scaling-percentage": "{percentage}%",
- "location-display": "{name} ({coordinates})",
- "signal-strength": "{signal}%",
- "cpu-temperature": "{temp}°C",
- "disk-usage": "{percent}%",
- "widget-settings-title": "Налаштування {widget}",
- "unknown-app": "Невідома програма",
- "no-media-player-detected": "Медіаплеєр не виявлено",
- "user-requested": "На запит користувача",
- "unknown": "Невідомо",
- "unknown-version": "Невідомо",
- "unknown-layout": "Невідомо"
- },
- "lock-screen": {
- "password": "Введіть ваш пароль...",
- "welcome-back": "З поверненням,",
- "authenticating": "Автентифікація...",
- "authentication-failed": "Помилка автентифікації",
- "shut-down": "Вимкнути",
- "restart": "Перезапустити",
- "suspend": "Призупинити"
+ "clipboard-search-description": "Пошук в історії буфера обміну"
},
"quickSettings": {
+ "bluetooth": {
+ "label": {
+ "disabled": "Bluetooth",
+ "enabled": "Bluetooth"
+ },
+ "tooltip": {
+ "action": "Клацніть для керування пристроями Bluetooth"
+ }
+ },
+ "keepAwake": {
+ "label": {
+ "disabled": "Не спати",
+ "enabled": "Не спати"
+ },
+ "tooltip": {
+ "action": "Клацніть для перемикання режиму неспання"
+ }
+ },
+ "nightLight": {
+ "label": {
+ "disabled": "Нічне світло",
+ "enabled": "Нічне світло",
+ "forced": "Нічне світло"
+ },
+ "tooltip": {
+ "action": "Клацніть для циклічної зміни режиму нічного світла\nПравий клік: Відкрити налаштування"
+ }
+ },
"notifications": {
"label": {
- "enabled": "Сповіщення",
- "disabled": "Не турбувати"
+ "disabled": "Не турбувати",
+ "enabled": "Сповіщення"
},
"tooltip": {
"action": "Лівий клік: Відкрити історію сповіщень\nПравий клік: Перемкнути \"Не турбувати\""
}
},
- "screenRecorder": {
- "label": {
- "recording": "Зупинити",
- "stopped": "Записати"
- },
- "tooltip": {
- "action": "Клацніть для початку/зупинки запису екрана"
- }
- },
"powerProfile": {
"label": {
"unavailable": "Профіль живлення"
@@ -1659,33 +616,13 @@
"disabled": "Встановіть power-profiles-daemon для використання профілів живлення"
}
},
- "wifi": {
+ "screenRecorder": {
"label": {
- "ethernet": "Ethernet",
- "wifi": "Wi-Fi",
- "disconnected": "Wi-Fi відключено"
+ "recording": "Зупинити",
+ "stopped": "Записати"
},
"tooltip": {
- "action": "Клацніть для керування підключеннями Wi-Fi"
- }
- },
- "bluetooth": {
- "label": {
- "enabled": "Bluetooth",
- "disabled": "Bluetooth"
- },
- "tooltip": {
- "action": "Клацніть для керування пристроями Bluetooth"
- }
- },
- "nightLight": {
- "label": {
- "enabled": "Нічне світло",
- "forced": "Нічне світло",
- "disabled": "Нічне світло"
- },
- "tooltip": {
- "action": "Клацніть для циклічної зміни режиму нічного світла\nПравий клік: Відкрити налаштування"
+ "action": "Клацніть для початку/зупинки запису екрана"
}
},
"wallpaperSelector": {
@@ -1694,177 +631,1386 @@
"action": "Лівий клік: Відкрити вибір шпалер\nПравий клік: Встановити випадкові шпалери"
}
},
- "keepAwake": {
+ "wifi": {
"label": {
- "enabled": "Не спати",
- "disabled": "Не спати"
+ "disconnected": "Wi-Fi відключено",
+ "ethernet": "Ethernet",
+ "wifi": "Wi-Fi"
},
"tooltip": {
- "action": "Клацніть для перемикання режиму неспання"
+ "action": "Клацніть для керування підключеннями Wi-Fi"
}
}
},
+ "session-menu": {
+ "action-in-seconds": "{action} через {seconds} секунд...",
+ "lock": "Заблокувати",
+ "lock-and-suspend": "Заблокувати та призупинити",
+ "logout": "Вийти",
+ "reboot": "Перезавантажити",
+ "shutdown": "Вимкнути",
+ "suspend": "Призупинити",
+ "title": "Меню сеансу"
+ },
+ "settings": {
+ "about": {
+ "contributors": {
+ "section": {
+ "description": "Вітаємо нашого {count} чудового учасника!",
+ "description_plural": "Вітаємо наших {count} чудових учасників!",
+ "label": "Учасники"
+ }
+ },
+ "noctalia": {
+ "download-latest": "Завантажити останній реліз",
+ "installed-version": "Встановлена версія:",
+ "latest-version": "Остання версія:",
+ "section": {
+ "description": "Елегантна та мінімалістична оболонка робочого столу, ретельно створена для Wayland, побудована на Quickshell.",
+ "label": "Оболонка Noctalia"
+ }
+ },
+ "support": "Підтримати нас",
+ "title": "Про програму"
+ },
+ "audio": {
+ "devices": {
+ "input-device": {
+ "description": "Виберіть бажаний пристрій введення звуку.",
+ "label": "Пристрій введення"
+ },
+ "output-device": {
+ "description": "Виберіть бажаний пристрій виведення звуку.",
+ "label": "Пристрій виведення"
+ },
+ "section": {
+ "description": "Налаштуйте доступні пристрої введення та виведення звуку.",
+ "label": "Аудіопристрої"
+ }
+ },
+ "media": {
+ "excluded-player": {
+ "description": "Додайте ключові слова для плеєрів, які система має ігнорувати. Кожне ключове слово на новому рядку.",
+ "label": "Виключений плеєр",
+ "placeholder": "введіть підрядок та натисніть +"
+ },
+ "frame-rate": {
+ "description": "Вищі значення плавніші, але споживають більше ресурсів.",
+ "label": "Частота кадрів"
+ },
+ "primary-player": {
+ "description": "Введіть ключове слово для ідентифікації основного плеєра.",
+ "label": "Основний плеєр",
+ "placeholder": "напр. spotify, vlc, mpv"
+ },
+ "scrolling-speed": {
+ "description": "Час у секундах для прокрутки назви від початку до кінця",
+ "label": "Швидкість прокрутки"
+ },
+ "scrolling-title": {
+ "description": "Увімкнути безперервну прокрутку для довгих назв медіа",
+ "label": "Прокрутка назви"
+ },
+ "section": {
+ "description": "Встановіть бажані та ігноровані медіапрограми.",
+ "label": "Медіаплеєри"
+ },
+ "visualizer-quality": {
+ "description": "Висока якість використовує більше ресурсів GPU.",
+ "label": "Якість візуалізації"
+ },
+ "visualizer-type": {
+ "description": "Виберіть тип візуалізації для відтворення медіа",
+ "label": "Тип візуалізації"
+ }
+ },
+ "title": "Аудіо",
+ "volumes": {
+ "input-volume": {
+ "description": "Рівень гучності мікрофона.",
+ "label": "Вхідна гучність"
+ },
+ "mute-input": {
+ "description": "Вимкнути стандартний аудіовхід (мікрофон).",
+ "label": "Вимкнути вхідний звук"
+ },
+ "mute-output": {
+ "description": "Вимкнути основний аудіовихід системи.",
+ "label": "Вимкнути вихідний звук"
+ },
+ "output-volume": {
+ "description": "Системний рівень гучності.",
+ "label": "Вихідна гучність"
+ },
+ "section": {
+ "description": "Налаштуйте регулятори гучності та рівні звуку.",
+ "label": "Гучність"
+ },
+ "step-size": {
+ "description": "Налаштуйте крок зміни гучності (колесо миші, гарячі клавіші).",
+ "label": "Крок зміни гучності"
+ },
+ "volume-overdrive": {
+ "description": "Дозволити підвищення гучності понад 100%. Може не підтримуватись усім обладнанням.",
+ "label": "Дозволити перегучність"
+ }
+ }
+ },
+ "bar": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Налаштуйте непрозорість фону панелі.",
+ "label": "Непрозорість фону"
+ },
+ "density": {
+ "description": "Налаштуйте відступи панелі для компактного або просторого вигляду.",
+ "label": "Щільність панелі"
+ },
+ "floating": {
+ "description": "Відображати панель як плаваючу 'таблетку'. Примітка: Це перемістить кути екрана до країв.",
+ "label": "Плаваюча панель"
+ },
+ "margins": {
+ "description": "Налаштуйте поля навколо плаваючої панелі.",
+ "horizontal": "Горизонтальні",
+ "label": "Поля",
+ "vertical": "Вертикальні"
+ },
+ "outer-corners": {
+ "description": "Відображає назовні закруглені кути на панелі.",
+ "label": "Зовнішні кути"
+ },
+ "position": {
+ "description": "Виберіть, де розмістити панель на екрані.",
+ "label": "Положення панелі"
+ },
+ "section": {
+ "description": "Налаштуйте зовнішній вигляд та положення панелі.",
+ "label": "Зовнішній вигляд"
+ },
+ "show-capsule": {
+ "description": "Показувати фон віджетів.",
+ "label": "Показувати капсулу"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Показувати панель на певних моніторах. За замовчуванням на всіх, якщо не вибрано.",
+ "label": "Відображення на моніторах"
+ }
+ },
+ "title": "Панель",
+ "tray": {
+ "back": "Назад",
+ "blacklist": {
+ "description": "Додайте правила виключення з трея, підтримує шаблони (*).",
+ "label": "Чорний список",
+ "placeholder": "напр., nm-applet, Fcitx*"
+ },
+ "pin-application": "Закріпити Застосунок",
+ "unpin-application": "Відкріпити Застосунок"
+ },
+ "widgets": {
+ "section": {
+ "description": "Перетягуйте віджети для зміни порядку. Позначки вказують використання: [L]івий, [C]ентр, [R]правий.",
+ "label": "Розміщення віджетів"
+ }
+ }
+ },
+ "color-scheme": {
+ "color-source": {
+ "matugen-scheme-type": {
+ "description": {
+ "scheme-content": "Виводить кольори, що тісно збігаються з базовим зображенням",
+ "scheme-expressive": "Яскрава палітра з грайливою насиченістю",
+ "scheme-fidelity": "Високоточна палітра, що зберігає вихідні відтінки",
+ "scheme-fruit-salad": "Барвистий мікс яскравих контрастних акцентів",
+ "scheme-monochrome": "Мінімальна палітра на основі одного відтінку",
+ "scheme-neutral": "Приглушена палітра з стриманими та заспокійливими тонами",
+ "scheme-rainbow": "Різноманітна палітра, що охоплює весь спектр",
+ "scheme-tonal-spot": "Збалансована палітра з фокусованими акцентами"
+ },
+ "label": "Тип схеми Matugen"
+ },
+ "section": {
+ "description": "Основні налаштування для кольорів Noctalia.",
+ "label": "Джерело кольорів"
+ },
+ "use-wallpaper-colors": {
+ "description": "Генерувати колірні схеми зі шпалер за допомогою Matugen. Автоматично витягує кольори для створення цілісної теми.",
+ "label": "Використовувати кольори шпалер"
+ }
+ },
+ "dark-mode": {
+ "mode": {
+ "description": "Увімкнути автоматичне перемикання між світлим і темним режимом.",
+ "label": "Розклад темного режиму",
+ "location": "Розташування",
+ "manual": "Вручну",
+ "off": "Вимкнено"
+ },
+ "switch": {
+ "description": "Перемикає на темнішу тему для зручного перегляду вночі.",
+ "label": "Темний режим"
+ }
+ },
+ "predefined": {
+ "generate-templates": {
+ "description": "Генерувати шаблони Matugen (GTK, теми терміналу тощо) при використанні попередньо визначених колірних схем.",
+ "label": "Генерувати шаблони для попередньо визначених схем"
+ },
+ "section": {
+ "description": "Виберіть із колекції попередньо визначених колірних схем.",
+ "label": "Попередньо визначені колірні схеми"
+ }
+ },
+ "templates": {
+ "misc": {
+ "description": "Додаткові параметри конфігурації.",
+ "label": "Різне",
+ "user-templates": {
+ "description": "Увімкнути визначений користувачем конфіг Matugen. Файл шаблону буде створено за адресою ~/.config/noctalia/user-templates.toml при першому увімкненні",
+ "label": "Користувацькі шаблони"
+ }
+ },
+ "programs": {
+ "code": {
+ "description": "Записати {filepath}. Тему Hyprluna потрібно встановити та активувати вручну.",
+ "description-missing": "Потрібна установка {app}"
+ },
+ "spicetify": {
+ "description": "Записати {filepath}. Тему Comfy потрібно встановити та активувати вручну.",
+ "description-missing": "Потрібна установка {app}"
+ },
+ "description": "Оформлення окремих програм.",
+ "discord": {
+ "description": "Записати {filepath} для {client}. Тему Hyprluna потрібно активувати вручну.",
+ "description-missing": "Клієнт Discord не виявлено. Встановіть vencord, vesktop, webcord, armcord, equibop, lightcord або dorion."
+ },
+ "fuzzel": {
+ "description": "Записати {filepath} та перезавантажити",
+ "description-missing": "Потрібна установка {app}"
+ },
+ "label": "Програми",
+ "pywalfox": {
+ "description": "Записати {filepath} та запустити pywalfox update",
+ "description-missing": "Потрібна установка {app}"
+ },
+ "vicinae": {
+ "description": "Записати {filepath} та перезавантажити",
+ "description-missing": "Потрібна установка {app}"
+ },
+ "walker": {
+ "description": "Записати {filepath} та встановити тему noctalia",
+ "description-missing": "Потрібна установка {app}"
+ }
+ },
+ "section": {
+ "description": "Застосовувати кольори до зовнішніх програм.",
+ "label": "Шаблони"
+ },
+ "terminal": {
+ "alacritty": {
+ "description": "Записати {filepath} та перезавантажити",
+ "description-missing": "Потрібно встановити {app}"
+ },
+ "description": "Оформлення емулятора терміналу.",
+ "foot": {
+ "description": "Записати {filepath} та перезавантажити",
+ "description-missing": "Потрібна установка {app}"
+ },
+ "ghostty": {
+ "description": "Записати {filepath} та перезавантажити",
+ "description-missing": "Потрібна установка {app}"
+ },
+ "kitty": {
+ "description": "Записати {filepath} та перезавантажити",
+ "description-missing": "Потрібна установка {app}"
+ },
+ "label": "Термінал",
+ "wezterm": {
+ "description": "Запишіть {filepath} та перезавантажте",
+ "description-missing": "Потрібно встановити {app}"
+ }
+ },
+ "ui": {
+ "description": "Оформлення робочого середовища та інструментарію інтерфейсу.",
+ "gtk": {
+ "description": "Записати {filepath}"
+ },
+ "kcolorscheme": {
+ "description": "Записати {filepath}"
+ },
+ "label": "Інтерфейс",
+ "qt": {
+ "description": "Записати {filepath}"
+ }
+ }
+ },
+ "title": "Колірна схема"
+ },
+ "control-center": {
+ "cards": {
+ "section": {
+ "description": "Налаштуйте, які елементи керування з'являються в Центрі керування та в якому порядку.",
+ "label": "Картки"
+ }
+ },
+ "position": {
+ "description": "Виберіть, де з'являється панель Центру керування при відкритті.",
+ "label": "Положення"
+ },
+ "section": {
+ "description": "Налаштуйте положення та поведінку панелі Центру керування.",
+ "label": "Зовнішній вигляд"
+ },
+ "shortcuts": {
+ "custom-button": {
+ "browse": "Огляд",
+ "command": {
+ "description": "Команда, яка буде виконана при натисканні кнопки.",
+ "label": "Команда"
+ },
+ "enable-on-state-logic": {
+ "description": "Увімкнути другу іконку та 'гарячий' стан на основі команди перевірки.",
+ "label": "Увімкнути логіку увімкненого стану"
+ },
+ "general-tooltip-text": {
+ "description": "Загальний опис для підказки кнопки.",
+ "label": "Загальний текст підказки"
+ },
+ "icon": {
+ "description": "Виберіть іконку з бібліотеки.",
+ "label": "Іконка"
+ },
+ "on-clicked": {
+ "description": "Команда, яка буде виконана при натисканні лівої кнопки миші.",
+ "label": "Команда лівого кліку"
+ },
+ "on-middle-clicked": {
+ "description": "Команда, яка буде виконана при натисканні середньої кнопки миші.",
+ "label": "Команда середнього кліку"
+ },
+ "on-right-clicked": {
+ "description": "Команда, яка буде виконана при натисканні правої кнопки миші.",
+ "label": "Команда правого кліку"
+ },
+ "on-state-command": {
+ "description": "Команда для перевірки, чи повинна кнопка бути у стані 'увімкнено'. Повертає 0 для увімкненого стану, ненульове значення для вимкненого.",
+ "label": "Команда перевірки увімкненого стану"
+ },
+ "on-state-icon": {
+ "description": "Іконка для кнопки, коли вона у стані 'увімкнено'.",
+ "label": "Іконка увімкненого стану"
+ },
+ "state-checks": {
+ "add": "Додати перевірку стану",
+ "browse-icon": "Огляд",
+ "command": "Команда для виконання для цієї перевірки стану",
+ "label": "Перевірки стану",
+ "remove": "Видалити"
+ },
+ "tooltip": {
+ "description": "Підказка, яка буде відображатися при наведенні на кнопку.",
+ "label": "Підказка"
+ }
+ },
+ "dialog": {
+ "apply": "Застосувати",
+ "cancel": "Скасувати"
+ },
+ "section": {
+ "description": "Налаштуйте та керуйте віджетами швидкого доступу.",
+ "label": "Віджети швидкого доступу"
+ },
+ "sectionLeft": "Лівий",
+ "sectionRight": "Правий"
+ },
+ "title": "Центр керування"
+ },
+ "display": {
+ "monitors": {
+ "brightness": "Яскравість",
+ "brightness-step": {
+ "description": "Налаштуйте крок зміни яскравості (колесо миші та гарячі клавіші).",
+ "label": "Крок зміни яскравості"
+ },
+ "brightness-unavailable": {
+ "ddc-disabled": "Регулювання яскравості недоступне. Увімкніть \"Підтримку зовнішньої яскравості\", щоб керувати яскравістю цього дисплея.",
+ "generic": "Регулювання яскравості для цього дисплея недоступне."
+ },
+ "enforce-minimum": {
+ "description": "Вирішує проблему повного вимкнення підсвітки на деяких дисплеях при яскравості 0%.",
+ "label": "Примусова мінімальна яскравість (1%)"
+ },
+ "external-brightness": {
+ "description": "Увімкнути підтримку DDCUtil для керування яскравістю на зовнішніх дисплеях через протокол DDC/CI.",
+ "label": "Підтримка зовнішньої яскравості"
+ },
+ "section": {
+ "description": "Налаштуйте яскравість для кожного дисплея.",
+ "label": "Налаштування для кожного монітора"
+ }
+ },
+ "night-light": {
+ "auto-schedule": {
+ "description": "На основі часу заходу та сходу сонця в {location} - рекомендовано.",
+ "label": "Автоматичне планування"
+ },
+ "enable": {
+ "description": "Застосувати теплий колірний фільтр для зменшення випромінювання синього світла.",
+ "label": "Увімкнути нічне світло"
+ },
+ "force-activation": {
+ "description": "Ігнорує розклад і негайно застосовує нічний фільтр.",
+ "label": "Примусова активація"
+ },
+ "manual-schedule": {
+ "description": "Встановіть власний час сходу та заходу сонця.",
+ "label": "Ручне планування",
+ "select-start": "Виберіть час початку",
+ "select-stop": "Виберіть час зупинки",
+ "sunrise": "Час сходу сонця",
+ "sunset": "Час заходу сонця"
+ },
+ "section": {
+ "description": "Зменшіть випромінювання синього світла для кращого сну та зменшення втоми очей.",
+ "label": "Нічне світло"
+ },
+ "temperature": {
+ "day": "День",
+ "description": "Встановіть теплоту кольору для нічного та денного часу.",
+ "label": "Колірна температура",
+ "night": "Ніч"
+ }
+ },
+ "title": "Дисплей"
+ },
+ "dock": {
+ "appearance": {
+ "background-opacity": {
+ "description": "Налаштуйте непрозорість фону дока.",
+ "label": "Непрозорість фону"
+ },
+ "colorize-icons": {
+ "description": "Застосувати кольори теми до значків програм у доці (тільки неактивні програми).",
+ "label": "Розфарбувати значки"
+ },
+ "display": {
+ "always-visible": "Завжди видимий",
+ "auto-hide": "Автоприховування",
+ "description": "Виберіть, як поводиться док.",
+ "exclusive": "Ексклюзивний",
+ "label": "Відображення"
+ },
+ "floating-distance": {
+ "description": "Встановіть відстань між доком і краєм екрана.",
+ "label": "Відстань плавання дока"
+ },
+ "icon-size": {
+ "description": "Налаштуйте загальний розмір дока.",
+ "label": "Розмір дока"
+ },
+ "section": {
+ "description": "Налаштуйте поведінку та зовнішній вигляд дока.",
+ "label": "Зовнішній вигляд"
+ }
+ },
+ "enabled": {
+ "description": "Показати або сховати док повністю",
+ "label": "Увімкнути док"
+ },
+ "monitors": {
+ "only-same-output": {
+ "description": "Показувати тільки програми з виходу, де розташований док.",
+ "label": "Тільки програми з того ж виходу"
+ },
+ "section": {
+ "description": "Показувати док на певних моніторах. За замовчуванням на всіх, якщо не вибрано.",
+ "label": "Відображення на моніторах"
+ }
+ },
+ "title": "Док"
+ },
+ "general": {
+ "fonts": {
+ "default": {
+ "description": "Основний шрифт, що використовується в інтерфейсі.",
+ "label": "Стандартний шрифт",
+ "placeholder": "Виберіть стандартний шрифт...",
+ "scale": {
+ "description": "Збільшити або зменшити розмір звичайного тексту.",
+ "label": "Розмір стандартного шрифту"
+ },
+ "search-placeholder": "Пошук шрифту..."
+ },
+ "monospace": {
+ "description": "Моноширинний шрифт для відображення чисел та статистики.",
+ "label": "Моноширинний шрифт",
+ "placeholder": "Виберіть моноширинний шрифт...",
+ "scale": {
+ "description": "Збільшити або зменшити розмір моноширинного тексту.",
+ "label": "Розмір моноширинного шрифту"
+ },
+ "search-placeholder": "Пошук моноширинного шрифту..."
+ },
+ "reset-scaling": "Скинути масштаб",
+ "section": {
+ "description": "Виберіть шрифти, що використовуються в інтерфейсі.",
+ "label": "Шрифти"
+ }
+ },
+ "language": {
+ "section": {
+ "description": "Виберіть бажану мову програми.",
+ "label": "Мова"
+ },
+ "select": {
+ "auto-detect": "Автоматично",
+ "description": "Виберіть мову інтерфейсу програми.",
+ "label": "Мова програми"
+ }
+ },
+ "launch-setup-wizard": "Запустити майстер налаштування",
+ "profile": {
+ "picture": {
+ "description": "Ваше фото профілю, що відображається в інтерфейсі.",
+ "label": "Фото профілю {user}"
+ },
+ "section": {
+ "description": "Редагуйте дані користувача та аватар.",
+ "label": "Профіль"
+ },
+ "select-avatar": "Вибрати зображення аватара"
+ },
+ "screen-corners": {
+ "radius": {
+ "description": "Налаштуйте заокруглення кутів екрана.",
+ "label": "Радіус кутів екрана",
+ "reset": "Скинути радіус кутів екрана"
+ },
+ "section": {
+ "description": "Налаштуйте заокруглення кутів екрана та візуальні ефекти.",
+ "label": "Кути екрана"
+ },
+ "show-corners": {
+ "description": "Відображати заокруглені кути на краях екрана.",
+ "label": "Показувати кути екрана"
+ },
+ "solid-black": {
+ "description": "Використовувати суцільний чорний колір замість кольору фону панелі.",
+ "label": "Суцільно чорні кути"
+ }
+ },
+ "title": "Загальні"
+ },
+ "hooks": {
+ "info": {
+ "command-info": {
+ "description": "• Команди виконуються через оболонку (sh -c)\n• Команди запускаються у фоні (відокремлено)\n• Кнопки тестування виконують з поточними значеннями",
+ "label": "Інформація про команди хуків"
+ },
+ "parameters": {
+ "description": "• Хук шпалер: $1 = шлях до шпалер, $2 = назва екрана\n• Хук перемикання теми: $1 = true/false (стан темного режиму)",
+ "label": "Доступні параметри"
+ }
+ },
+ "system-hooks": {
+ "enable": {
+ "description": "Увімкнути або вимкнути всі команди хуків.",
+ "label": "Увімкнути хуки"
+ },
+ "section": {
+ "description": "Налаштуйте команди для виконання при системних подіях.",
+ "label": "Системні хуки"
+ }
+ },
+ "theme-changed": {
+ "description": "Команда для виконання при перемиканні теми між темним і світлим режимом.",
+ "label": "Тему змінено",
+ "placeholder": "напр., notify-send \"Тема\" \"Перемкнуто\""
+ },
+ "title": "Хуки",
+ "wallpaper-changed": {
+ "description": "Команда для виконання при зміні шпалер.",
+ "label": "Шпалери змінено",
+ "placeholder": "напр., notify-send \"Шпалери\" \"Змінено\""
+ }
+ },
+ "launcher": {
+ "settings": {
+ "background-opacity": {
+ "description": "Налаштуйте непрозорість фону запускача.",
+ "label": "Непрозорість фону"
+ },
+ "clipboard-history": {
+ "description": "Отримати доступ до раніше скопійованих елементів із запускача.",
+ "label": "Увімкнути історію буфера обміну"
+ },
+ "custom-launch-prefix": {
+ "description": "Додати префікс до команд власним запускачем (напр., 'runapp' для інтеграції з systemd).",
+ "label": "Власний префікс запуску"
+ },
+ "custom-launch-prefix-enabled": {
+ "description": "Використовувати власний префікс для запуску програм замість стандартного методу.",
+ "label": "Увімкнути власний префікс запуску"
+ },
+ "position": {
+ "description": "Виберіть, де з'являється панель запускача.",
+ "label": "Положення"
+ },
+ "section": {
+ "description": "Налаштуйте поведінку та зовнішній вигляд запускача.",
+ "label": "Зовнішній вигляд"
+ },
+ "sort-by-usage": {
+ "description": "Коли увімкнено, часто запускані програми з'являються першими в списку.",
+ "label": "Сортувати за використанням"
+ },
+ "terminal-command": {
+ "description": "Команда для запуску терміналу. Напр., 'kitty -e' або 'gnome-terminal --'.",
+ "label": "Команда терміналу"
+ },
+ "use-app2unit": {
+ "description": "Використовує альтернативний метод запуску для кращого управління процесами програм і запобігання проблемам.",
+ "label": "Використовувати App2Unit для запуску програм"
+ }
+ },
+ "title": "Запускач"
+ },
+ "location": {
+ "date-time": {
+ "12hour-format": {
+ "description": "Відображати час у 12-годинному форматі на екрані блокування та в календарі. Годинник на панелі має власні налаштування.",
+ "label": "Використовувати 12-годинний формат часу"
+ },
+ "first-day-of-week": {
+ "automatic": "Автоматично (використовувати системну локаль)",
+ "description": "Виберіть, який день починає тиждень у календарі.",
+ "label": "Перший день тижня"
+ },
+ "section": {
+ "description": "Налаштуйте, як відображаються дата та час.",
+ "label": "Дата і час"
+ },
+ "show-events": {
+ "description": "Відображати події на панелі календаря.",
+ "label": "Показувати події календаря"
+ },
+ "use-analog": {
+ "description": "Показувати аналоговий годинник на екрані календаря.",
+ "label": "Використовувати аналоговий годинник"
+ },
+ "week-numbers": {
+ "description": "Відображати номер тижня року (напр., Тиждень 38) у календарі.",
+ "label": "Показувати номери тижнів"
+ }
+ },
+ "location": {
+ "search": {
+ "description": "напр., Торонто, ОН",
+ "label": "Шукати розташування",
+ "placeholder": "Введіть назву місця"
+ },
+ "section": {
+ "description": "Отримуйте точну погоду та планування нічного світла, встановивши ваше розташування.",
+ "label": "Ваше розташування"
+ }
+ },
+ "title": "Розташування",
+ "weather": {
+ "enabled": {
+ "description": "Показувати інформацію про погоду в інтерфейсі та отримувати дані про погоду. Коли вимкнено, всі елементи погоди будуть приховані та мережеві запити не виконуватимуться.",
+ "label": "Увімкнути погоду"
+ },
+ "fahrenheit": {
+ "description": "Відображати температуру в Фаренгейтах замість Цельсія.",
+ "label": "Відображати температуру в Фаренгейтах (°F)"
+ },
+ "section": {
+ "description": "Виберіть бажану одиницю температури.",
+ "label": "Погода"
+ },
+ "show-in-calendar": {
+ "description": "Показувати щоденний прогноз погоди безпосередньо в календарі.",
+ "label": "Відображати погоду в календарі"
+ }
+ }
+ },
+ "lock-screen": {
+ "compact-lockscreen": {
+ "description": "Показувати тільки поле входу та системні елементи керування, приховуючи віджети погоди та медіа.",
+ "label": "Компактний екран блокування"
+ },
+ "lock-on-suspend": {
+ "description": "Автоматично блокувати екран при призупиненні системи.",
+ "label": "Блокувати при призупиненні"
+ },
+ "title": "Екран блокування"
+ },
+ "network": {
+ "bluetooth": {
+ "label": "Увімкнути Bluetooth"
+ },
+ "section": {
+ "description": "Керувати підключеннями Wi-Fi та Bluetooth."
+ },
+ "title": "Мережа",
+ "wifi": {
+ "label": "Увімкнути Wi-Fi"
+ }
+ },
+ "notifications": {
+ "duration": {
+ "critical-urgency": {
+ "description": "Як довго залишаються видимими критичні сповіщення.",
+ "label": "Критична терміновість"
+ },
+ "low-urgency": {
+ "description": "Як довго залишаються видимими сповіщення низького пріоритету.",
+ "label": "Низька терміновість"
+ },
+ "normal-urgency": {
+ "description": "Як довго залишаються видимими сповіщення звичайного пріоритету.",
+ "label": "Звичайна терміновість"
+ },
+ "respect-expire": {
+ "description": "Використовувати час закінчення, встановлений у сповіщенні.",
+ "label": "Дотримуватись часу закінчення"
+ },
+ "section": {
+ "description": "Налаштуйте, як довго сповіщення залишаються видимими залежно від рівня терміновості.",
+ "label": "Тривалість сповіщень"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "Показувати сповіщення на певних моніторах. За замовчуванням на всіх, якщо не вибрано.",
+ "label": "Відображення на моніторах"
+ }
+ },
+ "settings": {
+ "always-on-top": {
+ "description": "Відображати сповіщення поверх повноекранних вікон та інших шарів.",
+ "label": "Завжди зверху"
+ },
+ "background-opacity": {
+ "description": "Налаштуйте непрозорість фону сповіщень.",
+ "label": "Непрозорість фону"
+ },
+ "do-not-disturb": {
+ "description": "Вимкнути всі спливаючі сповіщення, коли увімкнено.",
+ "label": "Не турбувати"
+ },
+ "enable-osd": {
+ "description": "Показувати зміни гучності та яскравості в реальному часі.",
+ "label": "Увімкнути екранну індикацію"
+ },
+ "enabled": {
+ "description": "Увімкнути або вимкнути демон сповіщень, вимагає перезапуску noctalia-shell.",
+ "label": "Увімкнути сповіщення"
+ },
+ "location": {
+ "description": "Де з'являються сповіщення на екрані.",
+ "label": "Розташування"
+ },
+ "section": {
+ "description": "Налаштуйте зовнішній вигляд та поведінку сповіщень.",
+ "label": "Зовнішній вигляд"
+ }
+ },
+ "title": "Сповіщення"
+ },
+ "osd": {
+ "always-on-top": {
+ "description": "Відображати екранну індикацію поверх повноекранних вікон та інших шарів.",
+ "label": "Завжди зверху"
+ },
+ "description": "Налаштуйте екранні індикатори, такі як накладки гучності та яскравості.",
+ "duration": {
+ "auto-hide": {
+ "description": "Налаштуйте час до зникнення екранної індикації.",
+ "label": "Автоприховування через"
+ },
+ "section": {
+ "description": "Як довго екранна індикація залишається видимою перед автоматичним приховуванням.",
+ "label": "Час автоприховування"
+ }
+ },
+ "enabled": {
+ "description": "Показувати зміни гучності та яскравості в реальному часі.",
+ "label": "Увімкнути екранну індикацію"
+ },
+ "location": {
+ "description": "Де з'являються екранні індикації.",
+ "label": "Розташування"
+ },
+ "monitors": {
+ "section": {
+ "description": "Показувати екранну індикацію на певних моніторах. За замовчуванням на всіх, якщо не вибрано.",
+ "label": "Відображення на моніторах"
+ }
+ },
+ "section": {
+ "general": {
+ "description": "Налаштуйте видимість та поведінку екранної індикації.",
+ "label": "Загальні"
+ }
+ },
+ "title": "Екранна індикація"
+ },
+ "screen-recorder": {
+ "audio": {
+ "audio-codec": {
+ "description": "Opus рекомендується для найкращої продуктивності та найменшого розміру аудіо.",
+ "label": "Кодек аудіо"
+ },
+ "audio-source": {
+ "description": "Джерело аудіо для захоплення під час запису.",
+ "label": "Джерело аудіо"
+ },
+ "section": {
+ "description": "Налаштуйте параметри запису аудіо.",
+ "label": "Налаштування аудіо"
+ }
+ },
+ "general": {
+ "output-folder": {
+ "description": "Тека, де зберігатимуться записи екрана.",
+ "label": "Тека виведення",
+ "tooltip": "Огляд теки виведення"
+ },
+ "section": {
+ "description": "Керуйте виведенням та вмістом запису екрана.",
+ "label": "Загальні налаштування"
+ },
+ "select-output-folder": "Вибрати теку виведення",
+ "show-cursor": {
+ "description": "Записувати курсор миші у відео.",
+ "label": "Показувати курсор"
+ }
+ },
+ "title": "Запис екрана",
+ "video": {
+ "color-range": {
+ "description": "Обмежений рекомендується для кращої сумісності.",
+ "label": "Діапазон кольорів"
+ },
+ "frame-rate": {
+ "description": "Цільова частота кадрів для записів екрана.",
+ "label": "Частота кадрів"
+ },
+ "section": {
+ "description": "Налаштуйте параметри запису відео.",
+ "label": "Налаштування відео"
+ },
+ "video-codec": {
+ "description": "h264 є найпоширенішим кодеком.",
+ "label": "Кодек відео"
+ },
+ "video-quality": {
+ "description": "Вища якість призводить до більшого розміру файлів.",
+ "label": "Якість відео"
+ },
+ "video-source": {
+ "description": "Рекомендується Portal, якщо виникають артефакти, спробуйте Screen.",
+ "label": "Джерело відео"
+ }
+ }
+ },
+ "user-interface": {
+ "animation-disable": {
+ "description": "Вимкнути всі анімації для швидшого, більш відгукового досвіду.",
+ "label": "Вимкнути анімації інтерфейсу"
+ },
+ "animation-speed": {
+ "description": "Налаштувати глобальну швидкість анімації.",
+ "label": "Швидкість анімації",
+ "reset": "Скинути швидкість анімації"
+ },
+ "border-radius": {
+ "description": "Керує заокругленістю кутів вікон, кнопок та інших елементів.",
+ "label": "Радіус меж",
+ "reset": "Скинути радіус меж"
+ },
+ "dim-desktop": {
+ "description": "Затемнювати робочий стіл, коли відкриті панелі або меню.",
+ "label": "Приглушити робочий стіл"
+ },
+ "panels-attached-to-bar": {
+ "description": "Панелі фіксуються до штанги та країв екрана, створюючи бездоганний вигляд зі стильними перевернутими кутами.",
+ "label": "Прив'язувати панелі до країв"
+ },
+ "panels-overlay": {
+ "description": "Забезпечує видимість панелей і панелі завдань, навіть поверх повноекранних програм.",
+ "label": "Тримати панелі та панель зверху"
+ },
+ "scaling": {
+ "description": "Змінює розмір загального користувацького інтерфейсу, окрім панелі.",
+ "label": "Масштабування інтерфейсу",
+ "reset-scaling": "Скинути масштабування інтерфейсу"
+ },
+ "section": {
+ "description": "Налаштуйте вигляд, відчуття та поведінку інтерфейсу.",
+ "label": "Зовнішній вигляд"
+ },
+ "settings-panel-attached-to-bar": {
+ "description": "Вирівнюйте вікно налаштувань відносно панелі, щоб зберегти цілісний вигляд.",
+ "label": "Прив'язувати вікно налаштувань до панелі"
+ },
+ "shadows": {
+ "description": "Увімкнути тіні під панелями та смугами.",
+ "direction": {
+ "description": "Виберіть, звідки падає тінь.",
+ "label": "Напрямок тіні"
+ },
+ "label": "Тіні"
+ },
+ "title": "Користувацький інтерфейс",
+ "tooltips": {
+ "description": "Увімкнути або вимкнути підказки в інтерфейсі.",
+ "label": "Показувати підказки"
+ }
+ },
+ "wallpaper": {
+ "automation": {
+ "custom-interval": {
+ "description": "Введіть час як ГГ:ХХ (напр., 01:30).",
+ "label": "Власний інтервал"
+ },
+ "interval": {
+ "description": "Як часто автоматично змінювати шпалери.",
+ "label": "Інтервал шпалер"
+ },
+ "random-wallpaper": {
+ "description": "Планувати випадкову зміну шпалер через регулярні інтервали.",
+ "label": "Випадкові шпалери"
+ },
+ "section": {
+ "label": "Автоматизація"
+ }
+ },
+ "look-feel": {
+ "edge-smoothness": {
+ "description": "Застосовує м'який, розмитий ефект до краю переходів.",
+ "label": "Пом'якшення краю переходу"
+ },
+ "fill-color": {
+ "description": "Виберіть колір заповнення, що може з'являтися за шпалерами.",
+ "label": "Колір заповнення"
+ },
+ "fill-mode": {
+ "description": "Виберіть, як зображення має масштабуватися відповідно до роздільної здатності монітора.",
+ "label": "Режим заповнення"
+ },
+ "section": {
+ "label": "Вигляд і відчуття"
+ },
+ "transition-duration": {
+ "description": "Тривалість анімацій переходу в секундах.",
+ "label": "Тривалість переходу"
+ },
+ "transition-type": {
+ "description": "Тип анімації при перемиканні між шпалерами.",
+ "label": "Тип переходу"
+ }
+ },
+ "settings": {
+ "enable-management": {
+ "description": "Керувати шпалерами за допомогою Noctalia. Вимкніть, якщо надаєте перевагу іншій програмі.",
+ "label": "Увімкнути управління шпалерами"
+ },
+ "enable-overview": {
+ "description": "Застосовує розмиті та затемнені шпалери до екрана огляду.",
+ "label": "Увімкнути шпалери огляду"
+ },
+ "folder": {
+ "description": "Шлях до основної теки шпалер.",
+ "label": "Тека шпалер",
+ "tooltip": "Огляд теки шпалер"
+ },
+ "monitor-specific": {
+ "description": "Встановити різні теки шпалер для кожного монітора.",
+ "label": "Окремі теки для моніторів",
+ "tooltip": "Огляд теки шпалер"
+ },
+ "recursive-search": {
+ "description": "Також шукати шпалери в підтеках директорії шпалер.",
+ "label": "Шукати в підтеках"
+ },
+ "section": {
+ "description": "Керуйте управлінням та відображенням шпалер.",
+ "label": "Налаштування шпалер"
+ },
+ "select-folder": "Вибрати теку шпалер",
+ "select-monitor-folder": "Вибрати теку шпалер монітора",
+ "selector-position": {
+ "description": "Виберіть, де відображатиметься панель вибору шпалер.",
+ "label": "Позиція"
+ }
+ },
+ "title": "Шпалери"
+ }
+ },
+ "setup": {
+ "appearance": {
+ "header": "Зовнішній вигляд",
+ "subheader": "Виберіть темний режим та джерела кольорів (Matugen або попередньо визначені)."
+ },
+ "customize": {
+ "header": "Налаштуйте ваш досвід",
+ "subheader": "Налаштуйте положення панелі, щільність, масштабування та інше."
+ },
+ "wallpaper": {
+ "choose-dir": "Виберіть теку, що містить ваші зображення шпалер",
+ "dir": {
+ "browse": "Огляд теки шпалер",
+ "description": "Виберіть теку, що містить ваші шпалери",
+ "label": "Тека шпалер",
+ "select-title": "Вибрати теку шпалер"
+ },
+ "header": "Виберіть ваші шпалери",
+ "no-dir": "Тека шпалер не вибрана",
+ "no-valid": "Не знайдено дійсних файлів зображень у: {dir}",
+ "none-in-dir": "Шпалери не знайдено в теці",
+ "preview-error": "Не вдалося завантажити зображення",
+ "select-prompt": "Виберіть шпалери нижче",
+ "subheader": "Створіть настрій за допомогою красивого фону."
+ },
+ "welcome": {
+ "note": "Лише кілька основних речей для початку - повні опції в Налаштуваннях"
+ }
+ },
+ "system": {
+ "cpu-temperature": "{temp}°C",
+ "disk-usage": "{percent}%",
+ "location-display": "{name} ({coordinates})",
+ "monitor-description": "{model} ({width}x{height} @ {scale}x)",
+ "no-media-player-detected": "Медіаплеєр не виявлено",
+ "scaling-percentage": "{percentage}%",
+ "signal-strength": "{signal}%",
+ "unknown": "Невідомо",
+ "unknown-app": "Невідома програма",
+ "unknown-layout": "Невідомо",
+ "unknown-version": "Невідомо",
+ "uptime": "Час роботи: {uptime}",
+ "user-requested": "На запит користувача",
+ "welcome-back": "З поверненням,",
+ "widget-settings-title": "Налаштування {widget}"
+ },
"toast": {
- "night-light": {
- "enabled": "Увімкнено",
- "disabled": "Вимкнено",
- "not-installed": "wlsunset не встановлено",
- "forced": "Примусова активація",
- "normal": "Звичайний режим"
- },
- "keep-awake": {
- "enabled": "Увімкнено",
- "disabled": "Вимкнено"
- },
- "wallpaper-colors": {
- "enabled": "Кольори шпалер увімкнено",
- "disabled": "Кольори шпалер вимкнено",
- "not-installed": "Matugen не встановлено - потрібний для витягу кольорів зі шпалер"
- },
- "matugen": {
- "failed": "Помилка обробки Matugen",
- "failed-general": "Matugen зіткнувся з помилкою під час обробки шаблонів"
- },
- "recording": {
- "stopping": "Зупинка запису…",
- "started": "Запис розпочато",
- "saved": "Запис збережено",
- "failed-start": "Не вдалося розпочати запис",
- "failed-gpu": "gpu-screen-recorder несподівано завершився.",
- "failed-general": "Рекордер завершився з помилкою.",
- "no-portals": "Портали робочого столу не запущені",
- "no-portals-desc": "Запустіть xdg-desktop-portal та портал композитора (wlr/hyprland/gnome/kde).",
- "not-installed": "gpu-screen-recorder не встановлено",
- "not-installed-desc": "Будь ласка, встановіть gpu-screen-recorder для використання функцій запису екрана."
- },
- "clipboard": {
- "unavailable": "Історія буфера обміну недоступна",
- "unavailable-desc": "Програма 'cliphist' не встановлена. Будь ласка, встановіть її для використання функцій історії буфера обміну."
- },
- "ipc": {
- "powerpanel-deprecated": "PowerPanel перейменовано на SessionMenu, цей виклик IPC незабаром буде застарілим. Будь ласка, використовуйте \"ipc call sessionMenu toggle\" замість цього.",
- "sidepanel-deprecated": "SidePanel перейменовано на ControlCenter, цей виклик IPC незабаром буде застарілим. Будь ласка, використовуйте \"ipc call controlCenter toggle\" замість цього."
- },
- "wifi": {
- "enabled": "Увімкнено",
- "disabled": "Вимкнено",
- "connected": "Підключено до '{ssid}'",
- "disconnected": "Відключено від '{ssid}'"
- },
- "bluetooth": {
- "enabled": "Увімкнено",
- "disabled": "Вимкнено"
- },
"airplane-mode": {
- "title": "Режим польоту",
+ "disabled": "Вимкнено",
"enabled": "Увімкнено",
- "disabled": "Вимкнено"
- },
- "internet": {
- "limited": "Підключено без інтернету"
- },
- "kofi": {
- "opened": "Сторінка Ko-fi відкрита у вашому браузері"
- },
- "do-not-disturb": {
- "enabled": "'Не турбувати' увімкнено",
- "disabled": "'Не турбувати' вимкнено",
- "enabled-desc": "Ви знайдете ці сповіщення в своїй історії.",
- "disabled-desc": "Показ усіх сповіщень."
- },
- "power-profile": {
- "changed": "Профіль живлення змінено",
- "profile-name": "\"{profile}\""
+ "title": "Режим польоту"
},
"battery": {
"low": "Низький заряд батареї",
"low-desc": "Батарея на {percent}%. Будь ласка, підключіть зарядний пристрій."
},
"battery-manager": {
- "title": "Поріг батареї",
- "set-success-desc": "Поріг батареї встановлено на {percent}%",
"initial-setup": "Потрібне початкове налаштування",
- "set-failed": "Не вдалося встановити поріг батареї",
- "install-success": "Встановлено успішно",
- "install-missing": "Необхідні файли відсутні",
- "install-unsupported": "Система не підтримується",
"install-failed": "Помилка встановлення",
+ "install-missing": "Необхідні файли відсутні",
+ "install-success": "Встановлено успішно",
+ "install-unsupported": "Система не підтримується",
+ "set-failed": "Не вдалося встановити поріг батареї",
+ "set-success-desc": "Поріг батареї встановлено на {percent}%",
+ "title": "Поріг батареї",
+ "uninstall-failed": "Помилка видалення",
"uninstall-setup": "Видалення, потрібна автентифікація",
- "uninstall-success": "Видалено успішно",
- "uninstall-failed": "Помилка видалення"
+ "uninstall-success": "Видалено успішно"
+ },
+ "bluetooth": {
+ "disabled": "Вимкнено",
+ "enabled": "Увімкнено"
+ },
+ "clipboard": {
+ "unavailable": "Історія буфера обміну недоступна",
+ "unavailable-desc": "Програма 'cliphist' не встановлена. Будь ласка, встановіть її для використання функцій історії буфера обміну."
+ },
+ "do-not-disturb": {
+ "disabled": "'Не турбувати' вимкнено",
+ "disabled-desc": "Показ усіх сповіщень.",
+ "enabled": "'Не турбувати' увімкнено",
+ "enabled-desc": "Ви знайдете ці сповіщення в своїй історії."
+ },
+ "internet": {
+ "limited": "Підключено без інтернету"
+ },
+ "ipc": {
+ "powerpanel-deprecated": "PowerPanel перейменовано на SessionMenu, цей виклик IPC незабаром буде застарілим. Будь ласка, використовуйте \"ipc call sessionMenu toggle\" замість цього.",
+ "sidepanel-deprecated": "SidePanel перейменовано на ControlCenter, цей виклик IPC незабаром буде застарілим. Будь ласка, використовуйте \"ipc call controlCenter toggle\" замість цього."
+ },
+ "keep-awake": {
+ "disabled": "Вимкнено",
+ "enabled": "Увімкнено"
+ },
+ "kofi": {
+ "opened": "Сторінка Ko-fi відкрита у вашому браузері"
},
"missing-control-center": {
- "label": "Останній віджет Центру керування видалено",
- "description": "Віджет Центру керування видалено з панелі. Щоб знову отримати до нього доступ з панелі, вам потрібно повторно додати віджет. Ви також можете відкрити його, клацнувши правою кнопкою миші на панелі."
+ "description": "Віджет Центру керування видалено з панелі. Щоб знову отримати до нього доступ з панелі, вам потрібно повторно додати віджет. Ви також можете відкрити його, клацнувши правою кнопкою миші на панелі.",
+ "label": "Останній віджет Центру керування видалено"
+ },
+ "night-light": {
+ "disabled": "Вимкнено",
+ "enabled": "Увімкнено",
+ "forced": "Примусова активація",
+ "normal": "Звичайний режим",
+ "not-installed": "wlsunset не встановлено"
+ },
+ "noctalia-performance": {
+ "disabled": "Режим продуктивності вимкнено.",
+ "enabled": "Режим продуктивності ввімкнено.",
+ "label": "Виступ Noctalia"
+ },
+ "power-profile": {
+ "changed": "Профіль живлення змінено",
+ "profile-name": "\"{profile}\""
+ },
+ "recording": {
+ "failed-general": "Рекордер завершився з помилкою.",
+ "failed-gpu": "gpu-screen-recorder несподівано завершився.",
+ "failed-start": "Не вдалося розпочати запис",
+ "no-portals": "Портали робочого столу не запущені",
+ "no-portals-desc": "Запустіть xdg-desktop-portal та портал композитора (wlr/hyprland/gnome/kde).",
+ "not-installed": "gpu-screen-recorder не встановлено",
+ "not-installed-desc": "Будь ласка, встановіть gpu-screen-recorder для використання функцій запису екрана.",
+ "saved": "Запис збережено",
+ "started": "Запис розпочато",
+ "stopping": "Зупинка запису…"
+ },
+ "theming-processor-failed": {
+ "desc-generic": "Виникла помилка під час обробки шаблонів.",
+ "title-matugen": "Помилка обробки шаблонів Matugen",
+ "title-predefined": "Помилка обробки попередньо визначеної колірної схеми"
+ },
+ "wallpaper-colors": {
+ "disabled": "Кольори шпалер вимкнено",
+ "enabled": "Кольори шпалер увімкнено",
+ "label": "Генерація кольорів шпалер",
+ "not-installed": "Matugen не встановлено - потрібний для витягу кольорів зі шпалер"
+ },
+ "wifi": {
+ "connected": "Підключено до '{ssid}'",
+ "disabled": "Вимкнено",
+ "disconnected": "Відключено від '{ssid}'",
+ "enabled": "Увімкнено"
}
},
+ "tooltips": {
+ "add-widget": "Додати віджет",
+ "bluetooth-devices": "Пристрої Bluetooth",
+ "brightness-at": "Яскравість: {brightness}%\nПравий клік для налаштувань.\nПрокрутка для зміни яскравості.",
+ "cancel-timer": "Скасувати таймер",
+ "clear-history": "Очистити історію",
+ "click-to-start-recording": "Клацніть для початку запису",
+ "click-to-stop-recording": "Клацніть для зупинки запису",
+ "close": "Закрити",
+ "connect-disconnect-devices": "Лівий клік для підключення. Правий клік для забування.",
+ "delete-notification": "Видалити сповіщення",
+ "disable-keep-awake": "Вимкнути режим неспання",
+ "do-not-disturb-disabled": "'Не турбувати' вимкнено",
+ "do-not-disturb-enabled": "'Не турбувати' увімкнено",
+ "enable-keep-awake": "Увімкнути режим неспання",
+ "forget-network": "Забути мережу",
+ "home": "Додому",
+ "input-muted": "Перемкнути вимкнення входу",
+ "keep-awake": "Не спати",
+ "keyboard-layout": "Розкладка клавіатури {layout}",
+ "manage-wifi": "Керувати Wi-Fi",
+ "microphone-volume-at": "Гучність мікрофона на {volume}%\nЛівий клік для налаштувань. Правий клік для вимкнення звуку.\nПрокрутка для зміни гучності.",
+ "move-to-center-section": "Перемістити в центральну секцію",
+ "move-to-left-section": "Перемістити в ліву секцію",
+ "move-to-right-section": "Перемістити в праву секцію",
+ "next-media": "Наступне медіа",
+ "next-month": "Наступний місяць",
+ "night-light-disabled": "Нічне світло вимкнено.\nЛівий клік для циклічного режиму.\nПравий клік для доступу до налаштувань.",
+ "night-light-enabled": "Нічне світло увімкнено.\nЛівий клік для циклічного режиму.\nПравий клік для доступу до налаштувань.",
+ "night-light-forced": "Нічне світло примусово.\nЛівий клік для циклічного режиму.\nПравий клік для доступу до налаштувань.",
+ "night-light-not-installed": "Нічне світло недоступне.\nwlsunset не встановлено.",
+ "noctalia-performance-disabled": "Режим продуктивності Noctalia вимкнено.\nЛівий клік для увімкнення.",
+ "noctalia-performance-enabled": "Режим продуктивності Noctalia увімкнено.\nЛівий клік для вимкнення.",
+ "open-control-center": "Відкрити центр керування",
+ "open-notification-history-disable-dnd": "Відкрити історію сповіщень\nПравий клік для вимкнення \"Не турбувати\".",
+ "open-notification-history-enable-dnd": "Відкрити історію сповіщень\nПравий клік для увімкнення \"Не турбувати\".",
+ "open-settings": "Відкрити налаштування",
+ "open-tray-dropdown": "Відкрити спадне меню трею",
+ "open-wallpaper-selector": "Відкрити вибір шпалер",
+ "output-muted": "Перемкнути вимкнення виходу",
+ "pause": "Пауза",
+ "play": "Відтворити",
+ "power-profile": "Профіль живлення '{profile}'",
+ "previous-media": "Попереднє медіа",
+ "previous-month": "Попередній місяць",
+ "refresh": "Оновити",
+ "refresh-devices": "Оновити пристрої",
+ "refresh-wallpaper-list": "Оновити список шпалер",
+ "remove-widget": "Видалити віджет",
+ "screen-recorder-not-installed": "Запис екрана не встановлено",
+ "session-menu": "Меню сеансу",
+ "set-power-profile": "Встановити профіль живлення \"{profile}\"",
+ "start-screen-recording": "Почати запис екрана",
+ "stop-screen-recording": "Зупинити запис екрана",
+ "switch-to-dark-mode": "Перемкнутися на темний режим",
+ "switch-to-light-mode": "Перемкнутися на світлий режим",
+ "up": "Вгору",
+ "volume-at": "Вихідна гучність на {volume}%\nЛівий клік для налаштувань. Правий клік для вимкнення звуку.\nПрокрутка для зміни гучності.",
+ "wallpaper-selector": "Лівий клік: Відкрити вибір шпалер.\nПравий клік: Встановити випадкові шпалери.",
+ "widget-settings": "Налаштування віджета"
+ },
+ "wallpaper": {
+ "configure-directory": "Налаштуйте вашу теку шпалер зі зображеннями.",
+ "fill-modes": {
+ "center": "Центр",
+ "crop": "Обрізати (Заповнити)",
+ "fit": "Вмістити (Містити)",
+ "stretch": "Розтягнути"
+ },
+ "no-match": "Збігів не знайдено.",
+ "no-wallpaper": "Шпалери не знайдено.",
+ "panel": {
+ "apply-all-monitors": {
+ "description": "Застосувати вибрані шпалери до всіх моніторів одночасно.",
+ "label": "Застосувати до всіх моніторів"
+ },
+ "search": "Пошук:",
+ "title": "Вибір шпалер"
+ },
+ "transitions": {
+ "disc": "Диск",
+ "fade": "Затухання",
+ "none": "Немає",
+ "random": "Випадковий",
+ "stripes": "Смуги",
+ "wipe": "Змітання"
+ },
+ "try-different-search": "Спробуйте інший пошуковий запит."
+ },
"weather": {
"clear-sky": "Ясне небо",
- "mainly-clear": "Переважно ясно",
- "partly-cloudy": "Частково хмарно",
- "overcast": "Похмуро",
- "fog": "Туман",
"drizzle": "Мряка",
- "snow": "Сніг",
+ "fog": "Туман",
+ "mainly-clear": "Переважно ясно",
+ "overcast": "Похмуро",
+ "partly-cloudy": "Частково хмарно",
"rain-showers": "Дощові зливи",
+ "snow": "Сніг",
"thunderstorm": "Гроза",
"unknown": "Невідомо"
},
- "authentication": {
- "failed": "Помилка автентифікації",
- "error": "Помилка автентифікації"
- },
- "general": {
- "no-results": "Немає результатів",
- "no-summary": "Немає резюме",
- "unknown": "Невідомо"
- },
- "battery": {
- "no-battery-detected": "Батарею не виявлено.",
- "charging-rate": "Швидкість зарядки: {rate} Вт.",
- "discharging-rate": "Швидкість розрядки: {rate} Вт.",
- "charging": "Зарядка.",
- "discharging": "Розрядка.",
- "plugged-in": "Підключено.",
- "idle": "Бездіяльність.",
- "time-left": "Залишилось часу: {time}.",
- "time-until-full": "Час до повного заряду: {time}.",
- "health": "Здоров'я: {percent}%",
- "panel": {
- "title": "Поріг зарядки",
- "full": "Повна ємність ({percent}%)",
- "balanced": "Збалансований ({percent}%)",
- "lifespan": "Подовжений термін служби ({percent}%)",
- "disabled": "Менеджер батареї вимкнено"
- }
- },
- "setup": {
- "customize": {
- "header": "Налаштуйте ваш досвід",
- "subheader": "Налаштуйте положення панелі, щільність, масштабування та інше."
+ "widgets": {
+ "color-picker": {
+ "apply": "Застосувати",
+ "brightness": "Яскравість",
+ "cancel": "Скасувати",
+ "hex": {
+ "description": "Введіть шістнадцятковий код кольору.",
+ "label": "Hex-колір"
+ },
+ "palette": {
+ "description": "Виберіть із широкого діапазону попередньо визначених кольорів.",
+ "label": "Палітра"
+ },
+ "rgb": {
+ "description": "Налаштуйте значення червоного, зеленого, синього та яскравості.",
+ "label": "Значення RGB"
+ },
+ "theme-colors": {
+ "description": "Швидкий доступ до колірної палітри вашої теми.",
+ "label": "Кольори теми"
+ },
+ "title": "Вибір кольору"
},
- "appearance": {
- "header": "Зовнішній вигляд",
- "subheader": "Виберіть темний режим та джерела кольорів (Matugen або попередньо визначені)."
- },
- "wallpaper": {
- "header": "Виберіть ваші шпалери",
- "subheader": "Створіть настрій за допомогою красивого фону.",
- "select-prompt": "Виберіть шпалери нижче",
- "preview-error": "Не вдалося завантажити зображення",
- "none-in-dir": "Шпалери не знайдено в теці",
- "no-dir": "Тека шпалер не вибрана",
- "no-valid": "Не знайдено дійсних файлів зображень у: {dir}",
- "choose-dir": "Виберіть теку, що містить ваші зображення шпалер",
- "dir": {
- "label": "Тека шпалер",
- "description": "Виберіть теку, що містить ваші шпалери",
- "browse": "Огляд теки шпалер",
- "select-title": "Вибрати теку шпалер"
+ "datetime-tokens": {
+ "ampm": {
+ "lowercase": "am/pm малими літерами",
+ "uppercase": "AM/PM великими літерами"
+ },
+ "common": {
+ "12hour-time-minutes": "12-годинний час з хвилинами",
+ "24hour-time-minutes": "24-годинний час з хвилинами",
+ "24hour-time-seconds": "24-годинний час з секундами",
+ "european-date": "Європейський формат дати",
+ "iso-date": "Формат дати ISO",
+ "us-date": "Формат дати США",
+ "weekday-date": "День тижня з датою",
+ "weekday-month-day": "День тижня, місяць і день"
+ },
+ "day": {
+ "abbreviated": "Скорочена назва дня",
+ "full": "Повна назва дня",
+ "leading-zero": "День з нулем попереду (01-31)",
+ "no-leading-zero": "День без нуля попереду (1-31)"
+ },
+ "hour": {
+ "leading-zero": "Година з нулем попереду (00-23) - 24-годинний формат",
+ "no-leading-zero": "Година без нуля попереду (0-23) - 24-годинний формат"
+ },
+ "minute": {
+ "leading-zero": "Хвилина з нулем попереду (00-59)",
+ "no-leading-zero": "Хвилина без нуля попереду (0-59)"
+ },
+ "month": {
+ "abbreviated": "Скорочена назва місяця",
+ "full": "Повна назва місяця",
+ "number-leading-zero": "Місяць як число з нулем попереду (01-12)",
+ "number-no-zero": "Місяць як число без нуля попереду (1-12)"
+ },
+ "second": {
+ "leading-zero": "Секунда з нулем попереду (00-59)",
+ "no-leading-zero": "Секунда без нуля попереду (0-59)"
+ },
+ "timezone": {
+ "abbreviation": "Абревіатура часового поясу"
+ },
+ "year": {
+ "four-digit": "Рік як чотиризначне число",
+ "two-digit": "Рік як двозначне число (00-99)"
}
},
- "welcome": {
- "note": "Лише кілька основних речей для початку - повні опції в Налаштуваннях"
+ "file-picker": {
+ "cancel": "Скасувати",
+ "search-placeholder": "Пошук файлів та тек...",
+ "select-current": "Вибрати поточне",
+ "select-file": "Вибрати файл",
+ "select-folder": "Вибрати теку",
+ "title": "Вибір файлу"
+ },
+ "icon-picker": {
+ "apply": "Застосувати",
+ "cancel": "Скасувати",
+ "search": {
+ "label": "Пошук"
+ },
+ "title": "Вибір значка"
+ },
+ "text-input": {
+ "clear": "Очистити"
+ },
+ "tooltip": {
+ "placeholder": "Заповнювач"
+ }
+ },
+ "wifi": {
+ "panel": {
+ "connect": "Підключити",
+ "connected": "Підключено",
+ "disabled": "Wi-Fi вимкнено",
+ "disconnect": "Відключити",
+ "disconnecting": "Відключення...",
+ "enable-message": "Увімкніть Wi-Fi, щоб побачити доступні мережі.",
+ "enter-password": "Введіть пароль...",
+ "forget": "Забути",
+ "forget-network": "Забути цю мережу?",
+ "forgetting": "Забування...",
+ "no-networks": "Мереж не знайдено",
+ "password": "Пароль",
+ "saved": "Збережено",
+ "scan-again": "Сканувати знову",
+ "searching": "Пошук близьких мереж...",
+ "title": "Wi-Fi"
}
}
-}
\ No newline at end of file
+}
diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json
index 63cf9230..0474d2e2 100644
--- a/Assets/Translations/zh-CN.json
+++ b/Assets/Translations/zh-CN.json
@@ -1,1244 +1,285 @@
{
- "settings": {
- "general": {
- "title": "通用",
- "profile": {
- "section": {
- "label": "个人资料",
- "description": "编辑您的用户详细信息和头像。"
- },
- "picture": {
- "label": "{user} 的个人资料图片",
- "description": "在整个界面中显示您的个人资料图片。"
- },
- "select-avatar": "选择头像图片"
- },
- "screen-corners": {
- "section": {
- "label": "屏幕边角",
- "description": "自定义屏幕边角的圆度和视觉效果。"
- },
- "show-corners": {
- "label": "显示屏幕圆角",
- "description": "在屏幕边缘显示圆角。"
- },
- "solid-black": {
- "label": "实心黑色边角",
- "description": "使用实心黑色而非状态栏背景色。"
- },
- "radius": {
- "label": "屏幕边角半径",
- "description": "调整屏幕圆角的弧度。",
- "reset": "重置屏幕圆角半径"
- }
- },
- "fonts": {
- "reset-scaling": "恢复默认缩放",
- "section": {
- "label": "字体",
- "description": "选择整个界面中使用的字体。"
- },
- "default": {
- "label": "默认字体",
- "description": "整个界面使用的主要字体。",
- "placeholder": "选择默认字体...",
- "search-placeholder": "搜索字体...",
- "scale": {
- "label": "默认字体大小",
- "description": "增大或减小标准文本的尺寸"
- }
- },
- "monospace": {
- "label": "等宽字体",
- "description": "用于数字和统计信息显示的等宽字体。",
- "placeholder": "选择等宽字体...",
- "search-placeholder": "搜索等宽字体...",
- "scale": {
- "label": "默认等宽字体大小",
- "description": "增大或减小等宽文本的尺寸"
- }
- }
- },
- "language": {
- "section": {
- "label": "语言",
- "description": "选择您偏好的应用程序语言。"
- },
- "select": {
- "label": "应用程序语言",
- "description": "选择应用程序界面中使用的语言。",
- "auto-detect": "自动检测"
- }
- },
- "launch-setup-wizard": "启动安装向导"
- },
- "audio": {
- "title": "音频",
- "volumes": {
- "section": {
- "label": "音量",
- "description": "调整音量控制和音频级别。"
- },
- "output-volume": {
- "label": "输出音量",
- "description": "系统全局音量级别。"
- },
- "mute-output": {
- "label": "静音输出",
- "description": "静音系统主音频输出。"
- },
- "input-volume": {
- "label": "输入音量",
- "description": "麦克风输入音量级别。"
- },
- "mute-input": {
- "label": "静音输入",
- "description": "静音默认音频输入(麦克风)。"
- },
- "step-size": {
- "label": "音量步长",
- "description": "调整音量变化的步长(滚轮、键盘快捷键)。"
- },
- "volume-overdrive": {
- "label": "允许音量过载",
- "description": "允许将音量提高到100%以上。可能不被所有硬件支持。"
- }
- },
- "devices": {
- "section": {
- "label": "音频设备",
- "description": "配置可用的音频输入和输出设备。"
- },
- "output-device": {
- "label": "输出设备",
- "description": "选择所需的音频输出设备。"
- },
- "input-device": {
- "label": "输入设备",
- "description": "选择所需的音频输入设备。"
- }
- },
- "media": {
- "section": {
- "label": "媒体播放器",
- "description": "设置您首选和要忽略的媒体应用程序。"
- },
- "primary-player": {
- "label": "主要播放器",
- "description": "输入关键词以识别您的主要播放器。",
- "placeholder": "例如:spotify, vlc, mpv"
- },
- "excluded-player": {
- "label": "排除的播放器",
- "description": "添加您希望系统忽略的播放器关键词。每个关键词应单独占一行。",
- "placeholder": "输入关键词并按 + 添加"
- },
- "visualizer-type": {
- "label": "可视化类型",
- "description": "选择媒体播放的可视化类型"
- },
- "frame-rate": {
- "label": "帧率",
- "description": "帧率越高越流畅,但会占用更多资源。"
- },
- "scrolling-title": {
- "label": "标题滚动",
- "description": "为长媒体标题启用连续滚动"
- },
- "scrolling-speed": {
- "label": "滚动速度",
- "description": "标题从头到尾滚动所需的时间(秒)"
- }
- }
- },
- "display": {
- "title": "显示",
- "monitors": {
- "section": {
- "label": "显示器设置",
- "description": "调整每个显示器的亮度设置。"
- },
- "brightness": "亮度",
- "brightness-step": {
- "label": "亮度步长",
- "description": "调整亮度变化的步长(滚轮和键盘快捷键)。"
- },
- "enforce-minimum": {
- "label": "限制最小亮度为 1%",
- "description": "解决亮度为 0% 时部分显示器背光完全关闭的问题。"
- }
- },
- "night-light": {
- "section": {
- "label": "夜间模式",
- "description": "减少蓝光发射,帮助您更好地睡眠并减轻眼睛疲劳。"
- },
- "enable": {
- "label": "启用夜间模式",
- "description": "应用暖色滤镜以减少蓝光发射。"
- },
- "temperature": {
- "label": "色温",
- "description": "设置夜间和白天的颜色温暖度。",
- "night": "夜间",
- "day": "白天"
- },
- "auto-schedule": {
- "label": "自动调度",
- "description": "基于 {location} 的日落和日出时间 - 推荐。"
- },
- "manual-schedule": {
- "label": "手动调度",
- "description": "为日出和日落设置自定义时间。",
- "sunrise": "日出时间",
- "sunset": "日落时间",
- "select-start": "选择开始时间",
- "select-stop": "选择停止时间"
- },
- "force-activation": {
- "label": "强制激活",
- "description": "忽略调度并立即应用夜间滤镜。"
- }
- }
- },
- "bar": {
- "title": "状态栏",
- "appearance": {
- "section": {
- "label": "外观",
- "description": "自定义状态栏的外观和位置。"
- },
- "position": {
- "label": "状态栏位置",
- "description": "选择在屏幕上的位置。"
- },
- "density": {
- "label": "状态栏密度",
- "description": "调整状态栏的内边距以获得紧凑或宽松的外观。"
- },
- "background-opacity": {
- "label": "背景不透明度",
- "description": "调整状态栏的背景不透明度。"
- },
- "show-capsule": {
- "label": "显示组件背景",
- "description": "为每个小部件添加背景。"
- },
- "floating": {
- "label": "浮动状态栏",
- "description": "将状态栏显示为浮动样式(类似于一个药丸)。注意:这会将屏幕边角移动到边缘。"
- },
- "margins": {
- "label": "边距",
- "description": "调整浮动状态栏周围的边距。",
- "vertical": "垂直",
- "horizontal": "水平"
- }
- },
- "widgets": {
- "section": {
- "label": "小部件定位",
- "description": "拖放小部件以重新排序。徽章表示使用情况:[L]左、[C]中、[R]右。"
- }
- },
- "monitors": {
- "section": {
- "label": "显示器显示",
- "description": "在特定显示器上显示状态栏。如果未选择,则默认为全部。"
- }
- },
- "tray": {
- "blacklist": {
- "label": "黑名单",
- "description": "添加托盘排除规则,支持通配符 (*)。",
- "placeholder": "例如:nm-applet, Fcitx*"
- }
- }
- },
- "dock": {
- "title": "Dock",
- "enabled": {
- "label": "启用 Dock",
- "description": "完全显示或隐藏 Dock"
- },
- "appearance": {
- "section": {
- "label": "外观",
- "description": "自定义 Dock 的行为和外观。"
- },
- "display": {
- "label": "显示",
- "description": "选择 Dock 的行为方式。",
- "always-visible": "始终可见",
- "auto-hide": "自动隐藏",
- "exclusive": "独占"
- },
- "background-opacity": {
- "label": "背景不透明度",
- "description": "调整 Dock 的背景不透明度。"
- },
- "floating-distance": {
- "label": "Dock 浮动距离",
- "description": "调整距离屏幕边缘的浮动距离。"
- },
- "icon-size": {
- "label": "Dock 大小",
- "description": "调整 Dock 的整体大小。"
- },
- "colorize-icons": {
- "label": "着色图标",
- "description": "将主题颜色应用到 Dock 应用图标(仅限非聚焦应用)。"
- }
- },
- "monitors": {
- "section": {
- "label": "显示器显示",
- "description": "在特定显示器上显示 Dock。如果未选择任何显示器,则默认为全部。"
- },
- "only-same-output": {
- "description": "仅显示输出结果中dock所在位置的应用。",
- "label": "仅来自相同输出的应用"
- }
- }
- },
- "launcher": {
- "title": "启动器",
- "settings": {
- "section": {
- "label": "外观",
- "description": "自定义启动器的行为和外观。"
- },
- "position": {
- "label": "位置",
- "description": "选择启动器面板出现的位置。"
- },
- "background-opacity": {
- "label": "背景不透明度",
- "description": "调整启动器的背景不透明度。"
- },
- "clipboard-history": {
- "label": "启用剪贴板历史记录",
- "description": "从启动器访问之前复制的项目。"
- },
- "sort-by-usage": {
- "label": "按使用频率排序",
- "description": "启用后,经常启动的应用程序将显示在列表首位。"
- },
- "use-app2unit": {
- "label": "使用 App2Unit 启动应用程序",
- "description": "使用替代启动方法以更好地管理应用程序进程并防止问题。"
- },
- "terminal-command": {
- "description": "启动终端的命令。例如,'kitty -e'或'gnome-terminal --'。",
- "label": "终端命令"
- },
- "custom-launch-prefix": {
- "label": "自定义启动前缀",
- "description": "使用自定义启动器前缀命令(例如,'runapp'用于systemd集成)。"
- },
- "custom-launch-prefix-enabled": {
- "label": "启用自定义启动前缀",
- "description": "使用自定义前缀启动应用程序,而不是默认方法。"
- }
- }
- },
- "notifications": {
- "title": "通知",
- "settings": {
- "section": {
- "label": "外观",
- "description": "配置通知的外观和行为。"
- },
- "do-not-disturb": {
- "label": "勿扰模式",
- "description": "启用后禁用所有通知弹出窗口。"
- },
- "enable-osd": {
- "label": "启用屏幕显示",
- "description": "实时显示音量和亮度变化。"
- },
- "location": {
- "label": "位置",
- "description": "通知在屏幕上出现的位置。"
- },
- "always-on-top": {
- "label": "始终置顶",
- "description": "在全屏窗口和其他图层上方显示通知。"
- },
- "background-opacity": {
- "label": "背景透明度",
- "description": "调整通知背景的透明度。"
- }
- },
- "duration": {
- "section": {
- "label": "通知持续时间",
- "description": "根据紧急级别配置通知保持可见的时间。"
- },
- "respect-expire": {
- "label": "尊重过期超时",
- "description": "使用通知中设置的过期超时。"
- },
- "low-urgency": {
- "label": "低紧急度",
- "description": "低优先级通知保持可见的时间。"
- },
- "normal-urgency": {
- "label": "正常紧急度",
- "description": "正常优先级通知保持可见的时间。"
- },
- "critical-urgency": {
- "label": "高紧急度",
- "description": "高优先级通知保持可见的时间。"
- }
- },
- "monitors": {
- "section": {
- "label": "显示器显示",
- "description": "在特定显示器上显示通知。如果未选择,则默认为全部。"
- }
- }
- },
- "osd": {
- "title": "屏幕显示",
- "description": "配置屏幕叠加指示器,例如音量和亮度。",
- "section": {
- "general": {
- "label": "常规",
- "description": "配置屏幕显示(OSD)的可见性与行为。"
- }
- },
- "enabled": {
- "label": "启用屏幕显示",
- "description": "实时显示音量与亮度变化。"
- },
- "always-on-top": {
- "label": "始终置顶",
- "description": "在全屏窗口和其他图层之上显示OSD。"
- },
- "location": {
- "label": "位置",
- "description": "屏幕显示出现的位置。"
- },
- "duration": {
- "section": {
- "label": "自动隐藏超时",
- "description": "屏幕显示自动隐藏前保持可见的时长。"
- },
- "auto-hide": {
- "label": "在此之后隐藏",
- "description": "调整屏幕显示消失前的时间。"
- }
- },
- "monitors": {
- "section": {
- "label": "显示器显示",
- "description": "在特定显示器上显示屏幕显示。若未选择,则默认全部。"
- }
- }
- },
- "wallpaper": {
- "title": "壁纸",
- "settings": {
- "section": {
- "label": "壁纸设置",
- "description": "控制壁纸的管理和显示方式。"
- },
- "enable-management": {
- "label": "启用壁纸管理",
- "description": "使用 Noctalia 管理壁纸。如果您更喜欢使用其他应用程序,请取消选中。"
- },
- "folder": {
- "label": "壁纸文件夹",
- "description": "您的主壁纸文件夹路径。",
- "tooltip": "浏览壁纸文件夹"
- },
- "recursive-search": {
- "label": "搜索子文件夹",
- "description": "同时在壁纸目录的子文件夹中搜索壁纸。"
- },
- "monitor-specific": {
- "label": "显示器特定目录",
- "description": "为每个显示器设置不同的壁纸文件夹。",
- "tooltip": "浏览壁纸文件夹"
- },
- "select-folder": "选择壁纸文件夹",
- "select-monitor-folder": "选择显示器壁纸文件夹"
- },
- "look-feel": {
- "section": {
- "label": "外观和感觉"
- },
- "fill-mode": {
- "label": "填充模式",
- "description": "选择图像应如何缩放以匹配显示器的分辨率。"
- },
- "fill-color": {
- "label": "填充颜色",
- "description": "选择可能出现在壁纸后面的填充颜色。"
- },
- "transition-type": {
- "label": "过渡类型",
- "description": "切换壁纸时的动画类型。"
- },
- "transition-duration": {
- "label": "过渡持续时间",
- "description": "过渡动画的持续时间(秒)。"
- },
- "edge-smoothness": {
- "label": "软化过渡边缘",
- "description": "对过渡边缘应用柔和、羽化的效果。"
- }
- },
- "automation": {
- "section": {
- "label": "自动化"
- },
- "random-wallpaper": {
- "label": "随机壁纸",
- "description": "按固定间隔调度随机壁纸更改。"
- },
- "interval": {
- "label": "壁纸间隔",
- "description": "自动更改壁纸的频率。"
- },
- "custom-interval": {
- "label": "自定义间隔",
- "description": "以 HH:MM 格式输入时间(例如:01:30)。"
- }
- }
- },
- "color-scheme": {
- "title": "配色方案",
- "color-source": {
- "section": {
- "label": "颜色来源",
- "description": "Noctalia 颜色的主要设置。"
- },
- "use-wallpaper-colors": {
- "label": "使用壁纸颜色",
- "description": "使用 Matugen 从壁纸生成颜色方案。自动提取颜色以创建一致的主题。"
- },
- "matugen-scheme-type": {
- "label": "Matugen 配色方案类型",
- "description": {
- "scheme-content": "推导出与底层图像紧密匹配的颜色",
- "scheme-expressive": "充满活力的调色板,色彩饱和度高",
- "scheme-fidelity": "保留原始色调的高保真调色板",
- "scheme-fruit-salad": "鲜艳对比色的缤纷组合",
- "scheme-monochrome": "围绕单一色调构建的极简调色板",
- "scheme-neutral": "柔和的色调,沉稳平静。",
- "scheme-rainbow": "涵盖整个光谱的多元调色板",
- "scheme-tonal-spot": "平衡的色调,重点突出"
- }
- }
- },
- "dark-mode": {
- "switch": {
- "label": "深色模式",
- "description": "切换到更暗的主题,便于夜间观看。"
- },
- "mode": {
- "description": "启用自动切换浅色和深色模式。",
- "label": "深色模式计划",
- "location": "位置",
- "manual": "手册",
- "off": "关"
- }
- },
- "predefined": {
- "section": {
- "label": "预定义配色方案",
- "description": "从预定义配色方案集合中选择。"
- },
- "generate-templates": {
- "label": "为预定义方案生成模板",
- "description": "使用预定义配色方案时生成 Matugen 模板(GTK、终端主题等)。"
- }
- },
- "templates": {
- "ui": {
- "label": "用户界面",
- "description": "桌面环境和 UI 工具包主题。",
- "gtk": {
- "description": "写入 {filepath}"
- },
- "qt": {
- "description": "写入 {filepath}"
- },
- "kcolorscheme": {
- "description": "写入 {filepath}"
- }
- },
- "terminal": {
- "label": "终端",
- "description": "终端模拟器主题。",
- "kitty": {
- "description": "写入 {filepath} 并重新加载",
- "description-missing": "需要安装 {app}"
- },
- "ghostty": {
- "description": "写入 {filepath} 并重新加载",
- "description-missing": "需要安装 {app}"
- },
- "foot": {
- "description": "写入 {filepath} 并重新加载",
- "description-missing": "需要安装 {app}"
- }
- },
- "programs": {
- "label": "程序",
- "description": "应用程序特定主题。",
- "fuzzel": {
- "description": "写入 {filepath} 并重新加载",
- "description-missing": "需要安装 {app}"
- },
- "vicinae": {
- "description": "写入 {filepath} 并重新加载",
- "description-missing": "需要安装 {app}"
- },
- "walker": {
- "description": "写入 {filepath} 并将主题设置为 noctalia",
- "description-missing": "需要安装 {app}"
- },
- "discord": {
- "description": "为 {client} 写入 {filepath}",
- "description-missing": "未检测到 Discord 客户端。请安装 vencord、vesktop、webcord、armcord、equibop、lightcord 或 dorion。"
- },
- "pywalfox": {
- "description": "写入 {filepath} 并运行 pywalfox update",
- "description-missing": "需要安装 {app}"
- },
- "code": {
- "description": "写入 {filepath}。Hyprluna 主题需要手动安装和激活。",
- "description-missing": "需要安装 {app}"
- }
- },
- "misc": {
- "label": "杂项",
- "description": "其他配置选项。",
- "user-templates": {
- "label": "用户模板",
- "description": "启用用户定义的 Matugen 配置。首次启用时将在 ~/.config/noctalia/user-templates.toml 创建模板文件"
- }
- },
- "section": {
- "description": "将颜色应用于外部应用程序。",
- "label": "模板"
- }
- }
- },
- "location": {
- "title": "位置",
- "location": {
- "section": {
- "label": "您的位置",
- "description": "通过设置您的位置获取准确的天气和调度夜间模式。"
- },
- "search": {
- "label": "搜索位置",
- "description": "例如:多伦多, 安大略省",
- "placeholder": "输入位置名称"
- }
- },
- "weather": {
- "section": {
- "label": "天气",
- "description": "选择您喜欢的温度单位。"
- },
- "enabled": {
- "label": "启用天气",
- "description": "在整个界面中显示天气信息并获取天气数据。禁用后,所有天气元素将被隐藏,不会进行网络请求。"
- },
- "fahrenheit": {
- "label": "以华氏度显示温度 (°F)",
- "description": "以华氏度而非摄氏度显示温度。"
- },
- "show-in-calendar": {
- "description": "直接在您的日历视图中显示每日天气预报。",
- "label": "在日历中显示天气"
- }
- },
- "date-time": {
- "section": {
- "label": "日期和时间",
- "description": "自定义日期和时间的显示方式。"
- },
- "12hour-format": {
- "label": "使用12小时制时间格式",
- "description": "在锁屏和日历上以 12 小时制格式显示时间。状态栏时钟有其自己的设置。"
- },
- "week-numbers": {
- "label": "显示周数",
- "description": "在日历中显示一年中的第几周(例如:第 38 周)。"
- },
- "show-events": {
- "label": "显示日历事件",
- "description": "在日历面板中显示事件。"
- },
- "use-analog": {
- "description": "在日历屏幕上显示一个模拟时钟。",
- "label": "使用模拟时钟样式"
- },
- "first-day-of-week": {
- "automatic": "自动(使用系统区域设置)",
- "description": "选择日历中一周的起始日。",
- "label": "一周的第一天"
- }
- }
- },
- "network": {
- "title": "网络",
- "section": {
- "description": "管理 Wi-Fi 和蓝牙连接。"
- },
- "wifi": {
- "label": "启用 Wi-Fi"
- },
- "bluetooth": {
- "label": "启用蓝牙"
- }
- },
- "screen-recorder": {
- "title": "屏幕录制",
- "general": {
- "section": {
- "label": "常规设置",
- "description": "管理屏幕录制输出和内容。"
- },
- "output-folder": {
- "label": "输出文件夹",
- "description": "屏幕录制将保存的文件夹。",
- "tooltip": "浏览输出文件夹"
- },
- "show-cursor": {
- "label": "显示光标",
- "description": "在视频中录制鼠标光标。"
- },
- "select-output-folder": "选择输出文件夹"
- },
- "video": {
- "section": {
- "label": "视频设置",
- "description": "配置视频录制选项。"
- },
- "video-source": {
- "label": "视频源",
- "description": "推荐使用 Portal,如果出现伪影请尝试 Screen。"
- },
- "frame-rate": {
- "label": "帧率",
- "description": "屏幕录制的目标帧率。"
- },
- "video-quality": {
- "label": "视频质量",
- "description": "更高的质量会导致更大的文件大小。"
- },
- "video-codec": {
- "label": "视频编解码器",
- "description": "h264 是最常见的编解码器。"
- },
- "color-range": {
- "label": "颜色范围",
- "description": "推荐使用 Limited 以获得更好的兼容性。"
- }
- },
- "audio": {
- "section": {
- "label": "音频设置",
- "description": "配置音频录制选项。"
- },
- "audio-source": {
- "label": "音频源",
- "description": "录制期间要捕获的音频源。"
- },
- "audio-codec": {
- "label": "音频编解码器",
- "description": "推荐使用 Opus 以获得最佳性能和最小的音频大小。"
- }
- }
- },
- "about": {
- "title": "关于",
- "noctalia": {
- "section": {
- "label": "Noctalia shell",
- "description": "一款为 Wayland 精心打造的时尚简约桌面 shell,基于 Quickshell 构建。"
- },
- "latest-version": "最新版本:",
- "installed-version": "已安装版本:",
- "download-latest": "下载最新版本"
- },
- "contributors": {
- "section": {
- "label": "贡献者",
- "description": "向我们 {count} 位超棒的贡献者致敬!",
- "description_plural": "向我们 {count} 位超棒的贡献者致敬!"
- }
- },
- "support": "支持我们"
- },
- "hooks": {
- "title": "钩子",
- "system-hooks": {
- "section": {
- "label": "系统钩子",
- "description": "配置在系统事件发生时执行的命令。"
- },
- "enable": {
- "label": "启用钩子",
- "description": "启用或禁用所有钩子命令。"
- }
- },
- "wallpaper-changed": {
- "label": "壁纸已更改",
- "description": "壁纸更改时执行的命令。",
- "placeholder": "例如:notify-send \"壁纸\" \"已更改\""
- },
- "theme-changed": {
- "label": "主题已更改",
- "description": "主题在深色和浅色模式之间切换时执行的命令。",
- "placeholder": "例如:notify-send \"主题\" \"已切换\""
- },
- "info": {
- "command-info": {
- "label": "钩子命令信息",
- "description": "• 命令通过 shell 执行 (sh -c)\n• 命令在后台运行(分离)\n• 测试按钮使用当前值执行"
- },
- "parameters": {
- "label": "可用参数",
- "description": "• 壁纸钩子: $1 = 壁纸路径, $2 = 屏幕名称\n• 主题切换钩子: $1 = true/false (深色模式状态)"
- }
- }
- },
- "control-center": {
- "position": {
- "description": "选择打开控制中心时面板出现的位置。",
- "label": "位置"
- },
- "section": {
- "description": "配置控制中心面板的位置和行为。",
- "label": "外观"
- },
- "title": "控制中心",
- "cards": {
- "section": {
- "description": "自定义在控制中心显示的控制项及其顺序。",
- "label": "卡片"
- }
- },
- "shortcuts": {
- "section": {
- "description": "配置和管理快捷方式小部件。",
- "label": "快捷方式小部件"
- },
- "sectionLeft": "左",
- "sectionRight": "右"
- }
- },
- "user-interface": {
- "animation-disable": {
- "description": "禁用所有动画以获得更快、更流畅的体验。",
- "label": "禁用 UI 动画"
- },
- "panels-overlay": {
- "label": "在覆盖层中打开面板",
- "description": "面板将显示在全屏窗口上方"
- },
- "animation-speed": {
- "description": "调整全局动画速度。",
- "label": "动画速度",
- "reset": "重置动画速度"
- },
- "border-radius": {
- "description": "控制窗口、按钮和其他元素的圆角程度。",
- "label": "边框半径",
- "reset": "重置边框半径"
- },
- "scaling": {
- "description": "更改通用用户界面大小,不包括栏。",
- "label": "界面缩放",
- "reset-scaling": "重置界面缩放"
- },
- "section": {
- "description": "自定义界面的外观、感觉和行为。",
- "label": "外观"
- },
- "title": "用户界面",
- "tooltips": {
- "description": "启用或禁用整个界面的工具提示。",
- "label": "显示工具提示"
- },
- "panels-attached-to-bar": {
- "description": "启用后,面板将以美观的倒角设计附加到栏上。",
- "label": "将面板连接到杆上"
- }
- },
- "lock-screen": {
- "compact-lockscreen": {
- "description": "仅显示登录输入和系统控制,隐藏天气和媒体小部件。",
- "label": "紧凑型锁屏"
- },
- "lock-on-suspend": {
- "description": "系统挂起时自动锁定屏幕。",
- "label": "挂起时锁定"
- },
- "title": "锁屏"
- }
- },
- "widgets": {
- "tooltip": {
- "placeholder": "占位符"
- },
- "file-picker": {
- "select-folder": "选择文件夹",
- "select-file": "选择文件",
- "cancel": "取消",
- "search-placeholder": "搜索文件和文件夹...",
- "select-current": "选择当前",
- "title": "文件选择器"
- },
- "datetime-tokens": {
- "common": {
- "12hour-time-minutes": "12小时制带分钟",
- "24hour-time-minutes": "24小时制带分钟",
- "24hour-time-seconds": "24小时制带秒",
- "weekday-month-day": "星期、月份和日期",
- "iso-date": "ISO 日期格式",
- "us-date": "美国日期格式",
- "european-date": "欧洲日期格式",
- "weekday-date": "星期和日期"
- },
- "hour": {
- "no-leading-zero": "小时无前导零 (0-23) - 24小时制",
- "leading-zero": "小时有前导零 (00-23) - 24小时制"
- },
- "minute": {
- "no-leading-zero": "分钟无前导零 (0-59)",
- "leading-zero": "分钟有前导零 (00-59)"
- },
- "second": {
- "no-leading-zero": "秒无前导零 (0-59)",
- "leading-zero": "秒有前导零 (00-59)"
- },
- "ampm": {
- "uppercase": "AM/PM 大写",
- "lowercase": "am/pm 小写"
- },
- "timezone": {
- "abbreviation": "时区缩写"
- },
- "year": {
- "two-digit": "年份为两位数 (00-99)",
- "four-digit": "年份为四位数"
- },
- "month": {
- "number-no-zero": "月份数字无前导零 (1-12)",
- "number-leading-zero": "月份数字有前导零 (01-12)",
- "abbreviated": "月份名称缩写",
- "full": "月份名称全称"
- },
- "day": {
- "no-leading-zero": "日期无前导零 (1-31)",
- "leading-zero": "日期有前导零 (01-31)",
- "abbreviated": "星期名称缩写",
- "full": "星期名称全称"
- }
- },
- "icon-picker": {
- "title": "图标选择器",
- "search": {
- "label": "搜索"
- },
- "cancel": "取消",
- "apply": "应用"
- },
- "color-picker": {
- "title": "颜色选择器",
- "hex": {
- "label": "十六进制颜色",
- "description": "输入十六进制颜色代码。"
- },
- "rgb": {
- "label": "RGB 值",
- "description": "调整红、绿、蓝和亮度值。"
- },
- "brightness": "亮度",
- "theme-colors": {
- "label": "主题颜色",
- "description": "快速访问您的主题调色板。"
- },
- "palette": {
- "label": "调色板",
- "description": "从各种预定义颜色中选择。"
- },
- "cancel": "取消",
- "apply": "应用"
- },
- "text-input": {
- "clear": "清除"
- }
+ "authentication": {
+ "error": "认证错误",
+ "failed": "认证失败"
},
"bar": {
"widget-settings": {
- "dialog": {
- "cancel": "取消",
- "apply": "应用"
- },
- "section-editor": {
- "placeholder": "选择要添加的小部件...",
- "search-placeholder": "搜索小部件..."
- },
"active-window": {
+ "colorize-icons": {
+ "description": "将主题颜色应用到活动窗口图标。",
+ "label": "着色图标"
+ },
"hide-mode": {
- "label": "隐藏模式",
- "description": "控制当没有活动窗口时小部件的行为。"
- },
- "show-app-icon": {
- "label": "显示应用图标",
- "description": "在窗口标题旁边显示应用程序图标。"
- },
- "scrolling-mode": {
- "label": "滚动模式",
- "description": "控制何时为长窗口标题启用文本滚动。"
+ "description": "控制当没有活动窗口时小部件的行为。",
+ "label": "隐藏模式"
},
"max-width": {
- "label": "最大宽度",
- "description": "设置小部件的最大水平尺寸。当内容较短时,小部件会自动收缩以适应内容。"
+ "description": "设置小部件的最大水平尺寸。当内容较短时,小部件会自动收缩以适应内容。",
+ "label": "最大宽度"
+ },
+ "scrolling-mode": {
+ "description": "控制何时为长窗口标题启用文本滚动。",
+ "label": "滚动模式"
+ },
+ "show-app-icon": {
+ "description": "在窗口标题旁边显示应用程序图标。",
+ "label": "显示应用图标"
},
"use-fixed-width": {
- "label": "使用固定宽度",
- "description": "启用后,小部件将始终使用最大宽度,而不根据内容动态调整。"
- },
- "colorize-icons": {
- "label": "着色图标",
- "description": "将主题颜色应用到活动窗口图标。"
+ "description": "启用后,小部件将始终使用最大宽度,而不根据内容动态调整。",
+ "label": "使用固定宽度"
}
},
- "system-monitor": {
- "cpu-usage": {
- "label": "CPU使用率",
- "description": "显示当前CPU使用百分比。"
+ "audio-visualizer": {
+ "color-name": {
+ "description": "选择可视化工具的颜色。",
+ "label": "填充颜色"
},
- "cpu-temperature": {
- "label": "CPU温度",
- "description": "如果可用,显示CPU温度读数。"
+ "hide-when-idle": {
+ "description": "启用后,除非正在播放媒体,否则隐藏可视化显示。",
+ "label": "无媒体播放时隐藏"
},
- "memory-usage": {
- "label": "内存使用率",
- "description": "显示当前RAM使用信息。"
- },
- "memory-percentage": {
- "label": "内存百分比",
- "description": "以百分比而不是绝对值显示内存使用情况。"
- },
- "network-traffic": {
- "label": "网络流量",
- "description": "显示网络上传和下载速度。"
- },
- "storage-usage": {
- "label": "存储使用率",
- "description": "显示磁盘空间使用信息。"
- }
- },
- "notification-history": {
- "show-unread-badge": {
- "label": "显示未读徽章",
- "description": "显示显示未读通知数量的徽章。"
- },
- "hide-badge-when-zero": {
- "label": "零时隐藏徽章",
- "description": "当没有未读通知时隐藏通知徽章。"
+ "width": {
+ "description": "自定义组件的宽度。",
+ "label": "宽度"
}
},
"battery": {
"display-mode": {
- "label": "显示模式",
- "description": "选择您希望此值显示的方式。"
+ "description": "选择您希望此值显示的方式。",
+ "label": "显示模式"
},
"low-battery-threshold": {
- "label": "低电量警告阈值",
- "description": "当电量低于此百分比时显示警告。"
- }
- },
- "control-center": {
- "use-distro-logo": {
- "label": "使用发行版徽标而不是图标",
- "description": "使用您的发行版徽标而不是自定义图标。"
- },
- "icon": {
- "label": "图标",
- "description": "从库中选择图标或自定义文件。"
- },
- "browse-library": "浏览库",
- "browse-file": "浏览文件",
- "select-custom-icon": "选择自定义图标"
- },
- "keyboard-layout": {
- "display-mode": {
- "label": "显示模式",
- "description": "选择您希望此值显示的方式。"
- }
- },
- "volume": {
- "display-mode": {
- "label": "显示模式",
- "description": "选择您希望此值显示的方式。"
- }
- },
- "workspace": {
- "label-mode": {
- "label": "标签模式",
- "description": "选择工作区标签的显示方式。"
- },
- "hide-unoccupied": {
- "label": "隐藏未占用",
- "description": "不显示没有窗口的工作区。"
- },
- "character-count": {
- "label": "字符数量",
- "description": "显示工作区名称的字符数量(1-10)。"
- }
- },
- "microphone": {
- "display-mode": {
- "label": "显示模式",
- "description": "选择您希望此值显示的方式。"
+ "description": "当电量低于此百分比时显示警告。",
+ "label": "低电量警告阈值"
}
},
"brightness": {
"display-mode": {
- "label": "显示模式",
- "description": "选择您希望此值显示的方式。"
+ "description": "选择您希望此值显示的方式。",
+ "label": "显示模式"
}
},
- "spacer": {
- "width": {
- "label": "宽度",
- "description": "间距宽度(像素)"
- }
- },
- "custom-button": {
- "icon": {
- "label": "图标",
- "description": "从库中选择图标。"
- },
- "browse": "浏览",
- "left-click": {
- "label": "左键点击",
- "description": "左键点击按钮时执行的命令。"
- },
- "right-click": {
- "label": "右键点击",
- "description": "右键点击按钮时执行的命令。"
- },
- "middle-click": {
- "label": "中键点击",
- "description": "中键点击按钮时执行的命令。"
- },
- "dynamic-text": "动态文本",
- "display-command-output": {
- "label": "显示命令输出",
- "description": "输入一个定期运行的命令。其输出的第一行将显示为文本。",
- "stream-description": "输入一个要持续运行的命令。"
- },
- "refresh-interval": {
- "label": "刷新间隔",
- "description": "间隔时间(毫秒)。"
- },
- "text-stream": {
- "label": "流",
- "description": "来自命令的流式输出行将作为文本显示在按钮上。"
- },
- "collapse-condition": {
- "label": "折叠条件",
- "description": "如果输出文本与此值匹配,按钮将折叠。"
- },
- "parse-json": {
- "label": "将输出解析为 JSON",
- "description": "将命令输出解析为 JSON 对象,以动态设置文本和图标。"
- }
- },
- "media-mini": {
- "hide-mode": {
- "label": "隐藏模式",
- "description": "控制当没有媒体播放时小部件的行为。"
- },
- "show-album-art": {
- "label": "显示专辑封面",
- "description": "显示当前播放曲目的专辑封面。"
- },
- "show-visualizer": {
- "label": "显示可视化器",
- "description": "播放音乐时显示音频可视化器。"
- },
- "visualizer-type": {
- "label": "可视化器类型",
- "description": "选择要显示的音频可视化器样式。"
- },
- "max-width": {
- "label": "最大宽度",
- "description": "设置小部件的最大水平尺寸。当内容较短时,小部件会自动收缩以适应内容。"
- },
- "use-fixed-width": {
- "label": "使用固定宽度",
- "description": "启用后,小部件将始终使用最大宽度,而不根据内容动态调整。"
- },
- "scrolling-mode": {
- "label": "滚动模式",
- "description": "控制何时为长曲目标题启用文本滚动。"
- },
- "no-active-player": "无活动播放器"
- },
"clock": {
- "use-primary-color": {
- "label": "使用主颜色",
- "description": "启用后,将应用主颜色进行强调。"
- },
- "use-monospaced-font": {
- "label": "使用等宽字体",
- "description": "启用后,时钟将使用等宽字体。"
- },
"clock-display": {
- "label": "时钟显示",
- "description": "通过从下面的列表添加标记来自定义时钟显示。要使用 12 小时制,必须包含 'AP' 标记。"
+ "description": "通过从下面的列表添加标记来自定义时钟显示。要使用 12 小时制,必须包含 'AP' 标记。",
+ "label": "时钟显示"
},
- "horizontal-bar": {
- "label": "水平栏",
- "description": "提示:使用 \\n 创建换行。"
- },
- "vertical-bar": {
- "label": "垂直栏",
- "description": "使用空格将每个部分分隔到新行。"
- },
- "preview": "预览",
"custom-font": {
"description": "选择时钟显示使用的自定义字体。",
"label": "自定义字体",
"placeholder": "选择自定义字体...",
"search-placeholder": "搜索字体..."
},
+ "horizontal-bar": {
+ "description": "提示:使用 \\n 创建换行。",
+ "label": "水平栏"
+ },
+ "preview": "预览",
"use-custom-font": {
"description": "使用自定义字体覆盖时钟的默认字体选择。",
"label": "使用自定义字体"
+ },
+ "use-monospaced-font": {
+ "description": "启用后,时钟将使用等宽字体。",
+ "label": "使用等宽字体"
+ },
+ "use-primary-color": {
+ "description": "启用后,将应用主颜色进行强调。",
+ "label": "使用主颜色"
+ },
+ "vertical-bar": {
+ "description": "使用空格将每个部分分隔到新行。",
+ "label": "垂直栏"
+ }
+ },
+ "control-center": {
+ "browse-file": "浏览文件",
+ "browse-library": "浏览库",
+ "icon": {
+ "description": "从库中选择图标或自定义文件。",
+ "label": "图标"
+ },
+ "select-custom-icon": "选择自定义图标",
+ "use-distro-logo": {
+ "description": "使用您的发行版徽标而不是自定义图标。",
+ "label": "使用发行版徽标而不是图标"
+ },
+ "colorize-distro-logo": {
+ "description": "将主题颜色应用到您的发行版徽标。",
+ "label": "为发行版徽标着色"
+ }
+ },
+ "custom-button": {
+ "browse": "浏览",
+ "collapse-condition": {
+ "description": "如果输出文本与此值匹配,按钮将折叠。",
+ "label": "折叠条件"
+ },
+ "display-command-output": {
+ "description": "输入一个定期运行的命令。其输出的第一行将显示为文本。",
+ "label": "显示命令输出",
+ "stream-description": "输入一个要持续运行的命令。"
+ },
+ "dynamic-text": "动态文本",
+ "hide-vertical": {
+ "description": "如果启用,当栏处于垂直布局(左或右)时,将不显示命令输出的文本。",
+ "label": "在垂直栏中隐藏文本"
+ },
+ "icon": {
+ "description": "从库中选择图标。",
+ "label": "图标"
+ },
+ "left-click": {
+ "description": "左键点击按钮时执行的命令。",
+ "label": "左键点击"
+ },
+ "middle-click": {
+ "description": "中键点击按钮时执行的命令。",
+ "label": "中键点击"
+ },
+ "parse-json": {
+ "description": "将命令输出解析为 JSON 对象,以动态设置文本和图标。",
+ "label": "将输出解析为 JSON"
+ },
+ "refresh-interval": {
+ "description": "间隔时间(毫秒)。",
+ "label": "刷新间隔"
+ },
+ "right-click": {
+ "description": "右键点击按钮时执行的命令。",
+ "label": "右键点击"
+ },
+ "text-stream": {
+ "description": "来自命令的流式输出行将作为文本显示在按钮上。",
+ "label": "流"
+ }
+ },
+ "dialog": {
+ "apply": "应用",
+ "cancel": "取消"
+ },
+ "keyboard-layout": {
+ "display-mode": {
+ "description": "选择您希望此值显示的方式。",
+ "label": "显示模式"
+ }
+ },
+ "lock-keys": {
+ "browse": "浏览",
+ "show-caps-lock": {
+ "description": "显示大写锁定状态。",
+ "label": "大写锁定"
+ },
+ "show-num-lock": {
+ "description": "显示数字锁定键状态。",
+ "label": "数字锁定"
+ },
+ "show-scroll-lock": {
+ "description": "显示滚动锁定状态。",
+ "label": "滚动锁定"
+ }
+ },
+ "media-mini": {
+ "hide-mode": {
+ "description": "控制当没有媒体播放时小部件的行为。",
+ "label": "隐藏模式"
+ },
+ "max-width": {
+ "description": "设置小部件的最大水平尺寸。当内容较短时,小部件会自动收缩以适应内容。",
+ "label": "最大宽度"
+ },
+ "no-active-player": "无活动播放器",
+ "scrolling-mode": {
+ "description": "控制何时为长曲目标题启用文本滚动。",
+ "label": "滚动模式"
+ },
+ "show-album-art": {
+ "description": "显示当前播放曲目的专辑封面。",
+ "label": "显示专辑封面"
+ },
+ "show-visualizer": {
+ "description": "播放音乐时显示音频可视化器。",
+ "label": "显示可视化器"
+ },
+ "use-fixed-width": {
+ "description": "启用后,小部件将始终使用最大宽度,而不根据内容动态调整。",
+ "label": "使用固定宽度"
+ },
+ "visualizer-type": {
+ "description": "选择要显示的音频可视化器样式。",
+ "label": "可视化器类型"
+ }
+ },
+ "microphone": {
+ "display-mode": {
+ "description": "选择您希望此值显示的方式。",
+ "label": "显示模式"
+ }
+ },
+ "notification-history": {
+ "hide-badge-when-zero": {
+ "description": "当没有未读通知时隐藏通知徽章。",
+ "label": "零时隐藏徽章"
+ },
+ "show-unread-badge": {
+ "description": "显示显示未读通知数量的徽章。",
+ "label": "显示未读徽章"
+ }
+ },
+ "section-editor": {
+ "placeholder": "选择要添加的小部件...",
+ "search-placeholder": "搜索小部件..."
+ },
+ "spacer": {
+ "width": {
+ "description": "间距宽度(像素)",
+ "label": "宽度"
+ }
+ },
+ "system-monitor": {
+ "cpu-temperature": {
+ "description": "如果可用,显示CPU温度读数。",
+ "label": "CPU温度"
+ },
+ "cpu-usage": {
+ "description": "显示当前CPU使用百分比。",
+ "label": "CPU使用率"
+ },
+ "memory-percentage": {
+ "description": "以百分比而不是绝对值显示内存使用情况。",
+ "label": "内存百分比"
+ },
+ "memory-usage": {
+ "description": "显示当前RAM使用信息。",
+ "label": "内存使用率"
+ },
+ "network-traffic": {
+ "description": "显示网络上传和下载速度。",
+ "label": "网络流量"
+ },
+ "storage-usage": {
+ "description": "显示磁盘空间使用信息。",
+ "label": "存储使用率"
}
},
"taskbar": {
+ "colorize-icons": {
+ "description": "将主题颜色应用到任务栏图标。",
+ "label": "着色图标"
+ },
"hide-mode": {
- "label": "隐藏模式",
- "description": "当没有匹配的窗口时控制小部件的行为。"
+ "description": "当没有匹配的窗口时控制小部件的行为。",
+ "label": "隐藏模式"
},
"only-active-workspaces": {
- "label": "仅显示活动工作区",
- "description": "仅显示来自活动工作区的应用程序"
+ "description": "仅显示来自活动工作区的应用程序",
+ "label": "仅显示活动工作区"
},
"only-same-output": {
- "label": "仅显示同屏幕",
- "description": "仅显示任务栏所在屏幕上的应用程序"
- },
- "colorize-icons": {
- "label": "着色图标",
- "description": "将主题颜色应用到任务栏图标。"
+ "description": "仅显示任务栏所在屏幕上的应用程序",
+ "label": "仅显示同屏幕"
}
},
"taskbar-grouped": {
@@ -1253,118 +294,69 @@
},
"tray": {
"colorize-icons": {
- "label": "着色图标",
- "description": "将主题颜色应用到系统托盘图标。"
+ "description": "将主题颜色应用到系统托盘图标。",
+ "label": "着色图标"
+ },
+ "drawer-enabled": {
+ "description": "启用时,未固定的托盘项显示在抽屉面板中。禁用时,所有托盘项都内联显示。",
+ "label": "启用抽屉"
}
},
- "audio-visualizer": {
- "width": {
- "label": "宽度",
- "description": "自定义组件的宽度。"
- },
- "hide-when-idle": {
- "label": "无媒体播放时隐藏",
- "description": "启用后,除非正在播放媒体,否则隐藏可视化显示。"
- },
- "color-name": {
- "description": "选择可视化工具的颜色。",
- "label": "填充颜色"
+ "volume": {
+ "display-mode": {
+ "description": "选择您希望此值显示的方式。",
+ "label": "显示模式"
}
},
- "lock-keys": {
- "show-caps-lock": {
- "description": "显示大写锁定状态。",
- "label": "大写锁定"
+ "workspace": {
+ "character-count": {
+ "description": "显示工作区名称的字符数量(1-10)。",
+ "label": "字符数量"
},
- "show-num-lock": {
- "description": "显示数字锁定键状态。",
- "label": "数字锁定"
+ "hide-unoccupied": {
+ "description": "不显示没有窗口的工作区。",
+ "label": "隐藏未占用"
},
- "show-scroll-lock": {
- "description": "显示滚动锁定状态。",
- "label": "滚动锁定"
- },
- "indicator-style": {
- "hex": "六边形字母",
- "large": "大写字母",
- "small": "小写字母",
- "square": "方块字",
- "square-round": "圆角方形字母",
- "circle": "圆圈字母",
- "circle-dash": "虚线圆圈字母",
- "circle-dot": "虚线圆圈字母",
- "description": "锁键指示器的图标样式",
- "label": "指标样式"
+ "label-mode": {
+ "description": "选择工作区标签的显示方式。",
+ "label": "标签模式"
}
}
}
},
- "notifications": {
+ "battery": {
+ "charging": "正在充电。",
+ "charging-rate": "充电速率:{rate} W。",
+ "discharging": "正在放电。",
+ "discharging-rate": "放电速率:{rate} W。",
+ "health": "健康状况:{percent}%",
+ "idle": "空闲。",
+ "no-battery-detected": "未检测到电池。",
"panel": {
- "title": "通知",
- "no-notifications": "无通知",
- "description": "您的通知将在到达时显示在此处。",
- "click-to-expand": "点击展开"
- }
- },
- "wallpaper": {
- "panel": {
- "title": "壁纸选择器",
- "apply-all-monitors": {
- "label": "应用到所有显示器",
- "description": "一次性将选定的壁纸应用到所有显示器。"
- },
- "search": "搜索:"
+ "balanced": "平衡 ({percent}%)",
+ "disabled": "电池管理器已禁用",
+ "full": "完全容量 ({percent}%)",
+ "lifespan": "延长寿命 ({percent}%)",
+ "title": "充电阈值"
},
- "transitions": {
- "none": "无",
- "random": "随机",
- "fade": "淡入淡出",
- "disc": "圆盘",
- "stripes": "条纹",
- "wipe": "擦除"
- },
- "fill-modes": {
- "center": "居中",
- "crop": "裁剪(填充)",
- "fit": "适应(包含)",
- "stretch": "拉伸"
- },
- "no-match": "未找到匹配项。",
- "no-wallpaper": "未找到壁纸。",
- "try-different-search": "尝试不同的搜索查询。",
- "configure-directory": "使用图像配置您的壁纸目录。"
+ "plugged-in": "已接通电源。",
+ "time-left": "剩余时间:{time}。",
+ "time-until-full": "充满所需时间:{time}。"
},
"bluetooth": {
"panel": {
- "title": "蓝牙",
- "disabled": "蓝牙已禁用",
- "enable-message": "启用蓝牙以查看可用设备。",
- "connected-devices": "已连接设备",
- "known-devices": "已知设备",
"available-devices": "可用设备",
- "scanning": "正在扫描设备...",
- "pairing-mode": "确保您的设备处于配对模式。"
- }
- },
- "wifi": {
- "panel": {
- "title": "Wi-Fi",
- "disabled": "Wi-Fi 已禁用",
- "enable-message": "启用 Wi-Fi 以查看可用网络。",
- "searching": "正在搜索附近网络...",
- "connected": "已连接",
- "disconnecting": "正在断开连接...",
- "forgetting": "正在忘记...",
- "saved": "已保存",
- "disconnect": "断开连接",
- "enter-password": "输入密码...",
+ "blocked": "已阻止",
"connect": "连接",
- "password": "密码",
- "forget-network": "忘记此网络?",
- "forget": "忘记",
- "no-networks": "未找到网络",
- "scan-again": "重新扫描"
+ "connected-devices": "已连接设备",
+ "disabled": "蓝牙已禁用",
+ "disconnect": "断开",
+ "enable-message": "启用蓝牙以查看可用设备。",
+ "known-devices": "已知设备",
+ "pairing": "配对...",
+ "pairing-mode": "确保您的设备处于配对模式。",
+ "scanning": "正在扫描设备...",
+ "title": "蓝牙"
}
},
"calendar": {
@@ -1375,281 +367,246 @@
"loading": "正在加载天气…"
}
},
- "tooltips": {
- "refresh": "刷新",
- "close": "关闭",
- "up": "向上",
- "home": "主目录",
- "refresh-wallpaper-list": "刷新壁纸列表",
- "refresh-devices": "刷新设备",
- "forget-network": "忘记网络",
- "clear-history": "清除历史记录",
- "delete-notification": "删除通知",
- "previous-month": "上个月",
- "next-month": "下个月",
- "add-widget": "添加小部件",
- "widget-settings": "小部件设置",
- "remove-widget": "移除小部件",
- "move-to-left-section": "移动到左侧部分",
- "move-to-center-section": "移动到中央部分",
- "move-to-right-section": "移动到右侧部分",
- "open-settings": "打开设置",
- "session-menu": "会话菜单",
- "cancel-timer": "取消计时器",
- "start-screen-recording": "开始屏幕录制",
- "stop-screen-recording": "停止屏幕录制",
- "screen-recorder-not-installed": "屏幕录制器未安装",
- "keep-awake": "保持唤醒",
- "enable-keep-awake": "启用保持唤醒",
- "disable-keep-awake": "禁用保持唤醒",
- "wallpaper-selector": "左键点击:打开壁纸选择器。\n右键点击:设置随机壁纸。",
- "do-not-disturb-enabled": "'勿扰模式'已启用",
- "do-not-disturb-disabled": "'勿扰模式'已禁用",
- "connect-disconnect-devices": "左键点击连接。右键点击忘记。",
- "set-power-profile": "设置\"{profile}\"电源模式",
- "switch-to-light-mode": "切换到浅色模式",
- "switch-to-dark-mode": "切换到深色模式",
- "night-light-disabled": "夜间模式已禁用。\n左键点击循环模式。\n右键点击访问设置。",
- "night-light-enabled": "夜间模式已启用。\n左键点击循环模式。\n右键点击访问设置。",
- "night-light-forced": "夜间模式已强制启用。\n左键点击循环模式。\n右键点击访问设置。",
- "night-light-not-installed": "夜间模式不可用。\nwlsunset 未安装。",
- "click-to-start-recording": "点击开始录制",
- "click-to-stop-recording": "点击停止录制",
- "open-control-center": "打开控制中心",
- "brightness-at": "亮度:{brightness}%\n右键点击进入设置。\n滚动调整亮度。",
- "manage-wifi": "管理 Wi-Fi",
- "bluetooth-devices": "蓝牙设备",
- "open-notification-history-enable-dnd": "打开通知历史记录\n右键点击启用\"勿扰模式\"。",
- "open-notification-history-disable-dnd": "打开通知历史记录\n右键点击禁用\"勿扰模式\"。",
- "open-wallpaper-selector": "打开壁纸选择器",
- "previous-media": "上一媒体",
- "pause": "暂停",
- "play": "播放",
- "next-media": "下一媒体",
- "power-profile": "'{profile}' 电源模式",
- "keyboard-layout": "{layout} 键盘布局",
- "output-muted": "静音输出设备",
- "input-muted": "静音输入设备",
- "microphone-volume-at": "麦克风音量 {volume}%\n左键点击进入设置。右键点击切换静音。\n滚动滚轮调节音量。",
- "volume-at": "输出音量 {volume}%\n左键点击进入设置。右键点击切换静音。\n滚动滚轮调节音量。"
- },
"clock": {
"tooltip": "打开日历"
},
"dock": {
"menu": {
+ "close": "关闭",
"focus": "聚焦",
"pin": "固定",
- "unpin": "取消固定",
- "close": "关闭"
+ "unpin": "取消固定"
}
},
+ "general": {
+ "no-results": "无结果",
+ "no-summary": "无摘要",
+ "unknown": "未知"
+ },
"launcher": {
"pin": "固定到 Dock",
"unpin": "从 Dock 取消固定"
},
- "placeholders": {
- "search-icons": "例如:noctalia, niri, battery, cloud",
- "profile-picture-path": "/home/user/.face",
- "enter-width-pixels": "输入宽度(像素)",
- "enter-command": "输入要执行的命令(应用程序或自定义脚本)",
- "command-example": "echo \"Hello World\"",
- "enter-text-to-collapse": "例如,“nothing is playing”。使用 /regex/ 表示匹配模式。",
- "search-wallpapers": "输入以筛选壁纸...",
- "search-launcher": "搜索条目...或使用 > 执行命令",
- "search": "搜索...",
- "select": "选择",
- "cancel": "取消",
- "test": "测试"
+ "lock-screen": {
+ "authenticating": "正在验证...",
+ "authentication-failed": "验证失败",
+ "password": "请输入密码...",
+ "restart": "重启",
+ "shut-down": "关机",
+ "suspend": "挂起",
+ "welcome-back": "欢迎回来,"
+ },
+ "notifications": {
+ "panel": {
+ "click-to-expand": "点击展开",
+ "description": "您的通知将在到达时显示在此处。",
+ "no-notifications": "无通知",
+ "title": "通知"
+ }
},
"options": {
"bar": {
- "position": {
- "top": "顶部",
- "bottom": "底部",
- "left": "左侧",
- "right": "右侧"
- },
"density": {
+ "comfortable": "舒适",
"compact": "紧凑",
"default": "默认",
- "comfortable": "舒适",
"mini": "迷你"
+ },
+ "position": {
+ "bottom": "底部",
+ "left": "左侧",
+ "right": "右侧",
+ "top": "顶部"
}
},
- "launcher": {
- "position": {
- "center": "居中(默认)",
- "top_left": "左上角",
- "top_right": "右上角",
- "bottom_left": "左下角",
- "bottom_right": "右下角",
- "bottom_center": "底部居中",
- "top_center": "顶部居中",
- "center_left": "左侧居中",
- "center_right": "右侧居中"
- }
+ "colors": {
+ "error": "错误色",
+ "onSurface": "表层对应色",
+ "primary": "主要色",
+ "secondary": "次要色",
+ "tertiary": "第三色"
},
"control-center": {
"position": {
- "close_to_bar_button": "靠近栏按钮",
- "top_left": "左上角",
- "top_right": "右上角",
+ "bottom_center": "底部居中",
"bottom_left": "左下角",
"bottom_right": "右下角",
- "bottom_center": "底部居中",
+ "center": "居中",
+ "close_to_bar_button": "靠近栏按钮",
"top_center": "顶部居中",
- "center": "居中"
+ "top_left": "左上角",
+ "top_right": "右上角"
},
"quickSettingsStyle": {
- "modern": "现代",
"classic": "经典",
- "compact": "紧凑"
+ "compact": "紧凑",
+ "modern": "现代"
+ }
+ },
+ "display-mode": {
+ "always-hide": "始终隐藏",
+ "always-show": "始终显示",
+ "force-open": "强制打开",
+ "on-hover": "悬停时显示"
+ },
+ "frame-rates": {
+ "fps": "{fps} FPS"
+ },
+ "hide-modes": {
+ "hidden": "当为空时隐藏",
+ "idle": "空闲时隐藏",
+ "transparent": "空时透明",
+ "visible": "始终可见"
+ },
+ "launcher": {
+ "position": {
+ "bottom_center": "底部居中",
+ "bottom_left": "左下角",
+ "bottom_right": "右下角",
+ "center": "居中(默认)",
+ "center_left": "左侧居中",
+ "center_right": "右侧居中",
+ "follow_bar": "跟随栏(默认)",
+ "top_center": "顶部居中",
+ "top_left": "左上角",
+ "top_right": "右上角"
}
},
"osd": {
"position": {
- "top_left": "左上角",
- "top_right": "右上角",
- "top_center": "顶部居中",
+ "bottom_center": "底部居中",
"bottom_left": "左下角",
"bottom_right": "右下角",
- "bottom_center": "底部居中",
"center_left": "左侧居中",
- "center_right": "右侧居中"
+ "center_right": "右侧居中",
+ "top_center": "顶部居中",
+ "top_left": "左上角",
+ "top_right": "右上角"
}
},
- "display-mode": {
- "on-hover": "悬停时显示",
- "always-show": "始终显示",
- "always-hide": "始终隐藏",
- "force-open": "强制打开"
- },
- "workspace-labels": {
- "none": "无",
- "index": "索引",
- "name": "名称"
- },
- "visualizer-types": {
- "none": "无",
- "linear": "线性",
- "mirrored": "镜像",
- "wave": "波形"
+ "screen-recording": {
+ "audio-sources": {
+ "both": "系统输出 + 麦克风输入",
+ "microphone-input": "麦克风输入",
+ "system-output": "系统输出"
+ },
+ "color-range": {
+ "full": "完全",
+ "limited": "有限"
+ },
+ "quality": {
+ "high": "高",
+ "medium": "中等",
+ "ultra": "超高",
+ "very-high": "很高"
+ },
+ "sources": {
+ "portal": "Portal",
+ "screen": "屏幕"
+ }
},
"scrolling-modes": {
"always": "始终滚动",
"hover": "悬停时滚动",
"never": "从不滚动"
},
- "frame-rates": {
- "fps": "{fps} FPS"
+ "shadow-direction": {
+ "bottom": "下方",
+ "bottom_left": "左下",
+ "bottom_right": "右下",
+ "center": "居中",
+ "left": "左侧",
+ "right": "右侧",
+ "top": "上方",
+ "top_left": "左上",
+ "top_right": "右上"
},
- "screen-recording": {
- "sources": {
- "portal": "Portal",
- "screen": "屏幕"
- },
- "quality": {
- "medium": "中等",
- "high": "高",
- "very-high": "很高",
- "ultra": "超高"
- },
- "color-range": {
- "limited": "有限",
- "full": "完全"
- },
- "audio-sources": {
- "system-output": "系统输出",
- "microphone-input": "麦克风输入",
- "both": "系统输出 + 麦克风输入"
- }
+ "visualizer-quality": {
+ "high": "高",
+ "low": "低"
},
- "hide-modes": {
- "hidden": "当为空时隐藏",
- "transparent": "空时透明",
- "visible": "始终可见"
+ "visualizer-types": {
+ "linear": "线性",
+ "mirrored": "镜像",
+ "none": "无",
+ "wave": "波形"
},
- "colors": {
- "error": "错误",
- "primary": "主要的",
- "secondary": "次要的",
- "tertiary": "第三纪",
- "onSurface": "表面之上"
+ "workspace-labels": {
+ "index": "索引",
+ "name": "名称",
+ "none": "无"
}
},
- "session-menu": {
- "title": "会话菜单",
- "action-in-seconds": "{action} in {seconds} seconds...",
- "lock": "锁定",
- "lock-and-suspend": "锁定并挂起",
- "suspend": "挂起",
- "reboot": "重启",
- "logout": "注销",
- "shutdown": "关机"
+ "placeholders": {
+ "cancel": "取消",
+ "command-example": "echo \"Hello World\"",
+ "enter-command": "输入要执行的命令(应用程序或自定义脚本)",
+ "enter-text-to-collapse": "例如,“nothing is playing”。使用 /regex/ 表示匹配模式。",
+ "enter-tooltip": "输入工具提示",
+ "enter-width-pixels": "输入宽度(像素)",
+ "profile-picture-path": "/home/user/.face",
+ "search": "搜索...",
+ "search-icons": "例如:noctalia, niri, battery, cloud",
+ "search-launcher": "搜索条目...或使用 > 执行命令",
+ "search-wallpapers": "输入以筛选壁纸...",
+ "select": "选择",
+ "test": "测试"
},
"plugins": {
"applications": "应用程序",
- "clipboard": "剪贴板历史记录",
"calculator": "计算器",
- "clipboard-search-description": "搜索剪贴板历史记录",
+ "calculator-description": "计算器 - 计算数学表达式",
+ "calculator-enter-expression": "输入数学表达式",
+ "calculator-error": "错误",
+ "calculator-name": "计算器",
+ "clipboard": "剪贴板历史记录",
"clipboard-clear-description": "清除所有剪贴板历史记录",
+ "clipboard-clear-description-full": "从剪贴板历史记录中移除所有项目",
+ "clipboard-clear-history": "清除剪贴板历史记录",
"clipboard-history-disabled": "剪贴板历史记录已禁用",
"clipboard-history-disabled-description": "在设置中启用剪贴板历史记录或安装 cliphist",
- "clipboard-clear-history": "清除剪贴板历史记录",
- "clipboard-clear-description-full": "从剪贴板历史记录中移除所有项目",
"clipboard-loading": "正在加载剪贴板历史记录...",
"clipboard-loading-description": "请稍候",
- "calculator-description": "计算器 - 计算数学表达式",
- "calculator-name": "计算器",
- "calculator-enter-expression": "输入数学表达式",
- "calculator-error": "错误"
- },
- "system": {
- "uptime": "系统运行时间:{uptime}",
- "welcome-back": "欢迎回来,",
- "monitor-description": "{model} ({width}x{height} @ {scale}x)",
- "scaling-percentage": "{percentage}%",
- "location-display": "{name} ({coordinates})",
- "signal-strength": "{signal}%",
- "cpu-temperature": "{temp}°C",
- "disk-usage": "{percent}%",
- "widget-settings-title": "{widget} 设置",
- "unknown-app": "未知应用",
- "no-media-player-detected": "未检测到媒体播放器",
- "user-requested": "用户请求",
- "unknown": "未知",
- "unknown-version": "未知",
- "unknown-layout": "未知"
- },
- "lock-screen": {
- "password": "请输入密码...",
- "welcome-back": "欢迎回来,",
- "authenticating": "正在验证...",
- "authentication-failed": "验证失败",
- "shut-down": "关机",
- "restart": "重启",
- "suspend": "挂起"
+ "clipboard-search-description": "搜索剪贴板历史记录"
},
"quickSettings": {
+ "bluetooth": {
+ "label": {
+ "disabled": "蓝牙",
+ "enabled": "蓝牙"
+ },
+ "tooltip": {
+ "action": "点击管理蓝牙设备"
+ }
+ },
+ "keepAwake": {
+ "label": {
+ "disabled": "保持唤醒",
+ "enabled": "保持唤醒"
+ },
+ "tooltip": {
+ "action": "点击切换保持唤醒模式"
+ }
+ },
+ "nightLight": {
+ "label": {
+ "disabled": "夜间模式",
+ "enabled": "夜间模式",
+ "forced": "夜间模式"
+ },
+ "tooltip": {
+ "action": "点击切换夜间模式\n右键:打开设置"
+ }
+ },
"notifications": {
"label": {
- "enabled": "通知",
- "disabled": "勿扰模式"
+ "disabled": "勿扰模式",
+ "enabled": "通知"
},
"tooltip": {
"action": "左键:打开通知历史\n右键:切换勿扰模式"
}
},
- "screenRecorder": {
- "label": {
- "recording": "停止",
- "stopped": "录制"
- },
- "tooltip": {
- "action": "点击开始/停止屏幕录制"
- }
- },
"powerProfile": {
"label": {
"unavailable": "电源模式"
@@ -1659,33 +616,13 @@
"disabled": "安装 power-profiles-daemon 以使用电源模式"
}
},
- "wifi": {
+ "screenRecorder": {
"label": {
- "ethernet": "以太网",
- "wifi": "Wi-Fi",
- "disconnected": "Wi-Fi 已断开"
+ "recording": "停止",
+ "stopped": "录制"
},
"tooltip": {
- "action": "点击管理 Wi-Fi 连接"
- }
- },
- "bluetooth": {
- "label": {
- "enabled": "蓝牙",
- "disabled": "蓝牙"
- },
- "tooltip": {
- "action": "点击管理蓝牙设备"
- }
- },
- "nightLight": {
- "label": {
- "enabled": "夜间模式",
- "forced": "夜间模式",
- "disabled": "夜间模式"
- },
- "tooltip": {
- "action": "点击切换夜间模式\n右键:打开设置"
+ "action": "点击开始/停止屏幕录制"
}
},
"wallpaperSelector": {
@@ -1694,177 +631,1386 @@
"action": "左键:打开壁纸选择器\n右键:设置随机壁纸"
}
},
- "keepAwake": {
+ "wifi": {
"label": {
- "enabled": "保持唤醒",
- "disabled": "保持唤醒"
+ "disconnected": "Wi-Fi 已断开",
+ "ethernet": "以太网",
+ "wifi": "Wi-Fi"
},
"tooltip": {
- "action": "点击切换保持唤醒模式"
+ "action": "点击管理 Wi-Fi 连接"
}
}
},
+ "session-menu": {
+ "action-in-seconds": "在 {seconds} 秒后将{action}...",
+ "lock": "锁定",
+ "lock-and-suspend": "锁定并挂起",
+ "logout": "注销",
+ "reboot": "重启",
+ "shutdown": "关机",
+ "suspend": "挂起",
+ "title": "会话菜单"
+ },
+ "settings": {
+ "about": {
+ "contributors": {
+ "section": {
+ "description": "向我们 {count} 位超棒的贡献者致敬!",
+ "description_plural": "向我们 {count} 位超棒的贡献者致敬!",
+ "label": "贡献者"
+ }
+ },
+ "noctalia": {
+ "download-latest": "下载最新版本",
+ "installed-version": "已安装版本:",
+ "latest-version": "最新版本:",
+ "section": {
+ "description": "一款为 Wayland 精心打造的时尚简约桌面 shell,基于 Quickshell 构建。",
+ "label": "Noctalia shell"
+ }
+ },
+ "support": "支持我们",
+ "title": "关于"
+ },
+ "audio": {
+ "devices": {
+ "input-device": {
+ "description": "选择所需的音频输入设备。",
+ "label": "输入设备"
+ },
+ "output-device": {
+ "description": "选择所需的音频输出设备。",
+ "label": "输出设备"
+ },
+ "section": {
+ "description": "配置可用的音频输入和输出设备。",
+ "label": "音频设备"
+ }
+ },
+ "media": {
+ "excluded-player": {
+ "description": "添加您希望系统忽略的播放器关键词。每个关键词应单独占一行。",
+ "label": "排除的播放器",
+ "placeholder": "输入关键词并按 + 添加"
+ },
+ "frame-rate": {
+ "description": "帧率越高越流畅,但会占用更多资源。",
+ "label": "帧率"
+ },
+ "primary-player": {
+ "description": "输入关键词以识别您的主要播放器。",
+ "label": "主要播放器",
+ "placeholder": "例如:spotify, vlc, mpv"
+ },
+ "scrolling-speed": {
+ "description": "标题从头到尾滚动所需的时间(秒)",
+ "label": "滚动速度"
+ },
+ "scrolling-title": {
+ "description": "为长媒体标题启用连续滚动",
+ "label": "标题滚动"
+ },
+ "section": {
+ "description": "设置您首选和要忽略的媒体应用程序。",
+ "label": "媒体播放器"
+ },
+ "visualizer-quality": {
+ "description": "高质量使用更多 GPU 资源。",
+ "label": "可视化质量"
+ },
+ "visualizer-type": {
+ "description": "选择媒体播放的可视化类型",
+ "label": "可视化类型"
+ }
+ },
+ "title": "音频",
+ "volumes": {
+ "input-volume": {
+ "description": "麦克风输入音量级别。",
+ "label": "输入音量"
+ },
+ "mute-input": {
+ "description": "静音默认音频输入(麦克风)。",
+ "label": "静音输入"
+ },
+ "mute-output": {
+ "description": "静音系统主音频输出。",
+ "label": "静音输出"
+ },
+ "output-volume": {
+ "description": "系统全局音量级别。",
+ "label": "输出音量"
+ },
+ "section": {
+ "description": "调整音量控制和音频级别。",
+ "label": "音量"
+ },
+ "step-size": {
+ "description": "调整音量变化的步长(滚轮、键盘快捷键)。",
+ "label": "音量步长"
+ },
+ "volume-overdrive": {
+ "description": "允许将音量提高到100%以上。可能不被所有硬件支持。",
+ "label": "允许音量过载"
+ }
+ }
+ },
+ "bar": {
+ "appearance": {
+ "background-opacity": {
+ "description": "调整状态栏的背景不透明度。",
+ "label": "背景不透明度"
+ },
+ "density": {
+ "description": "调整状态栏的内边距以获得紧凑或宽松的外观。",
+ "label": "状态栏密度"
+ },
+ "floating": {
+ "description": "将工具栏显示为浮动的“药丸”形状。",
+ "label": "浮动状态栏"
+ },
+ "margins": {
+ "description": "调整浮动状态栏周围的边距。",
+ "horizontal": "水平",
+ "label": "边距",
+ "vertical": "垂直"
+ },
+ "outer-corners": {
+ "description": "在栏上显示向外弯曲的角。",
+ "label": "外角"
+ },
+ "position": {
+ "description": "选择在屏幕上的位置。",
+ "label": "状态栏位置"
+ },
+ "section": {
+ "description": "自定义状态栏的外观和位置。",
+ "label": "外观"
+ },
+ "show-capsule": {
+ "description": "为每个小部件添加背景。",
+ "label": "显示组件背景"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "在特定显示器上显示状态栏。如果未选择,则默认为全部。",
+ "label": "显示器显示"
+ }
+ },
+ "title": "状态栏",
+ "tray": {
+ "back": "返回",
+ "blacklist": {
+ "description": "添加托盘排除规则,支持通配符 (*)。",
+ "label": "黑名单",
+ "placeholder": "例如:nm-applet, Fcitx*"
+ },
+ "pin-application": "固定应用程序",
+ "unpin-application": "取消固定应用程序"
+ },
+ "widgets": {
+ "section": {
+ "description": "拖放小部件以重新排序。徽章表示使用情况:[L]左、[C]中、[R]右。",
+ "label": "小部件定位"
+ }
+ }
+ },
+ "color-scheme": {
+ "color-source": {
+ "matugen-scheme-type": {
+ "description": {
+ "scheme-content": "推导出与底层图像紧密匹配的颜色",
+ "scheme-expressive": "充满活力的调色板,色彩饱和度高",
+ "scheme-fidelity": "保留原始色调的高保真调色板",
+ "scheme-fruit-salad": "鲜艳对比色的缤纷组合",
+ "scheme-monochrome": "围绕单一色调构建的极简调色板",
+ "scheme-neutral": "柔和的色调,沉稳平静。",
+ "scheme-rainbow": "涵盖整个光谱的多元调色板",
+ "scheme-tonal-spot": "平衡的色调,重点突出"
+ },
+ "label": "Matugen 配色方案类型"
+ },
+ "section": {
+ "description": "Noctalia 颜色的主要设置。",
+ "label": "颜色来源"
+ },
+ "use-wallpaper-colors": {
+ "description": "使用 Matugen 从壁纸生成颜色方案。自动提取颜色以创建一致的主题。",
+ "label": "使用壁纸颜色"
+ }
+ },
+ "dark-mode": {
+ "mode": {
+ "description": "启用自动切换浅色和深色模式。",
+ "label": "深色模式计划",
+ "location": "位置",
+ "manual": "手动",
+ "off": "关"
+ },
+ "switch": {
+ "description": "切换到更暗的主题,便于夜间观看。",
+ "label": "深色模式"
+ }
+ },
+ "predefined": {
+ "generate-templates": {
+ "description": "使用预定义配色方案时生成 Matugen 模板(GTK、终端主题等)。",
+ "label": "为预定义方案生成模板"
+ },
+ "section": {
+ "description": "从预定义配色方案集合中选择。",
+ "label": "预定义配色方案"
+ }
+ },
+ "templates": {
+ "misc": {
+ "description": "其他配置选项。",
+ "label": "杂项",
+ "user-templates": {
+ "description": "启用用户定义的 Matugen 配置。首次启用时将在 ~/.config/noctalia/user-templates.toml 创建模板文件",
+ "label": "用户模板"
+ }
+ },
+ "programs": {
+ "code": {
+ "description": "写入 {filepath}。Hyprluna 主题需要手动安装和激活。",
+ "description-missing": "需要安装 {app}"
+ },
+ "spicetify": {
+ "description": "写入 {filepath}。Comfy 主题需要手动安装和激活。",
+ "description-missing": "需要安装 {app}"
+ },
+ "description": "应用程序特定主题。",
+ "discord": {
+ "description": "为 {client} 写入 {filepath}",
+ "description-missing": "未检测到 Discord 客户端。请安装 vencord、vesktop、webcord、armcord、equibop、lightcord 或 dorion。"
+ },
+ "fuzzel": {
+ "description": "写入 {filepath} 并重新加载",
+ "description-missing": "需要安装 {app}"
+ },
+ "label": "程序",
+ "pywalfox": {
+ "description": "写入 {filepath} 并运行 pywalfox update",
+ "description-missing": "需要安装 {app}"
+ },
+ "vicinae": {
+ "description": "写入 {filepath} 并重新加载",
+ "description-missing": "需要安装 {app}"
+ },
+ "walker": {
+ "description": "写入 {filepath} 并将主题设置为 noctalia",
+ "description-missing": "需要安装 {app}"
+ }
+ },
+ "section": {
+ "description": "将颜色应用于外部应用程序。",
+ "label": "模板"
+ },
+ "terminal": {
+ "alacritty": {
+ "description": "写入 {filepath} 并重新加载",
+ "description-missing": "需要安装 {app}"
+ },
+ "description": "终端模拟器主题。",
+ "foot": {
+ "description": "写入 {filepath} 并重新加载",
+ "description-missing": "需要安装 {app}"
+ },
+ "ghostty": {
+ "description": "写入 {filepath} 并重新加载",
+ "description-missing": "需要安装 {app}"
+ },
+ "kitty": {
+ "description": "写入 {filepath} 并重新加载",
+ "description-missing": "需要安装 {app}"
+ },
+ "label": "终端",
+ "wezterm": {
+ "description": "写入 {filepath} 并重新加载",
+ "description-missing": "需要安装{app}"
+ }
+ },
+ "ui": {
+ "description": "桌面环境和 UI 工具包主题。",
+ "gtk": {
+ "description": "写入 {filepath}"
+ },
+ "kcolorscheme": {
+ "description": "写入 {filepath}"
+ },
+ "label": "用户界面",
+ "qt": {
+ "description": "写入 {filepath}"
+ }
+ }
+ },
+ "title": "配色方案"
+ },
+ "control-center": {
+ "cards": {
+ "section": {
+ "description": "自定义在控制中心显示的控制项及其顺序。",
+ "label": "卡片"
+ }
+ },
+ "position": {
+ "description": "选择打开控制中心时面板出现的位置。",
+ "label": "位置"
+ },
+ "section": {
+ "description": "配置控制中心面板的位置和行为。",
+ "label": "外观"
+ },
+ "shortcuts": {
+ "custom-button": {
+ "browse": "浏览",
+ "command": {
+ "description": "单击按钮时执行的命令。",
+ "label": "命令"
+ },
+ "enable-on-state-logic": {
+ "description": "根据检查命令启用第二个图标和“高亮”状态。",
+ "label": "启用开启状态逻辑"
+ },
+ "general-tooltip-text": {
+ "description": "按钮工具提示的常规说明。",
+ "label": "常规工具提示文本"
+ },
+ "icon": {
+ "description": "从库中选择一个图标。",
+ "label": "图标"
+ },
+ "on-clicked": {
+ "description": "左键单击按钮时执行的命令。",
+ "label": "左键单击命令"
+ },
+ "on-middle-clicked": {
+ "description": "中键单击按钮时执行的命令。",
+ "label": "中键单击命令"
+ },
+ "on-right-clicked": {
+ "description": "右键单击按钮时执行的命令。",
+ "label": "右键单击命令"
+ },
+ "on-state-command": {
+ "description": "执行以检查按钮是否应处于“开启”状态的命令。返回 0 表示开启,非零表示关闭。",
+ "label": "开启状态检查命令"
+ },
+ "on-state-icon": {
+ "description": "按钮处于“开启”状态时的图标。",
+ "label": "开启状态图标"
+ },
+ "state-checks": {
+ "add": "添加状态检查",
+ "browse-icon": "浏览",
+ "command": "要为此状态检查执行的命令",
+ "label": "状态检查",
+ "remove": "移除"
+ },
+ "tooltip": {
+ "description": "悬停在按钮上时显示的工具提示。",
+ "label": "工具提示"
+ }
+ },
+ "dialog": {
+ "apply": "应用",
+ "cancel": "取消"
+ },
+ "section": {
+ "description": "配置和管理快捷方式小部件。",
+ "label": "快捷方式小部件"
+ },
+ "sectionLeft": "左",
+ "sectionRight": "右"
+ },
+ "title": "控制中心"
+ },
+ "display": {
+ "monitors": {
+ "brightness": "亮度",
+ "brightness-step": {
+ "description": "调整亮度变化的步长(滚轮和键盘快捷键)。",
+ "label": "亮度步长"
+ },
+ "brightness-unavailable": {
+ "ddc-disabled": "亮度控制不可用。启用“外部亮度支持”以控制此显示器的亮度。",
+ "generic": "此显示器无法使用亮度控制。"
+ },
+ "enforce-minimum": {
+ "description": "解决亮度为 0% 时部分显示器背光完全关闭的问题。",
+ "label": "限制最小亮度为 1%"
+ },
+ "external-brightness": {
+ "description": "启用 DDCUtil 支持,通过 DDC/CI 协议控制外部显示器的亮度。",
+ "label": "外部亮度支持"
+ },
+ "section": {
+ "description": "调整每个显示器的亮度设置。",
+ "label": "显示器设置"
+ }
+ },
+ "night-light": {
+ "auto-schedule": {
+ "description": "基于 {location} 的日落和日出时间 - 推荐。",
+ "label": "自动调度"
+ },
+ "enable": {
+ "description": "应用暖色滤镜以减少蓝光发射。",
+ "label": "启用夜间模式"
+ },
+ "force-activation": {
+ "description": "忽略调度并立即应用夜间滤镜。",
+ "label": "强制激活"
+ },
+ "manual-schedule": {
+ "description": "为日出和日落设置自定义时间。",
+ "label": "手动调度",
+ "select-start": "选择开始时间",
+ "select-stop": "选择停止时间",
+ "sunrise": "日出时间",
+ "sunset": "日落时间"
+ },
+ "section": {
+ "description": "减少蓝光发射,帮助您更好地睡眠并减轻眼睛疲劳。",
+ "label": "夜间模式"
+ },
+ "temperature": {
+ "day": "白天",
+ "description": "设置夜间和白天的颜色温暖度。",
+ "label": "色温",
+ "night": "夜间"
+ }
+ },
+ "title": "显示"
+ },
+ "dock": {
+ "appearance": {
+ "background-opacity": {
+ "description": "调整 Dock 的背景不透明度。",
+ "label": "背景不透明度"
+ },
+ "colorize-icons": {
+ "description": "将主题颜色应用到 Dock 应用图标(仅限非聚焦应用)。",
+ "label": "着色图标"
+ },
+ "display": {
+ "always-visible": "始终可见",
+ "auto-hide": "自动隐藏",
+ "description": "选择 Dock 的行为方式。",
+ "exclusive": "独占",
+ "label": "显示"
+ },
+ "floating-distance": {
+ "description": "调整距离屏幕边缘的浮动距离。",
+ "label": "Dock 浮动距离"
+ },
+ "icon-size": {
+ "description": "调整 Dock 的整体大小。",
+ "label": "Dock 大小"
+ },
+ "section": {
+ "description": "自定义 Dock 的行为和外观。",
+ "label": "外观"
+ }
+ },
+ "enabled": {
+ "description": "完全显示或隐藏 Dock",
+ "label": "启用 Dock"
+ },
+ "monitors": {
+ "only-same-output": {
+ "description": "仅显示输出结果中dock所在位置的应用。",
+ "label": "仅来自相同输出的应用"
+ },
+ "section": {
+ "description": "在特定显示器上显示 Dock。如果未选择任何显示器,则默认为全部。",
+ "label": "显示器显示"
+ }
+ },
+ "title": "Dock"
+ },
+ "general": {
+ "fonts": {
+ "default": {
+ "description": "整个界面使用的主要字体。",
+ "label": "默认字体",
+ "placeholder": "选择默认字体...",
+ "scale": {
+ "description": "增大或减小标准文本的尺寸",
+ "label": "默认字体大小"
+ },
+ "search-placeholder": "搜索字体..."
+ },
+ "monospace": {
+ "description": "用于数字和统计信息显示的等宽字体。",
+ "label": "等宽字体",
+ "placeholder": "选择等宽字体...",
+ "scale": {
+ "description": "增大或减小等宽文本的尺寸",
+ "label": "默认等宽字体大小"
+ },
+ "search-placeholder": "搜索等宽字体..."
+ },
+ "reset-scaling": "恢复默认缩放",
+ "section": {
+ "description": "选择整个界面中使用的字体。",
+ "label": "字体"
+ }
+ },
+ "language": {
+ "section": {
+ "description": "选择您偏好的应用程序语言。",
+ "label": "语言"
+ },
+ "select": {
+ "auto-detect": "自动检测",
+ "description": "选择应用程序界面中使用的语言。",
+ "label": "应用程序语言"
+ }
+ },
+ "launch-setup-wizard": "启动安装向导",
+ "profile": {
+ "picture": {
+ "description": "在整个界面中显示您的个人资料图片。",
+ "label": "{user} 的个人资料图片"
+ },
+ "section": {
+ "description": "编辑您的用户详细信息和头像。",
+ "label": "个人资料"
+ },
+ "select-avatar": "选择头像图片"
+ },
+ "screen-corners": {
+ "radius": {
+ "description": "调整屏幕圆角的弧度。",
+ "label": "屏幕边角半径",
+ "reset": "重置屏幕圆角半径"
+ },
+ "section": {
+ "description": "自定义屏幕边角的圆度和视觉效果。",
+ "label": "屏幕边角"
+ },
+ "show-corners": {
+ "description": "在屏幕边缘显示圆角。",
+ "label": "显示屏幕圆角"
+ },
+ "solid-black": {
+ "description": "使用实心黑色而非状态栏背景色。",
+ "label": "实心黑色边角"
+ }
+ },
+ "title": "通用"
+ },
+ "hooks": {
+ "info": {
+ "command-info": {
+ "description": "• 命令通过 shell 执行 (sh -c)\n• 命令在后台运行(分离)\n• 测试按钮使用当前值执行",
+ "label": "钩子命令信息"
+ },
+ "parameters": {
+ "description": "• 壁纸钩子: $1 = 壁纸路径, $2 = 屏幕名称\n• 主题切换钩子: $1 = true/false (深色模式状态)",
+ "label": "可用参数"
+ }
+ },
+ "system-hooks": {
+ "enable": {
+ "description": "启用或禁用所有钩子命令。",
+ "label": "启用钩子"
+ },
+ "section": {
+ "description": "配置在系统事件发生时执行的命令。",
+ "label": "系统钩子"
+ }
+ },
+ "theme-changed": {
+ "description": "主题在深色和浅色模式之间切换时执行的命令。",
+ "label": "主题已更改",
+ "placeholder": "例如:notify-send \"主题\" \"已切换\""
+ },
+ "title": "钩子",
+ "wallpaper-changed": {
+ "description": "壁纸更改时执行的命令。",
+ "label": "壁纸已更改",
+ "placeholder": "例如:notify-send \"壁纸\" \"已更改\""
+ }
+ },
+ "launcher": {
+ "settings": {
+ "background-opacity": {
+ "description": "调整启动器的背景不透明度。",
+ "label": "背景不透明度"
+ },
+ "clipboard-history": {
+ "description": "从启动器访问之前复制的项目。",
+ "label": "启用剪贴板历史记录"
+ },
+ "custom-launch-prefix": {
+ "description": "使用自定义启动器前缀命令(例如,'runapp'用于systemd集成)。",
+ "label": "自定义启动前缀"
+ },
+ "custom-launch-prefix-enabled": {
+ "description": "使用自定义前缀启动应用程序,而不是默认方法。",
+ "label": "启用自定义启动前缀"
+ },
+ "position": {
+ "description": "选择启动器面板出现的位置。",
+ "label": "位置"
+ },
+ "section": {
+ "description": "自定义启动器的行为和外观。",
+ "label": "外观"
+ },
+ "sort-by-usage": {
+ "description": "启用后,经常启动的应用程序将显示在列表首位。",
+ "label": "按使用频率排序"
+ },
+ "terminal-command": {
+ "description": "启动终端的命令。例如,'kitty -e'或'gnome-terminal --'。",
+ "label": "终端命令"
+ },
+ "use-app2unit": {
+ "description": "使用替代启动方法以更好地管理应用程序进程并防止问题。",
+ "label": "使用 App2Unit 启动应用程序"
+ }
+ },
+ "title": "启动器"
+ },
+ "location": {
+ "date-time": {
+ "12hour-format": {
+ "description": "在锁屏和日历上以 12 小时制格式显示时间。状态栏时钟有其自己的设置。",
+ "label": "使用12小时制时间格式"
+ },
+ "first-day-of-week": {
+ "automatic": "自动(使用系统区域设置)",
+ "description": "选择日历中一周的起始日。",
+ "label": "一周的第一天"
+ },
+ "section": {
+ "description": "自定义日期和时间的显示方式。",
+ "label": "日期和时间"
+ },
+ "show-events": {
+ "description": "在日历面板中显示事件。",
+ "label": "显示日历事件"
+ },
+ "use-analog": {
+ "description": "在日历窗口和锁定屏幕上显示模拟时钟。",
+ "label": "使用模拟时钟样式"
+ },
+ "week-numbers": {
+ "description": "在日历中显示一年中的第几周(例如:第 38 周)。",
+ "label": "显示周数"
+ }
+ },
+ "location": {
+ "search": {
+ "description": "例如:多伦多, 安大略省",
+ "label": "搜索位置",
+ "placeholder": "输入位置名称"
+ },
+ "section": {
+ "description": "通过设置您的位置获取准确的天气和调度夜间模式。",
+ "label": "您的位置"
+ }
+ },
+ "title": "位置",
+ "weather": {
+ "enabled": {
+ "description": "在整个界面中显示天气信息并获取天气数据。禁用后,所有天气元素将被隐藏,不会进行网络请求。",
+ "label": "启用天气"
+ },
+ "fahrenheit": {
+ "description": "以华氏度而非摄氏度显示温度。",
+ "label": "以华氏度显示温度 (°F)"
+ },
+ "section": {
+ "description": "选择您喜欢的温度单位。",
+ "label": "天气"
+ },
+ "show-in-calendar": {
+ "description": "直接在您的日历视图中显示每日天气预报。",
+ "label": "在日历中显示天气"
+ }
+ }
+ },
+ "lock-screen": {
+ "compact-lockscreen": {
+ "description": "仅显示登录输入和系统控制,隐藏天气和媒体小部件。",
+ "label": "紧凑型锁屏"
+ },
+ "lock-on-suspend": {
+ "description": "系统挂起时自动锁定屏幕。",
+ "label": "挂起时锁定"
+ },
+ "title": "锁屏"
+ },
+ "network": {
+ "bluetooth": {
+ "label": "启用蓝牙"
+ },
+ "section": {
+ "description": "管理 Wi-Fi 和蓝牙连接。"
+ },
+ "title": "网络",
+ "wifi": {
+ "label": "启用 Wi-Fi"
+ }
+ },
+ "notifications": {
+ "duration": {
+ "critical-urgency": {
+ "description": "高优先级通知保持可见的时间。",
+ "label": "高紧急度"
+ },
+ "low-urgency": {
+ "description": "低优先级通知保持可见的时间。",
+ "label": "低紧急度"
+ },
+ "normal-urgency": {
+ "description": "正常优先级通知保持可见的时间。",
+ "label": "正常紧急度"
+ },
+ "respect-expire": {
+ "description": "使用通知中设置的过期超时。",
+ "label": "尊重过期超时"
+ },
+ "section": {
+ "description": "根据紧急级别配置通知保持可见的时间。",
+ "label": "通知持续时间"
+ }
+ },
+ "monitors": {
+ "section": {
+ "description": "在特定显示器上显示通知。如果未选择,则默认为全部。",
+ "label": "显示器显示"
+ }
+ },
+ "settings": {
+ "always-on-top": {
+ "description": "在全屏窗口和其他图层上方显示通知。",
+ "label": "始终置顶"
+ },
+ "background-opacity": {
+ "description": "调整通知背景的透明度。",
+ "label": "背景透明度"
+ },
+ "do-not-disturb": {
+ "description": "启用后禁用所有通知弹出窗口。",
+ "label": "勿扰模式"
+ },
+ "enable-osd": {
+ "description": "实时显示音量和亮度变化。",
+ "label": "启用屏显菜单"
+ },
+ "enabled": {
+ "description": "启用或禁用通知守护进程,需要重启 noctalia-shell。",
+ "label": "启用通知"
+ },
+ "location": {
+ "description": "通知在屏幕上出现的位置。",
+ "label": "位置"
+ },
+ "section": {
+ "description": "配置通知的外观和行为。",
+ "label": "外观"
+ }
+ },
+ "title": "通知"
+ },
+ "osd": {
+ "always-on-top": {
+ "description": "在全屏窗口和其他图层之上显示OSD。",
+ "label": "始终置顶"
+ },
+ "description": "配置屏幕叠加指示器,例如音量和亮度。",
+ "duration": {
+ "auto-hide": {
+ "description": "调整屏显菜单消失前的时间。",
+ "label": "自动隐藏超时"
+ },
+ "section": {
+ "description": "屏显菜单自动隐藏前保持可见的时长。",
+ "label": "自动隐藏超时"
+ }
+ },
+ "enabled": {
+ "description": "实时显示音量与亮度变化。",
+ "label": "启用屏显菜单"
+ },
+ "location": {
+ "description": "屏显菜单出现的位置。",
+ "label": "位置"
+ },
+ "monitors": {
+ "section": {
+ "description": "在特定显示器上显示屏显菜单。若未选择,则默认全部。",
+ "label": "显示器显示"
+ }
+ },
+ "section": {
+ "general": {
+ "description": "配置屏显菜单(OSD)的可见性与行为。",
+ "label": "常规"
+ }
+ },
+ "title": "屏显菜单"
+ },
+ "screen-recorder": {
+ "audio": {
+ "audio-codec": {
+ "description": "推荐使用 Opus 以获得最佳性能和最小的音频大小。",
+ "label": "音频编解码器"
+ },
+ "audio-source": {
+ "description": "录制期间要捕获的音频源。",
+ "label": "音频源"
+ },
+ "section": {
+ "description": "配置音频录制选项。",
+ "label": "音频设置"
+ }
+ },
+ "general": {
+ "output-folder": {
+ "description": "屏幕录制将保存的文件夹。",
+ "label": "输出文件夹",
+ "tooltip": "浏览输出文件夹"
+ },
+ "section": {
+ "description": "管理屏幕录制输出和内容。",
+ "label": "常规设置"
+ },
+ "select-output-folder": "选择输出文件夹",
+ "show-cursor": {
+ "description": "在视频中录制鼠标光标。",
+ "label": "显示光标"
+ }
+ },
+ "title": "屏幕录制",
+ "video": {
+ "color-range": {
+ "description": "推荐使用 Limited 以获得更好的兼容性。",
+ "label": "颜色范围"
+ },
+ "frame-rate": {
+ "description": "屏幕录制的目标帧率。",
+ "label": "帧率"
+ },
+ "section": {
+ "description": "配置视频录制选项。",
+ "label": "视频设置"
+ },
+ "video-codec": {
+ "description": "h264 是最常见的编解码器。",
+ "label": "视频编解码器"
+ },
+ "video-quality": {
+ "description": "更高的质量会导致更大的文件大小。",
+ "label": "视频质量"
+ },
+ "video-source": {
+ "description": "推荐使用 Portal,如果出现伪影请尝试 Screen。",
+ "label": "视频源"
+ }
+ }
+ },
+ "user-interface": {
+ "animation-disable": {
+ "description": "禁用所有动画以获得更快、更流畅的体验。",
+ "label": "禁用 UI 动画"
+ },
+ "animation-speed": {
+ "description": "调整全局动画速度。",
+ "label": "动画速度",
+ "reset": "重置动画速度"
+ },
+ "border-radius": {
+ "description": "控制窗口、按钮和其他元素的圆角程度。",
+ "label": "边框半径",
+ "reset": "重置边框半径"
+ },
+ "dim-desktop": {
+ "description": "当面板或菜单打开时,桌面变暗。",
+ "label": "昏暗的桌面"
+ },
+ "panels-attached-to-bar": {
+ "description": "面板锁定到边栏和屏幕边缘,形成无缝外观,并具有时尚的倒角。",
+ "label": "将面板对齐到边缘"
+ },
+ "panels-overlay": {
+ "description": "确保面板和栏保持可见,即使在全屏应用程序上也是如此。",
+ "label": "将面板和栏保持在顶部"
+ },
+ "scaling": {
+ "description": "更改通用用户界面大小,不包括栏。",
+ "label": "界面缩放",
+ "reset-scaling": "重置界面缩放"
+ },
+ "section": {
+ "description": "自定义界面的外观、感觉和行为。",
+ "label": "外观"
+ },
+ "settings-panel-attached-to-bar": {
+ "description": "使设置窗口与边栏保持对齐,实现统一的外观。",
+ "label": "将设置窗口贴合边栏"
+ },
+ "shadows": {
+ "description": "启用条形图和面板下的阴影。",
+ "direction": {
+ "description": "选择阴影投射的方向。",
+ "label": "阴影方向"
+ },
+ "label": "阴影"
+ },
+ "title": "用户界面",
+ "tooltips": {
+ "description": "启用或禁用整个界面的工具提示。",
+ "label": "显示工具提示"
+ }
+ },
+ "wallpaper": {
+ "automation": {
+ "custom-interval": {
+ "description": "以 HH:MM 格式输入时间(例如:01:30)。",
+ "label": "自定义间隔"
+ },
+ "interval": {
+ "description": "自动更改壁纸的频率。",
+ "label": "壁纸间隔"
+ },
+ "random-wallpaper": {
+ "description": "按固定间隔调度随机壁纸更改。",
+ "label": "随机壁纸"
+ },
+ "section": {
+ "label": "自动化"
+ }
+ },
+ "look-feel": {
+ "edge-smoothness": {
+ "description": "对过渡边缘应用柔和、羽化的效果。",
+ "label": "软化过渡边缘"
+ },
+ "fill-color": {
+ "description": "选择可能出现在壁纸后面的填充颜色。",
+ "label": "填充颜色"
+ },
+ "fill-mode": {
+ "description": "选择图像应如何缩放以匹配显示器的分辨率。",
+ "label": "填充模式"
+ },
+ "section": {
+ "label": "外观与体验"
+ },
+ "transition-duration": {
+ "description": "过渡动画的持续时间(秒)。",
+ "label": "过渡持续时间"
+ },
+ "transition-type": {
+ "description": "切换壁纸时的动画类型。",
+ "label": "过渡类型"
+ }
+ },
+ "settings": {
+ "enable-management": {
+ "description": "使用 Noctalia 管理壁纸。如果您更喜欢使用其他应用程序,请取消选中。",
+ "label": "启用壁纸管理"
+ },
+ "enable-overview": {
+ "description": "将模糊且变暗的壁纸应用于概览屏幕。",
+ "label": "启用概览壁纸"
+ },
+ "folder": {
+ "description": "您的主壁纸文件夹路径。",
+ "label": "壁纸文件夹",
+ "tooltip": "浏览壁纸文件夹"
+ },
+ "monitor-specific": {
+ "description": "为每个显示器设置不同的壁纸文件夹。",
+ "label": "显示器特定目录",
+ "tooltip": "浏览壁纸文件夹"
+ },
+ "recursive-search": {
+ "description": "同时在壁纸目录的子文件夹中搜索壁纸。",
+ "label": "搜索子文件夹"
+ },
+ "section": {
+ "description": "控制壁纸的管理和显示方式。",
+ "label": "壁纸设置"
+ },
+ "select-folder": "选择壁纸文件夹",
+ "select-monitor-folder": "选择显示器壁纸文件夹",
+ "selector-position": {
+ "description": "选择壁纸选择面板的显示位置。",
+ "label": "选择器位置"
+ }
+ },
+ "title": "壁纸"
+ }
+ },
+ "setup": {
+ "appearance": {
+ "header": "外观",
+ "subheader": "选择深色模式与配色来源(Matugen 或预设)。"
+ },
+ "customize": {
+ "header": "自定义体验",
+ "subheader": "调整状态栏位置、密度、缩放等。"
+ },
+ "wallpaper": {
+ "choose-dir": "选择包含壁纸图片的目录",
+ "dir": {
+ "browse": "浏览文件夹",
+ "description": "选择存放壁纸的文件夹",
+ "label": "壁纸目录",
+ "select-title": "选择壁纸文件夹"
+ },
+ "header": "选择你的壁纸",
+ "no-dir": "未选择壁纸目录",
+ "no-valid": "在 {dir} 中未找到有效图片文件",
+ "none-in-dir": "目录中未找到壁纸",
+ "preview-error": "图片加载失败",
+ "select-prompt": "在下方选择一张壁纸",
+ "subheader": "用精美壁纸营造氛围。"
+ },
+ "welcome": {
+ "note": "先进行一些基础设置——更多选项可在“设置”中找到"
+ }
+ },
+ "system": {
+ "cpu-temperature": "{temp}°C",
+ "disk-usage": "{percent}%",
+ "location-display": "{name} ({coordinates})",
+ "monitor-description": "{model} ({width}x{height} @ {scale}x)",
+ "no-media-player-detected": "未检测到媒体播放器",
+ "scaling-percentage": "{percentage}%",
+ "signal-strength": "{signal}%",
+ "unknown": "未知",
+ "unknown-app": "未知应用",
+ "unknown-layout": "未知",
+ "unknown-version": "未知",
+ "uptime": "系统运行时间:{uptime}",
+ "user-requested": "用户请求",
+ "welcome-back": "欢迎回来,",
+ "widget-settings-title": "{widget} 设置"
+ },
"toast": {
- "night-light": {
- "enabled": "已启用",
- "disabled": "已禁用",
- "not-installed": "未安装 wlsunset",
- "forced": "强制激活",
- "normal": "正常模式"
- },
- "keep-awake": {
- "enabled": "已启用",
- "disabled": "已禁用"
- },
- "wallpaper-colors": {
- "enabled": "壁纸颜色已启用",
- "disabled": "壁纸颜色已禁用",
- "not-installed": "Matugen 未安装 - 壁纸颜色提取所需"
- },
- "matugen": {
- "failed": "Matugen 处理失败",
- "failed-general": "Matugen 在处理模板时遇到错误"
- },
- "recording": {
- "stopping": "正在停止录制…",
- "started": "录制已开始",
- "saved": "录制已保存",
- "failed-start": "开始录制失败",
- "failed-gpu": "gpu-screen-recorder 意外退出。",
- "failed-general": "录制器因错误退出。",
- "no-portals": "桌面门户未运行",
- "no-portals-desc": "请启动 xdg-desktop-portal 和一个合成器门户 (wlr/hyprland/gnome/kde)。",
- "not-installed": "gpu-screen-recorder 未安装",
- "not-installed-desc": "请安装 gpu-screen-recorder 以使用屏幕录制功能。"
- },
- "clipboard": {
- "unavailable": "剪贴板历史记录不可用",
- "unavailable-desc": "未安装 'cliphist' 应用程序。请安装它以使用剪贴板历史记录功能。"
- },
- "ipc": {
- "powerpanel-deprecated": "PowerPanel 已重命名为 SessionMenu,此 IPC 调用将很快弃用。请改用 \"ipc call sessionMenu toggle\"。",
- "sidepanel-deprecated": "SidePanel 已重命名为 ControlCenter,此 IPC 调用将很快弃用。请改用 \"ipc call controlCenter toggle\"。"
- },
- "wifi": {
- "enabled": "已启用",
- "disabled": "已禁用",
- "connected": "已连接到 '{ssid}'",
- "disconnected": "已断开与 '{ssid}' 的连接"
- },
- "bluetooth": {
- "enabled": "已启用",
- "disabled": "已禁用"
- },
"airplane-mode": {
- "title": "飞行模式",
+ "disabled": "已禁用",
"enabled": "已启用",
- "disabled": "已禁用"
- },
- "internet": {
- "limited": "已连接但无互联网"
- },
- "kofi": {
- "opened": "Ko-fi页面已在您的浏览器中打开"
- },
- "do-not-disturb": {
- "enabled": "'勿扰模式'已启用",
- "disabled": "'勿扰模式'已禁用",
- "enabled-desc": "您可以在历史记录中找到这些通知。",
- "disabled-desc": "显示所有通知。"
- },
- "power-profile": {
- "changed": "电源模式已更改",
- "profile-name": "\"{profile}\""
+ "title": "飞行模式"
},
"battery": {
"low": "电量低",
"low-desc": "电量为 {percent}%。请连接充电器。"
},
"battery-manager": {
- "title": "电池阈值",
- "set-success-desc": "电池阈值已设置为 {percent}%",
"initial-setup": "需要初始设置",
- "set-failed": "设置电池阈值失败",
- "install-success": "安装成功",
- "install-missing": "缺少必要文件",
- "install-unsupported": "系统不受支持",
"install-failed": "安装失败",
+ "install-missing": "缺少必要文件",
+ "install-success": "安装成功",
+ "install-unsupported": "系统不受支持",
+ "set-failed": "设置电池阈值失败",
+ "set-success-desc": "电池阈值已设置为 {percent}%",
+ "title": "电池阈值",
+ "uninstall-failed": "卸载失败",
"uninstall-setup": "正在卸载,需要身份验证",
- "uninstall-success": "卸载成功",
- "uninstall-failed": "卸载失败"
+ "uninstall-success": "卸载成功"
+ },
+ "bluetooth": {
+ "disabled": "已禁用",
+ "enabled": "已启用"
+ },
+ "clipboard": {
+ "unavailable": "剪贴板历史记录不可用",
+ "unavailable-desc": "未安装 'cliphist' 应用程序。请安装它以使用剪贴板历史记录功能。"
+ },
+ "do-not-disturb": {
+ "disabled": "'勿扰模式'已禁用",
+ "disabled-desc": "显示所有通知。",
+ "enabled": "'勿扰模式'已启用",
+ "enabled-desc": "您可以在历史记录中找到这些通知。"
+ },
+ "internet": {
+ "limited": "已连接但无互联网"
+ },
+ "ipc": {
+ "powerpanel-deprecated": "PowerPanel 已重命名为 SessionMenu,此 IPC 调用将很快弃用。请改用 \"ipc call sessionMenu toggle\"。",
+ "sidepanel-deprecated": "SidePanel 已重命名为 ControlCenter,此 IPC 调用将很快弃用。请改用 \"ipc call controlCenter toggle\"。"
+ },
+ "keep-awake": {
+ "disabled": "已禁用",
+ "enabled": "已启用"
+ },
+ "kofi": {
+ "opened": "Ko-fi 页面已在您的浏览器中打开"
},
"missing-control-center": {
- "label": "最后一个控制中心小部件已移除",
- "description": "控制中心小部件已从状态栏中移除。要再次从状态栏访问它,您需要重新添加小部件。您也可以通过右键点击状态栏来打开它。"
+ "description": "控制中心小部件已从状态栏中移除。要再次从状态栏访问它,您需要重新添加小部件。您也可以通过右键点击状态栏来打开它。",
+ "label": "最后一个控制中心小部件已移除"
+ },
+ "night-light": {
+ "disabled": "已禁用",
+ "enabled": "已启用",
+ "forced": "强制激活",
+ "normal": "正常模式",
+ "not-installed": "未安装 wlsunset"
+ },
+ "noctalia-performance": {
+ "disabled": "性能模式已禁用",
+ "enabled": "已启用性能模式",
+ "label": "夜之颂演出"
+ },
+ "power-profile": {
+ "changed": "电源模式已更改",
+ "profile-name": "\"{profile}\""
+ },
+ "recording": {
+ "failed-general": "录制器因错误退出。",
+ "failed-gpu": "gpu-screen-recorder 意外退出。",
+ "failed-start": "开始录制失败",
+ "no-portals": "桌面门户未运行",
+ "no-portals-desc": "请启动 xdg-desktop-portal 和一个合成器门户 (wlr/hyprland/gnome/kde)。",
+ "not-installed": "gpu-screen-recorder 未安装",
+ "not-installed-desc": "请安装 gpu-screen-recorder 以使用屏幕录制功能。",
+ "saved": "录制已保存",
+ "started": "录制已开始",
+ "stopping": "正在停止录制…"
+ },
+ "theming-processor-failed": {
+ "desc-generic": "处理模板时遇到错误",
+ "title-matugen": "Matugen模板处理失败",
+ "title-predefined": "预定义的颜色方案处理失败"
+ },
+ "wallpaper-colors": {
+ "disabled": "壁纸颜色已禁用",
+ "enabled": "壁纸颜色已启用",
+ "label": "壁纸颜色生成",
+ "not-installed": "Matugen 未安装 - 壁纸颜色提取所需"
+ },
+ "wifi": {
+ "connected": "已连接到 '{ssid}'",
+ "disabled": "已禁用",
+ "disconnected": "已断开与 '{ssid}' 的连接",
+ "enabled": "已启用"
}
},
+ "tooltips": {
+ "add-widget": "添加小部件",
+ "bluetooth-devices": "蓝牙设备",
+ "brightness-at": "亮度:{brightness}%\n右键点击进入设置。\n滚动调整亮度。",
+ "cancel-timer": "取消计时器",
+ "clear-history": "清除历史记录",
+ "click-to-start-recording": "点击开始录制",
+ "click-to-stop-recording": "点击停止录制",
+ "close": "关闭",
+ "connect-disconnect-devices": "左键点击连接。右键点击忘记。",
+ "delete-notification": "删除通知",
+ "disable-keep-awake": "禁用保持唤醒",
+ "do-not-disturb-disabled": "'勿扰模式'已禁用",
+ "do-not-disturb-enabled": "'勿扰模式'已启用",
+ "enable-keep-awake": "启用保持唤醒",
+ "forget-network": "忘记网络",
+ "home": "主目录",
+ "input-muted": "静音输入设备",
+ "keep-awake": "保持唤醒",
+ "keyboard-layout": "{layout} 键盘布局",
+ "manage-wifi": "管理 Wi-Fi",
+ "microphone-volume-at": "麦克风音量 {volume}%\n左键点击进入设置。右键点击切换静音。\n滚动滚轮调节音量。",
+ "move-to-center-section": "移动到中央部分",
+ "move-to-left-section": "移动到左侧部分",
+ "move-to-right-section": "移动到右侧部分",
+ "next-media": "下一媒体",
+ "next-month": "下个月",
+ "night-light-disabled": "夜间模式已禁用。\n左键点击循环模式。\n右键点击访问设置。",
+ "night-light-enabled": "夜间模式已启用。\n左键点击循环模式。\n右键点击访问设置。",
+ "night-light-forced": "夜间模式已强制启用。\n左键点击循环模式。\n右键点击访问设置。",
+ "night-light-not-installed": "夜间模式不可用。\nwlsunset 未安装。",
+ "noctalia-performance-disabled": "Noctalia 性能模式已禁用。\n左键点击以启用。",
+ "noctalia-performance-enabled": "Noctalia 性能模式已启用。\n左键点击以禁用。",
+ "open-control-center": "打开控制中心",
+ "open-notification-history-disable-dnd": "打开通知历史记录\n右键点击禁用\"勿扰模式\"。",
+ "open-notification-history-enable-dnd": "打开通知历史记录\n右键点击启用\"勿扰模式\"。",
+ "open-settings": "打开设置",
+ "open-tray-dropdown": "打开系统托盘下拉菜单",
+ "open-wallpaper-selector": "打开壁纸选择器",
+ "output-muted": "静音输出设备",
+ "pause": "暂停",
+ "play": "播放",
+ "power-profile": "'{profile}' 电源模式",
+ "previous-media": "上一媒体",
+ "previous-month": "上个月",
+ "refresh": "刷新",
+ "refresh-devices": "刷新设备",
+ "refresh-wallpaper-list": "刷新壁纸列表",
+ "remove-widget": "移除小部件",
+ "screen-recorder-not-installed": "屏幕录制器未安装",
+ "session-menu": "会话菜单",
+ "set-power-profile": "设置\"{profile}\"电源模式",
+ "start-screen-recording": "开始屏幕录制",
+ "stop-screen-recording": "停止屏幕录制",
+ "switch-to-dark-mode": "切换到深色模式",
+ "switch-to-light-mode": "切换到浅色模式",
+ "up": "向上",
+ "volume-at": "输出音量 {volume}%\n左键点击进入设置。右键点击切换静音。\n滚动滚轮调节音量。",
+ "wallpaper-selector": "左键点击:打开壁纸选择器。\n右键点击:设置随机壁纸。",
+ "widget-settings": "小部件设置"
+ },
+ "wallpaper": {
+ "configure-directory": "使用图像配置您的壁纸目录。",
+ "fill-modes": {
+ "center": "居中",
+ "crop": "裁剪(填充)",
+ "fit": "适应(包含)",
+ "stretch": "拉伸"
+ },
+ "no-match": "未找到匹配项。",
+ "no-wallpaper": "未找到壁纸。",
+ "panel": {
+ "apply-all-monitors": {
+ "description": "一次性将选定的壁纸应用到所有显示器。",
+ "label": "应用到所有显示器"
+ },
+ "search": "搜索:",
+ "title": "壁纸选择器"
+ },
+ "transitions": {
+ "disc": "圆盘",
+ "fade": "淡入淡出",
+ "none": "无",
+ "random": "随机",
+ "stripes": "条纹",
+ "wipe": "擦除"
+ },
+ "try-different-search": "尝试不同的搜索查询。"
+ },
"weather": {
"clear-sky": "晴朗",
- "mainly-clear": "基本晴朗",
- "partly-cloudy": "局部多云",
- "overcast": "阴天",
- "fog": "雾",
"drizzle": "毛毛雨",
- "snow": "雪",
+ "fog": "雾",
+ "mainly-clear": "基本晴朗",
+ "overcast": "阴天",
+ "partly-cloudy": "局部多云",
"rain-showers": "阵雨",
+ "snow": "雪",
"thunderstorm": "雷暴",
"unknown": "未知"
},
- "authentication": {
- "failed": "认证失败",
- "error": "认证错误"
- },
- "general": {
- "no-results": "无结果",
- "no-summary": "无摘要",
- "unknown": "未知"
- },
- "battery": {
- "no-battery-detected": "未检测到电池。",
- "charging-rate": "充电速率:{rate} W。",
- "discharging-rate": "放电速率:{rate} W。",
- "charging": "正在充电。",
- "discharging": "正在放电。",
- "plugged-in": "已接通电源。",
- "idle": "空闲。",
- "time-left": "剩余时间:{time}。",
- "time-until-full": "充满所需时间:{time}。",
- "health": "健康状况:{percent}%",
- "panel": {
- "title": "充电阈值",
- "full": "完全容量 ({percent}%)",
- "balanced": "平衡 ({percent}%)",
- "lifespan": "延长寿命 ({percent}%)",
- "disabled": "电池管理器已禁用"
- }
- },
- "setup": {
- "customize": {
- "header": "自定义体验",
- "subheader": "调整状态栏位置、密度、缩放等。"
+ "widgets": {
+ "color-picker": {
+ "apply": "应用",
+ "brightness": "亮度",
+ "cancel": "取消",
+ "hex": {
+ "description": "输入十六进制颜色代码。",
+ "label": "十六进制颜色"
+ },
+ "palette": {
+ "description": "从各种预定义颜色中选择。",
+ "label": "调色板"
+ },
+ "rgb": {
+ "description": "调整红、绿、蓝和亮度值。",
+ "label": "RGB 值"
+ },
+ "theme-colors": {
+ "description": "快速访问您的主题调色板。",
+ "label": "主题颜色"
+ },
+ "title": "颜色选择器"
},
- "appearance": {
- "header": "外观",
- "subheader": "选择深色模式与配色来源(Matugen 或预设)。"
- },
- "wallpaper": {
- "header": "选择你的壁纸",
- "subheader": "用精美壁纸营造氛围。",
- "select-prompt": "在下方选择一张壁纸",
- "preview-error": "图片加载失败",
- "none-in-dir": "目录中未找到壁纸",
- "no-dir": "未选择壁纸目录",
- "no-valid": "在 {dir} 中未找到有效图片文件",
- "choose-dir": "选择包含壁纸图片的目录",
- "dir": {
- "label": "壁纸目录",
- "description": "选择存放壁纸的文件夹",
- "browse": "浏览文件夹",
- "select-title": "选择壁纸文件夹"
+ "datetime-tokens": {
+ "ampm": {
+ "lowercase": "am/pm 小写",
+ "uppercase": "AM/PM 大写"
+ },
+ "common": {
+ "12hour-time-minutes": "12小时制带分钟",
+ "24hour-time-minutes": "24小时制带分钟",
+ "24hour-time-seconds": "24小时制带秒",
+ "european-date": "欧洲日期格式",
+ "iso-date": "ISO 日期格式",
+ "us-date": "美国日期格式",
+ "weekday-date": "星期和日期",
+ "weekday-month-day": "星期、月份和日期"
+ },
+ "day": {
+ "abbreviated": "星期名称缩写",
+ "full": "星期名称全称",
+ "leading-zero": "日期有前导零 (01-31)",
+ "no-leading-zero": "日期无前导零 (1-31)"
+ },
+ "hour": {
+ "leading-zero": "小时有前导零 (00-23) - 24小时制",
+ "no-leading-zero": "小时无前导零 (0-23) - 24小时制"
+ },
+ "minute": {
+ "leading-zero": "分钟有前导零 (00-59)",
+ "no-leading-zero": "分钟无前导零 (0-59)"
+ },
+ "month": {
+ "abbreviated": "月份名称缩写",
+ "full": "月份名称全称",
+ "number-leading-zero": "月份数字有前导零 (01-12)",
+ "number-no-zero": "月份数字无前导零 (1-12)"
+ },
+ "second": {
+ "leading-zero": "秒有前导零 (00-59)",
+ "no-leading-zero": "秒无前导零 (0-59)"
+ },
+ "timezone": {
+ "abbreviation": "时区缩写"
+ },
+ "year": {
+ "four-digit": "年份为四位数",
+ "two-digit": "年份为两位数 (00-99)"
}
},
- "welcome": {
- "note": "先进行一些基础设置——更多选项可在“设置”中找到"
+ "file-picker": {
+ "cancel": "取消",
+ "search-placeholder": "搜索文件和文件夹...",
+ "select-current": "选择当前",
+ "select-file": "选择文件",
+ "select-folder": "选择文件夹",
+ "title": "文件选择器"
+ },
+ "icon-picker": {
+ "apply": "应用",
+ "cancel": "取消",
+ "search": {
+ "label": "搜索"
+ },
+ "title": "图标选择器"
+ },
+ "text-input": {
+ "clear": "清除"
+ },
+ "tooltip": {
+ "placeholder": "占位符"
+ }
+ },
+ "wifi": {
+ "panel": {
+ "connect": "连接",
+ "connected": "已连接",
+ "disabled": "Wi-Fi 已禁用",
+ "disconnect": "断开连接",
+ "disconnecting": "正在断开连接...",
+ "enable-message": "启用 Wi-Fi 以查看可用网络。",
+ "enter-password": "输入密码...",
+ "forget": "忘记",
+ "forget-network": "忘记此网络?",
+ "forgetting": "正在忘记...",
+ "no-networks": "未找到网络",
+ "password": "密码",
+ "saved": "已保存",
+ "scan-again": "重新扫描",
+ "searching": "正在搜索附近网络...",
+ "title": "Wi-Fi"
}
}
}
diff --git a/Assets/settings-default.json b/Assets/settings-default.json
index 60096117..94b3dfe2 100644
--- a/Assets/settings-default.json
+++ b/Assets/settings-default.json
@@ -1,5 +1,5 @@
{
- "settingsVersion": 16,
+ "settingsVersion": 21,
"setupCompleted": false,
"bar": {
"position": "top",
@@ -10,8 +10,13 @@
"floating": false,
"marginVertical": 0.25,
"marginHorizontal": 0.25,
+ "outerCorners": true,
+ "exclusive": true,
"widgets": {
"left": [
+ {
+ "id": "ControlCenter"
+ },
{
"id": "SystemMonitor"
},
@@ -48,15 +53,13 @@
},
{
"id": "Clock"
- },
- {
- "id": "ControlCenter"
}
]
}
},
"general": {
"avatarImage": "",
+ "dimDesktop": true,
"showScreenCorners": false,
"forceBlackScreenCorners": false,
"scaleRatio": 1,
@@ -66,8 +69,21 @@
"animationDisabled": false,
"compactLockScreen": false,
"lockOnSuspend": true,
+ "enableShadows": true,
+ "shadowDirection": "bottom_right",
+ "shadowOffsetX": 2,
+ "shadowOffsetY": 3,
"language": ""
},
+ "ui": {
+ "fontDefault": "Roboto",
+ "fontFixed": "DejaVu Sans Mono",
+ "fontDefaultScale": 1,
+ "fontFixedScale": 1,
+ "tooltipsEnabled": true,
+ "panelsAttachedToBar": true,
+ "settingsPanelAttachToBar": false
+ },
"location": {
"name": "Tokyo",
"weatherEnabled": true,
@@ -92,6 +108,7 @@
},
"wallpaper": {
"enabled": true,
+ "overviewEnabled": false,
"directory": "",
"enableMultiMonitorDirectories": false,
"recursiveSearch": false,
@@ -104,7 +121,8 @@
"transitionDuration": 1500,
"transitionType": "random",
"transitionEdgeSmoothness": 0.05,
- "monitors": []
+ "monitors": [],
+ "panelPosition": "follow_bar"
},
"appLauncher": {
"enableClipboardHistory": false,
@@ -187,7 +205,7 @@
"wifiEnabled": true
},
"notifications": {
- "doNotDisturb": false,
+ "enabled": true,
"monitors": [],
"location": "top_right",
"overlayLayer": true,
@@ -207,23 +225,16 @@
"audio": {
"volumeStep": 5,
"volumeOverdrive": false,
- "cavaFrameRate": 60,
+ "cavaFrameRate": 30,
"visualizerType": "linear",
+ "visualizerQuality": "high",
"mprisBlacklist": [],
"preferredPlayer": ""
},
- "ui": {
- "fontDefault": "Roboto",
- "fontFixed": "DejaVu Sans Mono",
- "fontDefaultScale": 1,
- "fontFixedScale": 1,
- "tooltipsEnabled": true,
- "panelsAttachedToBar": true,
- "panelsOverlayLayer": true
- },
"brightness": {
"brightnessStep": 5,
- "enforceMinimum": true
+ "enforceMinimum": true,
+ "enableDdcSupport": false
},
"colorSchemes": {
"useWallpaperColors": false,
@@ -239,22 +250,25 @@
"gtk": false,
"qt": false,
"kcolorscheme": false,
+ "alacritty": false,
"kitty": false,
"ghostty": false,
"foot": false,
+ "wezterm": false,
"fuzzel": false,
"discord": false,
+ "pywalfox": false,
+ "vicinae": false,
+ "walker": false,
+ "code": false,
+ "spicetify": false,
+ "enableUserTemplates": false,
"discord_vesktop": false,
"discord_webcord": false,
"discord_armcord": false,
"discord_equibop": false,
"discord_lightcord": false,
- "discord_dorion": false,
- "pywalfox": false,
- "vicinae": false,
- "walker": false,
- "code": false,
- "enableUserTemplates": false
+ "discord_dorion": false
},
"nightLight": {
"enabled": false,
diff --git a/Bin/colors-apply.sh b/Bin/colors-apply.sh
index 12ae87f9..0c4bff32 100755
--- a/Bin/colors-apply.sh
+++ b/Bin/colors-apply.sh
@@ -1,11 +1,10 @@
#!/usr/bin/env -S bash
-
# Ensure exactly one argument is provided.
if [ "$#" -ne 1 ]; then
# Print usage information to standard error.
echo "Error: No application specified." >&2
- echo "Usage: $0 {kitty|ghostty|foot|alacritty|fuzzel|walker|pywalfox}" >&2
+ echo "Usage: $0 {kitty|ghostty|foot|alacritty|wezterm|fuzzel|walker|pywalfox}" >&2
exit 1
fi
@@ -13,162 +12,199 @@ APP_NAME="$1"
# --- Apply theme based on the application name ---
case "$APP_NAME" in
- kitty)
- echo "🎨 Applying 'noctalia' theme to kitty..."
- kitty +kitten themes --reload-in=all noctalia
- ;;
+kitty)
+ echo "🎨 Applying 'noctalia' theme to kitty..."
+ kitty +kitten themes --reload-in=all noctalia
+ ;;
- ghostty)
- echo "🎨 Applying 'noctalia' theme to ghostty..."
- CONFIG_FILE="$HOME/.config/ghostty/config"
- # Check if the config file exists before trying to modify it.
- if [ -f "$CONFIG_FILE" ]; then
- # Check if theme is already set to noctalia
- if grep -q "^theme = noctalia" "$CONFIG_FILE"; then
- echo "Theme already set to noctalia, skipping modification."
- else
- # Remove any existing theme include line to prevent duplicates.
- sed -i '/theme/d' "$CONFIG_FILE"
- # Add the new theme include line to the end of the file.
- echo "theme = noctalia" >> "$CONFIG_FILE"
- fi
- pkill -SIGUSR2 ghostty
+ghostty)
+ echo "🎨 Applying 'noctalia' theme to ghostty..."
+ CONFIG_FILE="$HOME/.config/ghostty/config"
+ # Check if the config file exists before trying to modify it.
+ if [ -f "$CONFIG_FILE" ]; then
+ # Check if theme is already set to noctalia
+ if grep -q "^theme = noctalia" "$CONFIG_FILE"; then
+ echo "Theme already set to noctalia, skipping modification."
else
- echo "Error: ghostty config file not found at $CONFIG_FILE" >&2
- exit 1
+ # Remove any existing theme include line to prevent duplicates.
+ sed -i '/theme/d' "$CONFIG_FILE"
+ # Add the new theme include line to the end of the file.
+ echo "theme = noctalia" >>"$CONFIG_FILE"
fi
- ;;
+ pkill -SIGUSR2 ghostty
+ else
+ echo "Error: ghostty config file not found at $CONFIG_FILE" >&2
+ exit 1
+ fi
+ ;;
- foot)
- echo "🎨 Applying 'noctalia' theme to foot..."
- CONFIG_FILE="$HOME/.config/foot/foot.ini"
+foot)
+ echo "🎨 Applying 'noctalia' theme to foot..."
+ CONFIG_FILE="$HOME/.config/foot/foot.ini"
- # Check if the config file exists, create it if it doesn't.
- if [ ! -f "$CONFIG_FILE" ]; then
- echo "Config file not found, creating $CONFIG_FILE..."
- # Create the config directory if it doesn't exist
- mkdir -p "$(dirname "$CONFIG_FILE")"
- # Create the config file with the noctalia theme
- cat > "$CONFIG_FILE" << 'EOF'
+ # Check if the config file exists, create it if it doesn't.
+ if [ ! -f "$CONFIG_FILE" ]; then
+ echo "Config file not found, creating $CONFIG_FILE..."
+ # Create the config directory if it doesn't exist
+ mkdir -p "$(dirname "$CONFIG_FILE")"
+ # Create the config file with the noctalia theme
+ cat >"$CONFIG_FILE" <<'EOF'
[main]
include=~/.config/foot/themes/noctalia
EOF
- echo "Created new config file with noctalia theme."
+ echo "Created new config file with noctalia theme."
+ else
+ # Check if theme is already set to noctalia
+ if grep -q "include=~/.config/foot/themes/noctalia" "$CONFIG_FILE"; then
+ echo "Theme already set to noctalia, skipping modification."
else
- # Check if theme is already set to noctalia
- if grep -q "include=~/.config/foot/themes/noctalia" "$CONFIG_FILE"; then
- echo "Theme already set to noctalia, skipping modification."
+ # Remove any existing theme include line to prevent duplicates.
+ sed -i '/include=.*themes/d' "$CONFIG_FILE"
+ if grep -q '^\[main\]' "$CONFIG_FILE"; then
+ # Insert the include line after the existing [main] section header
+ sed -i '/^\[main\]/a include=~/.config/foot/themes/noctalia' "$CONFIG_FILE"
else
- # Remove any existing theme include line to prevent duplicates.
- sed -i '/include=.*themes/d' "$CONFIG_FILE"
- if grep -q '^\[main\]' "$CONFIG_FILE"; then
- # Insert the include line after the existing [main] section header
- sed -i '/^\[main\]/a include=~/.config/foot/themes/noctalia' "$CONFIG_FILE"
+ # If [main] doesn't exist, create it at the beginning with the include
+ sed -i '1i [main]\ninclude=~/.config/foot/themes/noctalia\n' "$CONFIG_FILE"
+ fi
+ fi
+ fi
+ ;;
+
+alacritty)
+ echo "🎨 Applying 'noctalia' theme to alacritty..."
+ CONFIG_FILE="$HOME/.config/alacritty/alacritty.toml"
+
+ # Check if the config file exists.
+ if [ -f "$CONFIG_FILE" ]; then
+ # Check if theme is already imported
+ if grep -q 'import = \[.*"themes/noctalia.toml".*\]' "$CONFIG_FILE"; then
+ echo "Theme already set to noctalia, skipping modification."
+ else
+ # Check if [general] section exists
+ if grep -q '^\[general\]' "$CONFIG_FILE"; then
+ # Check if import line exists under [general]
+ if sed -n '/^\[general\]/,/^\[/p' "$CONFIG_FILE" | grep -q '^import = \['; then
+ # Replace existing import line with noctalia theme
+ sed -i '/^\[general\]/,/^\[/{s|^import = \[.*\]|import = ["themes/noctalia.toml"]|}' "$CONFIG_FILE"
else
- # If [main] doesn't exist, create it at the beginning with the include
- sed -i '1i [main]\ninclude=~/.config/foot/themes/noctalia\n' "$CONFIG_FILE"
+ # Add import line after [general] section
+ sed -i '/^\[general\]/a import = ["themes/noctalia.toml"]' "$CONFIG_FILE"
+ fi
+ else
+ # Create [general] section with import at the beginning of the file
+ sed -i '1i [general]\nimport = ["themes/noctalia.toml"]\n' "$CONFIG_FILE"
+ fi
+ fi
+ else
+ echo "Error: alacritty config file not found at $CONFIG_FILE" >&2
+ exit 1
+ fi
+ ;;
+
+wezterm)
+ echo "🎨 Applying 'noctalia' theme to wezterm..."
+ CONFIG_FILE="$HOME/.config/wezterm/wezterm.lua"
+ local wezterm_scheme_line="config\.color\_scheme = \"Noctalia\""
+
+ # Check if the config file exists.
+ if [ -f "$CONFIG_FILE" ]; then
+
+ # Check if theme is already set to Noctalia (matches 'Noctalia' or "Noctalia")
+ if grep -q "^\s*config\.color\_scheme\s*\=\s*[\'\"]Noctalia[\'\"]\s*" "$CONFIG_FILE"; then
+ echo "Theme already set to Noctalia, skipping modification."
+ else
+ # Not set to Noctalia. Check if *any* color_scheme line exists.
+ if grep -q '^\s*config\.color\_scheme\s*\=' "$CONFIG_FILE"; then
+ # It exists, so we replace it with our desired line.
+ sed -i "s|^\(\s*config\.color_scheme\s*=\s*\).*$|\1$wezterm_scheme_line|" "$CONFIG_FILE"
+ else
+ # It doesn't exist, so we add it before the 'return config' line.
+ if grep -q '^\s*return\s*config' "$CONFIG_FILE"; then
+ # 'return config' exists. Insert the line before it.
+ sed -i "/^\s*return\s*config/i\\$wezterm_scheme_line" "$CONFIG_FILE"
+ else
+ # This is a problem. We can't find the insertion point.
+ echo "Warning: 'config.color_scheme' not set and 'return config' line not found." >&2
+ echo " Make sure $CONFIG_FILE is correct: https://wezterm.org/config/files.html" >&2
fi
fi
fi
- ;;
+ # touching the config file fools wezterm into reloading it
+ touch "$CONFIG_FILE"
+ else
+ echo "Error: wezterm.lua not found at $CONFIG_FILE" >&2
+ echo "Instructions to create it: https://wezterm.org/config/files.html" >&2
+ exit 1
+ fi
+ ;;
- alacritty)
- echo "🎨 Applying 'noctalia' theme to alacritty..."
- CONFIG_FILE="$HOME/.config/alacritty/alacritty.toml"
+fuzzel)
+ echo "🎨 Applying 'noctalia' theme to fuzzel..."
+ CONFIG_FILE="$HOME/.config/fuzzel/fuzzel.ini"
- # Check if the config file exists.
- if [ -f "$CONFIG_FILE" ]; then
- # Check if theme is already imported
- if grep -q 'import = \[.*"themes/noctalia.toml".*\]' "$CONFIG_FILE"; then
- echo "Theme already set to noctalia, skipping modification."
- else
- # Check if [general] section exists
- if grep -q '^\[general\]' "$CONFIG_FILE"; then
- # Check if import line exists under [general]
- if sed -n '/^\[general\]/,/^\[/p' "$CONFIG_FILE" | grep -q '^import = \['; then
- # Replace existing import line with noctalia theme
- sed -i '/^\[general\]/,/^\[/{s|^import = \[.*\]|import = ["themes/noctalia.toml"]|}' "$CONFIG_FILE"
- else
- # Add import line after [general] section
- sed -i '/^\[general\]/a import = ["themes/noctalia.toml"]' "$CONFIG_FILE"
- fi
- else
- # Create [general] section with import at the beginning of the file
- sed -i '1i [general]\nimport = ["themes/noctalia.toml"]\n' "$CONFIG_FILE"
- fi
- fi
- else
- echo "Error: alacritty config file not found at $CONFIG_FILE" >&2
- exit 1
- fi
- ;;
-
- fuzzel)
- echo "🎨 Applying 'noctalia' theme to fuzzel..."
- CONFIG_FILE="$HOME/.config/fuzzel/fuzzel.ini"
-
- # Check if the config file exists, create it if it doesn't.
- if [ ! -f "$CONFIG_FILE" ]; then
- echo "Config file not found, creating $CONFIG_FILE..."
- # Create the config directory if it doesn't exist
- mkdir -p "$(dirname "$CONFIG_FILE")"
- # Create the config file with the noctalia theme
- cat > "$CONFIG_FILE" << 'EOF'
+ # Check if the config file exists, create it if it doesn't.
+ if [ ! -f "$CONFIG_FILE" ]; then
+ echo "Config file not found, creating $CONFIG_FILE..."
+ # Create the config directory if it doesn't exist
+ mkdir -p "$(dirname "$CONFIG_FILE")"
+ # Create the config file with the noctalia theme
+ cat >"$CONFIG_FILE" <<'EOF'
include=~/.config/fuzzel/themes/noctalia
EOF
- echo "Created new config file with noctalia theme."
+ echo "Created new config file with noctalia theme."
+ else
+ # Check if theme is already set to noctalia
+ if grep -q "include=~/.config/fuzzel/themes/noctalia" "$CONFIG_FILE"; then
+ echo "Theme already set to noctalia, skipping modification."
else
- # Check if theme is already set to noctalia
- if grep -q "include=~/.config/fuzzel/themes/noctalia" "$CONFIG_FILE"; then
- echo "Theme already set to noctalia, skipping modification."
+ # Remove any existing theme include line.
+ sed -i '/themes/d' "$CONFIG_FILE"
+ # Add the new theme include line.
+ echo "include=~/.config/fuzzel/themes/noctalia" >>"$CONFIG_FILE"
+ fi
+ fi
+ ;;
+
+walker)
+ echo "🎨 Applying 'noctalia' theme to walker..."
+ CONFIG_FILE="$HOME/.config/walker/config.toml"
+
+ # Check if the config file exists.
+ if [ -f "$CONFIG_FILE" ]; then
+ # Check if theme is already set to noctalia
+ if grep -q '^theme = "noctalia"' "$CONFIG_FILE"; then
+ echo "Theme already set to noctalia, skipping modification."
+ else
+ # Check if a theme line exists and replace it, otherwise append
+ if grep -q '^theme = ' "$CONFIG_FILE"; then
+ sed -i 's/^theme = .*/theme = "noctalia"/' "$CONFIG_FILE"
else
- # Remove any existing theme include line.
- sed -i '/themes/d' "$CONFIG_FILE"
- # Add the new theme include line.
- echo "include=~/.config/fuzzel/themes/noctalia" >> "$CONFIG_FILE"
+ echo 'theme = "noctalia"' >>"$CONFIG_FILE"
fi
fi
- ;;
-
- walker)
- echo "🎨 Applying 'noctalia' theme to walker..."
- CONFIG_FILE="$HOME/.config/walker/config.toml"
-
- # Check if the config file exists.
- if [ -f "$CONFIG_FILE" ]; then
- # Check if theme is already set to noctalia
- if grep -q '^theme = "noctalia"' "$CONFIG_FILE"; then
- echo "Theme already set to noctalia, skipping modification."
- else
- # Check if a theme line exists and replace it, otherwise append
- if grep -q '^theme = ' "$CONFIG_FILE"; then
- sed -i 's/^theme = .*/theme = "noctalia"/' "$CONFIG_FILE"
- else
- echo 'theme = "noctalia"' >> "$CONFIG_FILE"
- fi
- fi
- else
- echo "Error: walker config file not found at $CONFIG_FILE" >&2
- exit 1
- fi
- ;;
-
- vicinae)
- echo "🎨 Applying 'matugen' theme to vicinae..."
- # Apply the theme
- vicinae theme set matugen
- ;;
-
- pywalfox)
- echo "🎨 Updating pywalfox themes..."
- pywalfox update
- ;;
-
- *)
- # Handle unknown application names.
- echo "Error: Unknown application '$APP_NAME'." >&2
+ else
+ echo "Error: walker config file not found at $CONFIG_FILE" >&2
exit 1
- ;;
+ fi
+ ;;
+
+vicinae)
+ echo "🎨 Applying 'matugen' theme to vicinae..."
+ # Apply the theme
+ vicinae theme set matugen
+ ;;
+
+pywalfox)
+ echo "🎨 Updating pywalfox themes..."
+ pywalfox update
+ ;;
+
+*)
+ # Handle unknown application names.
+ echo "Error: Unknown application '$APP_NAME'." >&2
+ exit 1
+ ;;
esac
-echo "✅ Command sent for $APP_NAME."
\ No newline at end of file
+echo "✅ Command sent for $APP_NAME."
diff --git a/Bin/dev/i18n-json.sh b/Bin/dev/i18n-json.sh
index 0db1585b..30c7ad0c 100755
--- a/Bin/dev/i18n-json.sh
+++ b/Bin/dev/i18n-json.sh
@@ -272,10 +272,10 @@ extract_empty_keys() {
# Function to remove empty objects recursively from JSON file
remove_empty_objects() {
local json_file=$1
-
+
# Create a temporary file
local temp_file=$(mktemp)
-
+
# Use jq to recursively remove empty objects
# This function walks the entire JSON tree and removes any object that contains no leaf values
jq '
@@ -303,7 +303,26 @@ remove_empty_objects() {
end;
remove_empty
' "$json_file" > "$temp_file" 2>/dev/null
-
+
+ if [[ $? -eq 0 ]]; then
+ mv "$temp_file" "$json_file"
+ return 0
+ else
+ rm -f "$temp_file"
+ return 1
+ fi
+}
+
+# Function to sort JSON keys alphabetically (recursively)
+sort_json_keys() {
+ local json_file=$1
+
+ # Create a temporary file
+ local temp_file=$(mktemp)
+
+ # Use jq to recursively sort all object keys
+ jq --sort-keys '.' "$json_file" > "$temp_file" 2>/dev/null
+
if [[ $? -eq 0 ]]; then
mv "$temp_file" "$json_file"
return 0
@@ -329,7 +348,7 @@ generate_header() {
echo "Reference file: $REFERENCE_FILE"
echo "Folder: $(realpath "$FOLDER_PATH")"
if $TRANSLATE_MODE; then
- echo "Mode: TRANSLATION ENABLED (translates missing keys, removes extra/empty keys and empty objects)"
+ echo "Mode: TRANSLATION ENABLED (translates missing keys, removes extra/empty keys and empty objects, sorts all keys alphabetically)"
fi
echo ""
echo "Notes:"
@@ -342,6 +361,7 @@ generate_header() {
echo "- Results are sorted by descending line numbers for easier editing"
if $TRANSLATE_MODE; then
echo "- In translation mode, extra keys, empty keys, and empty objects are automatically removed"
+ echo "- In translation mode, all keys are sorted alphabetically to ensure consistency across languages"
fi
echo ""
echo "This report compares all language JSON files against the English reference file"
@@ -622,8 +642,18 @@ compare_language() {
print_color $RED "✗ Failed to clean up empty objects" >&2
echo ""
fi
+
+ # Sort all keys alphabetically to maintain consistency across language files
+ print_color $BLUE "Sorting keys alphabetically in $lang_name..." >&2
+ if sort_json_keys "$lang_file"; then
+ print_color $GREEN "✓ Keys sorted alphabetically" >&2
+ echo ""
+ else
+ print_color $RED "✗ Failed to sort keys" >&2
+ echo ""
+ fi
fi
-
+
# Clean up
rm -f "$lang_keys_file"
}
@@ -699,12 +729,24 @@ main() {
# Generate report header
generate_header
-
+
+ # Sort the English reference file if in translate mode
+ if $TRANSLATE_MODE; then
+ print_color $BLUE "Sorting keys alphabetically in English reference file..." >&2
+ if sort_json_keys "$ref_file_path"; then
+ print_color $GREEN "✓ English reference file keys sorted alphabetically" >&2
+ echo "" >&2
+ else
+ print_color $RED "✗ Failed to sort English reference file keys" >&2
+ echo "" >&2
+ fi
+ fi
+
local processed=0
for lang_file in "${language_files[@]}"; do
local filename=$(basename "$lang_file")
local lang_name="${filename%.json}"
-
+
# Skip the reference file in all-languages mode
if [[ -z "$target_language" && "$filename" == "$REFERENCE_FILE" ]]; then
continue
@@ -737,7 +779,7 @@ main() {
echo "Target language: $target_language"
fi
if $TRANSLATE_MODE; then
- echo "Translation mode: ENABLED (translated missing keys, removed extra keys, removed empty keys and objects)"
+ echo "Translation mode: ENABLED (translated missing keys, removed extra keys, removed empty keys and objects, sorted keys alphabetically)"
fi
echo "Report generated: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
@@ -761,8 +803,9 @@ show_usage() {
echo "" >&2
echo "Arguments:" >&2
echo " --translate Enable automatic translation of missing keys, removal of extra keys," >&2
- echo " removal of empty keys (empty strings or null values), and removal of" >&2
- echo " empty objects (nested objects containing no actual values)" >&2
+ echo " removal of empty keys (empty strings or null values), removal of" >&2
+ echo " empty objects (nested objects containing no actual values), and" >&2
+ echo " alphabetical sorting of all keys for consistency" >&2
echo " --list-models List all available Gemini models and exit" >&2
echo " language_code Optional. Compare only the specified language (e.g., 'fr', 'es', 'de')" >&2
echo " If not provided, all language files will be compared" >&2
@@ -775,8 +818,8 @@ show_usage() {
echo " $0 # Compare all languages" >&2
echo " $0 fr # Compare only French (fr.json)" >&2
echo " $0 --list-models # List available Gemini models" >&2
- echo " $0 --translate # Compare all, translate missing, remove extra/empty keys and objects" >&2
- echo " $0 --translate fr # Translate and clean French only" >&2
+ echo " $0 --translate # Compare all, translate missing, remove extra/empty keys and objects, sort keys" >&2
+ echo " $0 --translate fr # Translate, clean, and sort French only" >&2
echo "" >&2
echo "Requirements:" >&2
echo " - jq must be installed" >&2
@@ -790,6 +833,7 @@ show_usage() {
echo " - Progress messages are printed to stderr" >&2
echo " - Results are sorted by descending line numbers for easier editing" >&2
echo " - In translate mode, extra keys, empty keys, and empty objects are removed" >&2
+ echo " - In translate mode, all keys are sorted alphabetically for consistency" >&2
}
# Handle command line arguments
diff --git a/Bin/dev/i18n-qml-check.sh b/Bin/dev/i18n-qml-check.sh
deleted file mode 100755
index fdeb4f8f..00000000
--- a/Bin/dev/i18n-qml-check.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env -S bash
-
-# Comprehensive i18n checker for QML files
-# Finds hardcoded strings in various QML properties
-
-find . -name "*.qml" -type f | while read -r file; do
- # Skip if file doesn't exist or is not readable
- [[ ! -r "$file" ]] && continue
-
- # Check for hardcoded strings in common properties
- # Matches: property: "text with letters" but excludes I18n.tr calls
- issues=$(grep -n -E '(label|text|title|description|placeholder|tooltipText|tooltip):\s*"[^"]*[a-zA-Z][^"]*"' "$file" | grep -v 'I18n\.tr')
-
- # Also check for template literals with hardcoded text
- template_issues=$(grep -n -E '(label|text|title|description|placeholder|tooltipText|tooltip):\s*`[^`]*[a-zA-Z][^`]*`' "$file" | grep -v 'I18n\.tr')
-
- # Check for property assignments with hardcoded strings
- property_issues=$(grep -n -E 'property\s+string\s+\w+:\s*"[^"]*[a-zA-Z][^"]*"' "$file" | grep -v 'I18n\.tr')
-
- # Check for JavaScript object properties with hardcoded strings (like in arrays/models)
- js_object_issues=$(grep -n -E '"(label|text|title|description|placeholder|name)":\s*"[^"]*[a-zA-Z][^"]*"' "$file" | grep -v 'I18n\.tr')
-
- if [[ -n "$issues" || -n "$template_issues" || -n "$property_issues" || -n "$js_object_issues" ]]; then
- echo "$file"
- [[ -n "$issues" ]] && echo "$issues"
- [[ -n "$template_issues" ]] && echo "$template_issues"
- [[ -n "$property_issues" ]] && echo "$property_issues"
- [[ -n "$js_object_issues" ]] && echo "$js_object_issues"
- echo
- fi
-done
\ No newline at end of file
diff --git a/Bin/test-notification-replace.sh b/Bin/test-notification-replace.sh
new file mode 100755
index 00000000..50fcf1ae
--- /dev/null
+++ b/Bin/test-notification-replace.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+# Test script for notification replacement functionality
+
+echo "Testing notification replacement..."
+echo ""
+
+# Send initial notification and capture the ID
+echo "Step 1: Sending initial notification 'asdf'..."
+NOTIF_ID=$(notify-send -p "asdf")
+echo "Notification ID: $NOTIF_ID"
+echo ""
+
+# Wait a moment for the notification to appear
+sleep 1
+
+# Replace the notification
+echo "Step 2: Replacing notification $NOTIF_ID with 'test'..."
+notify-send -r "$NOTIF_ID" -p "test"
+echo ""
+
+echo "The notification should now show 'test' instead of 'asdf'."
+echo "If it still shows 'asdf', the replacement is not working."
+
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 00000000..e6a59884
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,429 @@
+# Noctalia Shell
+
+**A beautiful, minimal desktop shell for Wayland that actually gets out of your way.**
+
+Noctalia is a desktop shell built on Quickshell (Qt/QML framework) with a warm lavender aesthetic. It provides a complete desktop environment experience with panels, dock, notifications, lock screen, and extensive customization options.
+
+## AI Guidance
+
+* After receiving tool results, carefully reflect on their quality and determine optimal next steps before proceeding. Use your thinking to plan and iterate based on this new information, and then take the best next action.
+* For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
+* Before you finish, please verify your solution
+* Do what has been asked; nothing more, nothing less.
+* NEVER create files unless they're absolutely necessary for achieving your goal.
+* ALWAYS prefer editing an existing file to creating a new one.
+* NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
+
+## Project Overview
+
+- **Primary Language**: QML (Qt Quick)
+- **Framework**: Quickshell (Wayland-native shell framework)
+- **Supported Compositors**: Niri, Hyprland, Sway (with support for other Wayland compositors)
+- **License**: MIT
+- **Design Philosophy**: "quiet by design" - minimal, non-intrusive UI
+
+## Architecture
+
+### Core Entry Point
+- [shell.qml](shell.qml) - Main shell root that orchestrates all components
+ - Initializes services in a specific order
+ - Manages screen-specific instances of bars and panels
+ - Uses lazy loading with QML Loaders for memory optimization
+ - Implements MainScreen for each screen to manage bar + panels
+
+### Directory Structure
+
+#### `/Modules/` - UI Components
+Core visual modules and panels:
+- **Bar/** - Top/bottom bar with multiple widgets
+ - Bar.qml - Main bar component
+ - Widgets/ - Bar widget components
+ - Extras/ - Additional bar functionality
+- **Panels/** - Overlay panels (13 panel types)
+ - Audio/ - Audio device panel
+ - Battery/ - Battery status panel
+ - Bluetooth/ - Bluetooth device panel
+ - Calendar/ - Calendar panel
+ - ControlCenter/ - Quick settings panel
+ - Launcher/ - Application launcher/search
+ - NotificationHistory/ - Notification history panel
+ - SessionMenu/ - Power menu (logout, shutdown, etc.)
+ - Settings/ - Shell configuration UI
+ - SetupWizard/ - First-run setup experience
+ - Tray/ - System tray panel
+ - Wallpaper/ - Wallpaper management panel
+ - WiFi/ - WiFi network panel
+- **Background/** - Background/wallpaper rendering
+- **Dock/** - Application dock/launcher
+- **LockScreen/** - Screen locking functionality
+- **MainScreen/** - Main screen window management
+- **Notification/** - Notification system
+- **OSD/** - On-screen display for volume, brightness, etc.
+- **Toast/** - Toast notifications
+- **Tooltip/** - Tooltip system
+
+#### `/Services/` - Business Logic
+Core services that power the shell (40+ services):
+
+**System Integration:**
+- `CompositorService.qml` - Compositor-agnostic API
+- `HyprlandService.qml` - Hyprland-specific integration
+- `NiriService.qml` - Niri-specific integration
+- `SwayService.qml` - Sway-specific integration
+- `IPCService.qml` - Inter-process communication
+
+**Hardware & System:**
+- `AudioService.qml` - Audio control and monitoring
+- `BatteryService.qml` - Battery status and management
+- `BluetoothService.qml` - Bluetooth device management
+- `BrightnessService.qml` - Screen brightness control
+- `NetworkService.qml` - Network connection management
+- `PowerProfileService.qml` - Power profile management
+- `SystemStatService.qml` - System resource monitoring
+
+**UI & Theming:**
+- `AppThemeService.qml` - Main theming facade/orchestrator
+- `ColorPaletteGenerator.qml` - Material Design 3 palette generation
+- `TemplateRegistry.qml` - Template configurations (apps, terminals, Discord)
+- `TemplateProcessor.qml` - Matugen CLI + sed script execution
+- `ColorSchemeService.qml` - Color scheme management
+- `DarkModeService.qml` - Dark/light mode switching
+- `FontService.qml` - Font management
+- `WallpaperService.qml` - Wallpaper handling (with Matugen integration)
+- `NightLightService.qml` - Blue light filter
+
+**Features:**
+- `NotificationService.qml` - Notification daemon
+- `MediaService.qml` - Media player control (MPRIS)
+- `CalendarService.qml` - Calendar integration
+- `ClipboardService.qml` - Clipboard management
+- `LocationService.qml` - Geolocation for weather, etc.
+- `ScreenRecorderService.qml` - Screen recording functionality
+- `IdleInhibitorService.qml` - Prevent screen idle/sleep
+- `KeyboardLayoutService.qml` - Keyboard layout switching
+- `LockKeysService.qml` - Caps/Num lock status
+
+**Infrastructure:**
+- `BarService.qml` - Bar visibility and state management
+- `BarWidgetRegistry.qml` - Registry for bar widgets
+- `ControlCenterWidgetRegistry.qml` - Registry for control center widgets
+- `PanelService.qml` - Panel state management
+- `ToastService.qml` - Toast notification service
+- `TooltipService.qml` - Tooltip service
+- `HooksService.qml` - Custom hook execution
+- `ProgramCheckerService.qml` - Check for installed programs
+- `DistroService.qml` - Linux distribution detection
+- `GitHubService.qml` - GitHub API integration
+- `UpdateService.qml` - Update checking
+- `CavaService.qml` - Audio visualization (Cava integration)
+
+#### `/Commons/` - Shared Utilities
+Common components used throughout the shell:
+- `Settings.qml` - Centralized settings management
+- `I18n.qml` - Internationalization/translations
+- `Color.qml` - Color utilities and helpers
+- `Icons.qml` - Icon management
+- `IconsTabler.qml` - Tabler icon set (207KB icon definitions)
+- `ThemeIcons.qml` - Theme-specific icons
+- `Logger.qml` - Logging utility
+- `Style.qml` - Shared styling definitions
+- `Time.qml` - Time utilities
+
+#### `/Widgets/` - Reusable UI Components
+40+ custom QML widgets with the "N" prefix (Noctalia):
+- **Layout**: NBox, NPanel, NScrollView, NListView, NDivider
+- **Input**: NButton, NIconButton, NIconButtonHot, NTextInput, NSlider, NSpinBox, NToggle, NCheckbox, NRadioButton, NComboBox, NSearchableComboBox
+- **Display**: NLabel, NText, NIcon, NHeader, NImageCached, NImageCircled, NImageRounded
+- **Dialogs**: NColorPickerDialog, NFilePicker
+- **Special**: NContextMenu, NColorPicker, NIconPicker, NCircleStat, NCollapsible, NSectionEditor, NReorderCheckboxes, NDateTimeTokens, NBusyIndicator
+- **System**: MainScreen, BarExclusionZone
+
+#### `/Helpers/` - JavaScript Utilities
+Helper JavaScript modules:
+- `AdvancedMath.js` - Advanced mathematical functions
+- `ColorsConvert.js` - Color conversion utilities
+- `FuzzySort.js` - Fuzzy search implementation
+- `QtObj2JS.js` - Qt object to JavaScript conversion
+- `sha256.js` - SHA-256 hashing
+- `Debug.js` - Debug utilities
+
+#### `/Assets/` - Resources
+- Screenshots, icons, logos, themes
+- Default wallpapers
+- Theme resources
+
+#### `/Shaders/` - Graphics Shaders
+Custom shader effects for visual polish
+
+#### `/Bin/` - Executable Scripts
+Helper scripts and utilities
+
+## Key Features
+
+### 1. Multi-Monitor Support
+- Per-monitor bar configuration (TODO)
+- Screen-specific panel instances
+- Exclusion zones for proper compositor integration
+
+### 2. Theming System
+- Material You color generation (Matugen integration)
+- Dark/light mode support
+- Customizable color schemes
+- Font customization
+- Per-app theming capabilities
+
+### 3. Compositor Integration
+- Native support for Niri, Hyprland, Sway
+- Compositor-agnostic service layer
+- Workspace management
+- Window control
+
+### 4. Panel System
+Advanced panel management via MainScreen (13 panel types):
+- Audio panel
+- Battery panel
+- Bluetooth panel
+- Calendar panel
+- Control Center panel
+- Launcher panel
+- Notification history panel
+- Session menu panel
+- Settings panel
+- Setup wizard panel
+- Tray panel
+- Wallpaper panel
+- WiFi panel
+
+All panels use z-index layering and component-based loading.
+
+### 5. Customization
+- Setup wizard for first-time users
+- Extensive settings interface
+- Widget registry system for adding custom widgets
+- Hook system for custom scripts
+- Reorderable UI elements
+
+### 6. Audio Features
+- Multiple visualization types (Mirrored, Wave, Linear spectrum)
+- MPRIS media player integration
+- Audio device switching
+- Volume OSD
+
+### 7. Notifications
+- Custom notification daemon
+- Notification history
+- Do Not Disturb mode
+- Per-app notification settings
+
+## Development Setup
+
+```bash
+# Run the shell (requires Quickshell to be installed)
+qs -p .
+
+# Run with verbose output for debugging
+env NOCTALIA_DEBUG=1 qs -v -p .
+
+# Code formatting and linting
+qmlfmt -e -b 360 -t 2 -i 2 -w /path/to/file.qml # Format a QML file (requires qmlfmt, do not use qmlformat)
+qmllint **/*.qml # Lint all QML files for syntax errors
+```
+
+### Nix/NixOS (Recommended)
+```bash
+# Enter development shell
+nix develop
+
+# Or use the legacy shell
+nix-shell
+```
+
+The dev shell includes:
+- Quickshell with required features
+- Development utilities
+- Required environment variables
+
+### Package Structure
+- Nix flake with NixOS and Home Manager modules
+- Quickshell dependency (with X11 disabled, i3 enabled, hyprland enabled)
+- App2unit integration for .desktop file management
+
+## Configuration
+
+Settings are managed through `Commons/Settings.qml`:
+- Persistent configuration storage
+- Settings versioning
+- Migration handling
+- Type-safe settings access
+
+## Service Initialization Order
+
+From [shell.qml:150-164](shell.qml#L150-L164):
+1. WallpaperService
+2. AppThemeService
+3. ColorSchemeService
+4. BarWidgetRegistry
+5. LocationService
+6. NightLightService
+7. DarkModeService
+8. FontService
+9. HooksService
+10. BluetoothService
+11. BatteryService
+12. IdleInhibitorService
+13. PowerProfileService
+14. DistroService
+
+This order is critical - services depend on previously initialized services.
+
+## Component Lifecycle
+
+1. **Shell Root Initialization**
+ - Wait for I18n to load translations
+ - Wait for Settings to load configuration
+
+2. **Service Initialization**
+ - Services initialize in dependency order
+ - Each service may depend on Settings, I18n, or other services
+
+3. **Screen Components**
+ - MainScreen created per screen
+ - Bar and panel components loaded lazily
+ - Exclusion zones created after window loads
+
+4. **Background Components**
+ - Background/wallpaper
+ - Overview (workspace overview)
+ - Screen corners
+ - Dock
+ - Notifications
+ - Lock screen
+ - Toast overlay
+ - OSD
+
+## Special Patterns
+
+### Lazy Loading
+Components use QML Loaders extensively:
+- `active` property controls when components load
+- `asynchronous` for non-blocking loads
+- Memory optimization for unused screens/panels
+
+### Panel Management
+MainScreen pattern:
+- Single fullscreen window per screen
+- Manages bar + all overlay panels
+- Z-index based layering (panels at z-index 50)
+- Component-based architecture for panels
+
+### Registry Pattern
+BarWidgetRegistry and ControlCenterWidgetRegistry:
+- Centralized widget registration
+- Dynamic widget loading
+- Easy extension point for custom widgets
+
+### Singleton Pattern (Quickshell)
+Services in Quickshell use the `pragma Singleton` directive:
+- Add `pragma Singleton` at the top of the QML file (before imports)
+- No qmldir needed - Quickshell handles singleton resolution automatically
+- Import with standard path: `import qs.Services` or `import qs.Services.Theming`
+- Access via singleton name: `AppThemeService.generate()`
+
+Example:
+```qml
+pragma Singleton
+
+import QtQuick
+import qs.Commons
+
+Singleton {
+ id: root
+
+ function doSomething() {
+ // singleton logic
+ }
+}
+```
+
+### Theming Architecture
+The theming system was refactored (Jan 2025) into 4 focused services in `Services/`:
+
+**AppThemeService.qml** (~60 lines) - Main facade
+- Public API: `init()`, `generate()`, `generateFromWallpaper()`, `generateFromPredefinedScheme()`
+- Orchestrates theme generation based on wallpaper vs predefined schemes
+- Maintains backward compatibility with existing code
+
+**ColorPaletteGenerator.qml** (~100 lines) - Pure color logic
+- Single function: `generatePalette(colors, isDarkMode, isStrict)`
+- Generates complete Material Design 3 palette from base colors
+- No dependencies on other services (pure computation)
+
+**TemplateRegistry.qml** (~230 lines) - Configuration source of truth
+- Defines all application templates (`applications` array)
+- Defines terminal templates (`terminals` array)
+- Consolidated Discord clients (7 variants in single config)
+- Provides `discordClients` array for ProgramCheckerService
+- User template TOML generation
+
+**TemplateProcessor.qml** (~370 lines) - Template execution
+- Dual-path architecture:
+ - Wallpaper colors: Uses matugen CLI for color extraction + template processing
+ - Predefined schemes: Uses sed scripts to apply colors to templates
+- Handles terminal themes (copies pre-rendered files for predefined schemes)
+- User template support (matugen json/image subcommands)
+
+**Adding new application templates:**
+1. Add entry to `TemplateRegistry.applications` array in `Services/TemplateRegistry.qml`
+2. Create template file in `/Assets/MatugenTemplates/`
+3. Add Settings toggle in `Commons/Settings.qml` (templates.yourapp)
+4. Template will automatically work for both wallpaper and predefined paths
+
+**Discord client handling:**
+All Discord clients use the same template (`vesktop.css`) but different output paths.
+Auto-detected by ProgramCheckerService, enabled via single `Settings.data.templates.discord` toggle.
+
+## Git Hooks
+Uses `lefthook` for git hooks (see lefthook.yml)
+
+## Community Resources
+- Documentation: https://docs.noctalia.dev
+- Discord: https://discord.noctalia.dev
+- GitHub: https://github.com/noctalia-dev/noctalia-shell
+
+## Contributing
+See [development guidelines](https://docs.noctalia.dev/development/guideline)
+
+## Notes for AI Assistants
+
+### Code Style
+- QML component names use PascalCase
+- Service names end with "Service.qml"
+- Widget names start with "N" prefix (e.g., NButton, NPanel)
+- JavaScript helpers in Helpers/ directory
+
+### Common Tasks
+1. **Adding a new bar widget**: Register in BarWidgetRegistry
+2. **Adding a control center widget**: Register in ControlCenterWidgetRegistry
+3. **Creating a service**: Follow the Service pattern, add to init order if needed
+4. **Modifying theming**: Check AppThemeService and ColorSchemeService
+5. **Panel work**: Edit in Modules/Panels/, ensure proper z-index in shell.qml
+
+### Important Files to Check
+- Settings schema: `Commons/Settings.qml`
+- Service initialization: `shell.qml` (Component.onCompleted)
+- Panel registration: `shell.qml` (panelComponents array)
+- Theme system: `Services/AppThemeService.qml`
+- Template configurations: `Services/TemplateRegistry.qml`
+- Palette generation: `Services/ColorPaletteGenerator.qml`
+- Template processing: `Services/TemplateProcessor.qml`
+
+### Testing
+- Test on target compositors: Niri, Hyprland, Sway
+- Check multi-monitor scenarios
+- Verify lazy loading doesn't break functionality
+- Test settings persistence across restarts
+
+### Debugging
+- Use `Logger.qml` for logging (Logger.i, Logger.d, Logger.w, Logger.e)
+- Check console output for service initialization messages
+- Verify service initialization order if adding dependencies
diff --git a/Commons/Color.qml b/Commons/Color.qml
index 06c3436c..1e358344 100644
--- a/Commons/Color.qml
+++ b/Commons/Color.qml
@@ -4,7 +4,6 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
/*
@@ -42,11 +41,13 @@ Singleton {
property color mOutline: customColors.mOutline
property color mShadow: customColors.mShadow
- property color transparent: "transparent"
+ property color mHover: customColors.mHover
+ property color mOnHover: customColors.mOnHover
- // Extra colors that are auto generated
- readonly property color mHover: Settings.data.colorSchemes.darkMode ? Qt.darker(mOnSurface, 1.25) : Qt.lighter(mOnSurface, 1.25)
- readonly property color mOnHover: Settings.data.colorSchemes.darkMode ? Qt.darker(mSurface, 1.25) : Qt.lighter(mSurface, 1.25)
+ // --- Absolute Colors
+ property color transparent: "transparent"
+ property color black: "#000000"
+ property color white: "#ffffff"
// --------------------------------
// Default colors: RosePine
@@ -73,6 +74,9 @@ Singleton {
property color mOutline: "#342c42"
property color mShadow: "#120f18"
+
+ property color mHover: "#e0b7c9"
+ property color mOnHover: "#20161f"
}
// ----------------------------------------------------------------
@@ -101,6 +105,9 @@ Singleton {
property color mOutline: customColorsData.mOutline
property color mShadow: customColorsData.mShadow
+
+ property color mHover: customColorsData.mHover
+ property color mOnHover: customColorsData.mOnHover
}
// FileView to load custom colors data from colors.json
@@ -153,6 +160,9 @@ Singleton {
property color mOutline: defaultColors.mOutline
property color mShadow: defaultColors.mShadow
+
+ property color mHover: defaultColors.mHover
+ property color mOnHover: defaultColors.mOnHover
}
}
}
diff --git a/Commons/I18n.qml b/Commons/I18n.qml
index 1399700c..a5241444 100644
--- a/Commons/I18n.qml
+++ b/Commons/I18n.qml
@@ -279,7 +279,7 @@ Singleton {
interpolations = {}
if (!isLoaded) {
- Logger.d("I18n", "Translations not loaded yet")
+ //Logger.d("I18n", "Translations not loaded yet")
return key
}
diff --git a/Commons/Icons.qml b/Commons/Icons.qml
index 919782d6..acd7e1b7 100644
--- a/Commons/Icons.qml
+++ b/Commons/Icons.qml
@@ -10,9 +10,9 @@ Singleton {
// Expose the font family name for easy access
readonly property string fontFamily: currentFontLoader ? currentFontLoader.name : ""
- readonly property string defaultIcon: TablerIcons.defaultIcon
- readonly property var icons: TablerIcons.icons
- readonly property var aliases: TablerIcons.aliases
+ readonly property string defaultIcon: IconsTabler.defaultIcon
+ readonly property var icons: IconsTabler.icons
+ readonly property var aliases: IconsTabler.aliases
readonly property string fontPath: "/Assets/Fonts/tabler/tabler-icons.ttf"
// Current active font loader
diff --git a/Commons/TablerIcons.qml b/Commons/IconsTabler.qml
similarity index 99%
rename from Commons/TablerIcons.qml
rename to Commons/IconsTabler.qml
index 59b226d3..d067e2dd 100644
--- a/Commons/TablerIcons.qml
+++ b/Commons/IconsTabler.qml
@@ -71,6 +71,8 @@ Singleton {
"chevron-down": "chevron-down",
"caret-up": "caret-up-filled",
"caret-down": "caret-down-filled",
+ "caret-left": "caret-left-filled",
+ "caret-right": "caret-right-filled",
"star": "star",
"star-off": "star-off",
"battery-exclamation": "battery-exclamation",
diff --git a/Commons/KeyboardLayout.qml b/Commons/KeyboardLayout.qml
deleted file mode 100644
index 38d08aa1..00000000
--- a/Commons/KeyboardLayout.qml
+++ /dev/null
@@ -1,205 +0,0 @@
-pragma Singleton
-
-import QtQuick
-
-QtObject {
- id: root
-
- // Comprehensive language name to ISO code mapping
- property var languageMap: {
- "english"// English variants
- : "us",
- "american": "us",
- "united states": "us",
- "us english": "us",
- "british": "gb",
- "uk": "ua",
- "united kingdom"// FIXED: Ukrainian language code should map to Ukraine
- : "gb",
- "english (uk)": "gb",
- "canadian": "ca",
- "canada": "ca",
- "canadian english": "ca",
- "australian": "au",
- "australia": "au",
- "swedish"// Nordic countries
- : "se",
- "svenska": "se",
- "sweden": "se",
- "norwegian": "no",
- "norsk": "no",
- "norway": "no",
- "danish": "dk",
- "dansk": "dk",
- "denmark": "dk",
- "finnish": "fi",
- "suomi": "fi",
- "finland": "fi",
- "icelandic": "is",
- "íslenska": "is",
- "iceland": "is",
- "german"// Western/Central European Germanic
- : "de",
- "deutsch": "de",
- "germany": "de",
- "austrian": "at",
- "austria": "at",
- "österreich": "at",
- "swiss": "ch",
- "switzerland": "ch",
- "schweiz": "ch",
- "suisse": "ch",
- "dutch": "nl",
- "nederlands": "nl",
- "netherlands": "nl",
- "holland": "nl",
- "belgian": "be",
- "belgium": "be",
- "belgië": "be",
- "belgique": "be",
- "french"// Romance languages (Western/Southern Europe)
- : "fr",
- "français": "fr",
- "france": "fr",
- "canadian french": "ca",
- "spanish": "es",
- "español": "es",
- "spain": "es",
- "castilian": "es",
- "italian": "it",
- "italiano": "it",
- "italy": "it",
- "portuguese": "pt",
- "português": "pt",
- "portugal": "pt",
- "catalan": "ad",
- "català": "ad",
- "andorra": "ad",
- "romanian"// Eastern European Romance
- : "ro",
- "română": "ro",
- "romania": "ro",
- "russian"// Slavic languages (Eastern Europe)
- : "ru",
- "русский": "ru",
- "russia": "ru",
- "polish": "pl",
- "polski": "pl",
- "poland": "pl",
- "czech": "cz",
- "čeština": "cz",
- "czech republic": "cz",
- "slovak": "sk",
- "slovenčina": "sk",
- "slovakia": "sk",
- "uk": "ua",
- "ukrainian"// Ukrainian language code
- : "ua",
- "українська": "ua",
- "ukraine": "ua",
- "bulgarian": "bg",
- "български": "bg",
- "bulgaria": "bg",
- "serbian": "rs",
- "srpski": "rs",
- "serbia": "rs",
- "croatian": "hr",
- "hrvatski": "hr",
- "croatia": "hr",
- "slovenian": "si",
- "slovenščina": "si",
- "slovenia": "si",
- "bosnian": "ba",
- "bosanski": "ba",
- "bosnia": "ba",
- "macedonian": "mk",
- "македонски": "mk",
- "macedonia": "mk",
- "irish"// Celtic languages (Western Europe)
- : "ie",
- "gaeilge": "ie",
- "ireland": "ie",
- "welsh": "gb",
- "cymraeg": "gb",
- "wales": "gb",
- "scottish": "gb",
- "gàidhlig": "gb",
- "scotland": "gb",
- "estonian"// Baltic languages (Northern Europe)
- : "ee",
- "eesti": "ee",
- "estonia": "ee",
- "latvian": "lv",
- "latviešu": "lv",
- "latvia": "lv",
- "lithuanian": "lt",
- "lietuvių": "lt",
- "lithuania": "lt",
- "hungarian"// Other European languages
- : "hu",
- "magyar": "hu",
- "hungary": "hu",
- "greek": "gr",
- "ελληνικά": "gr",
- "greece": "gr",
- "albanian": "al",
- "shqip": "al",
- "albania": "al",
- "maltese": "mt",
- "malti": "mt",
- "malta": "mt",
- "turkish"// West/Southwest Asian languages
- : "tr",
- "türkçe": "tr",
- "turkey": "tr",
- "arabic": "ar",
- "العربية": "ar",
- "arab": "ar",
- "hebrew": "il",
- "עברית": "il",
- "israel": "il",
- "brazilian"// South American languages
- : "br",
- "brazilian portuguese": "br",
- "brasil": "br",
- "brazil": "br",
- "japanese"// East Asian languages
- : "jp",
- "日本語": "jp",
- "japan": "jp",
- "korean": "kr",
- "한국어": "kr",
- "korea": "kr",
- "south korea": "kr",
- "chinese": "cn",
- "中文": "cn",
- "china": "cn",
- "simplified chinese": "cn",
- "traditional chinese": "tw",
- "taiwan": "tw",
- "繁體中文": "tw",
- "thai"// Southeast Asian languages
- : "th",
- "ไทย": "th",
- "thailand": "th",
- "vietnamese": "vn",
- "tiếng việt": "vn",
- "vietnam": "vn",
- "hindi"// South Asian languages
- : "in",
- "हिन्दी": "in",
- "india": "in",
- "afrikaans"// African languages
- : "za",
- "south africa": "za",
- "south african": "za",
- "qwerty"// Layout variants
- : "us",
- "dvorak": "us",
- "colemak": "us",
- "workman": "us",
- "azerty": "fr",
- "norman": "fr",
- "qwertz": "de"
- }
-}
diff --git a/Commons/Logger.qml b/Commons/Logger.qml
index 5613d890..5c18b90b 100644
--- a/Commons/Logger.qml
+++ b/Commons/Logger.qml
@@ -25,27 +25,27 @@ Singleton {
}
}
- // Info log (always visible)
- function i(...args) {
- var msg = _formatMessage(...args)
- console.log(msg)
- }
-
// Debug log (only when Settings.isDebug is true)
function d(...args) {
if (Settings && Settings.isDebug) {
var msg = _formatMessage(...args)
- console.log(msg)
+ console.debug(msg)
}
}
- // Warning log
+ // Info log (always visible)
+ function i(...args) {
+ var msg = _formatMessage(...args)
+ console.info(msg)
+ }
+
+ // Warning log (always visible)
function w(...args) {
var msg = _formatMessage(...args)
console.warn(msg)
}
- // Error log
+ // Error log (always visible)
function e(...args) {
var msg = _formatMessage(...args)
console.error(msg)
diff --git a/Commons/Settings.qml b/Commons/Settings.qml
index 1483a519..81063cf2 100644
--- a/Commons/Settings.qml
+++ b/Commons/Settings.qml
@@ -4,7 +4,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
import "../Helpers/QtObj2JS.js" as QtObj2JS
Singleton {
@@ -14,7 +14,7 @@ Singleton {
readonly property alias data: adapter
property bool isLoaded: false
property bool directoriesCreated: false
- property int settingsVersion: 16
+ property int settingsVersion: 21
property bool isDebug: Quickshell.env("NOCTALIA_DEBUG") === "1"
// Define our app directories
@@ -54,8 +54,9 @@ Singleton {
// This should only be activated once when the settings structure has changed
// Then it should be commented out again, regular users don't need to generate
// default settings on every start
- // TODO: automate this someday!
- //generateDefaultSettings()
+ if (isDebug) {
+ generateDefaultSettings()
+ }
// Patch-in the local default, resolved to user's home
adapter.general.avatarImage = defaultAvatar
@@ -146,10 +147,18 @@ Singleton {
property real marginVertical: 0.25
property real marginHorizontal: 0.25
+ // Bar outer corners (inverted/concave corners at bar edges when not floating)
+ property bool outerCorners: true
+
+ // Reserves space with compositor
+ property bool exclusive: true
+
// Widget configuration for modular bar system
property JsonObject widgets
widgets: JsonObject {
property list left: [{
+ "id": "ControlCenter"
+ }, {
"id": "SystemMonitor"
}, {
"id": "ActiveWindow"
@@ -173,8 +182,6 @@ Singleton {
"id": "Brightness"
}, {
"id": "Clock"
- }, {
- "id": "ControlCenter"
}]
}
}
@@ -182,6 +189,7 @@ Singleton {
// general
property JsonObject general: JsonObject {
property string avatarImage: ""
+ property bool dimDesktop: true
property bool showScreenCorners: false
property bool forceBlackScreenCorners: false
property real scaleRatio: 1.0
@@ -191,9 +199,24 @@ Singleton {
property bool animationDisabled: false
property bool compactLockScreen: false
property bool lockOnSuspend: true
+ property bool enableShadows: true
+ property string shadowDirection: "bottom_right"
+ property int shadowOffsetX: 2
+ property int shadowOffsetY: 3
property string language: ""
}
+ // ui
+ property JsonObject ui: JsonObject {
+ property string fontDefault: "Roboto"
+ property string fontFixed: "DejaVu Sans Mono"
+ property real fontDefaultScale: 1.0
+ property real fontFixedScale: 1.0
+ property bool tooltipsEnabled: true
+ property bool panelsAttachedToBar: true
+ property bool settingsPanelAttachToBar: false
+ }
+
// location
property JsonObject location: JsonObject {
property string name: defaultLocation
@@ -223,6 +246,7 @@ Singleton {
// wallpaper
property JsonObject wallpaper: JsonObject {
property bool enabled: true
+ property bool overviewEnabled: false
property string directory: ""
property bool enableMultiMonitorDirectories: false
property bool recursiveSearch: false
@@ -236,6 +260,7 @@ Singleton {
property string transitionType: "random"
property real transitionEdgeSmoothness: 0.05
property list monitors: []
+ property string panelPosition: "follow_bar"
}
// applauncher
@@ -316,7 +341,7 @@ Singleton {
// notifications
property JsonObject notifications: JsonObject {
- property bool doNotDisturb: false
+ property bool enabled: true
property list monitors: []
property string location: "top_right"
property bool overlayLayer: true
@@ -340,27 +365,18 @@ Singleton {
property JsonObject audio: JsonObject {
property int volumeStep: 5
property bool volumeOverdrive: false
- property int cavaFrameRate: 60
+ property int cavaFrameRate: 30
property string visualizerType: "linear"
+ property string visualizerQuality: "high"
property list mprisBlacklist: []
property string preferredPlayer: ""
}
- // ui
- property JsonObject ui: JsonObject {
- property string fontDefault: "Roboto"
- property string fontFixed: "DejaVu Sans Mono"
- property real fontDefaultScale: 1.0
- property real fontFixedScale: 1.0
- property bool tooltipsEnabled: true
- property bool panelsAttachedToBar: true
- property bool panelsOverlayLayer: true
- }
-
// brightness
property JsonObject brightness: JsonObject {
property int brightnessStep: 5
property bool enforceMinimum: true
+ property bool enableDdcSupport: false
}
property JsonObject colorSchemes: JsonObject {
@@ -383,19 +399,22 @@ Singleton {
property bool kitty: false
property bool ghostty: false
property bool foot: false
+ property bool wezterm: false
property bool fuzzel: false
property bool discord: false
- property bool discord_vesktop: false
- property bool discord_webcord: false
- property bool discord_armcord: false
- property bool discord_equibop: false
- property bool discord_lightcord: false
- property bool discord_dorion: false
property bool pywalfox: false
property bool vicinae: false
property bool walker: false
property bool code: false
+ property bool spicetify: false
property bool enableUserTemplates: false
+
+ property bool discord_vesktop: false // To be deleted soon
+ property bool discord_webcord: false // To be deleted soon
+ property bool discord_armcord: false // To be deleted soon
+ property bool discord_equibop: false // To be deleted soon
+ property bool discord_lightcord: false // To be deleted soon
+ property bool discord_dorion: false // To be deleted soon
}
// night light
@@ -600,6 +619,28 @@ Singleton {
Logger.w("Settings", "Added a ControlCenter widget to the right section")
}
}
+
+ // -----------------
+ // 5th. Migrate Discord templates (version 20 → 21)
+ // Consolidate individual discord_* properties into unified discord property
+ if (adapter.settingsVersion < 21) {
+ var anyDiscordEnabled = false
+
+ // Check if any Discord client was enabled
+ const discordClients = ["discord_vesktop", "discord_webcord", "discord_armcord", "discord_equibop", "discord_lightcord", "discord_dorion"]
+
+ for (var i = 0; i < discordClients.length; i++) {
+ if (adapter.templates[discordClients[i]]) {
+ anyDiscordEnabled = true
+ break
+ }
+ }
+
+ // Set unified discord property
+ adapter.templates.discord = anyDiscordEnabled
+
+ Logger.i("Settings", "Migrated Discord templates to unified 'discord' property (enabled:", anyDiscordEnabled + ")")
+ }
}
// -----------------------------------------------------
diff --git a/Commons/Style.qml b/Commons/Style.qml
index b3731805..b2999d7b 100644
--- a/Commons/Style.qml
+++ b/Commons/Style.qml
@@ -2,7 +2,7 @@ pragma Singleton
import QtQuick
import Quickshell
-import Quickshell.Io
+import qs.Services.Power
Singleton {
id: root
@@ -10,7 +10,7 @@ Singleton {
/*
Preset sizes for font, radii, ?
- */
+ */
// Font size
property real fontSizeXXS: 8
@@ -57,11 +57,19 @@ Singleton {
property real opacityAlmost: 0.95
property real opacityFull: 1.0
+ // Shadows
+ property real shadowOpacity: 0.85
+ property real shadowBlur: 1.0
+ property int shadowBlurMax: 22
+ property real shadowHorizontalOffset: Settings.data.general.shadowOffsetX
+ property real shadowVerticalOffset: Settings.data.general.shadowOffsetY
+
// Animation duration (ms)
- property int animationFast: Settings.data.general.animationDisabled ? 0 : Math.round(150 / Settings.data.general.animationSpeed)
- property int animationNormal: Settings.data.general.animationDisabled ? 0 : Math.round(300 / Settings.data.general.animationSpeed)
- property int animationSlow: Settings.data.general.animationDisabled ? 0 : Math.round(450 / Settings.data.general.animationSpeed)
- property int animationSlowest: Settings.data.general.animationDisabled ? 0 : Math.round(750 / Settings.data.general.animationSpeed)
+ property int animationFaster: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(75 / Settings.data.general.animationSpeed)
+ property int animationFast: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(150 / Settings.data.general.animationSpeed)
+ property int animationNormal: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(300 / Settings.data.general.animationSpeed)
+ property int animationSlow: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(450 / Settings.data.general.animationSpeed)
+ property int animationSlowest: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(750 / Settings.data.general.animationSpeed)
// Delays
property int tooltipDelay: 300
@@ -92,15 +100,15 @@ Singleton {
property real capsuleHeight: {
switch (Settings.data.bar.density) {
case "mini":
- return barHeight * 1.0
+ return Math.round(barHeight * 1.0)
case "compact":
- return barHeight * 0.85
+ return Math.round(barHeight * 0.85)
case "comfortable":
- return barHeight * 0.73
+ return Math.round(barHeight * 0.73)
default:
case "default":
- return barHeight * 0.82
+ return Math.round(barHeight * 0.82)
}
}
}
diff --git a/Commons/ThemeIcons.qml b/Commons/ThemeIcons.qml
index aa8c4aea..addede93 100644
--- a/Commons/ThemeIcons.qml
+++ b/Commons/ThemeIcons.qml
@@ -2,7 +2,6 @@ pragma Singleton
import QtQuick
import Quickshell
-import qs.Services
Singleton {
id: root
diff --git a/Commons/Time.qml b/Commons/Time.qml
index 16dbfdca..b8245808 100644
--- a/Commons/Time.qml
+++ b/Commons/Time.qml
@@ -3,24 +3,46 @@ pragma Singleton
import Quickshell
import QtQuick
import qs.Commons
-import qs.Services
Singleton {
id: root
// Current date
- property var date: new Date()
+ property var now: new Date()
// Returns a Unix Timestamp (in seconds)
readonly property int timestamp: {
- return Math.floor(date / 1000)
+ return Math.floor(root.now / 1000)
}
Timer {
+ id: updateTimer
interval: 1000
repeat: true
running: true
- onTriggered: root.date = new Date()
+ triggeredOnStart: false
+ onTriggered: {
+ var newTime = new Date()
+ root.now = newTime
+
+ // Adjust next interval to sync with the start of the next second
+ var msIntoSecond = newTime.getMilliseconds()
+ if (msIntoSecond > 100) {
+ // If we're more than 100ms into the second, adjust for next time
+ updateTimer.interval = 1000 - msIntoSecond + 10 // +10ms buffer
+ updateTimer.restart()
+ } else {
+ updateTimer.interval = 1000
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ // Start by syncing to the next second boundary
+ var now = new Date()
+ var msUntilNextSecond = 1000 - now.getMilliseconds()
+ updateTimer.interval = msUntilNextSecond + 10 // +10ms buffer
+ updateTimer.restart()
}
// Formats a Date object into a YYYYMMDD-HHMMSS string.
diff --git a/Modules/Background/Background.qml b/Modules/Background/Background.qml
index 9da4118f..4cf8446b 100644
--- a/Modules/Background/Background.qml
+++ b/Modules/Background/Background.qml
@@ -2,8 +2,8 @@ import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Services
-import qs.Widgets
+import qs.Services.Compositor
+import qs.Services.UI
Variants {
id: backgroundVariants
@@ -21,6 +21,7 @@ Variants {
// Internal state management
property string transitionType: "fade"
property real transitionProgress: 0
+ property bool isStartupTransition: true
readonly property real edgeSmoothness: Settings.data.wallpaper.transitionEdgeSmoothness
readonly property var allTransitions: WallpaperService.allTransitions
@@ -77,7 +78,11 @@ Variants {
Connections {
target: CompositorService
function onDisplayScalesChanged() {
- setWallpaperInitial()
+ // Recalculate image sizes without interrupting startup transition
+ if (isStartupTransition) {
+ return
+ }
+ recalculateImageSizes()
}
}
@@ -85,7 +90,7 @@ Variants {
screen: modelData
WlrLayershell.layer: WlrLayer.Background
WlrLayershell.exclusionMode: ExclusionMode.Ignore
- WlrLayershell.namespace: "quickshell-wallpaper"
+ WlrLayershell.namespace: "noctalia-wallpaper-" + (screen?.name || "unknown")
anchors {
bottom: true
@@ -110,7 +115,7 @@ Variants {
property bool dimensionsCalculated: false
source: ""
- smooth: true
+ smooth: false
mipmap: false
visible: false
cache: false
@@ -139,7 +144,7 @@ Variants {
property bool dimensionsCalculated: false
source: ""
- smooth: true
+ smooth: false
mipmap: false
visible: false
cache: false
@@ -307,8 +312,10 @@ Variants {
transitionProgress = 0.0
// Now clear nextWallpaper after currentWallpaper has the new source
+ // Force complete cleanup to free texture memory (~18-25MB per monitor)
Qt.callLater(() => {
nextWallpaper.source = ""
+ nextWallpaper.sourceSize = undefined
Qt.callLater(() => {
currentWallpaper.asynchronous = true
})
@@ -330,10 +337,10 @@ Variants {
var dim = Qt.size(0, 0)
if (screenWidth >= screenHeight) {
const w = Math.min(screenWidth, wpWidth)
- dim = Qt.size(w, w / imageAspectRatio)
+ dim = Qt.size(Math.round(w), Math.round(w / imageAspectRatio))
} else {
const h = Math.min(screenHeight, wpHeight)
- dim = Qt.size(h * imageAspectRatio, h)
+ dim = Qt.size(Math.round(h * imageAspectRatio), Math.round(h))
}
Logger.d("Background", `Wallpaper resized on ${modelData.name} ${screenWidth}x${screenHeight} @ ${compositorScale}x`, "src:", wpWidth, wpHeight, "dst:", dim.width, dim.height)
@@ -342,11 +349,23 @@ Variants {
// ------------------------------------------------------
function recalculateImageSizes() {
+ // Re-evaluate and apply optimal sourceSize for both images when ready
if (currentWallpaper.status === Image.Ready) {
- currentWallpaper.calculateSourceSize()
+ const optimal = calculateOptimalWallpaperSize(currentWallpaper.implicitWidth, currentWallpaper.implicitHeight)
+ if (optimal !== undefined && optimal !== false) {
+ currentWallpaper.sourceSize = optimal
+ } else {
+ currentWallpaper.sourceSize = undefined
+ }
}
+
if (nextWallpaper.status === Image.Ready) {
- nextWallpaper.calculateSourceSize()
+ const optimal2 = calculateOptimalWallpaperSize(nextWallpaper.implicitWidth, nextWallpaper.implicitHeight)
+ if (optimal2 !== undefined && optimal2 !== false) {
+ nextWallpaper.sourceSize = optimal2
+ } else {
+ nextWallpaper.sourceSize = undefined
+ }
}
}
@@ -358,7 +377,10 @@ Variants {
return
}
- setWallpaperImmediate(WallpaperService.getWallpaper(modelData.name))
+ const wallpaperPath = WallpaperService.getWallpaper(modelData.name)
+
+ futureWallpaper = wallpaperPath
+ performStartupTransition()
}
// ------------------------------------------------------
@@ -366,8 +388,10 @@ Variants {
transitionAnimation.stop()
transitionProgress = 0.0
- // Clear with proper delay to allow GC
+ // Clear nextWallpaper completely to free texture memory
nextWallpaper.source = ""
+ nextWallpaper.sourceSize = undefined
+
currentWallpaper.source = ""
Qt.callLater(() => {
@@ -449,6 +473,52 @@ Variants {
break
}
}
+
+ // ------------------------------------------------------
+ // Dedicated function for startup animation
+ function performStartupTransition() {
+ // Get the transitionType from the settings
+ transitionType = Settings.data.wallpaper.transitionType
+
+ if (transitionType == "random") {
+ var index = Math.floor(Math.random() * allTransitions.length)
+ transitionType = allTransitions[index]
+ }
+
+ // Ensure the transition type really exists
+ if (transitionType !== "none" && !allTransitions.includes(transitionType)) {
+ transitionType = "fade"
+ }
+
+ // Apply transitionType so the shader loader picks the correct shader
+ this.transitionType = transitionType
+
+ switch (transitionType) {
+ case "none":
+ setWallpaperImmediate(futureWallpaper)
+ break
+ case "wipe":
+ wipeDirection = Math.random() * 4
+ setWallpaperWithTransition(futureWallpaper)
+ break
+ case "disc":
+ // Force center origin for elegant startup animation
+ discCenterX = 0.5
+ discCenterY = 0.5
+ setWallpaperWithTransition(futureWallpaper)
+ break
+ case "stripes":
+ stripesCount = Math.round(Math.random() * 20 + 4)
+ stripesAngle = Math.random() * 360
+ setWallpaperWithTransition(futureWallpaper)
+ break
+ default:
+ setWallpaperWithTransition(futureWallpaper)
+ break
+ }
+ // Mark startup transition complete
+ isStartupTransition = false
+ }
}
}
}
diff --git a/Modules/Background/Overview.qml b/Modules/Background/Overview.qml
index a4009366..6dcbd3bc 100644
--- a/Modules/Background/Overview.qml
+++ b/Modules/Background/Overview.qml
@@ -1,30 +1,38 @@
import QtQuick
-import QtQuick.Effects
import Quickshell
+import QtQuick.Effects
import Quickshell.Wayland
import qs.Commons
-import qs.Services
-import qs.Widgets
+import qs.Services.Compositor
+import qs.Services.UI
-Variants {
- model: Quickshell.screens
+Loader {
+ active: CompositorService.isNiri && Settings.data.wallpaper.enabled && Settings.data.wallpaper.overviewEnabled
- delegate: Loader {
- required property ShellScreen modelData
- property string wallpaper: ""
+ sourceComponent: Variants {
+ model: Quickshell.screens
- active: CompositorService.isNiri && Settings.data.wallpaper.enabled && modelData
-
- sourceComponent: PanelWindow {
+ delegate: PanelWindow {
id: panelWindow
+ required property ShellScreen modelData
+ property string wallpaper: ""
+ property bool overviewIsSeen: false
+
Component.onCompleted: {
if (modelData) {
- Logger.d("Overview", "Loading Overview component for Niri on", modelData.name)
+ Logger.d("Overview", "Loading overview for Niri on", modelData.name)
}
setWallpaperInitial()
}
+ Component.onDestruction: {
+ // Clean up resources to prevent memory leak when overviewEnabled is toggled off
+ timerDisableFx.stop()
+ bgImage.layer.enabled = false
+ bgImage.source = ""
+ }
+
// External state management
Connections {
target: WallpaperService
@@ -35,6 +43,27 @@ Variants {
}
}
+ Connections {
+ target: CompositorService.backend
+ function onOverviewActiveChanged() {
+ if (CompositorService.backend.overviewActive) {
+ overviewIsSeen = true
+ timerDisableFx.stop()
+ } else {
+ timerDisableFx.restart()
+ }
+ }
+ }
+
+ // Use to disable effects when overview is no longer necessary.
+ Timer {
+ id: timerDisableFx
+ interval: 2000
+ onTriggered: {
+ overviewIsSeen = false
+ }
+ }
+
function setWallpaperInitial() {
if (!WallpaperService || !WallpaperService.isInitialized) {
Qt.callLater(setWallpaperInitial)
@@ -50,7 +79,7 @@ Variants {
screen: modelData
WlrLayershell.layer: WlrLayer.Background
WlrLayershell.exclusionMode: ExclusionMode.Ignore
- WlrLayershell.namespace: "quickshell-overview"
+ WlrLayershell.namespace: "noctalia-overview-" + (screen?.name || "unknown")
anchors {
top: true
@@ -64,23 +93,28 @@ Variants {
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
source: wallpaper
- smooth: true
+ smooth: false
mipmap: false
cache: false
asynchronous: true
- // Image is heavily blurred, so might as well save a lot of memory here.
- sourceSize: Qt.size(1280, 720)
- }
+ visible: overviewIsSeen
- MultiEffect {
- anchors.fill: parent
- source: bgImage
- autoPaddingEnabled: false
- blurEnabled: true
- blur: 1.0
- blurMax: 64
- colorization: Style.opacityMedium
- colorizationColor: Settings.data.colorSchemes.darkMode ? Color.mSurface : Color.mOnSurface
+ // Image is heavily blurred, so use low resolution to save memory
+ sourceSize: Qt.size(1280, 720)
+
+ layer.enabled: overviewIsSeen
+ layer.smooth: false
+ layer.effect: MultiEffect {
+ blurEnabled: true
+ blur: 1.0
+ blurMax: 32
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: Settings.data.colorSchemes.darkMode ? Color.mSurface : Color.mOnSurface
+ opacity: 0.8
+ }
}
}
}
diff --git a/Modules/Background/ScreenCorners.qml b/Modules/Background/ScreenCorners.qml
deleted file mode 100644
index 6c35b008..00000000
--- a/Modules/Background/ScreenCorners.qml
+++ /dev/null
@@ -1,146 +0,0 @@
-import QtQuick
-import QtQuick.Effects
-import Quickshell
-import Quickshell.Wayland
-import qs.Commons
-import qs.Services
-import qs.Widgets
-
-Loader {
- active: Settings.data.general.showScreenCorners && (!Settings.data.ui.panelsAttachedToBar || Settings.data.bar.backgroundOpacity >= 1 || Settings.data.bar.floating)
-
- sourceComponent: Variants {
- model: Quickshell.screens
-
- PanelWindow {
- id: root
-
- required property ShellScreen modelData
- screen: modelData
-
- property color cornerColor: Settings.data.general.forceBlackScreenCorners ? Qt.rgba(0, 0, 0, 1) : Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity)
- property real cornerRadius: Style.screenRadius
- property real cornerSize: Style.screenRadius
-
- // Helper properties for margin calculations
- readonly property bool barOnThisMonitor: BarService.isVisible && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.backgroundOpacity > 0
- readonly property real barMargin: !Settings.data.bar.floating && barOnThisMonitor ? Style.barHeight : 0
-
- color: Color.transparent
-
- WlrLayershell.exclusionMode: ExclusionMode.Ignore
- WlrLayershell.namespace: "quickshell-corner"
- WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
-
- anchors {
- top: true
- bottom: true
- left: true
- right: true
- }
-
- margins {
- // When bar is floating, corners should be at screen edges (no margins)
- // When bar is not floating, respect bar margins as before
- top: Settings.data.bar.position === "top" ? barMargin : 0
- bottom: Settings.data.bar.position === "bottom" ? barMargin : 0
- left: Settings.data.bar.position === "left" ? barMargin : 0
- right: Settings.data.bar.position === "right" ? barMargin : 0
- }
-
- mask: Region {}
-
- // Reusable corner canvas component
- component CornerCanvas: Canvas {
- id: corner
-
- required property real arcCenterX
- required property real arcCenterY
-
- width: root.cornerSize
- height: root.cornerSize
- antialiasing: true
- renderTarget: Canvas.FramebufferObject
- smooth: true
-
- onPaint: {
- const ctx = getContext("2d")
- if (!ctx)
- return
-
- ctx.reset()
- ctx.clearRect(0, 0, width, height)
-
- // Fill the entire area with the corner color
- ctx.fillStyle = root.cornerColor
- ctx.fillRect(0, 0, width, height)
-
- // Cut out the rounded corner using destination-out
- ctx.globalCompositeOperation = "destination-out"
- ctx.fillStyle = "#ffffff"
- ctx.beginPath()
- ctx.arc(arcCenterX, arcCenterY, root.cornerRadius, 0, 2 * Math.PI)
- ctx.fill()
- }
-
- onWidthChanged: if (available)
- requestPaint()
- onHeightChanged: if (available)
- requestPaint()
- }
-
- // Consolidated repaint handler for all corners
- property var corners: [topLeftCorner, topRightCorner, bottomLeftCorner, bottomRightCorner]
-
- onCornerColorChanged: {
- corners.forEach(corner => {
- if (corner.available)
- corner.requestPaint()
- })
- }
-
- onCornerRadiusChanged: {
- corners.forEach(corner => {
- if (corner.available)
- corner.requestPaint()
- })
- }
-
- // Top-left concave corner
- CornerCanvas {
- id: topLeftCorner
- anchors.top: parent.top
- anchors.left: parent.left
- arcCenterX: width
- arcCenterY: height
- }
-
- // Top-right concave corner
- CornerCanvas {
- id: topRightCorner
- anchors.top: parent.top
- anchors.right: parent.right
- arcCenterX: 0
- arcCenterY: height
- }
-
- // Bottom-left concave corner
- CornerCanvas {
- id: bottomLeftCorner
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- arcCenterX: width
- arcCenterY: 0
- }
-
- // Bottom-right concave corner
- CornerCanvas {
- id: bottomRightCorner
- anchors.bottom: parent.bottom
- anchors.right: parent.right
- arcCenterX: 0
- arcCenterY: 0
- }
- }
- }
-}
diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml
index 31504433..76d0b5a1 100644
--- a/Modules/Bar/Bar.qml
+++ b/Modules/Bar/Bar.qml
@@ -5,65 +5,166 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Services.UPower
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
import qs.Modules.Notification
import qs.Modules.Bar.Extras
-Variants {
- model: Quickshell.screens
+// Bar Component
+Item {
+ id: root
- delegate: Loader {
- id: root
+ // This property will be set by MainScreen
+ property ShellScreen screen: null
- required property ShellScreen modelData
+ // Expose bar region for click-through mask
+ readonly property var barRegion: barContentLoader.item?.children[0] || null
- active: BarService.isVisible && modelData && modelData.name ? (Settings.data.bar.monitors.includes(modelData.name) || (Settings.data.bar.monitors.length === 0)) : false
+ // Expose the actual bar Item for unified background system
+ readonly property var barItem: barRegion
- sourceComponent: PanelWindow {
- screen: modelData || null
+ // Bar positioning properties
+ readonly property string barPosition: Settings.data.bar.position || "top"
+ readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
+ readonly property bool barFloating: Settings.data.bar.floating || false
+ readonly property real barMarginH: 0 //barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ readonly property real barMarginV: 0 //barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
- WlrLayershell.namespace: "noctalia-bar"
+ // Fill the parent (the Loader)
+ anchors.fill: parent
- implicitHeight: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? screen.height : Style.barHeight
- implicitWidth: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? Style.barHeight : screen.width
- color: Color.transparent
+ // Register bar when screen becomes available
+ onScreenChanged: {
+ if (screen && screen.name) {
+ Logger.d("Bar", "Bar screen set to:", screen.name)
+ Logger.d("Bar", " Position:", barPosition, "Floating:", barFloating)
+ Logger.d("Bar", " Margins - H:", barMarginH, "V:", barMarginV)
+ BarService.registerBar(screen.name)
+ }
+ }
- anchors {
- top: Settings.data.bar.position === "top" || Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
- bottom: Settings.data.bar.position === "bottom" || Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
- left: Settings.data.bar.position === "left" || Settings.data.bar.position === "top" || Settings.data.bar.position === "bottom"
- right: Settings.data.bar.position === "right" || Settings.data.bar.position === "top" || Settings.data.bar.position === "bottom"
+ // Wait for screen to be set before loading bar content
+ Loader {
+ id: barContentLoader
+ anchors.fill: parent
+ active: {
+ if (root.screen === null || root.screen === undefined) {
+ return false
}
- // Floating bar margins - only apply when floating is enabled
- // Also don't apply margin on the opposite side ot the bar orientation, ex: if bar is floating on top, margin is only applied on top, not bottom.
- margins {
- top: Settings.data.bar.floating && Settings.data.bar.position !== "bottom" ? Settings.data.bar.marginVertical * Style.marginXL : 0
- bottom: Settings.data.bar.floating && Settings.data.bar.position !== "top" ? Settings.data.bar.marginVertical * Style.marginXL : 0
- left: Settings.data.bar.floating && Settings.data.bar.position !== "right" ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
- right: Settings.data.bar.floating && Settings.data.bar.position !== "left" ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
- }
+ var monitors = Settings.data.bar.monitors || []
+ var result = monitors.length === 0 || monitors.includes(root.screen.name)
+ return result
+ }
- Component.onCompleted: {
- if (modelData && modelData.name) {
- BarService.registerBar(modelData.name)
- }
- }
+ sourceComponent: Item {
+ anchors.fill: parent
+ // Bar container (background rendered by AllBackgrounds)
Item {
- anchors.fill: parent
- clip: true
+ id: bar
- // Background fill with shadow
- Rectangle {
- id: bar
+ // Position and size the bar based on orientation and floating margins
+ x: {
+ var baseX = (root.barPosition === "right") ? (parent.width - Style.barHeight - root.barMarginH) : root.barMarginH
+ if (root.barPosition === "right")
+ return baseX // Extend left towards panels
+ return baseX
+ }
+ y: {
+ var baseY = (root.barPosition === "bottom") ? (parent.height - Style.barHeight - root.barMarginV) : root.barMarginV
+ if (root.barPosition === "bottom")
+ return baseY // Extend up towards panels
+ return baseY
+ }
+ width: {
+ var baseWidth = root.barIsVertical ? Style.barHeight : (parent.width - root.barMarginH * 2)
+ if (!root.barIsVertical)
+ return baseWidth // Horizontal bars extend via height, not width
+ return baseWidth + 1
+ }
+ height: {
+ var baseHeight = root.barIsVertical ? (parent.height - root.barMarginV * 2) : Style.barHeight
+ if (!root.barIsVertical)
+ return baseHeight
+ return baseHeight // Vertical bars extend via width, not height
+ }
- anchors.fill: parent
- color: Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity)
+ // Corner states for new unified background system
+ // State -1: No radius (flat/square corner)
+ // State 0: Normal (inner curve)
+ // State 1: Horizontal inversion (outer curve on X-axis)
+ // State 2: Vertical inversion (outer curve on Y-axis)
+ readonly property int topLeftCornerState: {
+ // Floating bar: always simple rounded corners
+ if (barFloating)
+ return 0
+ // Top bar: top corners against screen edge = no radius
+ if (barPosition === "top")
+ return -1
+ // Left bar: top-left against screen edge = no radius
+ if (barPosition === "left")
+ return -1
+ // Bottom/Right bar with outerCorners: inverted corner
+ if (Settings.data.bar.outerCorners && (barPosition === "bottom" || barPosition === "right")) {
+ return barIsVertical ? 1 : 2 // horizontal invert for vertical bars, vertical invert for horizontal
+ }
+ // No outerCorners = square
+ return -1
+ }
- // Floating bar rounded corners
- radius: Settings.data.bar.floating ? Style.radiusL : 0
+ readonly property int topRightCornerState: {
+ // Floating bar: always simple rounded corners
+ if (barFloating)
+ return 0
+ // Top bar: top corners against screen edge = no radius
+ if (barPosition === "top")
+ return -1
+ // Right bar: top-right against screen edge = no radius
+ if (barPosition === "right")
+ return -1
+ // Bottom/Left bar with outerCorners: inverted corner
+ if (Settings.data.bar.outerCorners && (barPosition === "bottom" || barPosition === "left")) {
+ return barIsVertical ? 1 : 2
+ }
+ // No outerCorners = square
+ return -1
+ }
+
+ readonly property int bottomLeftCornerState: {
+ // Floating bar: always simple rounded corners
+ if (barFloating)
+ return 0
+ // Bottom bar: bottom corners against screen edge = no radius
+ if (barPosition === "bottom")
+ return -1
+ // Left bar: bottom-left against screen edge = no radius
+ if (barPosition === "left")
+ return -1
+ // Top/Right bar with outerCorners: inverted corner
+ if (Settings.data.bar.outerCorners && (barPosition === "top" || barPosition === "right")) {
+ return barIsVertical ? 1 : 2
+ }
+ // No outerCorners = square
+ return -1
+ }
+
+ readonly property int bottomRightCornerState: {
+ // Floating bar: always simple rounded corners
+ if (barFloating)
+ return 0
+ // Bottom bar: bottom corners against screen edge = no radius
+ if (barPosition === "bottom")
+ return -1
+ // Right bar: bottom-right against screen edge = no radius
+ if (barPosition === "right")
+ return -1
+ // Top/Left bar with outerCorners: inverted corner
+ if (Settings.data.bar.outerCorners && (barPosition === "top" || barPosition === "left")) {
+ return barIsVertical ? 1 : 2
+ }
+ // No outerCorners = square
+ return -1
}
MouseArea {
@@ -73,8 +174,13 @@ Variants {
preventStealing: true
onClicked: function (mouse) {
if (mouse.button === Qt.RightButton) {
- // Important to pass the screen here so we get the right widget for the actual bar that was clicked.
- controlCenterPanel.toggle(BarService.lookupWidget("ControlCenter", screen.name))
+ var controlCenterPanel = PanelService.getPanel("controlCenterPanel", screen)
+ if (Settings.data.controlCenter.position === "close_to_bar_button") {
+ // Will attempt to open the panel next to the bar button if any.
+ controlCenterPanel?.toggle(null, "ControlCenter")
+ } else {
+ controlCenterPanel?.toggle()
+ }
mouse.accepted = true
}
}
@@ -84,168 +190,188 @@ Variants {
anchors.fill: parent
sourceComponent: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? verticalBarComponent : horizontalBarComponent
}
+ }
+ }
+ }
- // For vertical bars
- Component {
- id: verticalBarComponent
- Item {
- anchors.fill: parent
+ // For vertical bars
+ Component {
+ id: verticalBarComponent
+ Item {
+ anchors.fill: parent
+ clip: true
- // Top section (left widgets)
- ColumnLayout {
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.top: parent.top
- anchors.topMargin: Style.marginM
- spacing: Style.marginS
+ // Top section (left widgets)
+ ColumnLayout {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ anchors.topMargin: Style.marginM
+ spacing: Style.marginS
- Repeater {
- model: Settings.data.bar.widgets.left
- delegate: BarWidgetLoader {
- widgetId: (modelData.id !== undefined ? modelData.id : "")
- barDensity: Settings.data.bar.density
- widgetProps: {
- "screen": root.modelData || null,
- "widgetId": modelData.id,
- "section": "left",
- "sectionWidgetIndex": index,
- "sectionWidgetsCount": Settings.data.bar.widgets.left.length
- }
- Layout.alignment: Qt.AlignHCenter
- }
- }
- }
+ Repeater {
+ model: Settings.data.bar.widgets.left
+ delegate: BarWidgetLoader {
+ required property var modelData
+ required property int index
- // Center section (center widgets)
- ColumnLayout {
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
- spacing: Style.marginS
-
- Repeater {
- model: Settings.data.bar.widgets.center
- delegate: BarWidgetLoader {
- widgetId: (modelData.id !== undefined ? modelData.id : "")
- barDensity: Settings.data.bar.density
- widgetProps: {
- "screen": root.modelData || null,
- "widgetId": modelData.id,
- "section": "center",
- "sectionWidgetIndex": index,
- "sectionWidgetsCount": Settings.data.bar.widgets.center.length
- }
- Layout.alignment: Qt.AlignHCenter
- }
- }
- }
-
- // Bottom section (right widgets)
- ColumnLayout {
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: parent.bottom
- anchors.bottomMargin: Style.marginM
- spacing: Style.marginS
-
- Repeater {
- model: Settings.data.bar.widgets.right
- delegate: BarWidgetLoader {
- widgetId: (modelData.id !== undefined ? modelData.id : "")
- barDensity: Settings.data.bar.density
- widgetProps: {
- "screen": root.modelData || null,
- "widgetId": modelData.id,
- "section": "right",
- "sectionWidgetIndex": index,
- "sectionWidgetsCount": Settings.data.bar.widgets.right.length
- }
- Layout.alignment: Qt.AlignHCenter
- }
- }
- }
+ widgetId: modelData.id || ""
+ barDensity: Settings.data.bar.density
+ widgetScreen: root.screen
+ widgetProps: ({
+ "widgetId": modelData.id,
+ "section": "left",
+ "sectionWidgetIndex": index,
+ "sectionWidgetsCount": Settings.data.bar.widgets.left.length
+ })
+ Layout.alignment: Qt.AlignHCenter
}
}
+ }
- // For horizontal bars
- Component {
- id: horizontalBarComponent
- Item {
- anchors.fill: parent
+ // Center section (center widgets)
+ ColumnLayout {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: Style.marginS
- // Left Section
- RowLayout {
- id: leftSection
- objectName: "leftSection"
- anchors.left: parent.left
- anchors.leftMargin: Style.marginS
- anchors.verticalCenter: parent.verticalCenter
- spacing: Style.marginS
+ Repeater {
+ model: Settings.data.bar.widgets.center
+ delegate: BarWidgetLoader {
+ required property var modelData
+ required property int index
- Repeater {
- model: Settings.data.bar.widgets.left
- delegate: BarWidgetLoader {
- widgetId: (modelData.id !== undefined ? modelData.id : "")
- barDensity: Settings.data.bar.density
- widgetProps: {
- "screen": root.modelData || null,
- "widgetId": modelData.id,
- "section": "left",
- "sectionWidgetIndex": index,
- "sectionWidgetsCount": Settings.data.bar.widgets.left.length
- }
- Layout.alignment: Qt.AlignVCenter
- }
- }
- }
+ widgetId: modelData.id || ""
+ barDensity: Settings.data.bar.density
+ widgetScreen: root.screen
+ widgetProps: ({
+ "widgetId": modelData.id,
+ "section": "center",
+ "sectionWidgetIndex": index,
+ "sectionWidgetsCount": Settings.data.bar.widgets.center.length
+ })
+ Layout.alignment: Qt.AlignHCenter
+ }
+ }
+ }
- // Center Section
- RowLayout {
- id: centerSection
- objectName: "centerSection"
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
- spacing: Style.marginS
+ // Bottom section (right widgets)
+ ColumnLayout {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: Style.marginM
+ spacing: Style.marginS
- Repeater {
- model: Settings.data.bar.widgets.center
- delegate: BarWidgetLoader {
- widgetId: (modelData.id !== undefined ? modelData.id : "")
- barDensity: Settings.data.bar.density
- widgetProps: {
- "screen": root.modelData || null,
- "widgetId": modelData.id,
- "section": "center",
- "sectionWidgetIndex": index,
- "sectionWidgetsCount": Settings.data.bar.widgets.center.length
- }
- Layout.alignment: Qt.AlignVCenter
- }
- }
- }
+ Repeater {
+ model: Settings.data.bar.widgets.right
+ delegate: BarWidgetLoader {
+ required property var modelData
+ required property int index
- // Right Section
- RowLayout {
- id: rightSection
- objectName: "rightSection"
- anchors.right: parent.right
- anchors.rightMargin: Style.marginS
- anchors.verticalCenter: parent.verticalCenter
- spacing: Style.marginS
+ widgetId: modelData.id || ""
+ barDensity: Settings.data.bar.density
+ widgetScreen: root.screen
+ widgetProps: ({
+ "widgetId": modelData.id,
+ "section": "right",
+ "sectionWidgetIndex": index,
+ "sectionWidgetsCount": Settings.data.bar.widgets.right.length
+ })
+ Layout.alignment: Qt.AlignHCenter
+ }
+ }
+ }
+ }
+ }
- Repeater {
- model: Settings.data.bar.widgets.right
- delegate: BarWidgetLoader {
- widgetId: (modelData.id !== undefined ? modelData.id : "")
- barDensity: Settings.data.bar.density
- widgetProps: {
- "screen": root.modelData || null,
- "widgetId": modelData.id,
- "section": "right",
- "sectionWidgetIndex": index,
- "sectionWidgetsCount": Settings.data.bar.widgets.right.length
- }
- Layout.alignment: Qt.AlignVCenter
- }
- }
- }
+ // For horizontal bars
+ Component {
+ id: horizontalBarComponent
+ Item {
+ anchors.fill: parent
+ clip: true
+
+ // Left Section
+ RowLayout {
+ id: leftSection
+ objectName: "leftSection"
+ anchors.left: parent.left
+ anchors.leftMargin: Style.marginS
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: Style.marginS
+
+ Repeater {
+ model: Settings.data.bar.widgets.left
+ delegate: BarWidgetLoader {
+ required property var modelData
+ required property int index
+
+ widgetId: modelData.id || ""
+ barDensity: Settings.data.bar.density
+ widgetScreen: root.screen
+ widgetProps: ({
+ "widgetId": modelData.id,
+ "section": "left",
+ "sectionWidgetIndex": index,
+ "sectionWidgetsCount": Settings.data.bar.widgets.left.length
+ })
+ Layout.alignment: Qt.AlignVCenter
+ }
+ }
+ }
+
+ // Center Section
+ RowLayout {
+ id: centerSection
+ objectName: "centerSection"
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: Style.marginS
+
+ Repeater {
+ model: Settings.data.bar.widgets.center
+ delegate: BarWidgetLoader {
+ required property var modelData
+ required property int index
+
+ widgetId: modelData.id || ""
+ barDensity: Settings.data.bar.density
+ widgetScreen: root.screen
+ widgetProps: ({
+ "widgetId": modelData.id,
+ "section": "center",
+ "sectionWidgetIndex": index,
+ "sectionWidgetsCount": Settings.data.bar.widgets.center.length
+ })
+ Layout.alignment: Qt.AlignVCenter
+ }
+ }
+ }
+
+ // Right Section
+ RowLayout {
+ id: rightSection
+ objectName: "rightSection"
+ anchors.right: parent.right
+ anchors.rightMargin: Style.marginS
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: Style.marginS
+
+ Repeater {
+ model: Settings.data.bar.widgets.right
+ delegate: BarWidgetLoader {
+ required property var modelData
+ required property int index
+
+ widgetId: modelData.id || ""
+ barDensity: Settings.data.bar.density
+ widgetScreen: root.screen
+ widgetProps: ({
+ "widgetId": modelData.id,
+ "section": "right",
+ "sectionWidgetIndex": index,
+ "sectionWidgetsCount": Settings.data.bar.widgets.right.length
+ })
+ Layout.alignment: Qt.AlignVCenter
}
}
}
diff --git a/Modules/Bar/Calendar/AnalogClock.qml b/Modules/Bar/Calendar/AnalogClock.qml
deleted file mode 100644
index 2b3c5694..00000000
--- a/Modules/Bar/Calendar/AnalogClock.qml
+++ /dev/null
@@ -1,106 +0,0 @@
-import QtQuick
-import qs.Commons
-import Quickshell
-
-Item {
- property var now
- property color backgroundColor: Color.mPrimary
- property color clockColor: Color.mOnPrimary
- property color secondHandColor: Color.mError
- anchors.fill: parent
-
- Canvas {
- id: clockCanvas
- anchors.fill: parent
-
- property int hours: now.getHours()
- property int minutes: now.getMinutes()
- property int seconds: now.getSeconds()
-
- onPaint: {
- const markAlpha = 0.7
- var ctx = getContext("2d")
- ctx.reset()
- ctx.translate(width / 2, height / 2)
- var radius = Math.min(width, height) / 2
-
- // Hour marks
- ctx.strokeStyle = Qt.alpha(clockColor, markAlpha)
- ctx.lineWidth = 2 * Style.uiScaleRatio
- var scaleFactor = 0.7
-
- for (var i = 0; i < 12; i++) {
- var scaleFactor = 0.8
- if (i % 3 === 0) {
- scaleFactor = 0.65
- }
- ctx.save()
- ctx.rotate(i * Math.PI / 6)
- ctx.beginPath()
- ctx.moveTo(0, -radius * scaleFactor)
- ctx.lineTo(0, -radius)
- ctx.stroke()
- ctx.restore()
- }
-
- // Hour hand
- ctx.save()
- var hourAngle = (hours % 12 + minutes / 60) * Math.PI / 6
- ctx.rotate(hourAngle)
- ctx.strokeStyle = clockColor
- ctx.lineWidth = 3 * Style.uiScaleRatio
- ctx.lineCap = "round"
- ctx.beginPath()
- ctx.moveTo(0, 0)
- ctx.lineTo(0, -radius * 0.6)
- ctx.stroke()
- ctx.restore()
-
- // Minute hand
- ctx.save()
- var minuteAngle = (minutes + seconds / 60) * Math.PI / 30
- ctx.rotate(minuteAngle)
- ctx.strokeStyle = clockColor
- ctx.lineWidth = 2 * Style.uiScaleRatio
- ctx.lineCap = "round"
- ctx.beginPath()
- ctx.moveTo(0, 0)
- ctx.lineTo(0, -radius * 0.9)
- ctx.stroke()
- ctx.restore()
-
- // Second hand
- ctx.save()
- var secondAngle = seconds * Math.PI / 30
- ctx.rotate(secondAngle)
- ctx.strokeStyle = secondHandColor
- ctx.lineWidth = 1.6 * Style.uiScaleRatio
- ctx.lineCap = "round"
- ctx.beginPath()
- ctx.moveTo(0, 0)
- ctx.lineTo(0, -radius)
- ctx.stroke()
- ctx.restore()
-
- // Center dot
- ctx.beginPath()
- ctx.arc(0, 0, 3 * Style.uiScaleRatio, 0, 2 * Math.PI)
- ctx.fillStyle = clockColor
- ctx.fill()
- }
-
- Timer {
- interval: 1000
- running: true
- repeat: true
- onTriggered: {
- clockCanvas.hours = now.getHours()
- clockCanvas.minutes = now.getMinutes()
- clockCanvas.seconds = now.getSeconds()
- clockCanvas.requestPaint()
- }
- }
-
- Component.onCompleted: requestPaint()
- }
-}
diff --git a/Modules/Bar/Calendar/ClockLoader.qml b/Modules/Bar/Calendar/ClockLoader.qml
deleted file mode 100644
index 253c5a86..00000000
--- a/Modules/Bar/Calendar/ClockLoader.qml
+++ /dev/null
@@ -1,78 +0,0 @@
-import QtQuick
-import qs.Commons
-import qs.Services
-import Quickshell
-import "../../../Helpers/ColorsConvert.js" as ColorsConvert
-
-Item {
- id: clockRoot
- property var now
-
- // Default colors
- property color backgroundColor: Color.mPrimary
- property color clockColor: Color.mOnPrimary
-
- property color secondHandColor: {
- var defaultColor = Color.mError
- var bestContrast = 1.0 // 1.0 is "no contrast"
- var bestColor = defaultColor
- var candidates = [Color.mSecondary, Color.mTertiary, Color.mError]
-
- const minContrast = 1.149
-
- for (var i = 0; i < candidates.length; i++) {
- var candidate = candidates[i]
- var contrastClock = ColorsConvert.getContrastRatio(candidate.toString(), clockColor.toString())
- if (contrastClock < minContrast) {
- continue
- }
- var contrastBg = ColorsConvert.getContrastRatio(candidate.toString(), backgroundColor.toString())
- if (contrastBg < minContrast) {
- continue
- }
-
- var currentWorstContrast = Math.min(contrastBg, contrastClock)
-
- if (currentWorstContrast > bestContrast) {
- bestContrast = currentWorstContrast
- bestColor = candidate
- }
- }
-
- return bestColor
- }
-
- property color progressColor: clockRoot.secondHandColor
-
- height: Math.round((Style.fontSizeXXXL * 1.9) / 2 * Style.uiScaleRatio) * 2
- width: clockRoot.height
-
- Loader {
- id: clockLoader
- anchors.fill: parent
-
- source: Settings.data.location.analogClockInCalendar ? "AnalogClock.qml" : "DigitalClock.qml"
-
- onLoaded: {
- item.now = Qt.binding(function () {
- return clockRoot.now
- })
- item.backgroundColor = Qt.binding(function () {
- return clockRoot.backgroundColor
- })
- item.clockColor = Qt.binding(function () {
- return clockRoot.clockColor
- })
- if (item.hasOwnProperty("secondHandColor")) {
- item.secondHandColor = Qt.binding(function () {
- return clockRoot.secondHandColor
- })
- }
- if (item.hasOwnProperty("progressColor")) {
- item.progressColor = Qt.binding(function () {
- return clockRoot.progressColor
- })
- }
- }
- }
-}
diff --git a/Modules/Bar/Calendar/DigitalClock.qml b/Modules/Bar/Calendar/DigitalClock.qml
deleted file mode 100644
index 3d373910..00000000
--- a/Modules/Bar/Calendar/DigitalClock.qml
+++ /dev/null
@@ -1,80 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import qs.Commons
-import qs.Widgets
-import Quickshell
-
-Item {
-
- property var now
- property color backgroundColor: Color.mPrimary
- property color clockColor: Color.mOnPrimary
- property color progressColor: Color.mError
-
- anchors.fill: parent
-
- // Digital clock's seconds circular progress
- Canvas {
- id: secondsProgress
- anchors.fill: parent
- property real progress: now.getSeconds() / 60
- onProgressChanged: requestPaint()
- Connections {
- target: Time
- function onDateChanged() {
- const total = now.getSeconds() * 1000 + now.getMilliseconds()
- secondsProgress.progress = total / 60000
- }
- }
- onPaint: {
- var ctx = getContext("2d")
- var centerX = width / 2
- var centerY = height / 2
- var radius = Math.min(width, height) / 2 - 3
- ctx.reset()
-
- // Background circle
- ctx.beginPath()
- ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI)
- ctx.lineWidth = 2.5
- ctx.strokeStyle = Qt.alpha(clockColor, 0.15)
- ctx.stroke()
-
- // Progress arc
- ctx.beginPath()
- ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progress * 2 * Math.PI)
- ctx.lineWidth = 2.5
- ctx.strokeStyle = progressColor
- ctx.lineCap = "round"
- ctx.stroke()
- }
- }
-
- // Digital clock
- ColumnLayout {
- anchors.centerIn: parent
- spacing: -Style.marginXXS
-
- NText {
- text: {
- var t = Settings.data.location.use12hourFormat ? I18n.locale.toString(now, "hh AP") : I18n.locale.toString(now, "HH")
- return t.split(" ")[0]
- }
-
- pointSize: Style.fontSizeXS
- font.weight: Style.fontWeightBold
- color: clockColor
- family: Settings.data.ui.fontFixed
- Layout.alignment: Qt.AlignHCenter
- }
-
- NText {
- text: Qt.formatTime(now, "mm")
- pointSize: Style.fontSizeXXS
- font.weight: Style.fontWeightBold
- color: clockColor
- family: Settings.data.ui.fontFixed
- Layout.alignment: Qt.AlignHCenter
- }
- }
-}
diff --git a/Modules/Bar/Extras/BarExclusionZone.qml b/Modules/Bar/Extras/BarExclusionZone.qml
new file mode 100644
index 00000000..a2448a92
--- /dev/null
+++ b/Modules/Bar/Extras/BarExclusionZone.qml
@@ -0,0 +1,74 @@
+import QtQuick
+import Quickshell
+import Quickshell.Wayland
+import qs.Commons
+
+
+/**
+ * BarExclusionZone - Invisible PanelWindow that reserves exclusive space for the bar
+ *
+ * This is a minimal window that works with the compositor to reserve space,
+ * while the actual bar UI is rendered in NFullScreenWindow.
+ */
+PanelWindow {
+ id: root
+
+ property bool exclusive: Settings.data.bar.exclusive !== undefined ? Settings.data.bar.exclusive : false
+
+ readonly property string barPosition: Settings.data.bar.position || "top"
+ readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
+ readonly property bool barFloating: Settings.data.bar.floating || false
+ readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+
+ // Invisible - just reserves space
+ color: "transparent"
+
+ mask: Region {}
+
+ // Wayland layer shell configuration
+ WlrLayershell.layer: WlrLayer.Top
+ WlrLayershell.namespace: "noctalia-bar-exclusion-" + (screen?.name || "unknown")
+ WlrLayershell.exclusionMode: exclusive ? ExclusionMode.Auto : ExclusionMode.Ignore
+
+ // Anchor based on bar position
+ anchors {
+ top: barPosition === "top"
+ bottom: barPosition === "bottom"
+ left: barPosition === "left" || barPosition === "top" || barPosition === "bottom"
+ right: barPosition === "right" || barPosition === "top" || barPosition === "bottom"
+ }
+
+ // Size based on bar orientation
+ // When floating, only reserve space for the bar + margin on the anchored edge
+ implicitWidth: {
+ if (barIsVertical) {
+ // Vertical bar: reserve bar height + margin on the anchored edge only
+ if (barFloating) {
+ // For left bar, reserve left margin; for right bar, reserve right margin
+ return Style.barHeight + barMarginH
+ }
+ return Style.barHeight
+ }
+ return 0 // Auto-width when left/right anchors are true
+ }
+
+ implicitHeight: {
+ if (!barIsVertical) {
+ // Horizontal bar: reserve bar height + margin on the anchored edge only
+ if (barFloating) {
+ // For top bar, reserve top margin; for bottom bar, reserve bottom margin
+ return Style.barHeight + barMarginV
+ }
+ return Style.barHeight
+ }
+ return 0 // Auto-height when top/bottom anchors are true
+ }
+
+ Component.onCompleted: {
+ Logger.d("BarExclusionZone", "Created for screen:", screen?.name)
+ Logger.d("BarExclusionZone", " Position:", barPosition, "Exclusive:", exclusive, "Floating:", barFloating)
+ Logger.d("BarExclusionZone", " Anchors - top:", anchors.top, "bottom:", anchors.bottom, "left:", anchors.left, "right:", anchors.right)
+ Logger.d("BarExclusionZone", " Size:", width, "x", height, "implicitWidth:", implicitWidth, "implicitHeight:", implicitHeight)
+ }
+}
diff --git a/Modules/Bar/Extras/BarPill.qml b/Modules/Bar/Extras/BarPill.qml
index aef33339..a8c097cf 100644
--- a/Modules/Bar/Extras/BarPill.qml
+++ b/Modules/Bar/Extras/BarPill.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Commons
-import qs.Services
import qs.Widgets
Item {
@@ -18,6 +17,7 @@ Item {
property bool forceClose: false
property bool oppositeDirection: false
property bool hovered: false
+ property bool rotateText: false
readonly property string barPosition: Settings.data.bar.position
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
@@ -53,6 +53,7 @@ Item {
oppositeDirection: root.oppositeDirection
hovered: root.hovered
density: root.density
+ rotateText: root.rotateText
onShown: root.shown()
onHidden: root.hidden()
onEntered: root.entered()
diff --git a/Modules/Bar/Extras/BarPillHorizontal.qml b/Modules/Bar/Extras/BarPillHorizontal.qml
index c9d84869..ed74b74b 100644
--- a/Modules/Bar/Extras/BarPillHorizontal.qml
+++ b/Modules/Bar/Extras/BarPillHorizontal.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
Item {
diff --git a/Modules/Bar/Extras/BarPillVertical.qml b/Modules/Bar/Extras/BarPillVertical.qml
index 492ec735..5718c619 100644
--- a/Modules/Bar/Extras/BarPillVertical.qml
+++ b/Modules/Bar/Extras/BarPillVertical.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
Item {
@@ -18,6 +18,7 @@ Item {
property bool forceClose: false
property bool oppositeDirection: false
property bool hovered: false
+ property bool rotateText: false
// Bar position detection for pill direction
readonly property string barPosition: Settings.data.bar.position
@@ -48,8 +49,8 @@ Item {
readonly property int pillHeight: buttonSize
readonly property int pillPaddingVertical: 3 * 2 // Very precise adjustment don't replace by Style.margin
readonly property int pillOverlap: Math.round(buttonSize * 0.5)
- readonly property int maxPillWidth: buttonSize
- readonly property int maxPillHeight: Math.max(1, Math.round(textItem.implicitHeight + pillPaddingVertical * 4))
+ readonly property int maxPillWidth: rotateText ? Math.max(buttonSize, Math.round(textItem.implicitHeight + pillPaddingVertical * 2)) : buttonSize
+ readonly property int maxPillHeight: rotateText ? Math.max(1, Math.round(textItem.implicitWidth + pillPaddingVertical * 2 + Math.round(iconCircle.height / 4))) : Math.max(1, Math.round(textItem.implicitHeight + pillPaddingVertical * 4))
readonly property real iconSize: {
switch (root.density) {
@@ -109,14 +110,8 @@ Item {
id: textItem
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
- anchors.verticalCenterOffset: {
- var offset = openDownward ? Math.round(pillPaddingVertical * 0.75) : -Math.round(pillPaddingVertical * 0.75)
- if (forceOpen) {
- // If its force open, the icon disc background is the same color as the bg pill move text slightly
- offset += oppositeDirection ? -Style.marginXXS : Style.marginXXS
- }
- return offset
- }
+ anchors.verticalCenterOffset: rotateText ? Math.round(iconCircle.height / 4) : getVerticalCenterOffset()
+ rotation: rotateText ? -90 : 0
text: root.text + root.suffix
family: Settings.data.ui.fontFixed
pointSize: textSize
@@ -126,8 +121,15 @@ Item {
verticalAlignment: Text.AlignVCenter
color: forceOpen ? Color.mOnSurface : Color.mPrimary
visible: revealed
- }
+ function getVerticalCenterOffset() {
+ var offset = openDownward ? Math.round(pillPaddingVertical * 0.75) : -Math.round(pillPaddingVertical * 0.75)
+ if (forceOpen) {
+ offset += oppositeDirection ? -Style.marginXXS : Style.marginXXS
+ }
+ return offset
+ }
+ }
Behavior on width {
enabled: showAnim.running || hideAnim.running
NumberAnimation {
diff --git a/Modules/Bar/Extras/BarWidgetLoader.qml b/Modules/Bar/Extras/BarWidgetLoader.qml
index 37ea0391..71f21414 100644
--- a/Modules/Bar/Extras/BarWidgetLoader.qml
+++ b/Modules/Bar/Extras/BarWidgetLoader.qml
@@ -1,20 +1,22 @@
import QtQuick
import Quickshell
-import qs.Services
+import qs.Services.UI
import qs.Commons
Item {
id: root
- property string widgetId: ""
- property var widgetProps: ({})
- property string screenName: widgetProps && widgetProps.screen ? widgetProps.screen.name : ""
- property string section: widgetProps && widgetProps.section || ""
- property int sectionIndex: widgetProps && widgetProps.sectionWidgetIndex || 0
+ required property string widgetId
+ required property var widgetScreen
+ required property var widgetProps
property string barDensity: "default"
readonly property real scaling: barDensity === "mini" ? 0.8 : (barDensity === "compact" ? 0.9 : 1.0)
+ // Extract section info from widgetProps
+ readonly property string section: widgetProps.section || ""
+ readonly property int sectionIndex: widgetProps.sectionWidgetIndex || 0
+
// Don't reserve space unless the loaded widget is really visible
implicitWidth: getImplicitSize(loader.item, "implicitWidth")
implicitHeight: getImplicitSize(loader.item, "implicitHeight")
@@ -26,56 +28,54 @@ Item {
Loader {
id: loader
anchors.fill: parent
- active: widgetId !== ""
asynchronous: false
- sourceComponent: {
- if (!active) {
- return null
- }
- return BarWidgetRegistry.getWidget(widgetId)
- }
+ sourceComponent: BarWidgetRegistry.getWidget(widgetId)
onLoaded: {
- if (item && widgetProps) {
- // Apply properties to loaded widget
- for (var prop in widgetProps) {
- if (item.hasOwnProperty(prop)) {
- item[prop] = widgetProps[prop]
- }
- }
- // Explicitly set scaling property
- if (item.hasOwnProperty("scaling")) {
- item.scaling = Qt.binding(function () {
- return root.scaling
- })
+ if (!item)
+ return
+
+ Logger.d("BarWidgetLoader", "Loading widget", widgetId, "on screen:", widgetScreen.name)
+
+ // Apply properties to loaded widget
+ for (var prop in widgetProps) {
+ if (item.hasOwnProperty(prop)) {
+ item[prop] = widgetProps[prop]
}
}
+ // Set screen property
+ if (item.hasOwnProperty("screen")) {
+ item.screen = widgetScreen
+ }
+
+ // Set scaling property
+ if (item.hasOwnProperty("scaling")) {
+ item.scaling = Qt.binding(function () {
+ return root.scaling
+ })
+ }
+
// Register this widget instance with BarService
- if (screenName && section) {
- BarService.registerWidget(screenName, section, widgetId, sectionIndex, item)
- }
+ BarService.registerWidget(widgetScreen.name, section, widgetId, sectionIndex, item)
+ // Call custom onLoaded if it exists
if (item.hasOwnProperty("onLoaded")) {
item.onLoaded()
}
-
- //Logger.i("BarWidgetLoader", "Loaded", widgetId, "on screen", item.screen.name)
}
Component.onDestruction: {
// Unregister when destroyed
- if (screenName && section) {
- BarService.unregisterWidget(screenName, section, widgetId, sectionIndex)
+ if (widgetScreen && section) {
+ BarService.unregisterWidget(widgetScreen.name, section, widgetId, sectionIndex)
}
- // Explicitly clear references
- widgetProps = null
}
}
// Error handling
- onWidgetIdChanged: {
- if (widgetId && !BarWidgetRegistry.hasWidget(widgetId)) {
+ Component.onCompleted: {
+ if (!BarWidgetRegistry.hasWidget(widgetId)) {
Logger.w("BarWidgetLoader", "Widget not found in registry:", widgetId)
}
}
diff --git a/Modules/Bar/Extras/TrayMenu.qml b/Modules/Bar/Extras/TrayMenu.qml
deleted file mode 100644
index 22860079..00000000
--- a/Modules/Bar/Extras/TrayMenu.qml
+++ /dev/null
@@ -1,281 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import Quickshell
-import qs.Commons
-import qs.Services
-import qs.Widgets
-
-PopupWindow {
- id: root
- property QsMenuHandle menu
- property var anchorItem: null
- property real anchorX
- property real anchorY
- property bool isSubMenu: false
- property bool isHovered: rootMouseArea.containsMouse
- property ShellScreen screen
-
- readonly property int menuWidth: 180
-
- implicitWidth: menuWidth
-
- // Use the content height of the Flickable for implicit height
- implicitHeight: Math.min(screen ? screen.height * 0.9 : Screen.height * 0.9, flickable.contentHeight + (Style.marginS * 2))
- visible: false
- color: Color.transparent
- anchor.item: anchorItem
- anchor.rect.x: anchorX
- anchor.rect.y: anchorY - (isSubMenu ? 0 : 4)
-
- function showAt(item, x, y) {
- if (!item) {
- Logger.w("TrayMenu", "anchorItem is undefined, won't show menu.")
- return
- }
-
- if (!opener.children || opener.children.values.length === 0) {
- //Logger.w("TrayMenu", "Menu not ready, delaying show")
- Qt.callLater(() => showAt(item, x, y))
- return
- }
-
- anchorItem = item
- anchorX = x
- anchorY = y
-
- visible = true
- forceActiveFocus()
-
- // Force update after showing.
- Qt.callLater(() => {
- root.anchor.updateAnchor()
- })
- }
-
- function hideMenu() {
- visible = false
-
- // Clean up all submenus recursively
- for (var i = 0; i < columnLayout.children.length; i++) {
- const child = columnLayout.children[i]
- if (child?.subMenu) {
- child.subMenu.hideMenu()
- child.subMenu.destroy()
- child.subMenu = null
- }
- }
- }
-
- // Full-sized, transparent MouseArea to track the mouse.
- MouseArea {
- id: rootMouseArea
- anchors.fill: parent
- hoverEnabled: true
- }
-
- Item {
- anchors.fill: parent
- Keys.onEscapePressed: root.hideMenu()
- }
-
- QsMenuOpener {
- id: opener
- menu: root.menu
- }
-
- Rectangle {
- anchors.fill: parent
- color: Color.mSurface
- border.color: Color.mOutline
- border.width: Style.borderS
- radius: Style.radiusM
- }
-
- Flickable {
- id: flickable
- anchors.fill: parent
- anchors.margins: Style.marginS
- contentHeight: columnLayout.implicitHeight
- interactive: true
-
- // Use a ColumnLayout to handle menu item arrangement
- ColumnLayout {
- id: columnLayout
- width: flickable.width
- spacing: 0
-
- Repeater {
- model: opener.children ? [...opener.children.values] : []
-
- delegate: Rectangle {
- id: entry
- required property var modelData
-
- Layout.preferredWidth: parent.width
- Layout.preferredHeight: {
- if (modelData?.isSeparator) {
- return 8
- } else {
- // Calculate based on text content
- const textHeight = text.contentHeight || (Style.fontSizeS * 1.2)
- return Math.max(28, textHeight + (Style.marginS * 2))
- }
- }
-
- color: Color.transparent
- property var subMenu: null
-
- NDivider {
- anchors.centerIn: parent
- width: parent.width - (Style.marginM * 2)
- visible: modelData?.isSeparator ?? false
- }
-
- Rectangle {
- anchors.fill: parent
- color: mouseArea.containsMouse ? Color.mHover : Color.transparent
- radius: Style.radiusS
- visible: !(modelData?.isSeparator ?? false)
-
- RowLayout {
- anchors.fill: parent
- anchors.leftMargin: Style.marginM
- anchors.rightMargin: Style.marginM
- spacing: Style.marginS
-
- NText {
- id: text
- Layout.fillWidth: true
- color: (modelData?.enabled ?? true) ? (mouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface) : Color.mOnSurfaceVariant
- text: modelData?.text !== "" ? modelData?.text.replace(/[\n\r]+/g, ' ') : "..."
- pointSize: Style.fontSizeS
- verticalAlignment: Text.AlignVCenter
- wrapMode: Text.WordWrap
- }
-
- Image {
- Layout.preferredWidth: Style.marginL
- Layout.preferredHeight: Style.marginL
- source: modelData?.icon ?? ""
- visible: (modelData?.icon ?? "") !== ""
- fillMode: Image.PreserveAspectFit
- }
-
- NIcon {
- icon: modelData?.hasChildren ? "menu" : ""
- pointSize: Style.fontSizeS
- applyUiScale: false
- verticalAlignment: Text.AlignVCenter
- visible: modelData?.hasChildren ?? false
- color: (mouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface)
- }
- }
-
- MouseArea {
- id: mouseArea
- anchors.fill: parent
- hoverEnabled: true
- enabled: (modelData?.enabled ?? true) && !(modelData?.isSeparator ?? false) && root.visible
-
- onClicked: {
- if (modelData && !modelData.isSeparator && !modelData.hasChildren) {
- modelData.triggered()
- root.hideMenu()
- }
- }
-
- onEntered: {
- if (!root.visible)
- return
-
- // Close all sibling submenus
- for (var i = 0; i < columnLayout.children.length; i++) {
- const sibling = columnLayout.children[i]
- if (sibling !== entry && sibling?.subMenu) {
- sibling.subMenu.hideMenu()
- sibling.subMenu.destroy()
- sibling.subMenu = null
- }
- }
-
- // Create submenu if needed
- if (modelData?.hasChildren) {
- if (entry.subMenu) {
- entry.subMenu.hideMenu()
- entry.subMenu.destroy()
- }
-
- // Need a slight overlap so that menu don't close when moving the mouse to a submenu
- const submenuWidth = menuWidth // Assuming a similar width as the parent
- const overlap = 4 // A small overlap to bridge the mouse path
-
- // Determine submenu opening direction based on bar position and available space
- let openLeft = false
-
- // Check bar position first
- const barPosition = Settings.data.bar.position
- const globalPos = entry.mapToItem(null, 0, 0)
-
- if (barPosition === "right") {
- // Bar is on the right, prefer opening submenus to the left
- openLeft = true
- } else if (barPosition === "left") {
- // Bar is on the left, prefer opening submenus to the right
- openLeft = false
- } else {
- // Bar is horizontal (top/bottom) or undefined, use space-based logic
- openLeft = (globalPos.x + entry.width + submenuWidth > screen.width)
-
- // Secondary check: ensure we don't open off-screen
- if (openLeft && globalPos.x - submenuWidth < 0) {
- // Would open off the left edge, force right opening
- openLeft = false
- } else if (!openLeft && globalPos.x + entry.width + submenuWidth > screen.width) {
- // Would open off the right edge, force left opening
- openLeft = true
- }
- }
-
- // Position with overlap
- const anchorX = openLeft ? -submenuWidth + overlap : entry.width - overlap
-
- // Create submenu
- entry.subMenu = Qt.createComponent("TrayMenu.qml").createObject(root, {
- "menu": modelData,
- "anchorItem": entry,
- "anchorX": anchorX,
- "anchorY": 0,
- "isSubMenu": true,
- "screen": screen
- })
-
- if (entry.subMenu) {
- entry.subMenu.showAt(entry, anchorX, 0)
- }
- }
- }
-
- onExited: {
- Qt.callLater(() => {
- if (entry.subMenu && !entry.subMenu.isHovered) {
- entry.subMenu.hideMenu()
- entry.subMenu.destroy()
- entry.subMenu = null
- }
- })
- }
- }
- }
-
- Component.onDestruction: {
- if (subMenu) {
- subMenu.destroy()
- subMenu = null
- }
- }
- }
- }
- }
- }
-}
diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml
index 8772c5b4..bd8fc4f5 100644
--- a/Modules/Bar/Widgets/ActiveWindow.qml
+++ b/Modules/Bar/Widgets/ActiveWindow.qml
@@ -1,11 +1,10 @@
import QtQuick
-import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
-import Quickshell.Wayland
import Quickshell.Widgets
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.UI
import qs.Widgets
Item {
diff --git a/Modules/Bar/Widgets/AudioVisualizer.qml b/Modules/Bar/Widgets/AudioVisualizer.qml
index efb4a31e..cecc6952 100644
--- a/Modules/Bar/Widgets/AudioVisualizer.qml
+++ b/Modules/Bar/Widgets/AudioVisualizer.qml
@@ -1,9 +1,10 @@
import QtQuick
import Quickshell
import qs.Commons
-import qs.Modules.Audio
-import qs.Services
+import qs.Services.UI
+import qs.Services.Media
import qs.Widgets
+import qs.Widgets.AudioSpectrum
Item {
id: root
@@ -123,7 +124,7 @@ Item {
Component {
id: linearComponent
- LinearSpectrum {
+ NLinearSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: root.fillColor
@@ -134,7 +135,7 @@ Item {
Component {
id: mirroredComponent
- MirroredSpectrum {
+ NMirroredSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: root.fillColor
@@ -145,7 +146,7 @@ Item {
Component {
id: waveComponent
- WaveSpectrum {
+ NWaveSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: root.fillColor
diff --git a/Modules/Bar/Widgets/Battery.qml b/Modules/Bar/Widgets/Battery.qml
index e19e564f..08488fe5 100644
--- a/Modules/Bar/Widgets/Battery.qml
+++ b/Modules/Bar/Widgets/Battery.qml
@@ -3,7 +3,8 @@ import Quickshell
import Quickshell.Services.UPower
import QtQuick.Layouts
import qs.Commons
-import qs.Services
+import qs.Services.UI
+import qs.Services.Hardware
import qs.Widgets
import qs.Modules.Bar.Extras
@@ -94,7 +95,7 @@ Item {
autoHide: false
forceOpen: isReady && (testMode || battery.isLaptopBattery) && displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide" || !isReady || (!testMode && !battery.isLaptopBattery)
- onClicked: PanelService.getPanel("batteryPanel")?.toggle(this)
+ onClicked: PanelService.getPanel("batteryPanel", screen)?.toggle(this)
tooltipText: {
let lines = []
if (testMode) {
diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml
index 1c3fd2b0..2ce3fea6 100644
--- a/Modules/Bar/Widgets/Bluetooth.qml
+++ b/Modules/Bar/Widgets/Bluetooth.qml
@@ -1,8 +1,9 @@
import QtQuick
import Quickshell
import qs.Commons
-import qs.Services
import qs.Modules.Bar.Extras
+import qs.Services.Networking
+import qs.Services.UI
Item {
id: root
@@ -54,8 +55,8 @@ Item {
autoHide: false
forceOpen: !isBarVertical && root.displayMode === "alwaysShow"
forceClose: isBarVertical || root.displayMode === "alwaysHide" || BluetoothService.connectedDevices.length === 0
- onClicked: PanelService.getPanel("bluetoothPanel")?.toggle(this)
- onRightClicked: PanelService.getPanel("bluetoothPanel")?.toggle(this)
+ onClicked: PanelService.getPanel("bluetoothPanel", screen)?.toggle(this)
+ onRightClicked: PanelService.getPanel("bluetoothPanel", screen)?.toggle(this)
tooltipText: {
if (pill.text !== "") {
return pill.text
diff --git a/Modules/Bar/Widgets/Brightness.qml b/Modules/Bar/Widgets/Brightness.qml
index 5246a182..e426c4aa 100644
--- a/Modules/Bar/Widgets/Brightness.qml
+++ b/Modules/Bar/Widgets/Brightness.qml
@@ -2,8 +2,9 @@ import QtQuick
import Quickshell
import qs.Commons
import qs.Modules.Bar.Extras
-import qs.Modules.Settings
-import qs.Services
+import qs.Modules.Panels.Settings
+import qs.Services.Hardware
+import qs.Services.UI
import qs.Widgets
Item {
@@ -108,13 +109,13 @@ Item {
}
onClicked: {
- var settingsPanel = PanelService.getPanel("settingsPanel")
+ var settingsPanel = PanelService.getPanel("settingsPanel", screen)
settingsPanel.requestedTab = SettingsPanel.Tab.Display
settingsPanel.open()
}
onRightClicked: {
- var settingsPanel = PanelService.getPanel("settingsPanel")
+ var settingsPanel = PanelService.getPanel("settingsPanel", screen)
settingsPanel.requestedTab = SettingsPanel.Tab.Display
settingsPanel.open()
}
diff --git a/Modules/Bar/Widgets/Clock.qml b/Modules/Bar/Widgets/Clock.qml
index 02e8b6d6..4eb93dba 100644
--- a/Modules/Bar/Widgets/Clock.qml
+++ b/Modules/Bar/Widgets/Clock.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
Rectangle {
@@ -32,7 +32,7 @@ Rectangle {
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property bool density: Settings.data.bar.density
- readonly property var now: Time.date
+ readonly property var now: Time.now
// Resolve settings: try user settings or defaults from BarWidgetRegistry
readonly property bool usePrimaryColor: widgetSettings.usePrimaryColor !== undefined ? widgetSettings.usePrimaryColor : widgetMetadata.usePrimaryColor
@@ -121,7 +121,7 @@ Rectangle {
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
- if (!PanelService.getPanel("calendarPanel")?.active) {
+ if (!PanelService.getPanel("calendarPanel", screen)?.active) {
TooltipService.show(Screen, root, I18n.tr("clock.tooltip"), BarService.getTooltipDirection())
}
}
@@ -130,7 +130,7 @@ Rectangle {
}
onClicked: {
TooltipService.hide()
- PanelService.getPanel("calendarPanel")?.toggle(this)
+ PanelService.getPanel("calendarPanel", screen)?.toggle(this)
}
}
}
diff --git a/Modules/Bar/Widgets/ControlCenter.qml b/Modules/Bar/Widgets/ControlCenter.qml
index 9e08fa84..11daae13 100644
--- a/Modules/Bar/Widgets/ControlCenter.qml
+++ b/Modules/Bar/Widgets/ControlCenter.qml
@@ -4,7 +4,8 @@ import Quickshell.Widgets
import QtQuick.Effects
import qs.Commons
import qs.Widgets
-import qs.Services
+import qs.Services.UI
+import qs.Services.System
NIconButton {
id: root
@@ -31,6 +32,11 @@ NIconButton {
readonly property string customIcon: widgetSettings.icon || widgetMetadata.icon
readonly property bool useDistroLogo: (widgetSettings.useDistroLogo !== undefined) ? widgetSettings.useDistroLogo : widgetMetadata.useDistroLogo
readonly property string customIconPath: widgetSettings.customIconPath || ""
+ readonly property bool colorizeDistroLogo: {
+ if (widgetSettings.colorizeDistroLogo !== undefined)
+ return widgetSettings.colorizeDistroLogo
+ return widgetMetadata.colorizeDistroLogo !== undefined ? widgetMetadata.colorizeDistroLogo : false
+ }
// If we have a custom path or distro logo, don't use the theme icon.
icon: (customIconPath === "" && !useDistroLogo) ? customIcon : ""
@@ -44,8 +50,17 @@ NIconButton {
colorBgHover: useDistroLogo ? Color.mSurfaceVariant : Color.mHover
colorBorder: Color.transparent
colorBorderHover: useDistroLogo ? Color.mHover : Color.transparent
- onClicked: PanelService.getPanel("controlCenterPanel")?.toggle(this)
- onRightClicked: PanelService.getPanel("settingsPanel")?.toggle()
+ onClicked: {
+ var controlCenterPanel = PanelService.getPanel("controlCenterPanel", screen)
+ if (Settings.data.controlCenter.position === "close_to_bar_button") {
+ // Willopen the panel next to the bar button.
+ controlCenterPanel?.toggle(this)
+ } else {
+ controlCenterPanel?.toggle()
+ }
+ }
+ onRightClicked: PanelService.getPanel("settingsPanel", screen)?.toggle()
+ onMiddleClicked: PanelService.getPanel("launcherPanel", screen)?.toggle()
IconImage {
id: customOrDistroLogo
@@ -62,5 +77,12 @@ NIconButton {
visible: source !== ""
smooth: true
asynchronous: true
+ layer.enabled: useDistroLogo && colorizeDistroLogo
+ layer.effect: ShaderEffect {
+ property color targetColor: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mSurfaceVariant
+ property real colorizeMode: 1.0
+
+ fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb")
+ }
}
}
diff --git a/Modules/Bar/Widgets/CustomButton.qml b/Modules/Bar/Widgets/CustomButton.qml
index 9c501aef..3e67fb0c 100644
--- a/Modules/Bar/Widgets/CustomButton.qml
+++ b/Modules/Bar/Widgets/CustomButton.qml
@@ -3,9 +3,9 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
-import qs.Modules.Settings
+import qs.Modules.Panels.Settings
import qs.Modules.Bar.Extras
Item {
@@ -30,7 +30,8 @@ Item {
return {}
}
- // Use settings or defaults from BarWidgetRegistry
+ readonly property bool isVerticalBar: Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
+
readonly property string customIcon: widgetSettings.icon || widgetMetadata.icon
readonly property string leftClickExec: widgetSettings.leftClickExec || widgetMetadata.leftClickExec
readonly property string rightClickExec: widgetSettings.rightClickExec || widgetMetadata.rightClickExec
@@ -40,8 +41,11 @@ Item {
readonly property int textIntervalMs: widgetSettings.textIntervalMs !== undefined ? widgetSettings.textIntervalMs : (widgetMetadata.textIntervalMs || 3000)
readonly property string textCollapse: widgetSettings.textCollapse !== undefined ? widgetSettings.textCollapse : (widgetMetadata.textCollapse || "")
readonly property bool parseJson: widgetSettings.parseJson !== undefined ? widgetSettings.parseJson : (widgetMetadata.parseJson || false)
+ readonly property bool hideTextInVerticalBar: widgetSettings.hideTextInVerticalBar !== undefined ? widgetSettings.hideTextInVerticalBar : (widgetMetadata.hideTextInVerticalBar || false)
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
+ readonly property bool shouldShowText: !isVerticalBar || !hideTextInVerticalBar
+
implicitWidth: pill.width
implicitHeight: pill.height
@@ -50,25 +54,37 @@ Item {
oppositeDirection: BarService.getPillDirection(root)
icon: _dynamicIcon !== "" ? _dynamicIcon : customIcon
- text: _dynamicText
+ text: shouldShowText ? _dynamicText : ""
density: Settings.data.bar.density
+ rotateText: isVerticalBar && !hideTextInVerticalBar
autoHide: false
forceOpen: _dynamicText !== ""
tooltipText: {
- if (!hasExec) {
- return "Custom button, configure in settings."
- } else {
- var lines = []
+ var tooltipLines = []
+
+ if (hasExec) {
if (leftClickExec !== "") {
- lines.push(`Left click: ${leftClickExec}.`)
+ tooltipLines.push(`Left click: ${leftClickExec}.`)
}
if (rightClickExec !== "") {
- lines.push(`Right click: ${rightClickExec}.`)
+ tooltipLines.push(`Right click: ${rightClickExec}.`)
}
if (middleClickExec !== "") {
- lines.push(`Middle click: ${middleClickExec}.`)
+ tooltipLines.push(`Middle click: ${middleClickExec}.`)
}
- return lines.join("\n")
+ }
+
+ if (_dynamicTooltip !== "") {
+ if (tooltipLines.length > 0) {
+ tooltipLines.push("")
+ }
+ tooltipLines.push(_dynamicTooltip)
+ }
+
+ if (tooltipLines.length === 0) {
+ return "Custom button, configure in settings."
+ } else {
+ return tooltipLines.join("\n")
}
}
@@ -80,13 +96,14 @@ Item {
// Internal state for dynamic text
property string _dynamicText: ""
property string _dynamicIcon: ""
+ property string _dynamicTooltip: ""
// Periodically run the text command (if set)
Timer {
id: refreshTimer
interval: Math.max(250, textIntervalMs)
repeat: true
- running: !textStream && textCommand && textCommand.length > 0
+ running: shouldShowText && !textStream && textCommand && textCommand.length > 0
triggeredOnStart: true
onTriggered: root.runTextCommand()
}
@@ -95,7 +112,7 @@ Item {
Timer {
id: restartTimer
interval: 1000
- running: textStream && !textProc.running
+ running: shouldShowText && textStream && !textProc.running
onTriggered: root.runTextCommand()
}
@@ -123,38 +140,49 @@ Item {
function parseDynamicContent(content) {
var contentStr = String(content || "").trim()
- if (contentStr.indexOf("\n") !== -1) {
- contentStr = contentStr.split("\n")[0]
- }
if (parseJson) {
+ var lineToParse = contentStr
+
+ if (!textStream && contentStr.includes('\n')) {
+ const lines = contentStr.split('\n').filter(line => line.trim() !== '')
+ if (lines.length > 0) {
+ lineToParse = lines[lines.length - 1]
+ }
+ }
+
try {
- var parsed = JSON.parse(contentStr)
- var text = parsed.text || ""
+ const parsed = JSON.parse(lineToParse)
+ const text = parsed.text || ""
+ const icon = parsed.icon || ""
+ const tooltip = parsed.tooltip || ""
if (checkCollapse(text)) {
_dynamicText = ""
_dynamicIcon = ""
+ _dynamicTooltip = ""
return
}
_dynamicText = text
- _dynamicIcon = parsed.icon || ""
+ _dynamicIcon = icon
+ _dynamicTooltip = tooltip
return
} catch (e) {
-
- // Not a valid JSON, treat as plain text
+ Logger.w("CustomButton", `Failed to parse JSON. Content: "${lineToParse}"`)
}
}
if (checkCollapse(contentStr)) {
_dynamicText = ""
_dynamicIcon = ""
+ _dynamicTooltip = ""
return
}
_dynamicText = contentStr
_dynamicIcon = ""
+ _dynamicTooltip = ""
}
function checkCollapse(text) {
@@ -184,7 +212,7 @@ Item {
Logger.i("CustomButton", `Executing command: ${leftClickExec}`)
} else if (!hasExec) {
// No script was defined, open settings
- var settingsPanel = PanelService.getPanel("settingsPanel")
+ var settingsPanel = PanelService.getPanel("settingsPanel", screen)
settingsPanel.requestedTab = SettingsPanel.Tab.Bar
settingsPanel.open()
}
diff --git a/Modules/Bar/Widgets/DarkMode.qml b/Modules/Bar/Widgets/DarkMode.qml
index d9a2cb25..170b3f5f 100644
--- a/Modules/Bar/Widgets/DarkMode.qml
+++ b/Modules/Bar/Widgets/DarkMode.qml
@@ -1,7 +1,7 @@
import Quickshell
import qs.Commons
import qs.Widgets
-import qs.Services
+import qs.Services.UI
NIconButton {
id: root
diff --git a/Modules/Bar/Widgets/KeepAwake.qml b/Modules/Bar/Widgets/KeepAwake.qml
index c0546884..46f975d6 100644
--- a/Modules/Bar/Widgets/KeepAwake.qml
+++ b/Modules/Bar/Widgets/KeepAwake.qml
@@ -2,7 +2,8 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Power
+import qs.Services.UI
import qs.Widgets
NIconButton {
diff --git a/Modules/Bar/Widgets/KeyboardLayout.qml b/Modules/Bar/Widgets/KeyboardLayout.qml
index 41ffd027..e00fd2d6 100644
--- a/Modules/Bar/Widgets/KeyboardLayout.qml
+++ b/Modules/Bar/Widgets/KeyboardLayout.qml
@@ -4,7 +4,8 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
+import qs.Services.Keyboard
import qs.Widgets
import qs.Modules.Bar.Extras
diff --git a/Modules/Bar/Widgets/LockKeys.qml b/Modules/Bar/Widgets/LockKeys.qml
index 3b356e85..a13dbd9b 100644
--- a/Modules/Bar/Widgets/LockKeys.qml
+++ b/Modules/Bar/Widgets/LockKeys.qml
@@ -3,8 +3,9 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Modules.Settings
-import qs.Services
+import qs.Modules.Panels.Settings
+import qs.Services.Keyboard
+import qs.Services.UI
import qs.Widgets
//import qs.Modules.Bar.Extras
@@ -30,11 +31,14 @@ Rectangle {
readonly property string barPosition: Settings.data.bar.position
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
- readonly property string iconStyle: (widgetSettings.indicatorStyle !== undefined) ? widgetSettings.indicatorStyle : widgetMetadata.indicatorStyle
readonly property bool showCaps: (widgetSettings.showCapsLock !== undefined) ? widgetSettings.showCapsLock : widgetMetadata.showCapsLock
readonly property bool showNum: (widgetSettings.showNumLock !== undefined) ? widgetSettings.showNumLock : widgetMetadata.showNumLock
readonly property bool showScroll: (widgetSettings.showScrollLock !== undefined) ? widgetSettings.showScrollLock : widgetMetadata.showScrollLock
+ readonly property string capsIcon: widgetSettings.capsLockIcon !== undefined ? widgetSettings.capsLockIcon : widgetMetadata.capsLockIcon
+ readonly property string numIcon: widgetSettings.numLockIcon !== undefined ? widgetSettings.numLockIcon : widgetMetadata.numLockIcon
+ readonly property string scrollIcon: widgetSettings.scrollLockIcon !== undefined ? widgetSettings.scrollLockIcon : widgetMetadata.scrollLockIcon
+
implicitWidth: isVertical ? Style.capsuleHeight : Math.round(layout.implicitWidth + Style.marginM * 2)
implicitHeight: isVertical ? Math.round(layout.implicitHeight + Style.marginM * 2) : Style.capsuleHeight
@@ -51,8 +55,6 @@ Rectangle {
implicitWidth: rowLayout.visible ? rowLayout.implicitWidth : colLayout.implicitWidth
implicitHeight: rowLayout.visible ? rowLayout.implicitHeight : colLayout.implicitHeight
- readonly property var indicatorStyle: root.getIndicatorStyle(root.iconStyle)
-
RowLayout {
id: rowLayout
visible: !root.isVertical
@@ -60,17 +62,17 @@ Rectangle {
NIcon {
visible: root.showCaps
- icon: layout.indicatorStyle[0]
+ icon: root.capsIcon
color: LockKeysService.capsLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showNum
- icon: layout.indicatorStyle[1]
+ icon: root.numIcon
color: LockKeysService.numLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showScroll
- icon: layout.indicatorStyle[2]
+ icon: root.scrollIcon
color: LockKeysService.scrollLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
}
@@ -82,42 +84,19 @@ Rectangle {
NIcon {
visible: root.showCaps
- icon: layout.indicatorStyle[0]
+ icon: root.capsIcon
color: LockKeysService.capsLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showNum
- icon: layout.indicatorStyle[1]
+ icon: root.numIcon
color: LockKeysService.numLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showScroll
- icon: layout.indicatorStyle[2]
+ icon: root.scrollIcon
color: LockKeysService.scrollLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
}
}
-
- function getIndicatorStyle(styleName) {
- switch (styleName) {
- case "large":
- return ["letter-c", "letter-n", "letter-s"]
- case "small":
- return ["letter-c-small", "letter-n-small", "letter-s-small"]
- case "square":
- return ["square-letter-c", "square-letter-n", "square-letter-s"]
- case "square-round":
- return ["square-rounded-letter-c", "square-rounded-letter-n", "square-rounded-letter-s"]
- case "circle":
- return ["circle-letter-c", "circle-letter-n", "circle-letter-s"]
- case "circle-dash":
- return ["circle-dashed-letter-c", "circle-dashed-letter-n", "circle-dashed-letter-s"]
- case "circle-dot":
- return ["circle-dotted-letter-c", "circle-dotted-letter-n", "circle-dotted-letter-s"]
- case "hex":
- return ["hexagon-letter-c", "hexagon-letter-n", "hexagon-letter-s"]
- default:
- return ["letter-c", "letter-n", "letter-s"]
- }
- }
}
diff --git a/Modules/Bar/Widgets/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml
index 3d044208..0867514d 100644
--- a/Modules/Bar/Widgets/MediaMini.qml
+++ b/Modules/Bar/Widgets/MediaMini.qml
@@ -2,9 +2,10 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
-import qs.Modules.Audio
+import qs.Widgets.AudioSpectrum
import qs.Commons
-import qs.Services
+import qs.Services.Media
+import qs.Services.UI
import qs.Widgets
Item {
@@ -32,7 +33,9 @@ Item {
readonly property bool isVerticalBar: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right")
- readonly property string hideMode: (widgetSettings.hideMode !== undefined) ? widgetSettings.hideMode : "hidden" // "visible", "hidden", "transparent"
+ readonly property string hideMode: (widgetSettings.hideMode !== undefined) ? widgetSettings.hideMode : "hidden" // "visible", "hidden", "transparent", "idle"
+ // Backward compatibility: honor legacy hideWhenIdle setting if present
+ readonly property bool hideWhenIdle: (widgetSettings.hideWhenIdle !== undefined) ? widgetSettings.hideWhenIdle : (widgetMetadata.hideWhenIdle !== undefined ? widgetMetadata.hideWhenIdle : false)
readonly property bool showAlbumArt: (widgetSettings.showAlbumArt !== undefined) ? widgetSettings.showAlbumArt : widgetMetadata.showAlbumArt
readonly property bool showVisualizer: (widgetSettings.showVisualizer !== undefined) ? widgetSettings.showVisualizer : widgetMetadata.showVisualizer
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType !== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
@@ -60,12 +63,16 @@ Item {
return title
}
- implicitHeight: visible ? (isVerticalBar ? (((!hasActivePlayer) && (hideMode === "hidden" || hideMode === "transparent")) ? 0 : calculatedVerticalDimension()) : Style.capsuleHeight) : 0
- implicitWidth: visible ? (isVerticalBar ? (((!hasActivePlayer) && (hideMode === "hidden" || hideMode === "transparent")) ? 0 : calculatedVerticalDimension()) : (((!hasActivePlayer) && (hideMode === "hidden" || hideMode === "transparent")) ? 0 : dynamicWidth)) : 0
+ // Hide conditions
+ readonly property bool shouldHideIdle: ((hideMode === "idle") || hideWhenIdle) && !MediaService.isPlaying
+ readonly property bool isEmptyForHideMode: (!hasActivePlayer) && (hideMode === "hidden" || hideMode === "transparent")
- // "visible": Always Visible, "hidden": Hide When Empty, "transparent": Transparent When Empty
- visible: hideMode !== "hidden" || opacity > 0
- opacity: ((hideMode !== "hidden" || hasActivePlayer) && (hideMode !== "transparent" || hasActivePlayer)) ? 1.0 : 0.0
+ implicitHeight: visible ? (isVerticalBar ? ((shouldHideIdle || isEmptyForHideMode) ? 0 : calculatedVerticalDimension()) : Style.capsuleHeight) : 0
+ implicitWidth: visible ? (isVerticalBar ? ((shouldHideIdle || isEmptyForHideMode) ? 0 : calculatedVerticalDimension()) : ((shouldHideIdle || isEmptyForHideMode) ? 0 : dynamicWidth)) : 0
+
+ // "visible": Always Visible, "hidden": Hide When Empty, "transparent": Transparent When Empty, "idle": Hide When Idle (not playing)
+ visible: shouldHideIdle ? false : (hideMode !== "hidden" || opacity > 0)
+ opacity: shouldHideIdle ? 0.0 : (((hideMode !== "hidden" || hasActivePlayer) && (hideMode !== "transparent" || hasActivePlayer)) ? 1.0 : 0.0)
Behavior on opacity {
NumberAnimation {
duration: Style.animationNormal
@@ -155,8 +162,8 @@ Item {
visible: root.visible
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
- width: isVerticalBar ? (((!hasActivePlayer) && (hideMode === "hidden" || hideMode === "transparent")) ? 0 : calculatedVerticalDimension()) : (((!hasActivePlayer) && (hideMode === "hidden" || hideMode === "transparent")) ? 0 : dynamicWidth)
- height: isVerticalBar ? (((!hasActivePlayer) && (hideMode === "hidden" || hideMode === "transparent")) ? 0 : calculatedVerticalDimension()) : Style.capsuleHeight
+ width: isVerticalBar ? ((shouldHideIdle || isEmptyForHideMode) ? 0 : calculatedVerticalDimension()) : ((shouldHideIdle || isEmptyForHideMode) ? 0 : dynamicWidth)
+ height: isVerticalBar ? ((shouldHideIdle || isEmptyForHideMode) ? 0 : calculatedVerticalDimension()) : Style.capsuleHeight
radius: isVerticalBar ? width / 2 : Style.radiusM
color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
@@ -189,7 +196,7 @@ Item {
active: showVisualizer && visualizerType == "linear"
z: 0
- sourceComponent: LinearSpectrum {
+ sourceComponent: NLinearSpectrum {
width: mainContainer.width - Style.marginS
height: 20
values: CavaService.values
@@ -204,7 +211,7 @@ Item {
active: showVisualizer && visualizerType == "mirrored"
z: 0
- sourceComponent: MirroredSpectrum {
+ sourceComponent: NMirroredSpectrum {
width: mainContainer.width - Style.marginS
height: mainContainer.height - Style.marginS
values: CavaService.values
@@ -219,7 +226,7 @@ Item {
active: showVisualizer && visualizerType == "wave"
z: 0
- sourceComponent: WaveSpectrum {
+ sourceComponent: NWaveSpectrum {
width: mainContainer.width - Style.marginS
height: mainContainer.height - Style.marginS
values: CavaService.values
diff --git a/Modules/Bar/Widgets/Microphone.qml b/Modules/Bar/Widgets/Microphone.qml
index 4592653d..d384cfb5 100644
--- a/Modules/Bar/Widgets/Microphone.qml
+++ b/Modules/Bar/Widgets/Microphone.qml
@@ -3,10 +3,11 @@ import Quickshell
import Quickshell.Io
import Quickshell.Services.Pipewire
import qs.Commons
-import qs.Modules.Settings
-import qs.Services
-import qs.Widgets
import qs.Modules.Bar.Extras
+import qs.Modules.Panels.Settings
+import qs.Services.Media
+import qs.Services.UI
+import qs.Widgets
Item {
id: root
@@ -105,7 +106,7 @@ Item {
}
}
onClicked: {
- PanelService.getPanel("audioPanel")?.toggle(this)
+ PanelService.getPanel("audioPanel", screen)?.toggle(this)
}
onRightClicked: {
AudioService.setInputMuted(!AudioService.inputMuted)
diff --git a/Modules/Bar/Widgets/NightLight.qml b/Modules/Bar/Widgets/NightLight.qml
index 80d2c01d..c04d713d 100644
--- a/Modules/Bar/Widgets/NightLight.qml
+++ b/Modules/Bar/Widgets/NightLight.qml
@@ -4,8 +4,9 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Modules.Settings
-import qs.Services
+import qs.Modules.Panels.Settings
+import qs.Services.System
+import qs.Services.UI
import qs.Widgets
NIconButton {
@@ -43,7 +44,7 @@ NIconButton {
}
onRightClicked: {
- var settingsPanel = PanelService.getPanel("settingsPanel")
+ var settingsPanel = PanelService.getPanel("settingsPanel", screen)
settingsPanel.requestedTab = SettingsPanel.Tab.Display
settingsPanel.open()
}
diff --git a/Modules/Bar/Widgets/NoctaliaPerformance.qml b/Modules/Bar/Widgets/NoctaliaPerformance.qml
new file mode 100644
index 00000000..4263604a
--- /dev/null
+++ b/Modules/Bar/Widgets/NoctaliaPerformance.qml
@@ -0,0 +1,31 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import Quickshell
+import Quickshell.Wayland
+import qs.Commons
+import qs.Modules.Panels.Settings
+import qs.Services.Power
+import qs.Services.UI
+import qs.Widgets
+
+NIconButton {
+ id: root
+
+ property ShellScreen screen
+
+ density: Settings.data.bar.density
+ baseSize: Style.capsuleHeight
+ applyUiScale: false
+ colorBg: PowerProfileService.noctaliaPerformanceMode ? Color.mPrimary : (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent)
+ colorFg: PowerProfileService.noctaliaPerformanceMode ? Color.mOnPrimary : Color.mOnSurface
+ colorBorder: Color.transparent
+ colorBorderHover: Color.transparent
+
+ icon: PowerProfileService.noctaliaPerformanceMode ? "rocket" : "rocket-off"
+ tooltipText: PowerProfileService.noctaliaPerformanceMode ? I18n.tr("tooltips.noctalia-performance-enabled") : I18n.tr("tooltips.noctalia-performance-disabled")
+ tooltipDirection: BarService.getTooltipDirection()
+ onClicked: {
+ PowerProfileService.toggleNoctaliaPerformance()
+ }
+}
diff --git a/Modules/Bar/Widgets/NotificationHistory.qml b/Modules/Bar/Widgets/NotificationHistory.qml
index ccceeaa3..1dd21717 100644
--- a/Modules/Bar/Widgets/NotificationHistory.qml
+++ b/Modules/Bar/Widgets/NotificationHistory.qml
@@ -4,7 +4,8 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Services
+import qs.Services.UI
+import qs.Services.System
import qs.Widgets
NIconButton {
@@ -47,8 +48,8 @@ NIconButton {
baseSize: Style.capsuleHeight
applyUiScale: false
density: Settings.data.bar.density
- icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell"
- tooltipText: Settings.data.notifications.doNotDisturb ? I18n.tr("tooltips.open-notification-history-disable-dnd") : I18n.tr("tooltips.open-notification-history-enable-dnd")
+ icon: NotificationService.doNotDisturb ? "bell-off" : "bell"
+ tooltipText: NotificationService.doNotDisturb ? I18n.tr("tooltips.open-notification-history-disable-dnd") : I18n.tr("tooltips.open-notification-history-enable-dnd")
tooltipDirection: BarService.getTooltipDirection()
colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent)
colorFg: Color.mOnSurface
@@ -56,11 +57,11 @@ NIconButton {
colorBorderHover: Color.transparent
onClicked: {
- var panel = PanelService.getPanel("notificationHistoryPanel")
+ var panel = PanelService.getPanel("notificationHistoryPanel", screen)
panel?.toggle(this)
}
- onRightClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
+ onRightClicked: NotificationService.doNotDisturb = !NotificationService.doNotDisturb
Loader {
anchors.right: parent.right
diff --git a/Modules/Bar/Widgets/PowerProfile.qml b/Modules/Bar/Widgets/PowerProfile.qml
index da96fcf1..724d7a4d 100644
--- a/Modules/Bar/Widgets/PowerProfile.qml
+++ b/Modules/Bar/Widgets/PowerProfile.qml
@@ -3,7 +3,8 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Services.UPower
import qs.Commons
-import qs.Services
+import qs.Services.Power
+import qs.Services.UI
import qs.Widgets
NIconButton {
diff --git a/Modules/Bar/Widgets/ScreenRecorder.qml b/Modules/Bar/Widgets/ScreenRecorder.qml
index 4e528c2f..085db1e3 100644
--- a/Modules/Bar/Widgets/ScreenRecorder.qml
+++ b/Modules/Bar/Widgets/ScreenRecorder.qml
@@ -1,6 +1,8 @@
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
+import qs.Services.Media
+import qs.Services.System
import qs.Widgets
// Screen Recording Indicator
@@ -22,7 +24,7 @@ NIconButton {
function handleClick() {
if (!ScreenRecorderService.isAvailable) {
- ToastService.showError(I18n.tr("toast.recording.not-installed"), I18n.tr("toast.recording.not-installed-desc"), 7000)
+ ToastService.showError(I18n.tr("toast.recording.not-installed"), I18n.tr("toast.recording.not-installed-desc"))
return
}
ScreenRecorderService.toggleRecording()
diff --git a/Modules/Bar/Widgets/SessionMenu.qml b/Modules/Bar/Widgets/SessionMenu.qml
index bb24f446..64192d32 100644
--- a/Modules/Bar/Widgets/SessionMenu.qml
+++ b/Modules/Bar/Widgets/SessionMenu.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
NIconButton {
@@ -20,5 +20,5 @@ NIconButton {
colorFg: Color.mError
colorBorder: Color.transparent
colorBorderHover: Color.transparent
- onClicked: PanelService.getPanel("sessionMenuPanel")?.toggle()
+ onClicked: PanelService.getPanel("sessionMenuPanel", screen)?.toggle()
}
diff --git a/Modules/Bar/Widgets/Spacer.qml b/Modules/Bar/Widgets/Spacer.qml
index 3af0e792..92bc33f3 100644
--- a/Modules/Bar/Widgets/Spacer.qml
+++ b/Modules/Bar/Widgets/Spacer.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
Item {
diff --git a/Modules/Bar/Widgets/SystemMonitor.qml b/Modules/Bar/Widgets/SystemMonitor.qml
index bb4ec296..caab8fda 100644
--- a/Modules/Bar/Widgets/SystemMonitor.qml
+++ b/Modules/Bar/Widgets/SystemMonitor.qml
@@ -2,8 +2,9 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Modules.Settings
-import qs.Services
+import qs.Modules.Panels.Settings
+import qs.Services.UI
+import qs.Services.System
import qs.Widgets
Rectangle {
@@ -32,6 +33,7 @@ Rectangle {
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
readonly property bool density: Settings.data.bar.density
+ readonly property bool usePrimaryColor: widgetSettings.usePrimaryColor !== undefined ? widgetSettings.usePrimaryColor : widgetMetadata.usePrimaryColor
readonly property bool showCpuUsage: (widgetSettings.showCpuUsage !== undefined) ? widgetSettings.showCpuUsage : widgetMetadata.showCpuUsage
readonly property bool showCpuTemp: (widgetSettings.showCpuTemp !== undefined) ? widgetSettings.showCpuTemp : widgetMetadata.showCpuTemp
readonly property bool showMemoryUsage: (widgetSettings.showMemoryUsage !== undefined) ? widgetSettings.showMemoryUsage : widgetMetadata.showMemoryUsage
@@ -48,6 +50,7 @@ Rectangle {
readonly property int percentTextWidth: Math.ceil(percentMetrics.boundingRect.width + 3)
readonly property int tempTextWidth: Math.ceil(tempMetrics.boundingRect.width + 3)
readonly property int memTextWidth: Math.ceil(memMetrics.boundingRect.width + 3)
+ readonly property color textColor: usePrimaryColor ? Color.mPrimary : Color.mOnSurface
TextMetrics {
id: percentMetrics
@@ -130,7 +133,7 @@ Rectangle {
Layout.preferredWidth: isVertical ? -1 : percentTextWidth
horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: Color.mPrimary
+ color: textColor
Layout.row: isVertical ? 0 : 0
Layout.column: isVertical ? 0 : 1
scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0
@@ -173,7 +176,7 @@ Rectangle {
Layout.preferredWidth: isVertical ? -1 : tempTextWidth
horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: Color.mPrimary
+ color: textColor
Layout.row: isVertical ? 0 : 0
Layout.column: isVertical ? 0 : 1
scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0
@@ -216,7 +219,7 @@ Rectangle {
Layout.preferredWidth: isVertical ? -1 : (showMemoryAsPercent ? percentTextWidth : memTextWidth)
horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: Color.mPrimary
+ color: textColor
Layout.row: isVertical ? 0 : 0
Layout.column: isVertical ? 0 : 1
scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0
@@ -259,7 +262,7 @@ Rectangle {
Layout.preferredWidth: isVertical ? -1 : memTextWidth
horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: Color.mPrimary
+ color: textColor
Layout.row: isVertical ? 0 : 0
Layout.column: isVertical ? 0 : 1
scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0
@@ -302,7 +305,7 @@ Rectangle {
Layout.preferredWidth: isVertical ? -1 : memTextWidth
horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: Color.mPrimary
+ color: textColor
Layout.row: isVertical ? 0 : 0
Layout.column: isVertical ? 0 : 1
scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0
@@ -345,7 +348,7 @@ Rectangle {
Layout.preferredWidth: isVertical ? -1 : percentTextWidth
horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: Color.mPrimary
+ color: textColor
Layout.row: isVertical ? 0 : 0
Layout.column: isVertical ? 0 : 1
scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0
@@ -353,13 +356,4 @@ Rectangle {
}
}
}
-
- // MouseArea {
- // anchors.fill: parent
- // acceptedButtons: Qt.RightButton
- // onClicked: {
- // var directPanel = PanelService.getPanel("directWidgetSettingsPanel")
- // directPanel.openWidgetSettings(root.section, root.sectionWidgetIndex, root.widgetId, root.widgetSettings)
- // }
- // }
}
diff --git a/Modules/Bar/Widgets/Taskbar.qml b/Modules/Bar/Widgets/Taskbar.qml
index 959dc2df..30b41e72 100644
--- a/Modules/Bar/Widgets/Taskbar.qml
+++ b/Modules/Bar/Widgets/Taskbar.qml
@@ -5,7 +5,8 @@ import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.UI
import qs.Widgets
Rectangle {
diff --git a/Modules/Bar/Widgets/TaskbarGrouped.qml b/Modules/Bar/Widgets/TaskbarGrouped.qml
index ea43d51a..68b97ca6 100644
--- a/Modules/Bar/Widgets/TaskbarGrouped.qml
+++ b/Modules/Bar/Widgets/TaskbarGrouped.qml
@@ -5,7 +5,8 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.UI
import qs.Widgets
Item {
diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml
index 07e7b755..7f1a3921 100644
--- a/Modules/Bar/Widgets/Tray.qml
+++ b/Modules/Bar/Widgets/Tray.qml
@@ -7,7 +7,7 @@ import Quickshell.Services.SystemTray
import Quickshell.Widgets
import qs.Commons
import qs.Modules.Bar.Extras
-import qs.Services
+import qs.Services.UI
import qs.Widgets
Rectangle {
@@ -35,35 +35,13 @@ Rectangle {
readonly property string barPosition: Settings.data.bar.position
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
readonly property bool density: Settings.data.bar.density
- property real itemSize: Math.round(Style.capsuleHeight * 0.65)
+ readonly property real iconSize: Math.round(Style.capsuleHeight * 0.65)
+
property list blacklist: widgetSettings.blacklist || widgetMetadata.blacklist || [] // Read from settings
- property var filteredItems: []
-
- function wildCardMatch(str, rule) {
- if (!str || !rule) {
- return false
- }
- Logger.i("Tray", "wildCardMatch - Input str:", str, "rule:", rule)
-
- // Escape all special regex characters in the rule
- let escapedRule = rule.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
- // Convert '*' to '.*' for wildcard matching
- let pattern = escapedRule.replace(/\\\*/g, '.*')
- // Add ^ and $ to match the entire string
- pattern = '^' + pattern + '$'
-
- Logger.i("Tray", "wildCardMatch - Generated pattern:", pattern)
-
- try {
- const regex = new RegExp(pattern, 'i')
- // 'i' for case-insensitive
- Logger.i("Tray", "wildCardMatch - Regex test result:", regex.test(str))
- return regex.test(str)
- } catch (e) {
- Logger.w("Tray", "Invalid regex pattern for wildcard match:", rule, e.message)
- return false // If regex is invalid, it won't match
- }
- }
+ property list favorites: widgetSettings.favorites || widgetMetadata.favorites || [] // Pinned items (shown inline)
+ property bool drawerEnabled: widgetSettings.drawerEnabled !== undefined ? widgetSettings.drawerEnabled : (widgetMetadata.drawerEnabled !== undefined ? widgetMetadata.drawerEnabled : true) // Enable drawer panel
+ property var filteredItems: [] // Items to show inline (pinned/favorites)
+ property var dropdownItems: [] // Items to show in drawer (unpinned/non-favorites)
// Debounce timer for updateFilteredItems to prevent excessive calls
// when multiple events (e.g., SystemTray changes, settings saves)
@@ -77,15 +55,6 @@ Rectangle {
}
function _performFilteredItemsUpdate() {
- if (!root.blacklist || root.blacklist.length === 0) {
- if (SystemTray.items && SystemTray.items.values) {
- filteredItems = SystemTray.items.values
- } else {
- filteredItems = []
- }
- return
- }
-
let newItems = []
if (SystemTray.items && SystemTray.items.values) {
const trayItems = SystemTray.items.values
@@ -97,12 +66,15 @@ Rectangle {
const title = item.tooltipTitle || item.name || item.id || ""
+ // Check if blacklisted
let isBlacklisted = false
- for (var j = 0; j < root.blacklist.length; j++) {
- const rule = root.blacklist[j]
- if (wildCardMatch(title, rule)) {
- isBlacklisted = true
- break
+ if (root.blacklist && root.blacklist.length > 0) {
+ for (var j = 0; j < root.blacklist.length; j++) {
+ const rule = root.blacklist[j]
+ if (wildCardMatch(title, rule)) {
+ isBlacklisted = true
+ break
+ }
}
}
@@ -111,17 +83,89 @@ Rectangle {
}
}
}
- filteredItems = newItems
+
+ // If drawer is disabled, show all items inline
+ if (!root.drawerEnabled) {
+ filteredItems = newItems
+ dropdownItems = []
+ } else {
+ // Build inline (pinned/favorites) and drawer (unpinned/non-favorites) lists
+ // If favorites list is empty, all items go to drawer (none inline)
+ // If favorites list has items, favorites are inline, rest go to drawer
+ if (favorites && favorites.length > 0) {
+ let fav = []
+ for (var k = 0; k < newItems.length; k++) {
+ const item2 = newItems[k]
+ const title2 = item2.tooltipTitle || item2.name || item2.id || ""
+ for (var m = 0; m < favorites.length; m++) {
+ const rule2 = favorites[m]
+ if (wildCardMatch(title2, rule2)) {
+ fav.push(item2)
+ break
+ }
+ }
+ }
+ filteredItems = fav
+
+ // Non-favorites (unpinned) go to drawer
+ let nonFav = []
+ for (var v = 0; v < newItems.length; v++) {
+ const cand = newItems[v]
+ let isFavorite = false
+ for (var f = 0; f < filteredItems.length; f++) {
+ if (filteredItems[f] === cand) {
+ isFavorite = true
+ break
+ }
+ }
+ if (!isFavorite)
+ nonFav.push(cand)
+ }
+ dropdownItems = nonFav
+ } else {
+ // No favorites (pinned): all items go to drawer (none inline)
+ filteredItems = []
+ dropdownItems = newItems
+ }
+ }
}
function updateFilteredItems() {
updateDebounceTimer.restart()
}
- function onLoaded() {
- // When the widget is fully initialized with its props set the screen for the trayMenu
- if (trayMenu.item) {
- trayMenu.item.screen = screen
+ function wildCardMatch(str, rule) {
+ if (!str || !rule) {
+ return false
+ }
+ //Logger.d("Tray", "wildCardMatch - Input str:", str, "rule:", rule)
+
+ // Escape all special regex characters in the rule
+ let escapedRule = rule.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ // Convert '*' to '.*' for wildcard matching
+ let pattern = escapedRule.replace(/\\\*/g, '.*')
+ // Add ^ and $ to match the entire string
+ pattern = '^' + pattern + '$'
+
+ //Logger.d("Tray", "wildCardMatch - Generated pattern:", pattern)
+ try {
+ const regex = new RegExp(pattern, 'i')
+ // 'i' for case-insensitive
+ //Logger.d("Tray", "wildCardMatch - Regex test result:", regex.test(str))
+ return regex.test(str)
+ } catch (e) {
+ Logger.w("Tray", "Invalid regex pattern for wildcard match:", rule, e.message)
+ return false // If regex is invalid, it won't match
+ }
+ }
+
+ function toggleDrawer(button) {
+ TooltipService.hideImmediately()
+ const panel = PanelService.getPanel("trayDrawerPanel", root.screen)
+ if (panel) {
+ panel.widgetSection = root.section
+ panel.widgetIndex = root.sectionWidgetIndex
+ panel.toggle(this)
}
}
@@ -143,37 +187,66 @@ Rectangle {
root.updateFilteredItems() // Initial update
}
- visible: filteredItems.length > 0
- implicitWidth: isVertical ? Style.capsuleHeight : Math.round(trayFlow.implicitWidth + Style.marginM * 2)
- implicitHeight: isVertical ? Math.round(trayFlow.implicitHeight + Style.marginM * 2) : Style.capsuleHeight
+ visible: filteredItems.length > 0 || dropdownItems.length > 0
+ implicitWidth: isVertical ? Style.capsuleHeight : Math.round(trayFlow.implicitWidth)
+ implicitHeight: isVertical ? Math.round(trayFlow.implicitHeight) : Style.capsuleHeight
radius: Style.radiusM
color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
- Layout.alignment: Qt.AlignVCenter
-
Flow {
id: trayFlow
- anchors.centerIn: parent
- spacing: Style.marginM
+ spacing: Style.marginXS
flow: isVertical ? Flow.TopToBottom : Flow.LeftToRight
+ // Drawer opener (before items if opposite direction)
+ NIconButton {
+ id: chevronIconBefore
+ visible: root.drawerEnabled && dropdownItems.length > 0 && BarService.getPillDirection(root)
+ tooltipText: I18n.tr("tooltips.open-tray-dropdown")
+ tooltipDirection: BarService.getTooltipDirection()
+ density: Settings.data.bar.density
+ baseSize: Style.capsuleHeight
+ applyUiScale: false
+ colorBg: Settings.data.colorSchemes.darkMode ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary
+ colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
+ colorBorder: Color.transparent
+ colorBorderHover: Color.transparent
+ icon: {
+ switch (barPosition) {
+ case "bottom":
+ return "caret-up"
+ case "left":
+ return "caret-right"
+ case "right":
+ return "caret-left"
+ case "top":
+ default:
+ return "caret-down"
+ }
+ }
+ onClicked: toggleDrawer(this)
+ onRightClicked: toggleDrawer(this)
+ }
+
Repeater {
id: repeater
- model: filteredItems
+ model: root.filteredItems
delegate: Item {
- width: itemSize
- height: itemSize
+ width: Style.capsuleHeight
+ height: Style.capsuleHeight
visible: modelData
IconImage {
id: trayIcon
-
- property ShellScreen screen: root.screen
-
- anchors.fill: parent
+ width: iconSize
+ height: iconSize
+ anchors.centerIn: parent
asynchronous: true
backer.fillMode: Image.PreserveAspectFit
+
+ property bool menuJustOpened: false
+
source: {
let icon = modelData?.icon || ""
if (!icon) {
@@ -212,52 +285,54 @@ Rectangle {
if (mouse.button === Qt.LeftButton) {
// Close any open menu first
- trayPanel.close()
+ PanelService.getPanel("trayMenuPanel", root.screen)?.close()
if (!modelData.onlyMenu) {
modelData.activate()
}
} else if (mouse.button === Qt.MiddleButton) {
// Close any open menu first
- trayPanel.close()
+ PanelService.getPanel("trayMenuPanel", root.screen)?.close()
modelData.secondaryActivate && modelData.secondaryActivate()
} else if (mouse.button === Qt.RightButton) {
TooltipService.hideImmediately()
// Close the menu if it was visible
- if (trayPanel && trayPanel.visible) {
- trayPanel.close()
+ const menuPanel = PanelService.getPanel("trayMenuPanel", root.screen)
+ if (menuPanel && menuPanel.visible) {
+ menuPanel.close()
return
}
- if (modelData.hasMenu && modelData.menu && trayMenu.item) {
- trayPanel.open()
-
- // Position menu based on bar position
- let menuX, menuY
- if (barPosition === "left") {
- // For left bar: position menu to the right of the bar
- menuX = width + Style.marginM
- menuY = 0
- } else if (barPosition === "right") {
- // For right bar: position menu to the left of the bar
- menuX = -trayMenu.item.width - Style.marginM
- menuY = 0
+ if (modelData.hasMenu && modelData.menu) {
+ const panel = PanelService.getPanel("trayMenuPanel", root.screen)
+ if (panel) {
+ panel.menu = modelData.menu
+ panel.trayItem = modelData
+ panel.widgetSection = root.section
+ panel.widgetIndex = root.sectionWidgetIndex
+ panel.openAt(parent)
+ // Prevent onEntered from immediately closing the panel
+ trayIcon.menuJustOpened = true
} else {
- // For horizontal bars: center horizontally and position below
- menuX = (width / 2) - (trayMenu.item.width / 2)
- menuY = Style.barHeight
+ Logger.i("Tray", "TrayMenu not available")
}
- trayMenu.item.menu = modelData.menu
- trayMenu.item.showAt(parent, menuX, menuY)
} else {
Logger.i("Tray", "No menu available for", modelData.id, "or trayMenu not set")
}
}
}
onEntered: {
- trayPanel.close()
+ // Don't close menu immediately after opening it
+ if (!trayIcon.menuJustOpened) {
+ // Only close the menu if we're hovering over a DIFFERENT tray icon
+ const menuPanel = PanelService.getPanel("trayMenuPanel", root.screen)
+ if (menuPanel && menuPanel.trayItem !== modelData) {
+ menuPanel.close()
+ }
+ }
+ trayIcon.menuJustOpened = false
TooltipService.show(Screen, trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection())
}
onExited: TooltipService.hide()
@@ -265,39 +340,35 @@ Rectangle {
}
}
}
- }
- PanelWindow {
- id: trayPanel
- anchors.top: true
- anchors.left: true
- anchors.right: true
- anchors.bottom: true
- visible: false
- color: Color.transparent
- screen: screen
-
- function open() {
- visible = true
- PanelService.willOpenPanel(trayPanel)
- }
-
- function close() {
- visible = false
- if (trayMenu.item) {
- trayMenu.item.hideMenu()
+ // Drawer opener (after items if normal direction)
+ NIconButton {
+ id: chevronIconAfter
+ visible: root.drawerEnabled && dropdownItems.length > 0 && !BarService.getPillDirection(root)
+ tooltipText: I18n.tr("tooltips.open-tray-dropdown")
+ tooltipDirection: BarService.getTooltipDirection()
+ density: Settings.data.bar.density
+ baseSize: Style.capsuleHeight
+ applyUiScale: false
+ colorBg: Settings.data.colorSchemes.darkMode ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary
+ colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
+ colorBorder: Color.transparent
+ colorBorderHover: Color.transparent
+ icon: {
+ switch (barPosition) {
+ case "bottom":
+ return "caret-up"
+ case "left":
+ return "caret-right"
+ case "right":
+ return "caret-left"
+ case "top":
+ default:
+ return "caret-down"
+ }
}
- }
-
- // Clicking outside of the rectangle to close
- MouseArea {
- anchors.fill: parent
- onClicked: trayPanel.close()
- }
-
- Loader {
- id: trayMenu
- source: "../Extras/TrayMenu.qml"
+ onClicked: toggleDrawer(this)
+ onRightClicked: toggleDrawer(this)
}
}
}
diff --git a/Modules/Bar/Widgets/Volume.qml b/Modules/Bar/Widgets/Volume.qml
index d494d3cb..9fb72180 100644
--- a/Modules/Bar/Widgets/Volume.qml
+++ b/Modules/Bar/Widgets/Volume.qml
@@ -3,10 +3,11 @@ import Quickshell
import Quickshell.Io
import Quickshell.Services.Pipewire
import qs.Commons
-import qs.Modules.Settings
-import qs.Services
-import qs.Widgets
import qs.Modules.Bar.Extras
+import qs.Modules.Panels.Settings
+import qs.Services.UI
+import qs.Services.Media
+import qs.Widgets
Item {
id: root
@@ -90,7 +91,7 @@ Item {
}
}
onClicked: {
- PanelService.getPanel("audioPanel")?.toggle(this)
+ PanelService.getPanel("audioPanel", screen)?.toggle(this)
}
onRightClicked: {
AudioService.setOutputMuted(!AudioService.muted)
diff --git a/Modules/Bar/Widgets/WallpaperSelector.qml b/Modules/Bar/Widgets/WallpaperSelector.qml
index e48f368a..1dbbdff4 100644
--- a/Modules/Bar/Widgets/WallpaperSelector.qml
+++ b/Modules/Bar/Widgets/WallpaperSelector.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
NIconButton {
@@ -20,5 +20,5 @@ NIconButton {
colorFg: Color.mOnSurface
colorBorder: Color.transparent
colorBorderHover: Color.transparent
- onClicked: PanelService.getPanel("wallpaperPanel")?.toggle(this)
+ onClicked: PanelService.getPanel("wallpaperPanel", screen)?.toggle(this)
}
diff --git a/Modules/Bar/Widgets/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml
index 25de98ae..ad2d1ede 100644
--- a/Modules/Bar/Widgets/WiFi.qml
+++ b/Modules/Bar/Widgets/WiFi.qml
@@ -1,7 +1,8 @@
import QtQuick
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Networking
+import qs.Services.UI
import qs.Modules.Bar.Extras
Item {
@@ -51,7 +52,7 @@ Item {
break
}
}
- return connected ? NetworkService.signalIcon(signalStrength) : "wifi-off"
+ return connected ? NetworkService.signalIcon(signalStrength, true) : "wifi-off"
} catch (error) {
Logger.e("Wi-Fi", "Error getting icon:", error)
return "signal_wifi_bad"
@@ -76,8 +77,8 @@ Item {
autoHide: false
forceOpen: !isBarVertical && root.displayMode === "alwaysShow"
forceClose: isBarVertical || root.displayMode === "alwaysHide" || !pill.text
- onClicked: PanelService.getPanel("wifiPanel")?.toggle(this)
- onRightClicked: PanelService.getPanel("wifiPanel")?.toggle(this)
+ onClicked: PanelService.getPanel("wifiPanel", screen)?.toggle(this)
+ onRightClicked: PanelService.getPanel("wifiPanel", screen)?.toggle(this)
tooltipText: {
if (pill.text !== "") {
return pill.text
diff --git a/Modules/Bar/Widgets/Workspace.qml b/Modules/Bar/Widgets/Workspace.qml
index a0ec3048..f30af56f 100644
--- a/Modules/Bar/Widgets/Workspace.qml
+++ b/Modules/Bar/Widgets/Workspace.qml
@@ -6,7 +6,8 @@ import QtQuick.Effects
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.UI
import qs.Widgets
Item {
@@ -54,8 +55,8 @@ Item {
property bool effectsActive: false
property color effectColor: Color.mPrimary
- property int horizontalPadding: Math.round(Style.marginS)
- property int spacingBetweenPills: Math.round(Style.marginXS)
+ property int horizontalPadding: Style.marginS
+ property int spacingBetweenPills: Style.marginXS
// Wheel scroll handling
property int wheelAccumulatedDelta: 0
diff --git a/Modules/ControlCenter/Cards/MediaCard.qml b/Modules/ControlCenter/Cards/MediaCard.qml
deleted file mode 100644
index 0138a859..00000000
--- a/Modules/ControlCenter/Cards/MediaCard.qml
+++ /dev/null
@@ -1,443 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Effects
-import Quickshell
-import qs.Modules.Audio
-import qs.Commons
-import qs.Services
-import qs.Widgets
-
-NBox {
- id: root
-
- // Wrapper - rounded rect clipper
- Item {
- anchors.fill: parent
- layer.enabled: true
- layer.effect: MultiEffect {
- maskEnabled: true
- maskThresholdMin: 0.5
- maskSpreadAtMin: 0.0
- maskSource: ShaderEffectSource {
- sourceItem: Rectangle {
- width: root.width
- height: root.height
- radius: Style.radiusM
- color: "white"
- }
- }
- }
-
- // Background image that covers everything
- Image {
- readonly property int dim: Math.round(256 * Style.uiScaleRatio)
- id: bgImage
- anchors.fill: parent
- source: MediaService.trackArtUrl || WallpaperService.getWallpaper(Screen.name)
- sourceSize: Qt.size(dim, dim)
- fillMode: Image.PreserveAspectCrop
-
- layer.enabled: true
- layer.effect: MultiEffect {
- blurEnabled: true
- blur: 0.25
- blurMax: 16
- }
- }
-
- // Dark overlay for readability
- Rectangle {
- anchors.fill: parent
- color: Color.mSurfaceVariant
- opacity: 0.85
- radius: Style.radiusM
- }
-
- // Border
- Rectangle {
- anchors.fill: parent
- color: Color.transparent
- border.color: Color.mOutline
- border.width: 1
- radius: Style.radiusM
- }
- //}
-
- // Background visualizer on top of the artwork
- Loader {
- anchors.fill: parent
- active: Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
-
- sourceComponent: {
- switch (Settings.data.audio.visualizerType) {
- case "linear":
- return linearComponent
- case "mirrored":
- return mirroredComponent
- case "wave":
- return waveComponent
- default:
- return null
- }
- }
-
- Component {
- id: linearComponent
- LinearSpectrum {
- anchors.fill: parent
- values: CavaService.values
- fillColor: Color.mPrimary
- opacity: 0.5
- }
- }
-
- Component {
- id: mirroredComponent
- MirroredSpectrum {
- anchors.fill: parent
- values: CavaService.values
- fillColor: Color.mPrimary
- opacity: 0.5
- }
- }
-
- Component {
- id: waveComponent
- WaveSpectrum {
- anchors.fill: parent
- values: CavaService.values
- fillColor: Color.mPrimary
- opacity: 0.5
- }
- }
- }
- }
-
- // Player selector
- Rectangle {
- id: playerSelectorButton
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.topMargin: Style.marginXS
- anchors.leftMargin: Style.marginM
- anchors.rightMargin: Style.marginM
- height: Style.barHeight
- visible: MediaService.getAvailablePlayers().length > 1
- radius: Style.radiusM
- color: Color.transparent
-
- property var currentPlayer: MediaService.getAvailablePlayers()[MediaService.selectedPlayerIndex]
-
- RowLayout {
- anchors.fill: parent
- spacing: Style.marginS
-
- NIcon {
- icon: "caret-down"
- pointSize: Style.fontSizeXXL
- color: Color.mOnSurfaceVariant
- }
-
- NText {
- text: playerSelectorButton.currentPlayer ? playerSelectorButton.currentPlayer.identity : ""
- pointSize: Style.fontSizeXS
- color: Color.mOnSurfaceVariant
- Layout.fillWidth: true
- }
- }
-
- MouseArea {
- id: playerSelectorMouseArea
- anchors.fill: parent
- hoverEnabled: true
- cursorShape: Qt.PointingHandCursor
-
- onClicked: {
- var menuItems = []
- var players = MediaService.getAvailablePlayers()
- for (var i = 0; i < players.length; i++) {
- menuItems.push({
- "label": players[i].identity,
- "action": i.toString(),
- "icon": "disc",
- "enabled": true,
- "visible": true
- })
- }
- playerContextMenu.model = menuItems
- playerContextMenu.openAtItem(playerSelectorButton, playerSelectorButton.width - playerContextMenu.width, playerSelectorButton.height)
- }
- }
-
- NContextMenu {
- id: playerContextMenu
- parent: root
- width: 200
-
- onTriggered: function (action) {
- var index = parseInt(action)
- if (!isNaN(index)) {
- MediaService.switchToPlayer(index)
- }
- }
- }
- }
-
- ColumnLayout {
- anchors.fill: parent
- anchors.margins: Style.marginM
-
- // No media player detected
- ColumnLayout {
- id: fallback
-
- visible: !main.visible
- spacing: Style.marginS
-
- Item {
- Layout.fillWidth: true
- Layout.fillHeight: true
- }
-
- Item {
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- ColumnLayout {
- anchors.centerIn: parent
- spacing: Style.marginL
-
- Item {
- Layout.alignment: Qt.AlignHCenter
- Layout.preferredWidth: Style.fontSizeXXXL * 4
- Layout.preferredHeight: Style.fontSizeXXXL * 4
-
- // Pulsating audio circles (background)
- Repeater {
- model: 3
- Rectangle {
- anchors.centerIn: parent
- width: parent.width * (1.0 + index * 0.2)
- height: width
- radius: width / 2
- color: "transparent"
- border.color: Color.mOnSurfaceVariant
- border.width: 2
- opacity: 0
-
- SequentialAnimation on opacity {
- running: true
- loops: Animation.Infinite
- PauseAnimation {
- duration: index * 600
- }
- NumberAnimation {
- from: 1.0
- to: 0
- duration: 2000
- easing.type: Easing.OutQuad
- }
- }
-
- SequentialAnimation on scale {
- running: true
- loops: Animation.Infinite
- PauseAnimation {
- duration: index * 600
- }
- NumberAnimation {
- from: 0.5
- to: 1.2
- duration: 2000
- easing.type: Easing.OutQuad
- }
- }
- }
- }
-
- // Spinning disc
- NIcon {
- anchors.centerIn: parent
- icon: "disc"
- pointSize: Style.fontSizeXXXL * 3
- color: Color.mOnSurfaceVariant
-
- RotationAnimator on rotation {
- from: 0
- to: 360
- duration: 8000
- loops: Animation.Infinite
- running: true
- }
- }
- }
-
- // Descriptive text
- ColumnLayout {
- Layout.alignment: Qt.AlignHCenter
- spacing: Style.marginXS
- }
- }
- }
-
- Item {
- Layout.fillWidth: true
- Layout.fillHeight: true
- }
- }
-
- // MediaPlayer Main Content
- ColumnLayout {
- id: main
-
- visible: MediaService.currentPlayer && MediaService.canPlay
- spacing: Style.marginS
-
- // Spacer to push content down
- Item {
- Layout.preferredHeight: Style.marginM
- }
-
- // Metadata at the bottom left
- ColumnLayout {
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignLeft
- spacing: Style.marginXS
-
- NText {
- visible: MediaService.trackTitle !== ""
- text: MediaService.trackTitle
- pointSize: Style.fontSizeL
- font.weight: Style.fontWeightBold
- elide: Text.ElideRight
- wrapMode: Text.Wrap
- maximumLineCount: 2
- Layout.fillWidth: true
- }
-
- NText {
- visible: MediaService.trackArtist !== ""
- text: MediaService.trackArtist
- color: Color.mPrimary
- pointSize: Style.fontSizeS
- elide: Text.ElideRight
- Layout.fillWidth: true
- }
-
- NText {
- visible: MediaService.trackAlbum !== ""
- text: MediaService.trackAlbum
- color: Color.mOnSurfaceVariant
- pointSize: Style.fontSizeM
- elide: Text.ElideRight
- Layout.fillWidth: true
- }
- }
-
- // Progress slider
- Item {
- id: progressWrapper
- visible: (MediaService.currentPlayer && MediaService.trackLength > 0)
- Layout.fillWidth: true
- height: Style.baseWidgetSize * 0.5
-
- property real localSeekRatio: -1
- property real lastSentSeekRatio: -1
- property real seekEpsilon: 0.01
- property real progressRatio: {
- if (!MediaService.currentPlayer || MediaService.trackLength <= 0)
- return 0
- const r = MediaService.currentPosition / MediaService.trackLength
- if (isNaN(r) || !isFinite(r))
- return 0
- return Math.max(0, Math.min(1, r))
- }
- property real effectiveRatio: (MediaService.isSeeking && localSeekRatio >= 0) ? Math.max(0, Math.min(1, localSeekRatio)) : progressRatio
-
- Timer {
- id: seekDebounce
- interval: 75
- repeat: false
- onTriggered: {
- if (MediaService.isSeeking && progressWrapper.localSeekRatio >= 0) {
- const next = Math.max(0, Math.min(1, progressWrapper.localSeekRatio))
- if (progressWrapper.lastSentSeekRatio < 0 || Math.abs(next - progressWrapper.lastSentSeekRatio) >= progressWrapper.seekEpsilon) {
- MediaService.seekByRatio(next)
- progressWrapper.lastSentSeekRatio = next
- }
- }
- }
- }
-
- NSlider {
- id: progressSlider
- anchors.fill: parent
- from: 0
- to: 1
- stepSize: 0
- snapAlways: false
- enabled: MediaService.trackLength > 0 && MediaService.canSeek
- heightRatio: 0.6
-
- onMoved: {
- progressWrapper.localSeekRatio = value
- seekDebounce.restart()
- }
- onPressedChanged: {
- if (pressed) {
- MediaService.isSeeking = true
- progressWrapper.localSeekRatio = value
- MediaService.seekByRatio(value)
- progressWrapper.lastSentSeekRatio = value
- } else {
- seekDebounce.stop()
- MediaService.seekByRatio(value)
- MediaService.isSeeking = false
- progressWrapper.localSeekRatio = -1
- progressWrapper.lastSentSeekRatio = -1
- }
- }
- }
-
- Binding {
- target: progressSlider
- property: "value"
- value: progressWrapper.progressRatio
- when: !MediaService.isSeeking
- }
- }
-
- // Spacer to push media controls down
- Item {
- Layout.preferredHeight: Style.marginL
- }
-
- // Media controls
- RowLayout {
- spacing: Style.marginS
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignHCenter
-
- NIconButton {
- icon: "media-prev"
- visible: MediaService.canGoPrevious
- onClicked: MediaService.canGoPrevious ? MediaService.previous() : {}
- }
-
- NIconButton {
- icon: MediaService.isPlaying ? "media-pause" : "media-play"
- visible: (MediaService.canPlay || MediaService.canPause)
- onClicked: (MediaService.canPlay || MediaService.canPause) ? MediaService.playPause() : {}
- }
-
- NIconButton {
- icon: "media-next"
- visible: MediaService.canGoNext
- onClicked: MediaService.canGoNext ? MediaService.next() : {}
- }
- }
- }
- }
-}
diff --git a/Modules/ControlCenter/Widgets/Notifications.qml b/Modules/ControlCenter/Widgets/Notifications.qml
deleted file mode 100644
index ae291517..00000000
--- a/Modules/ControlCenter/Widgets/Notifications.qml
+++ /dev/null
@@ -1,15 +0,0 @@
-import QtQuick.Layouts
-import Quickshell
-import qs.Commons
-import qs.Services
-import qs.Widgets
-
-NIconButtonHot {
- property ShellScreen screen
-
- icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell"
- hot: Settings.data.notifications.doNotDisturb
- tooltipText: I18n.tr("quickSettings.notifications.tooltip.action")
- onClicked: PanelService.getPanel("notificationHistoryPanel")?.toggle(this)
- onRightClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
-}
diff --git a/Modules/Dock/Dock.qml b/Modules/Dock/Dock.qml
index f0caacbd..fc6c1614 100644
--- a/Modules/Dock/Dock.qml
+++ b/Modules/Dock/Dock.qml
@@ -6,547 +6,566 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
-Variants {
- model: Quickshell.screens
+Loader {
- delegate: Item {
- id: root
+ active: Settings.data.dock.enabled
+ sourceComponent: Variants {
+ model: Quickshell.screens
- required property ShellScreen modelData
+ delegate: Item {
+ id: root
- property bool barIsReady: modelData ? BarService.isBarReady(modelData.name) : false
+ required property ShellScreen modelData
- Connections {
- target: BarService
- function onBarReadyChanged(screenName) {
- if (screenName === modelData.name) {
- barIsReady = true
+ property bool barIsReady: modelData ? BarService.isBarReady(modelData.name) : false
+
+ Connections {
+ target: BarService
+ function onBarReadyChanged(screenName) {
+ if (screenName === modelData.name) {
+ barIsReady = true
+ }
}
}
- }
- // Update dock apps when toplevels change
- Connections {
- target: ToplevelManager ? ToplevelManager.toplevels : null
- function onValuesChanged() {
- updateDockApps()
- }
- }
-
- // Update dock apps when pinned apps change
- Connections {
- target: Settings.data.dock
- function onPinnedAppsChanged() {
- updateDockApps()
- }
- function onOnlySameOutputChanged() {
- updateDockApps()
- }
- }
-
- // Initial update when component is ready
- Component.onCompleted: {
- if (ToplevelManager) {
- updateDockApps()
- }
- }
-
- // Shared properties between peek and dock windows
- readonly property string displayMode: Settings.data.dock.displayMode
- readonly property bool autoHide: displayMode === "auto_hide"
- readonly property bool exclusive: displayMode === "exclusive"
- readonly property int hideDelay: 500
- readonly property int showDelay: 100
- readonly property int hideAnimationDuration: Style.animationFast
- readonly property int showAnimationDuration: Style.animationFast
- readonly property int peekHeight: 1
- readonly property int iconSize: Math.round(12 + 24 * (Settings.data.dock.size ?? 1))
- readonly property int floatingMargin: Settings.data.dock.floatingRatio * Style.marginL
-
- // Bar detection and positioning properties
- readonly property bool hasBar: modelData && modelData.name ? (Settings.data.bar.monitors.includes(modelData.name) || (Settings.data.bar.monitors.length === 0)) : false
- readonly property bool barAtBottom: hasBar && Settings.data.bar.position === "bottom"
- readonly property int barHeight: Style.barHeight
-
- // Shared state between windows
- property bool dockHovered: false
- property bool anyAppHovered: false
- property bool menuHovered: false
- property bool hidden: autoHide
- property bool peekHovered: false
-
- // Separate property to control Loader - stays true during animations
- property bool dockLoaded: !autoHide // Start loaded if autoHide is off
-
- // Track the currently open context menu
- property var currentContextMenu: null
-
- // Combined model of running apps and pinned apps
- property var dockApps: []
-
- // Function to close any open context menu
- function closeAllContextMenus() {
- if (currentContextMenu && currentContextMenu.visible) {
- currentContextMenu.hide()
- }
- }
-
- // Function to update the combined dock apps model
- function updateDockApps() {
- const runningApps = ToplevelManager ? (ToplevelManager.toplevels.values || []) : []
- const pinnedApps = Settings.data.dock.pinnedApps || []
- const combined = []
- const processedAppIds = new Set()
-
- // Strategy: Maintain app positions as much as possible
- // 1. First pass: Add all running apps (both pinned and non-pinned) in their current order
- runningApps.forEach(toplevel => {
- if (toplevel && toplevel.appId && !(Settings.data.dock.onlySameOutput && toplevel.screens && !toplevel.screens.includes(modelData))) {
- const isPinned = pinnedApps.includes(toplevel.appId)
- const appType = isPinned ? "pinned-running" : "running"
-
- combined.push({
- "type": appType,
- "toplevel": toplevel,
- "appId": toplevel.appId,
- "title": toplevel.title
- })
- processedAppIds.add(toplevel.appId)
- }
- })
-
- // 2. Second pass: Add non-running pinned apps at the end
- pinnedApps.forEach(pinnedAppId => {
- if (!processedAppIds.has(pinnedAppId)) {
- // Pinned app that is not running
- combined.push({
- "type": "pinned",
- "toplevel": null,
- "appId": pinnedAppId,
- "title": pinnedAppId
- })
- }
- })
-
- dockApps = combined
- }
-
- // Timer to unload dock after hide animation completes
- Timer {
- id: unloadTimer
- interval: hideAnimationDuration + 50 // Add small buffer
- onTriggered: {
- if (hidden && autoHide) {
- dockLoaded = false
+ // Update dock apps when toplevels change
+ Connections {
+ target: ToplevelManager ? ToplevelManager.toplevels : null
+ function onValuesChanged() {
+ updateDockApps()
}
}
- }
- // Timer for auto-hide delay
- Timer {
- id: hideTimer
- interval: hideDelay
- onTriggered: {
- if (autoHide && !dockHovered && !anyAppHovered && !peekHovered && !menuHovered) {
+ // Update dock apps when pinned apps change
+ Connections {
+ target: Settings.data.dock
+ function onPinnedAppsChanged() {
+ updateDockApps()
+ }
+ function onOnlySameOutputChanged() {
+ updateDockApps()
+ }
+ }
+
+ // Initial update when component is ready
+ Component.onCompleted: {
+ if (ToplevelManager) {
+ updateDockApps()
+ }
+ }
+
+ // Shared properties between peek and dock windows
+ readonly property string displayMode: Settings.data.dock.displayMode
+ readonly property bool autoHide: displayMode === "auto_hide"
+ readonly property bool exclusive: displayMode === "exclusive"
+ readonly property int hideDelay: 500
+ readonly property int showDelay: 100
+ readonly property int hideAnimationDuration: Style.animationFast
+ readonly property int showAnimationDuration: Style.animationFast
+ readonly property int peekHeight: 1
+ readonly property int iconSize: Math.round(12 + 24 * (Settings.data.dock.size ?? 1))
+ readonly property int floatingMargin: Settings.data.dock.floatingRatio * Style.marginL
+
+ // Bar detection and positioning properties
+ readonly property bool hasBar: modelData && modelData.name ? (Settings.data.bar.monitors.includes(modelData.name) || (Settings.data.bar.monitors.length === 0)) : false
+ readonly property bool barAtBottom: hasBar && Settings.data.bar.position === "bottom"
+ readonly property int barHeight: Style.barHeight
+
+ // Shared state between windows
+ property bool dockHovered: false
+ property bool anyAppHovered: false
+ property bool menuHovered: false
+ property bool hidden: autoHide
+ property bool peekHovered: false
+
+ // Separate property to control Loader - stays true during animations
+ property bool dockLoaded: !autoHide // Start loaded if autoHide is off
+
+ // Track the currently open context menu
+ property var currentContextMenu: null
+
+ // Combined model of running apps and pinned apps
+ property var dockApps: []
+
+ // Function to close any open context menu
+ function closeAllContextMenus() {
+ if (currentContextMenu && currentContextMenu.visible) {
+ currentContextMenu.hide()
+ }
+ }
+
+ // Function to update the combined dock apps model
+ function updateDockApps() {
+ const runningApps = ToplevelManager ? (ToplevelManager.toplevels.values || []) : []
+ const pinnedApps = Settings.data.dock.pinnedApps || []
+ const combined = []
+ const processedAppIds = new Set()
+
+ // Strategy: Maintain app positions as much as possible
+ // 1. First pass: Add all running apps (both pinned and non-pinned) in their current order
+ runningApps.forEach(toplevel => {
+ if (toplevel && toplevel.appId && !(Settings.data.dock.onlySameOutput && toplevel.screens && !toplevel.screens.includes(modelData))) {
+ const isPinned = pinnedApps.includes(toplevel.appId)
+ const appType = isPinned ? "pinned-running" : "running"
+
+ combined.push({
+ "type": appType,
+ "toplevel": toplevel,
+ "appId": toplevel.appId,
+ "title": toplevel.title
+ })
+ processedAppIds.add(toplevel.appId)
+ }
+ })
+
+ // 2. Second pass: Add non-running pinned apps at the end
+ pinnedApps.forEach(pinnedAppId => {
+ if (!processedAppIds.has(pinnedAppId)) {
+ // Pinned app that is not running
+ combined.push({
+ "type": "pinned",
+ "toplevel": null,
+ "appId": pinnedAppId,
+ "title": pinnedAppId
+ })
+ }
+ })
+
+ dockApps = combined
+ }
+
+ // Timer to unload dock after hide animation completes
+ Timer {
+ id: unloadTimer
+ interval: hideAnimationDuration + 50 // Add small buffer
+ onTriggered: {
+ if (hidden && autoHide) {
+ dockLoaded = false
+ }
+ }
+ }
+
+ // Timer for auto-hide delay
+ Timer {
+ id: hideTimer
+ interval: hideDelay
+ onTriggered: {
+ // Force menuHovered to false if no menu is current or visible
+ if (!root.currentContextMenu || !root.currentContextMenu.visible) {
+ menuHovered = false
+ }
+ if (autoHide && !dockHovered && !anyAppHovered && !peekHovered && !menuHovered) {
+ hidden = true
+ unloadTimer.restart() // Start unload timer when hiding
+ } else if (autoHide && !dockHovered && !peekHovered) {
+ // Restart timer if menu is closing (handles race condition)
+ restart()
+ }
+ }
+ }
+
+ // Timer for show delay
+ Timer {
+ id: showTimer
+ interval: showDelay
+ onTriggered: {
+ if (autoHide) {
+ dockLoaded = true // Load dock immediately
+ hidden = false // Then trigger show animation
+ unloadTimer.stop() // Cancel any pending unload
+ }
+ }
+ }
+
+ // Watch for autoHide setting changes
+ onAutoHideChanged: {
+ if (!autoHide) {
+ hidden = false
+ dockLoaded = true
+ hideTimer.stop()
+ showTimer.stop()
+ unloadTimer.stop()
+ } else {
hidden = true
- unloadTimer.restart() // Start unload timer when hiding
+ unloadTimer.restart() // Schedule unload after animation
}
}
- }
- // Timer for show delay
- Timer {
- id: showTimer
- interval: showDelay
- onTriggered: {
- if (autoHide) {
- dockLoaded = true // Load dock immediately
- hidden = false // Then trigger show animation
- unloadTimer.stop() // Cancel any pending unload
- }
- }
- }
+ // PEEK WINDOW - Always visible when auto-hide is enabled
+ Loader {
+ active: (barIsReady || !hasBar) && modelData && (Settings.data.dock.monitors.length === 0 || Settings.data.dock.monitors.includes(modelData.name)) && autoHide
- // Watch for autoHide setting changes
- onAutoHideChanged: {
- if (!autoHide) {
- hidden = false
- dockLoaded = true
- hideTimer.stop()
- showTimer.stop()
- unloadTimer.stop()
- } else {
- hidden = true
- unloadTimer.restart() // Schedule unload after animation
- }
- }
+ sourceComponent: PanelWindow {
+ id: peekWindow
- // PEEK WINDOW - Always visible when auto-hide is enabled
- Loader {
- active: Settings.data.dock.enabled && (barIsReady || !hasBar) && modelData && (Settings.data.dock.monitors.length === 0 || Settings.data.dock.monitors.includes(modelData.name)) && autoHide
+ screen: modelData
+ anchors.bottom: true
+ anchors.left: true
+ anchors.right: true
+ focusable: false
+ color: Color.transparent
- sourceComponent: PanelWindow {
- id: peekWindow
+ WlrLayershell.namespace: "noctalia-dock-peek-" + (screen?.name || "unknown")
+ WlrLayershell.exclusionMode: ExclusionMode.Ignore
+ implicitHeight: peekHeight
- screen: modelData
- anchors.bottom: true
- anchors.left: true
- anchors.right: true
- focusable: false
- color: Color.transparent
+ MouseArea {
+ id: peekArea
+ anchors.fill: parent
+ hoverEnabled: true
- WlrLayershell.namespace: "noctalia-dock-peek"
- WlrLayershell.exclusionMode: ExclusionMode.Ignore
- implicitHeight: peekHeight
-
- MouseArea {
- id: peekArea
- anchors.fill: parent
- hoverEnabled: true
-
- onEntered: {
- peekHovered = true
- if (hidden) {
- showTimer.start()
- }
- }
-
- onExited: {
- peekHovered = false
- if (!hidden && !dockHovered && !anyAppHovered && !menuHovered) {
- hideTimer.restart()
- }
- }
- }
- }
- }
-
- // DOCK WINDOW
- Loader {
- active: Settings.data.dock.enabled && (barIsReady || !hasBar) && modelData && (Settings.data.dock.monitors.length === 0 || Settings.data.dock.monitors.includes(modelData.name)) && dockLoaded && ToplevelManager && (dockApps.length > 0)
-
- sourceComponent: PanelWindow {
- id: dockWindow
-
- screen: modelData
-
- focusable: false
- color: Color.transparent
-
- WlrLayershell.namespace: "noctalia-dock-main"
- WlrLayershell.exclusionMode: exclusive ? ExclusionMode.Auto : ExclusionMode.Ignore
-
- // Size to fit the dock container exactly
- implicitWidth: dockContainerWrapper.width
- implicitHeight: dockContainerWrapper.height
-
- // Position above the bar if it's at bottom
- anchors.bottom: true
-
- margins.bottom: {
- switch (Settings.data.bar.position) {
- case "bottom":
- return (Style.barHeight + Style.marginM) + (Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL + floatingMargin : floatingMargin)
- default:
- return floatingMargin
- }
- }
-
- // Wrapper item for scale/opacity animations
- Item {
- id: dockContainerWrapper
- width: dockContainer.width
- height: dockContainer.height
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: parent.bottom
-
- // Apply animations to this wrapper
- opacity: hidden ? 0 : 1
- scale: hidden ? 0.85 : 1
-
- Behavior on opacity {
- NumberAnimation {
- duration: hidden ? hideAnimationDuration : showAnimationDuration
- easing.type: Easing.InOutQuad
- }
- }
-
- Behavior on scale {
- NumberAnimation {
- duration: hidden ? hideAnimationDuration : showAnimationDuration
- easing.type: hidden ? Easing.InQuad : Easing.OutBack
- easing.overshoot: hidden ? 0 : 1.05
- }
- }
-
- Rectangle {
- id: dockContainer
- width: dockLayout.implicitWidth + Style.marginM * 2
- height: Math.round(iconSize * 1.5)
- color: Qt.alpha(Color.mSurface, Settings.data.dock.backgroundOpacity)
- anchors.centerIn: parent
- radius: Style.radiusL
- border.width: Style.borderS
- border.color: Qt.alpha(Color.mOutline, Settings.data.dock.backgroundOpacity)
-
- MouseArea {
- id: dockMouseArea
- anchors.fill: parent
- hoverEnabled: true
-
- onEntered: {
- dockHovered = true
- if (autoHide) {
- showTimer.stop()
- hideTimer.stop()
- unloadTimer.stop() // Cancel unload if hovering
- }
- }
-
- onExited: {
- dockHovered = false
- if (autoHide && !anyAppHovered && !peekHovered && !menuHovered) {
- hideTimer.restart()
- }
- }
-
- onClicked: {
- // Close any open context menu when clicking on the dock background
- closeAllContextMenus()
+ onEntered: {
+ peekHovered = true
+ if (hidden) {
+ showTimer.start()
}
}
- Item {
- id: dock
- width: dockLayout.implicitWidth
- height: parent.height - (Style.marginM * 2)
+ onExited: {
+ peekHovered = false
+ if (!hidden && !dockHovered && !anyAppHovered && !menuHovered) {
+ hideTimer.restart()
+ }
+ }
+ }
+ }
+ }
+
+ // DOCK WINDOW
+ Loader {
+ id: dockWindowLoader
+ active: Settings.data.dock.enabled && (barIsReady || !hasBar) && modelData && (Settings.data.dock.monitors.length === 0 || Settings.data.dock.monitors.includes(modelData.name)) && dockLoaded && ToplevelManager && (dockApps.length > 0)
+
+ sourceComponent: PanelWindow {
+ id: dockWindow
+
+ screen: modelData
+
+ focusable: false
+ color: Color.transparent
+
+ WlrLayershell.namespace: "noctalia-dock-" + (screen?.name || "unknown")
+ WlrLayershell.exclusionMode: exclusive ? ExclusionMode.Auto : ExclusionMode.Ignore
+
+ // Size to fit the dock container exactly
+ implicitWidth: dockContainerWrapper.width
+ implicitHeight: dockContainerWrapper.height
+
+ // Position above the bar if it's at bottom
+ anchors.bottom: true
+
+ margins.bottom: {
+ switch (Settings.data.bar.position) {
+ case "bottom":
+ return (Style.barHeight + Style.marginM) + (Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL + floatingMargin : floatingMargin)
+ default:
+ return floatingMargin
+ }
+ }
+
+ // Wrapper item for scale/opacity animations
+ Item {
+ id: dockContainerWrapper
+ width: dockContainer.width
+ height: dockContainer.height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+
+ // Apply animations to this wrapper
+ opacity: hidden ? 0 : 1
+ scale: hidden ? 0.85 : 1
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: hidden ? hideAnimationDuration : showAnimationDuration
+ easing.type: Easing.InOutQuad
+ }
+ }
+
+ Behavior on scale {
+ NumberAnimation {
+ duration: hidden ? hideAnimationDuration : showAnimationDuration
+ easing.type: hidden ? Easing.InQuad : Easing.OutBack
+ easing.overshoot: hidden ? 0 : 1.05
+ }
+ }
+
+ Rectangle {
+ id: dockContainer
+ width: dockLayout.implicitWidth + Style.marginM * 2
+ height: Math.round(iconSize * 1.5)
+ color: Qt.alpha(Color.mSurface, Settings.data.dock.backgroundOpacity)
anchors.centerIn: parent
+ radius: Style.radiusL
+ border.width: Style.borderS
+ border.color: Qt.alpha(Color.mOutline, Settings.data.dock.backgroundOpacity)
- function getAppIcon(appData): string {
- if (!appData || !appData.appId)
- return ""
- return ThemeIcons.iconForAppId(appData.appId?.toLowerCase())
+ // Enable layer caching to reduce GPU usage from continuous animations
+ // (pulse animations on active indicators run infinitely)
+ layer.enabled: true
+
+ MouseArea {
+ id: dockMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+
+ onEntered: {
+ dockHovered = true
+ if (autoHide) {
+ showTimer.stop()
+ hideTimer.stop()
+ unloadTimer.stop() // Cancel unload if hovering
+ }
+ }
+
+ onExited: {
+ dockHovered = false
+ if (autoHide && !anyAppHovered && !peekHovered && !menuHovered) {
+ hideTimer.restart()
+ }
+ }
+
+ onClicked: {
+ // Close any open context menu when clicking on the dock background
+ closeAllContextMenus()
+ }
}
- RowLayout {
- id: dockLayout
- spacing: Style.marginM
- Layout.preferredHeight: parent.height
+ Item {
+ id: dock
+ width: dockLayout.implicitWidth
+ height: parent.height - (Style.marginM * 2)
anchors.centerIn: parent
- Repeater {
- model: dockApps
+ function getAppIcon(appData): string {
+ if (!appData || !appData.appId)
+ return ""
+ return ThemeIcons.iconForAppId(appData.appId?.toLowerCase())
+ }
- delegate: Item {
- id: appButton
- Layout.preferredWidth: iconSize
- Layout.preferredHeight: iconSize
- Layout.alignment: Qt.AlignCenter
+ RowLayout {
+ id: dockLayout
+ spacing: Style.marginM
+ Layout.preferredHeight: parent.height
+ anchors.centerIn: parent
- property bool isActive: modelData.toplevel && ToplevelManager.activeToplevel && ToplevelManager.activeToplevel === modelData.toplevel
- property bool hovered: appMouseArea.containsMouse
- property string appId: modelData ? modelData.appId : ""
- property string appTitle: modelData ? (modelData.title || modelData.appId) : ""
- property bool isRunning: modelData && (modelData.type === "running" || modelData.type === "pinned-running")
+ Repeater {
+ model: dockApps
- // Listen for the toplevel being closed
- Connections {
- target: modelData?.toplevel
- function onClosed() {
- Qt.callLater(root.updateDockApps)
- }
- }
+ delegate: Item {
+ id: appButton
+ Layout.preferredWidth: iconSize
+ Layout.preferredHeight: iconSize
+ Layout.alignment: Qt.AlignCenter
- Image {
- id: appIcon
- width: iconSize
- height: iconSize
- anchors.centerIn: parent
- source: dock.getAppIcon(modelData)
- visible: source.toString() !== ""
- sourceSize.width: iconSize * 2
- sourceSize.height: iconSize * 2
- smooth: true
- mipmap: true
- antialiasing: true
- fillMode: Image.PreserveAspectFit
- cache: true
+ property bool isActive: modelData.toplevel && ToplevelManager.activeToplevel && ToplevelManager.activeToplevel === modelData.toplevel
+ property bool hovered: appMouseArea.containsMouse
+ property string appId: modelData ? modelData.appId : ""
+ property string appTitle: modelData ? (modelData.title || modelData.appId) : ""
+ property bool isRunning: modelData && (modelData.type === "running" || modelData.type === "pinned-running")
- // Dim pinned apps that aren't running
- opacity: appButton.isRunning ? 1.0 : 0.6
-
- scale: appButton.hovered ? 1.15 : 1.0
-
- // Apply dock-specific colorization shader only to non-focused apps
- layer.enabled: !appButton.isActive && Settings.data.dock.colorizeIcons
- layer.effect: ShaderEffect {
- property color targetColor: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mSurfaceVariant
- property real colorizeMode: 0.0 // Dock mode (grayscale)
-
- fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb")
- }
-
- Behavior on scale {
- NumberAnimation {
- duration: Style.animationNormal
- easing.type: Easing.OutBack
- easing.overshoot: 1.2
+ // Listen for the toplevel being closed
+ Connections {
+ target: modelData?.toplevel
+ function onClosed() {
+ Qt.callLater(root.updateDockApps)
}
}
- Behavior on opacity {
- NumberAnimation {
- duration: Style.animationFast
- easing.type: Easing.OutQuad
+ Image {
+ id: appIcon
+ width: iconSize
+ height: iconSize
+ anchors.centerIn: parent
+ source: dock.getAppIcon(modelData)
+ visible: source.toString() !== ""
+ sourceSize.width: iconSize * 2
+ sourceSize.height: iconSize * 2
+ smooth: true
+ mipmap: true
+ antialiasing: true
+ fillMode: Image.PreserveAspectFit
+ cache: true
+
+ // Dim pinned apps that aren't running
+ opacity: appButton.isRunning ? 1.0 : 0.6
+
+ scale: appButton.hovered ? 1.15 : 1.0
+
+ // Apply dock-specific colorization shader only to non-focused apps
+ layer.enabled: !appButton.isActive && Settings.data.dock.colorizeIcons
+ layer.effect: ShaderEffect {
+ property color targetColor: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mSurfaceVariant
+ property real colorizeMode: 0.0 // Dock mode (grayscale)
+
+ fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb")
}
- }
- }
- // Fall back if no icon
- NIcon {
- anchors.centerIn: parent
- visible: !appIcon.visible
- icon: "question-mark"
- pointSize: iconSize * 0.7
- color: appButton.isActive ? Color.mPrimary : Color.mOnSurfaceVariant
- opacity: appButton.isRunning ? 1.0 : 0.6
- scale: appButton.hovered ? 1.15 : 1.0
+ Behavior on scale {
+ NumberAnimation {
+ duration: Style.animationNormal
+ easing.type: Easing.OutBack
+ easing.overshoot: 1.2
+ }
+ }
- Behavior on scale {
- NumberAnimation {
- duration: Style.animationFast
- easing.type: Easing.OutBack
- easing.overshoot: 1.2
+ Behavior on opacity {
+ NumberAnimation {
+ duration: Style.animationFast
+ easing.type: Easing.OutQuad
+ }
}
}
- Behavior on opacity {
- NumberAnimation {
- duration: Style.animationFast
- easing.type: Easing.OutQuad
- }
- }
- }
+ // Fall back if no icon
+ NIcon {
+ anchors.centerIn: parent
+ visible: !appIcon.visible
+ icon: "question-mark"
+ pointSize: iconSize * 0.7
+ color: appButton.isActive ? Color.mPrimary : Color.mOnSurfaceVariant
+ opacity: appButton.isRunning ? 1.0 : 0.6
+ scale: appButton.hovered ? 1.15 : 1.0
- // Context menu popup
- DockMenu {
- id: contextMenu
- onHoveredChanged: menuHovered = hovered
- onRequestClose: {
- contextMenu.hide()
- // Restart hide timer after menu action if auto-hide is enabled
- if (autoHide && !dockHovered && !anyAppHovered && !peekHovered) {
- hideTimer.restart()
+ Behavior on scale {
+ NumberAnimation {
+ duration: Style.animationFast
+ easing.type: Easing.OutBack
+ easing.overshoot: 1.2
+ }
+ }
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: Style.animationFast
+ easing.type: Easing.OutQuad
+ }
}
}
- onAppClosed: root.updateDockApps // Force immediate dock update when app is closed
- onVisibleChanged: {
- if (visible) {
- root.currentContextMenu = contextMenu
- } else if (root.currentContextMenu === contextMenu) {
- root.currentContextMenu = null
- // Reset menu hover state when menu becomes invisible
- menuHovered = false
- // Restart hide timer if conditions are met
- if (autoHide && !dockHovered && !anyAppHovered && !peekHovered) {
+
+ // Context menu popup
+ DockMenu {
+ id: contextMenu
+ onHoveredChanged: {
+ // Only update menuHovered if this menu is current and visible
+ if (root.currentContextMenu === contextMenu && contextMenu.visible) {
+ menuHovered = hovered
+ } else {
+ menuHovered = false
+ }
+ }
+
+ Connections {
+ target: contextMenu
+ function onRequestClose() {
+ // Clear current menu immediately to prevent hover updates
+ root.currentContextMenu = null
+ hideTimer.stop()
+ contextMenu.hide()
+ menuHovered = false
+ anyAppHovered = false
+ }
+ }
+ onAppClosed: root.updateDockApps // Force immediate dock update when app is closed
+ onVisibleChanged: {
+ if (visible) {
+ root.currentContextMenu = contextMenu
+ anyAppHovered = false
+ } else if (root.currentContextMenu === contextMenu) {
+ root.currentContextMenu = null
+ hideTimer.stop()
+ menuHovered = false
+ anyAppHovered = false
+ // Restart hide timer after menu closes
+ if (autoHide && !dockHovered && !anyAppHovered && !peekHovered && !menuHovered) {
+ hideTimer.restart()
+ }
+ }
+ }
+ }
+
+ MouseArea {
+ id: appMouseArea
+ objectName: "appMouseArea"
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
+
+ onEntered: {
+ anyAppHovered = true
+ const appName = appButton.appTitle || appButton.appId || "Unknown"
+ const tooltipText = appName.length > 40 ? appName.substring(0, 37) + "..." : appName
+ if (!contextMenu.visible) {
+ TooltipService.show(Screen, appButton, tooltipText, "top")
+ }
+ if (autoHide) {
+ showTimer.stop()
+ hideTimer.stop()
+ unloadTimer.stop() // Cancel unload if hovering app
+ }
+ }
+
+ onExited: {
+ anyAppHovered = false
+ TooltipService.hide()
+ // Clear menuHovered if no current menu or menu not visible
+ if (!root.currentContextMenu || !root.currentContextMenu.visible) {
+ menuHovered = false
+ }
+ if (autoHide && !dockHovered && !peekHovered && !menuHovered) {
hideTimer.restart()
}
}
- }
- }
- MouseArea {
- id: appMouseArea
- anchors.fill: parent
- hoverEnabled: true
- cursorShape: Qt.PointingHandCursor
- acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
-
- onEntered: {
- anyAppHovered = true
- const appName = appButton.appTitle || appButton.appId || "Unknown"
- const tooltipText = appName.length > 40 ? appName.substring(0, 37) + "..." : appName
- if (!contextMenu.visible) {
- TooltipService.show(Screen, appButton, tooltipText, "top")
- }
- if (autoHide) {
- showTimer.stop()
- hideTimer.stop()
- unloadTimer.stop() // Cancel unload if hovering app
- }
- }
-
- onExited: {
- anyAppHovered = false
- TooltipService.hide()
- if (autoHide && !dockHovered && !peekHovered && !menuHovered) {
- hideTimer.restart()
- }
- }
-
- onClicked: function (mouse) {
- if (mouse.button === Qt.RightButton) {
- // If right-clicking on the same app with an open context menu, close it
- if (root.currentContextMenu === contextMenu && contextMenu.visible) {
+ onClicked: function (mouse) {
+ if (mouse.button === Qt.RightButton) {
+ // If right-clicking on the same app with an open context menu, close it
+ if (root.currentContextMenu === contextMenu && contextMenu.visible) {
+ root.closeAllContextMenus()
+ return
+ }
+ // Close any other existing context menu first
root.closeAllContextMenus()
+ // Hide tooltip when showing context menu
+ TooltipService.hideImmediately()
+ contextMenu.show(appButton, modelData.toplevel || modelData)
return
}
- // Close any other existing context menu first
+
+ // Close any existing context menu for non-right-click actions
root.closeAllContextMenus()
- // Hide tooltip when showing context menu
- TooltipService.hideImmediately()
- contextMenu.show(appButton, modelData.toplevel || modelData)
- return
- }
- // Close any existing context menu for non-right-click actions
- root.closeAllContextMenus()
+ // Check if toplevel is still valid (not a stale reference)
+ const isValidToplevel = modelData?.toplevel && ToplevelManager && ToplevelManager.toplevels.values.includes(modelData.toplevel)
- // Check if toplevel is still valid (not a stale reference)
- const isValidToplevel = modelData?.toplevel && ToplevelManager && ToplevelManager.toplevels.values.includes(modelData.toplevel)
-
- if (mouse.button === Qt.MiddleButton && isValidToplevel && modelData.toplevel.close) {
- modelData.toplevel.close()
- Qt.callLater(root.updateDockApps) // Force immediate dock update
- } else if (mouse.button === Qt.LeftButton) {
- if (isValidToplevel && modelData.toplevel.activate) {
- // Running app - activate it
- modelData.toplevel.activate()
- } else if (modelData?.appId) {
- // Pinned app not running - launch it
- Quickshell.execDetached(["gtk-launch", modelData.appId])
+ if (mouse.button === Qt.MiddleButton && isValidToplevel && modelData.toplevel.close) {
+ modelData.toplevel.close()
+ Qt.callLater(root.updateDockApps) // Force immediate dock update
+ } else if (mouse.button === Qt.LeftButton) {
+ if (isValidToplevel && modelData.toplevel.activate) {
+ // Running app - activate it
+ modelData.toplevel.activate()
+ } else if (modelData?.appId) {
+ // Pinned app not running - launch it
+ Quickshell.execDetached(["gtk-launch", modelData.appId])
+ }
}
}
}
- }
- // Active indicator
- Rectangle {
- visible: isActive
- width: iconSize * 0.2
- height: iconSize * 0.1
- color: Color.mPrimary
- radius: Style.radiusXS
- anchors.top: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
-
- // Pulse animation for active indicator
- SequentialAnimation on opacity {
- running: isActive
- loops: Animation.Infinite
- NumberAnimation {
- to: 0.6
- duration: Style.animationSlowest
- easing.type: Easing.InOutQuad
- }
- NumberAnimation {
- to: 1.0
- duration: Style.animationSlowest
- easing.type: Easing.InOutQuad
- }
+ // Active indicator
+ Rectangle {
+ visible: isActive
+ width: iconSize * 0.2
+ height: iconSize * 0.1
+ color: Color.mPrimary
+ radius: Style.radiusXS
+ anchors.top: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
}
}
}
diff --git a/Modules/Dock/DockMenu.qml b/Modules/Dock/DockMenu.qml
index 060caa44..ec2cb15f 100644
--- a/Modules/Dock/DockMenu.qml
+++ b/Modules/Dock/DockMenu.qml
@@ -5,7 +5,6 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Commons
-import qs.Services
import qs.Widgets
PopupWindow {
@@ -121,7 +120,6 @@ PopupWindow {
function show(item, toplevelData) {
if (!item) {
- Logger.w("DockMenu", "anchorItem is undefined, won't show menu.")
return
}
@@ -175,8 +173,6 @@ PopupWindow {
if (root.onAppClosed && typeof root.onAppClosed === "function") {
Qt.callLater(root.onAppClosed)
}
- } else {
- Logger.w("DockMenu", "Cannot close app - invalid toplevel reference")
}
root.hide()
root.requestClose()
diff --git a/Modules/LockScreen/LockScreen.qml b/Modules/LockScreen/LockScreen.qml
index 61c16980..ceb832f5 100644
--- a/Modules/LockScreen/LockScreen.qml
+++ b/Modules/LockScreen/LockScreen.qml
@@ -9,10 +9,13 @@ import Quickshell.Services.UPower
import Quickshell.Io
import Quickshell.Widgets
import qs.Commons
-import qs.Services
+import qs.Services.Hardware
+import qs.Services.Location
+import qs.Services.Media
+import qs.Services.Compositor
+import qs.Services.UI
import qs.Widgets
-import qs.Modules.Audio
-import qs.Modules.Bar.Calendar
+import qs.Widgets.AudioSpectrum
Loader {
id: lockScreen
@@ -57,7 +60,7 @@ Loader {
locked: lockScreen.active
WlSessionLockSurface {
- readonly property var now: Time.date
+ readonly property var now: Time.now
Item {
id: batteryIndicator
@@ -110,7 +113,7 @@ Loader {
anchors.fill: parent
visible: Settings.data.general.showScreenCorners
- property color cornerColor: Settings.data.general.forceBlackScreenCorners ? Qt.rgba(0, 0, 0, 1) : Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity)
+ property color cornerColor: Settings.data.general.forceBlackScreenCorners ? Color.black : Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity)
property real cornerRadius: Style.screenRadius
property real cornerSize: Style.screenRadius
@@ -348,9 +351,11 @@ Loader {
"es": "dddd, d 'de' MMMM",
"fr": "dddd d MMMM",
"pt": "dddd, d 'de' MMMM",
- "zh": "yyyy年M月d日 dddd"
+ "zh": "yyyy年M月d日 dddd",
+ "uk": "dddd, d MMMM",
+ "tr": "dddd, d MMMM"
}
- return I18n.locale.toString(Time.date, formats[lang] || "dddd, MMMM d")
+ return I18n.locale.toString(Time.now, formats[lang] || "dddd, MMMM d")
}
pointSize: Style.fontSizeXL
font.weight: Font.Medium
@@ -365,8 +370,9 @@ Loader {
}
// Clock
- ClockLoader {
- now: Time.date
+ NClock {
+ now: Time.now
+ clockStyle: Settings.data.location.analogClockInCalendar ? "analog" : "digital"
Layout.preferredWidth: 70
Layout.preferredHeight: 70
Layout.alignment: Qt.AlignVCenter
@@ -582,7 +588,7 @@ Loader {
anchors.margins: 4
active: Settings.data.audio.visualizerType === "linear"
z: 0
- sourceComponent: LinearSpectrum {
+ sourceComponent: NLinearSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
@@ -595,7 +601,7 @@ Loader {
anchors.margins: 4
active: Settings.data.audio.visualizerType === "mirrored"
z: 0
- sourceComponent: MirroredSpectrum {
+ sourceComponent: NMirroredSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
@@ -608,7 +614,7 @@ Loader {
anchors.margins: 4
active: Settings.data.audio.visualizerType === "wave"
z: 0
- sourceComponent: WaveSpectrum {
+ sourceComponent: NWaveSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
diff --git a/Modules/MainScreen/AllScreens.qml b/Modules/MainScreen/AllScreens.qml
new file mode 100644
index 00000000..3211a332
--- /dev/null
+++ b/Modules/MainScreen/AllScreens.qml
@@ -0,0 +1,87 @@
+import QtQuick
+import Quickshell
+import Quickshell.Wayland
+
+import qs.Commons
+import qs.Services.UI
+import qs.Modules.MainScreen
+
+// ------------------------------
+// MainScreen for each screen (manages bar + all panels)
+// Wrapped in Loader to optimize memory - only loads when screen needs it
+Variants {
+ model: Quickshell.screens
+ delegate: Item {
+ required property ShellScreen modelData
+
+ property bool shouldBeActive: {
+ if (!modelData || !modelData.name) {
+ return false
+ }
+ Logger.d("Shell", "MainScreen activated for", modelData?.name)
+ return true
+ }
+
+ property bool windowLoaded: false
+
+ Loader {
+ id: windowLoader
+ active: parent.shouldBeActive
+ asynchronous: false
+
+ property ShellScreen loaderScreen: modelData
+
+ onLoaded: {
+ // Signal that window is loaded so exclusion zone can be created
+ parent.windowLoaded = true
+ }
+
+ sourceComponent: MainScreen {
+ screen: windowLoader.loaderScreen
+ }
+ }
+
+ // Bar content in separate windows to prevent fullscreen redraws
+ Loader {
+ active: {
+ if (!parent.windowLoaded || !parent.shouldBeActive || !BarService.isVisible)
+ return false
+
+ // Check if bar is configured for this screen
+ var monitors = Settings.data.bar.monitors || []
+ return monitors.length === 0 || monitors.includes(modelData?.name)
+ }
+ asynchronous: false
+
+ sourceComponent: BarContentWindow {
+ screen: modelData
+ }
+
+ onLoaded: {
+ Logger.d("Shell", "BarContentWindow created for", modelData?.name)
+ }
+ }
+
+ // BarExclusionZone - created after MainScreen has fully loaded
+ // Disabled when bar is hidden or not configured for this screen
+ Loader {
+ active: {
+ if (!parent.windowLoaded || !parent.shouldBeActive || !BarService.isVisible)
+ return false
+
+ // Check if bar is configured for this screen
+ var monitors = Settings.data.bar.monitors || []
+ return monitors.length === 0 || monitors.includes(modelData?.name)
+ }
+ asynchronous: false
+
+ sourceComponent: BarExclusionZone {
+ screen: modelData
+ }
+
+ onLoaded: {
+ Logger.d("Shell", "BarExclusionZone created for", modelData?.name)
+ }
+ }
+ }
+}
diff --git a/Modules/MainScreen/Backgrounds/AllBackgrounds.qml b/Modules/MainScreen/Backgrounds/AllBackgrounds.qml
new file mode 100644
index 00000000..acb6c53a
--- /dev/null
+++ b/Modules/MainScreen/Backgrounds/AllBackgrounds.qml
@@ -0,0 +1,173 @@
+import QtQuick
+import QtQuick.Shapes
+import qs.Commons
+import qs.Widgets
+
+
+/**
+ * AllBackgrounds - Unified Shape container for all bar and panel backgrounds
+ *
+ * Unified shadow system. This component contains a single Shape
+ * with multiple ShapePath children (one for bar, one for each panel type).
+ *
+ * Benefits:
+ * - Single GPU-accelerated rendering pass for all backgrounds
+ * - Unified shadow system (one MultiEffect for everything)
+ */
+Item {
+ id: root
+
+ // Reference Bar
+ required property var bar
+
+ // Reference to MainScreen (for panel access)
+ required property var windowRoot
+
+ anchors.fill: parent
+
+ // Wrapper with layer caching for better shadow performance
+ Item {
+ anchors.fill: parent
+
+ // Enable layer caching to prevent continuous re-rendering
+ // This caches the Shape to a GPU texture, reducing GPU tessellation overhead
+ layer.enabled: true
+
+ // The unified Shape container
+ Shape {
+ id: backgroundsShape
+ anchors.fill: parent
+
+ // Use curve renderer for smooth corners (GPU-accelerated)
+ preferredRendererType: Shape.CurveRenderer
+
+ enabled: false // Disable mouse input on the Shape itself
+
+ Component.onCompleted: {
+ Logger.d("AllBackgrounds", "AllBackgrounds initialized")
+ Logger.d("AllBackgrounds", " bar:", root.bar)
+ Logger.d("AllBackgrounds", " windowRoot:", root.windowRoot)
+ }
+
+
+ /**
+ * Bar
+ */
+ BarBackground {
+ bar: root.bar
+ shapeContainer: backgroundsShape
+ windowRoot: root.windowRoot
+ backgroundColor: Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity)
+ }
+
+
+ /**
+ * Panels
+ */
+
+ // Audio
+ PanelBackground {
+ panel: root.windowRoot.audioPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Battery
+ PanelBackground {
+ panel: root.windowRoot.batteryPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Bluetooth
+ PanelBackground {
+ panel: root.windowRoot.bluetoothPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Calendar
+ PanelBackground {
+ panel: root.windowRoot.calendarPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Control Center
+ PanelBackground {
+ panel: root.windowRoot.controlCenterPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Launcher
+ PanelBackground {
+ panel: root.windowRoot.launcherPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Qt.alpha(Color.mSurface, Settings.data.appLauncher.backgroundOpacity)
+ }
+
+ // Notification History
+ PanelBackground {
+ panel: root.windowRoot.notificationHistoryPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Session Menu
+ PanelBackground {
+ panel: root.windowRoot.sessionMenuPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Settings
+ PanelBackground {
+ panel: root.windowRoot.settingsPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Setup Wizard
+ PanelBackground {
+ panel: root.windowRoot.setupWizardPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // TrayDrawer
+ PanelBackground {
+ panel: root.windowRoot.trayDrawerPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // TrayMenu
+ PanelBackground {
+ panel: root.windowRoot.trayMenuPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // Wallpaper
+ PanelBackground {
+ panel: root.windowRoot.wallpaperPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+
+ // WiFi
+ PanelBackground {
+ panel: root.windowRoot.wifiPanel
+ shapeContainer: backgroundsShape
+ backgroundColor: Color.mSurface
+ }
+ }
+
+ // Apply shadow to the cached layer
+ NDropShadows {
+ anchors.fill: parent
+ source: backgroundsShape
+ }
+ }
+}
diff --git a/Modules/MainScreen/Backgrounds/BarBackground.qml b/Modules/MainScreen/Backgrounds/BarBackground.qml
new file mode 100644
index 00000000..11fd0404
--- /dev/null
+++ b/Modules/MainScreen/Backgrounds/BarBackground.qml
@@ -0,0 +1,169 @@
+import QtQuick
+import QtQuick.Shapes
+import qs.Commons
+import qs.Services.UI
+import qs.Modules.MainScreen.Backgrounds
+
+
+/**
+ * BarBackground - ShapePath component for rendering the bar background
+ *
+ * Unified shadow system. This component is a ShapePath that will be
+ * a child of the unified AllBackgrounds Shape container.
+ *
+ * Uses 4-state per-corner system for flexible corner rendering:
+ * - State -1: No radius (flat/square corner)
+ * - State 0: Normal (inner curve)
+ * - State 1: Horizontal inversion (outer curve on X-axis)
+ * - State 2: Vertical inversion (outer curve on Y-axis)
+ */
+ShapePath {
+ id: root
+
+ // Required reference to the bar component
+ required property var bar
+
+ // Required reference to AllBackgrounds shapeContainer
+ required property var shapeContainer
+
+ // Required reference to windowRoot for screen access
+ required property var windowRoot
+
+ required property color backgroundColor
+
+ // Check if bar should be visible on this screen
+ readonly property bool shouldShow: {
+ // Check global bar visibility
+ if (!BarService.isVisible)
+ return false
+
+ // Check screen-specific configuration
+ var monitors = Settings.data.bar.monitors || []
+ var screenName = windowRoot?.screen?.name || ""
+
+ // If no monitors specified, show on all screens
+ // If monitors specified, only show if this screen is in the list
+ return monitors.length === 0 || monitors.includes(screenName)
+ }
+
+ // Corner radius (from Style)
+ readonly property real radius: Style.radiusL
+
+ // Bar position - since bar's parent fills the screen and Shape also fills the screen,
+ // we can use bar.x and bar.y directly (they're already in screen coordinates)
+ readonly property point barMappedPos: bar ? Qt.point(bar.x, bar.y) : Qt.point(0, 0)
+
+ // Effective dimensions - 0 when bar shouldn't show (similar to panel behavior)
+ readonly property real barWidth: (bar && shouldShow) ? bar.width : 0
+ readonly property real barHeight: (bar && shouldShow) ? bar.height : 0
+
+ // Flatten corners if bar is too small (handle null bar)
+ readonly property bool shouldFlatten: bar ? ShapeCornerHelper.shouldFlatten(barWidth, barHeight, radius) : false
+ readonly property real effectiveRadius: shouldFlatten ? (bar ? ShapeCornerHelper.getFlattenedRadius(Math.min(barWidth, barHeight), radius) : 0) : radius
+
+ // Helper function for getting corner radius based on state
+ function getCornerRadius(cornerState) {
+ // State -1 = no radius (flat corner)
+ if (cornerState === -1)
+ return 0
+ // All other states use effectiveRadius
+ return effectiveRadius
+ }
+
+ // Per-corner multipliers and radii based on bar's corner states (handle null bar)
+ readonly property real tlMultX: bar ? ShapeCornerHelper.getMultX(bar.topLeftCornerState) : 1
+ readonly property real tlMultY: bar ? ShapeCornerHelper.getMultY(bar.topLeftCornerState) : 1
+ readonly property real tlRadius: bar ? getCornerRadius(bar.topLeftCornerState) : 0
+
+ readonly property real trMultX: bar ? ShapeCornerHelper.getMultX(bar.topRightCornerState) : 1
+ readonly property real trMultY: bar ? ShapeCornerHelper.getMultY(bar.topRightCornerState) : 1
+ readonly property real trRadius: bar ? getCornerRadius(bar.topRightCornerState) : 0
+
+ readonly property real brMultX: bar ? ShapeCornerHelper.getMultX(bar.bottomRightCornerState) : 1
+ readonly property real brMultY: bar ? ShapeCornerHelper.getMultY(bar.bottomRightCornerState) : 1
+ readonly property real brRadius: bar ? getCornerRadius(bar.bottomRightCornerState) : 0
+
+ readonly property real blMultX: bar ? ShapeCornerHelper.getMultX(bar.bottomLeftCornerState) : 1
+ readonly property real blMultY: bar ? ShapeCornerHelper.getMultY(bar.bottomLeftCornerState) : 1
+ readonly property real blRadius: bar ? getCornerRadius(bar.bottomLeftCornerState) : 0
+
+ // ShapePath configuration
+ strokeWidth: -1 // No stroke, fill only
+ fillColor: backgroundColor
+
+ // Starting position (top-left corner, after the arc)
+ // Use mapped coordinates relative to the Shape container
+ startX: barMappedPos.x + tlRadius * tlMultX
+ startY: barMappedPos.y
+
+ // Smooth color animation
+ Behavior on fillColor {
+ ColorAnimation {
+ duration: Style.animationFast
+ }
+ }
+
+ // ========== PATH DEFINITION ==========
+ // Draws a rectangle with potentially inverted corners
+ // All coordinates are relative to startX/startY
+
+ // Top edge (moving right)
+ PathLine {
+ relativeX: root.barWidth - root.tlRadius * root.tlMultX - root.trRadius * root.trMultX
+ relativeY: 0
+ }
+
+ // Top-right corner arc
+ PathArc {
+ relativeX: root.trRadius * root.trMultX
+ relativeY: root.trRadius * root.trMultY
+ radiusX: root.trRadius
+ radiusY: root.trRadius
+ direction: ShapeCornerHelper.getArcDirection(root.trMultX, root.trMultY)
+ }
+
+ // Right edge (moving down)
+ PathLine {
+ relativeX: 0
+ relativeY: root.barHeight - root.trRadius * root.trMultY - root.brRadius * root.brMultY
+ }
+
+ // Bottom-right corner arc
+ PathArc {
+ relativeX: -root.brRadius * root.brMultX
+ relativeY: root.brRadius * root.brMultY
+ radiusX: root.brRadius
+ radiusY: root.brRadius
+ direction: ShapeCornerHelper.getArcDirection(root.brMultX, root.brMultY)
+ }
+
+ // Bottom edge (moving left)
+ PathLine {
+ relativeX: -(root.barWidth - root.brRadius * root.brMultX - root.blRadius * root.blMultX)
+ relativeY: 0
+ }
+
+ // Bottom-left corner arc
+ PathArc {
+ relativeX: -root.blRadius * root.blMultX
+ relativeY: -root.blRadius * root.blMultY
+ radiusX: root.blRadius
+ radiusY: root.blRadius
+ direction: ShapeCornerHelper.getArcDirection(root.blMultX, root.blMultY)
+ }
+
+ // Left edge (moving up) - closes the path back to start
+ PathLine {
+ relativeX: 0
+ relativeY: -(root.barHeight - root.blRadius * root.blMultY - root.tlRadius * root.tlMultY)
+ }
+
+ // Top-left corner arc (back to start)
+ PathArc {
+ relativeX: root.tlRadius * root.tlMultX
+ relativeY: -root.tlRadius * root.tlMultY
+ radiusX: root.tlRadius
+ radiusY: root.tlRadius
+ direction: ShapeCornerHelper.getArcDirection(root.tlMultX, root.tlMultY)
+ }
+}
diff --git a/Modules/MainScreen/Backgrounds/PanelBackground.qml b/Modules/MainScreen/Backgrounds/PanelBackground.qml
new file mode 100644
index 00000000..e91bface
--- /dev/null
+++ b/Modules/MainScreen/Backgrounds/PanelBackground.qml
@@ -0,0 +1,145 @@
+import QtQuick
+import QtQuick.Shapes
+import qs.Commons
+import qs.Modules.MainScreen.Backgrounds
+
+
+/**
+ * PanelBackground - ShapePath component for rendering a single background
+ *
+ * Unified shadow system. This component is a ShapePath that will
+ * be a child of the unified AllBackgrounds Shape container.
+ *
+ * Uses 4-state per-corner system for flexible corner rendering:
+ * - State -1: No radius (flat/square corner)
+ * - State 0: Normal (inner curve)
+ * - State 1: Horizontal inversion (outer curve on X-axis)
+ * - State 2: Vertical inversion (outer curve on Y-axis)
+ */
+ShapePath {
+ id: root
+
+ // Required reference to the panel component (e.g., windowRoot.controlCenterPanel)
+ required property var panel
+
+ // Required reference to AllBackgrounds shapeContainer
+ required property var shapeContainer
+
+ // Corner radius (from Style)
+ readonly property real radius: Style.radiusL
+
+ required property color backgroundColor
+
+ // Get the actual panelBackground Item from SmartPanel
+ // Only access panelRegion if panel exists and is visible
+ readonly property var panelBg: (panel && panel.visible) ? panel.panelRegion : null
+
+ // Panel position - panelBg is in screen coordinates already
+ readonly property real panelX: panelBg ? panelBg.x : 0
+ readonly property real panelY: panelBg ? panelBg.y : 0
+ readonly property real panelWidth: panelBg ? panelBg.width : 0
+ readonly property real panelHeight: panelBg ? panelBg.height : 0
+
+ // Flatten corners if panel is too small
+ readonly property bool shouldFlatten: panelBg ? ShapeCornerHelper.shouldFlatten(panelWidth, panelHeight, radius) : false
+ readonly property real effectiveRadius: shouldFlatten ? ShapeCornerHelper.getFlattenedRadius(Math.min(panelWidth, panelHeight), radius) : radius
+
+ // Helper function for getting corner radius based on state
+ function getCornerRadius(cornerState) {
+ // State -1 = no radius (flat corner)
+ if (cornerState === -1)
+ return 0
+ // All other states use effectiveRadius
+ return effectiveRadius
+ }
+
+ // Per-corner multipliers and radii based on panelBg's corner states
+ readonly property real tlMultX: panelBg ? ShapeCornerHelper.getMultX(panelBg.topLeftCornerState) : 1
+ readonly property real tlMultY: panelBg ? ShapeCornerHelper.getMultY(panelBg.topLeftCornerState) : 1
+ readonly property real tlRadius: panelBg ? getCornerRadius(panelBg.topLeftCornerState) : 0
+
+ readonly property real trMultX: panelBg ? ShapeCornerHelper.getMultX(panelBg.topRightCornerState) : 1
+ readonly property real trMultY: panelBg ? ShapeCornerHelper.getMultY(panelBg.topRightCornerState) : 1
+ readonly property real trRadius: panelBg ? getCornerRadius(panelBg.topRightCornerState) : 0
+
+ readonly property real brMultX: panelBg ? ShapeCornerHelper.getMultX(panelBg.bottomRightCornerState) : 1
+ readonly property real brMultY: panelBg ? ShapeCornerHelper.getMultY(panelBg.bottomRightCornerState) : 1
+ readonly property real brRadius: panelBg ? getCornerRadius(panelBg.bottomRightCornerState) : 0
+
+ readonly property real blMultX: panelBg ? ShapeCornerHelper.getMultX(panelBg.bottomLeftCornerState) : 1
+ readonly property real blMultY: panelBg ? ShapeCornerHelper.getMultY(panelBg.bottomLeftCornerState) : 1
+ readonly property real blRadius: panelBg ? getCornerRadius(panelBg.bottomLeftCornerState) : 0
+
+ // ShapePath configuration
+ strokeWidth: -1 // No stroke, fill only
+
+ // Starting position (top-left corner, after the arc)
+ startX: panelX + tlRadius * tlMultX
+ startY: panelY
+
+ fillColor: backgroundColor
+
+ // ========== PATH DEFINITION ==========
+ // Draws a rectangle with potentially inverted corners
+ // All coordinates are relative to startX/startY
+
+ // Top edge (moving right)
+ PathLine {
+ relativeX: root.panelWidth - root.tlRadius * root.tlMultX - root.trRadius * root.trMultX
+ relativeY: 0
+ }
+
+ // Top-right corner arc
+ PathArc {
+ relativeX: root.trRadius * root.trMultX
+ relativeY: root.trRadius * root.trMultY
+ radiusX: root.trRadius
+ radiusY: root.trRadius
+ direction: ShapeCornerHelper.getArcDirection(root.trMultX, root.trMultY)
+ }
+
+ // Right edge (moving down)
+ PathLine {
+ relativeX: 0
+ relativeY: root.panelHeight - root.trRadius * root.trMultY - root.brRadius * root.brMultY
+ }
+
+ // Bottom-right corner arc
+ PathArc {
+ relativeX: -root.brRadius * root.brMultX
+ relativeY: root.brRadius * root.brMultY
+ radiusX: root.brRadius
+ radiusY: root.brRadius
+ direction: ShapeCornerHelper.getArcDirection(root.brMultX, root.brMultY)
+ }
+
+ // Bottom edge (moving left)
+ PathLine {
+ relativeX: -(root.panelWidth - root.brRadius * root.brMultX - root.blRadius * root.blMultX)
+ relativeY: 0
+ }
+
+ // Bottom-left corner arc
+ PathArc {
+ relativeX: -root.blRadius * root.blMultX
+ relativeY: -root.blRadius * root.blMultY
+ radiusX: root.blRadius
+ radiusY: root.blRadius
+ direction: ShapeCornerHelper.getArcDirection(root.blMultX, root.blMultY)
+ }
+
+ // Left edge (moving up) - closes the path back to start
+ PathLine {
+ relativeX: 0
+ relativeY: -(root.panelHeight - root.blRadius * root.blMultY - root.tlRadius * root.tlMultY)
+ }
+
+ // Top-left corner arc (back to start)
+ PathArc {
+ relativeX: root.tlRadius * root.tlMultX
+ relativeY: -root.tlRadius * root.tlMultY
+ radiusX: root.tlRadius
+ radiusY: root.tlRadius
+ direction: ShapeCornerHelper.getArcDirection(root.tlMultX, root.tlMultY)
+ }
+}
diff --git a/Modules/MainScreen/Backgrounds/ShapeCornerHelper.qml b/Modules/MainScreen/Backgrounds/ShapeCornerHelper.qml
new file mode 100644
index 00000000..3e766c7a
--- /dev/null
+++ b/Modules/MainScreen/Backgrounds/ShapeCornerHelper.qml
@@ -0,0 +1,81 @@
+pragma Singleton
+
+import QtQuick
+import QtQuick.Shapes
+import Quickshell
+
+
+/**
+ * ShapeCornerHelper - Utility singleton for shape corner calculations
+ *
+ * Uses 4-state per-corner system for flexible corner rendering:
+ * - State -1: No radius (flat/square corner)
+ * - State 0: Normal (inner curve)
+ * - State 1: Horizontal inversion (outer curve on X-axis)
+ * - State 2: Vertical inversion (outer curve on Y-axis)
+ *
+ * The key technique: Using PathArc direction control (Clockwise vs Counterclockwise)
+ * combined with multipliers to create both inner and outer corner curves.
+ */
+Singleton {
+ id: root
+
+
+ /**
+ * Get X-axis multiplier for a corner state
+ * State 1 (horizontal invert) returns -1, others return 1
+ */
+ function getMultX(cornerState) {
+ return cornerState === 1 ? -1 : 1
+ }
+
+
+ /**
+ * Get Y-axis multiplier for a corner state
+ * State 2 (vertical invert) returns -1, others return 1
+ */
+ function getMultY(cornerState) {
+ return cornerState === 2 ? -1 : 1
+ }
+
+
+ /**
+ * Get PathArc direction for a corner based on its multipliers
+ * Uses XOR logic: if X inverted differs from Y inverted, use Counterclockwise
+ * This creates the outer curve effect for inverted corners
+ */
+ function getArcDirection(multX, multY) {
+ return ((multX < 0) !== (multY < 0)) ? PathArc.Counterclockwise : PathArc.Clockwise
+ }
+
+
+ /**
+ * Convenience function to get arc direction directly from corner state
+ */
+ function getArcDirectionFromState(cornerState) {
+ const multX = getMultX(cornerState)
+ const multY = getMultY(cornerState)
+ return getArcDirection(multX, multY)
+ }
+
+
+ /**
+ * Get the "flattening" radius when shape dimensions are too small
+ * Prevents visual artifacts when radius exceeds dimensions
+ */
+ function getFlattenedRadius(dimension, requestedRadius) {
+ if (dimension < requestedRadius * 2) {
+ return dimension / 2
+ }
+ return requestedRadius
+ }
+
+
+ /**
+ * Check if a shape should use flattened corners
+ * Returns true if width or height is too small for the requested radius
+ */
+ function shouldFlatten(width, height, radius) {
+ return width < radius * 2 || height < radius * 2
+ }
+}
diff --git a/Modules/MainScreen/BarContentWindow.qml b/Modules/MainScreen/BarContentWindow.qml
new file mode 100644
index 00000000..66958b0e
--- /dev/null
+++ b/Modules/MainScreen/BarContentWindow.qml
@@ -0,0 +1,65 @@
+import QtQuick
+import Quickshell
+import Quickshell.Wayland
+import qs.Commons
+import qs.Services.UI
+import qs.Modules.Bar
+
+
+/**
+ * BarContentWindow - Separate transparent PanelWindow for bar content
+ *
+ * This window contains only the bar widgets (content), while the background
+ * is rendered in MainScreen's unified Shape system. This separation prevents
+ * fullscreen redraws when bar widgets redraw.
+ *
+ * This component should be instantiated once per screen by AllScreens.qml
+ */
+PanelWindow {
+ id: barWindow
+
+ // Note: screen property is inherited from PanelWindow and should be set by parent
+ color: Color.transparent // Transparent - background is in MainScreen below
+
+ Component.onCompleted: {
+ Logger.d("BarContentWindow", "Bar content window created for screen:", barWindow.screen?.name)
+ }
+
+ // Wayland layer configuration
+ WlrLayershell.namespace: "noctalia-bar-content-" + (barWindow.screen?.name || "unknown")
+ WlrLayershell.layer: WlrLayer.Top
+ WlrLayershell.exclusionMode: ExclusionMode.Ignore // Don't reserve space - BarExclusionZone in MainScreen handles that
+
+ // Position and size to match bar location
+ readonly property string barPosition: Settings.data.bar.position || "top"
+ readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
+ readonly property bool barFloating: Settings.data.bar.floating || false
+ readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+
+ // Anchor to the bar's edge
+ anchors {
+ top: barPosition === "top" || barIsVertical
+ bottom: barPosition === "bottom" || barIsVertical
+ left: barPosition === "left" || !barIsVertical
+ right: barPosition === "right" || !barIsVertical
+ }
+
+ // Handle floating margins
+ margins {
+ top: barPosition === "top" || barIsVertical ? barMarginV : 0
+ bottom: barPosition === "bottom" || barIsVertical ? barMarginV : 0
+ left: barPosition === "left" || !barIsVertical ? barMarginH : 0
+ right: barPosition === "right" || !barIsVertical ? barMarginH : 0
+ }
+
+ // Set a tight window size
+ implicitWidth: barIsVertical ? Style.barHeight : barWindow.screen.width
+ implicitHeight: barIsVertical ? barWindow.screen.height : Style.barHeight
+
+ // Bar content - just the widgets, no background
+ Bar {
+ anchors.fill: parent
+ screen: barWindow.screen
+ }
+}
diff --git a/Modules/MainScreen/BarExclusionZone.qml b/Modules/MainScreen/BarExclusionZone.qml
new file mode 100644
index 00000000..d8072355
--- /dev/null
+++ b/Modules/MainScreen/BarExclusionZone.qml
@@ -0,0 +1,74 @@
+import QtQuick
+import Quickshell
+import Quickshell.Wayland
+import qs.Commons
+
+
+/**
+ * BarExclusionZone - Invisible PanelWindow that reserves exclusive space for the bar
+ *
+ * This is a minimal window that works with the compositor to reserve space,
+ * while the actual bar UI is rendered in MainScreen.
+ */
+PanelWindow {
+ id: root
+
+ property bool exclusive: Settings.data.bar.exclusive !== undefined ? Settings.data.bar.exclusive : false
+
+ readonly property string barPosition: Settings.data.bar.position || "top"
+ readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
+ readonly property bool barFloating: Settings.data.bar.floating || false
+ readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+
+ // Invisible - just reserves space
+ color: "transparent"
+
+ mask: Region {}
+
+ // Wayland layer shell configuration
+ WlrLayershell.layer: WlrLayer.Top
+ WlrLayershell.namespace: "noctalia-bar-exclusion-" + (screen?.name || "unknown")
+ WlrLayershell.exclusionMode: exclusive ? ExclusionMode.Auto : ExclusionMode.Ignore
+
+ // Anchor based on bar position
+ anchors {
+ top: barPosition === "top"
+ bottom: barPosition === "bottom"
+ left: barPosition === "left" || barPosition === "top" || barPosition === "bottom"
+ right: barPosition === "right" || barPosition === "top" || barPosition === "bottom"
+ }
+
+ // Size based on bar orientation
+ // When floating, only reserve space for the bar + margin on the anchored edge
+ implicitWidth: {
+ if (barIsVertical) {
+ // Vertical bar: reserve bar height + margin on the anchored edge only
+ if (barFloating) {
+ // For left bar, reserve left margin; for right bar, reserve right margin
+ return Style.barHeight + barMarginH
+ }
+ return Style.barHeight
+ }
+ return 0 // Auto-width when left/right anchors are true
+ }
+
+ implicitHeight: {
+ if (!barIsVertical) {
+ // Horizontal bar: reserve bar height + margin on the anchored edge only
+ if (barFloating) {
+ // For top bar, reserve top margin; for bottom bar, reserve bottom margin
+ return Style.barHeight + barMarginV
+ }
+ return Style.barHeight
+ }
+ return 0 // Auto-height when top/bottom anchors are true
+ }
+
+ Component.onCompleted: {
+ Logger.d("BarExclusionZone", "Created for screen:", screen?.name)
+ Logger.d("BarExclusionZone", " Position:", barPosition, "Exclusive:", exclusive, "Floating:", barFloating)
+ Logger.d("BarExclusionZone", " Anchors - top:", anchors.top, "bottom:", anchors.bottom, "left:", anchors.left, "right:", anchors.right)
+ Logger.d("BarExclusionZone", " Size:", width, "x", height, "implicitWidth:", implicitWidth, "implicitHeight:", implicitHeight)
+ }
+}
diff --git a/Modules/MainScreen/MainScreen.qml b/Modules/MainScreen/MainScreen.qml
new file mode 100644
index 00000000..6586c59c
--- /dev/null
+++ b/Modules/MainScreen/MainScreen.qml
@@ -0,0 +1,596 @@
+import QtQuick
+import QtQuick.Effects
+import Quickshell
+import Quickshell.Wayland
+import qs.Commons
+import qs.Services.UI
+import "Backgrounds" as Backgrounds
+
+// All panels
+import qs.Modules.Bar
+import qs.Modules.Panels.Audio
+import qs.Modules.Panels.Battery
+import qs.Modules.Panels.Bluetooth
+import qs.Modules.Panels.Calendar
+import qs.Modules.Panels.ControlCenter
+import qs.Modules.Panels.Launcher
+import qs.Modules.Panels.NotificationHistory
+import qs.Modules.Panels.SessionMenu
+import qs.Modules.Panels.Settings
+import qs.Modules.Panels.SetupWizard
+import qs.Modules.Panels.Tray
+import qs.Modules.Panels.Wallpaper
+import qs.Modules.Panels.WiFi
+
+
+/**
+ * MainScreen - Single PanelWindow per screen that manages all panels and the bar
+ */
+PanelWindow {
+ id: root
+
+ // Expose panels as readonly property aliases
+ readonly property alias audioPanel: audioPanel
+ readonly property alias batteryPanel: batteryPanel
+ readonly property alias bluetoothPanel: bluetoothPanel
+ readonly property alias calendarPanel: calendarPanel
+ readonly property alias controlCenterPanel: controlCenterPanel
+ readonly property alias launcherPanel: launcherPanel
+ readonly property alias notificationHistoryPanel: notificationHistoryPanel
+ readonly property alias sessionMenuPanel: sessionMenuPanel
+ readonly property alias settingsPanel: settingsPanel
+ readonly property alias setupWizardPanel: setupWizardPanel
+ readonly property alias trayDrawerPanel: trayDrawerPanel
+ readonly property alias trayMenuPanel: trayMenuPanel
+ readonly property alias wallpaperPanel: wallpaperPanel
+ readonly property alias wifiPanel: wifiPanel
+
+ Component.onCompleted: {
+ Logger.d("MainScreen", "Initialized for screen:", screen?.name, "- Dimensions:", screen?.width, "x", screen?.height, "- Position:", screen?.x, ",", screen?.y)
+ }
+
+ // Wayland
+ // Always use Exclusive keyboard focus when a panel is open
+ // This ensures all keyboard shortcuts work reliably (Escape, etc.)
+ // The centralized shortcuts in this window handle delegation to panels
+ WlrLayershell.keyboardFocus: root.isPanelOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
+ WlrLayershell.layer: WlrLayer.Top
+ WlrLayershell.namespace: "noctalia-screen-" + (screen?.name || "unknown")
+ WlrLayershell.exclusionMode: ExclusionMode.Ignore // Don't reserve space - BarExclusionZone handles that
+
+ anchors {
+ top: true
+ bottom: true
+ left: true
+ right: true
+ }
+
+ // Desktop dimming when panels are open
+ property bool dimDesktop: Settings.data.general.dimDesktop
+ property bool isPanelOpen: (PanelService.openedPanel !== null) && (PanelService.openedPanel.screen === screen)
+ property bool isPanelClosing: (PanelService.openedPanel !== null) && PanelService.openedPanel.isClosing
+
+ color: {
+ if (dimDesktop && isPanelOpen) {
+ return Qt.alpha(Color.mShadow, 0.8)
+ }
+ return Color.transparent
+ }
+
+ Behavior on color {
+ ColorAnimation {
+ duration: Style.animationNormal
+ easing.type: Easing.OutQuad
+ }
+ }
+
+ // Fully reactive mask system, make everything click-through except bar and open panels
+ mask: Region {
+ id: clickableMask
+
+ // Cover entire window (everything is masked/click-through)
+ x: 0
+ y: 0
+ width: root.width
+ height: root.height
+ intersection: Intersection.Xor
+
+ // Direct binding to Variants.instances plus additional regions
+ regions: panelRegions.instances.concat([barMaskRegion, backgroundMaskRegion])
+
+ // Bar region - subtract bar area from mask
+ Region {
+ id: barMaskRegion
+
+ x: barPlaceholder.x
+ y: barPlaceholder.y
+ width: barPlaceholder.width
+ height: barPlaceholder.height
+ intersection: Intersection.Subtract
+ }
+
+ // Background region for click-to-close - reactive sizing
+ Region {
+ id: backgroundMaskRegion
+ x: 0
+ y: 0
+ width: root.isPanelOpen && !isPanelClosing ? root.width : 0
+ height: root.isPanelOpen && !isPanelClosing ? root.height : 0
+ intersection: Intersection.Subtract
+ }
+ }
+
+ // Container for all UI elements
+ Item {
+ id: container
+ width: root.width
+ height: root.height
+
+ // Unified backgrounds container / unified shadow system
+ // Renders all bar and panel backgrounds as ShapePaths within a single Shape
+ // This allows the shadow effect to apply to all backgrounds in one render pass
+ Backgrounds.AllBackgrounds {
+ id: unifiedBackgrounds
+ anchors.fill: parent
+ bar: barPlaceholder.barItem || null
+ windowRoot: root
+ z: 0 // Behind all content
+ }
+
+ // Background MouseArea for closing panels when clicking outside
+ // Active whenever a panel is open - the mask ensures it only receives clicks when panel is open
+ MouseArea {
+ anchors.fill: parent
+ enabled: root.isPanelOpen
+ acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
+ onClicked: mouse => {
+ if (PanelService.openedPanel) {
+ PanelService.openedPanel.close()
+ }
+ }
+ z: 0 // Behind panels and bar
+ }
+
+ // ---------------------------------------
+ // All panels always exist
+ // ---------------------------------------
+ AudioPanel {
+ id: audioPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "audioPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(audioPanel)
+ }
+ }
+
+ BatteryPanel {
+ id: batteryPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "batteryPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(batteryPanel)
+ }
+ }
+
+ BluetoothPanel {
+ id: bluetoothPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "bluetoothPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(bluetoothPanel)
+ }
+ }
+
+ ControlCenterPanel {
+ id: controlCenterPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "controlCenterPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(controlCenterPanel)
+ }
+ }
+
+ CalendarPanel {
+ id: calendarPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "calendarPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(calendarPanel)
+ }
+ }
+
+ Launcher {
+ id: launcherPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "launcherPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(launcherPanel)
+ }
+ }
+
+ NotificationHistoryPanel {
+ id: notificationHistoryPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "notificationHistoryPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(notificationHistoryPanel)
+ }
+ }
+
+ SessionMenu {
+ id: sessionMenuPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "sessionMenuPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(sessionMenuPanel)
+ }
+ }
+
+ SettingsPanel {
+ id: settingsPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "settingsPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(settingsPanel)
+ }
+ }
+
+ SetupWizard {
+ id: setupWizardPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "setupWizardPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(setupWizardPanel)
+ }
+ }
+
+ TrayDrawerPanel {
+ id: trayDrawerPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "trayDrawerPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(trayDrawerPanel)
+ }
+ }
+
+ TrayMenuPanel {
+ id: trayMenuPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "trayMenuPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(trayMenuPanel)
+ }
+ }
+
+ WallpaperPanel {
+ id: wallpaperPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "wallpaperPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(wallpaperPanel)
+ }
+ }
+
+ WiFiPanel {
+ id: wifiPanel
+ screen: root.screen
+ z: 50
+
+ Component.onCompleted: {
+ objectName = "wifiPanel-" + (screen?.name || "unknown")
+ PanelService.registerPanel(wifiPanel)
+ }
+ }
+
+ // Bar placeholder - just for background positioning (actual bar content is in BarContentWindow)
+ Item {
+ id: barPlaceholder
+
+ // Expose self as barItem for AllBackgrounds compatibility
+ readonly property var barItem: barPlaceholder
+
+ // Screen reference
+ property ShellScreen screen: root.screen
+
+ // Bar positioning properties (match Bar.qml logic)
+ readonly property string barPosition: Settings.data.bar.position || "top"
+ readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
+ readonly property bool barFloating: Settings.data.bar.floating || false
+ readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+
+ // Expose bar dimensions directly on this Item for BarBackground
+ // Use screen dimensions directly
+ x: {
+ if (barPosition === "right")
+ return screen.width - Style.barHeight - barMarginH
+ return barMarginH
+ }
+ y: {
+ if (barPosition === "bottom")
+ return screen.height - Style.barHeight - barMarginV
+ return barMarginV
+ }
+ width: {
+ if (barIsVertical) {
+ return Style.barHeight + 1
+ }
+ return screen.width - barMarginH * 2
+ }
+ height: {
+ if (barIsVertical) {
+ return screen.height - barMarginV * 2
+ }
+ return Style.barHeight
+ }
+
+ // Corner states (same as Bar.qml)
+ readonly property int topLeftCornerState: {
+ if (barFloating)
+ return 0
+ if (barPosition === "top")
+ return -1
+ if (barPosition === "left")
+ return -1
+ if (Settings.data.bar.outerCorners && (barPosition === "bottom" || barPosition === "right")) {
+ return barIsVertical ? 1 : 2
+ }
+ return -1
+ }
+
+ readonly property int topRightCornerState: {
+ if (barFloating)
+ return 0
+ if (barPosition === "top")
+ return -1
+ if (barPosition === "right")
+ return -1
+ if (Settings.data.bar.outerCorners && (barPosition === "bottom" || barPosition === "left")) {
+ return barIsVertical ? 1 : 2
+ }
+ return -1
+ }
+
+ readonly property int bottomLeftCornerState: {
+ if (barFloating)
+ return 0
+ if (barPosition === "bottom")
+ return -1
+ if (barPosition === "left")
+ return -1
+ if (Settings.data.bar.outerCorners && (barPosition === "top" || barPosition === "right")) {
+ return barIsVertical ? 1 : 2
+ }
+ return -1
+ }
+
+ readonly property int bottomRightCornerState: {
+ if (barFloating)
+ return 0
+ if (barPosition === "bottom")
+ return -1
+ if (barPosition === "right")
+ return -1
+ if (Settings.data.bar.outerCorners && (barPosition === "top" || barPosition === "left")) {
+ return barIsVertical ? 1 : 2
+ }
+ return -1
+ }
+
+ Component.onCompleted: {
+ Logger.d("MainScreen", "===== Bar placeholder loaded =====")
+ Logger.d("MainScreen", " Screen:", screen?.name, "Size:", screen?.width, "x", screen?.height)
+ Logger.d("MainScreen", " Bar position:", barPosition, "| isVertical:", barIsVertical)
+ Logger.d("MainScreen", " Bar dimensions: x=" + x, "y=" + y, "width=" + width, "height=" + height)
+ Logger.d("MainScreen", " Style.barHeight =", Style.barHeight)
+ Logger.d("MainScreen", " Margins: H=" + barMarginH, "V=" + barMarginV, "| Floating:", barFloating)
+ }
+ }
+
+
+ /**
+ * Screen Corners
+ */
+ ScreenCorners {}
+ }
+
+ // Centralized keyboard shortcuts - delegate to opened panel
+ // This ensures shortcuts work regardless of panel focus state
+ Shortcut {
+ sequence: "Escape"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onEscapePressed) {
+ PanelService.openedPanel.onEscapePressed()
+ } else if (PanelService.openedPanel) {
+ PanelService.openedPanel.close()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Tab"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onTabPressed) {
+ PanelService.openedPanel.onTabPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Shift+Tab"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onShiftTabPressed) {
+ PanelService.openedPanel.onShiftTabPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Up"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onUpPressed) {
+ PanelService.openedPanel.onUpPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Down"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onDownPressed) {
+ PanelService.openedPanel.onDownPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Return"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onReturnPressed) {
+ PanelService.openedPanel.onReturnPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Enter"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onReturnPressed) {
+ PanelService.openedPanel.onReturnPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Home"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onHomePressed) {
+ PanelService.openedPanel.onHomePressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "End"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onEndPressed) {
+ PanelService.openedPanel.onEndPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "PgUp"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onPageUpPressed) {
+ PanelService.openedPanel.onPageUpPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "PgDown"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onPageDownPressed) {
+ PanelService.openedPanel.onPageDownPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Ctrl+J"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onCtrlJPressed) {
+ PanelService.openedPanel.onCtrlJPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Ctrl+K"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onCtrlKPressed) {
+ PanelService.openedPanel.onCtrlKPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Ctrl+N"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onCtrlNPressed) {
+ PanelService.openedPanel.onCtrlNPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Ctrl+P"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onCtrlPPressed) {
+ PanelService.openedPanel.onCtrlPPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Left"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onLeftPressed) {
+ PanelService.openedPanel.onLeftPressed()
+ }
+ }
+ }
+
+ Shortcut {
+ sequence: "Right"
+ enabled: root.isPanelOpen
+ onActivated: {
+ if (PanelService.openedPanel && PanelService.openedPanel.onRightPressed) {
+ PanelService.openedPanel.onRightPressed()
+ }
+ }
+ }
+}
diff --git a/Modules/MainScreen/ScreenCorners.qml b/Modules/MainScreen/ScreenCorners.qml
new file mode 100644
index 00000000..89dec642
--- /dev/null
+++ b/Modules/MainScreen/ScreenCorners.qml
@@ -0,0 +1,229 @@
+import QtQuick
+import QtQuick.Shapes
+import qs.Commons
+
+
+/**
+ * ScreenCorners - Shape component for rendering screen corners
+ *
+ * Renders concave corners at the screen edges to create a rounded screen effect.
+ * Self-contained Shape component (no shadows).
+ */
+Item {
+ id: root
+
+ anchors.fill: parent
+
+ // Wrapper with layer caching to reduce GPU tessellation overhead
+ Item {
+ anchors.fill: parent
+
+ // Cache the Shape to a texture to prevent continuous re-tessellation
+ layer.enabled: true
+
+ Shape {
+ id: cornersShape
+
+ anchors.fill: parent
+ preferredRendererType: Shape.CurveRenderer
+ enabled: false // Disable mouse input
+
+ ShapePath {
+ id: cornersPath
+
+ // Corner configuration
+ readonly property color cornerColor: Settings.data.general.forceBlackScreenCorners ? Color.black : Color.mSurface
+ readonly property real cornerRadius: Style.screenRadius
+ readonly property real cornerSize: Style.screenRadius
+
+ // Determine margins based on bar position
+ readonly property real topMargin: 0
+ readonly property real bottomMargin: 0
+ readonly property real leftMargin: 0
+ readonly property real rightMargin: 0
+
+ // Screen dimensions
+ readonly property real screenWidth: cornersShape.width
+ readonly property real screenHeight: cornersShape.height
+
+ // Only show screen corners if enabled and appropriate conditions are met
+ readonly property bool shouldShow: Settings.data.general.showScreenCorners
+
+ // ShapePath configuration
+ strokeWidth: -1 // No stroke, fill only
+ fillColor: shouldShow ? cornerColor : Color.transparent
+
+ // Smooth color animation
+ Behavior on fillColor {
+ ColorAnimation {
+ duration: Style.animationFast
+ }
+ }
+
+ // ========== PATH DEFINITION ==========
+ // Draws 4 separate corner squares at screen edges
+ // Each corner square has a concave arc on the inner diagonal
+
+ // ========== TOP-LEFT CORNER ==========
+ // Arc is at the bottom-right of this square (inner diagonal)
+ // Start at top-left screen corner
+ startX: leftMargin
+ startY: topMargin
+
+ // Top edge (moving right)
+ PathLine {
+ relativeX: cornersPath.cornerSize
+ relativeY: 0
+ }
+
+ // Right edge (moving down toward arc)
+ PathLine {
+ relativeX: 0
+ relativeY: cornersPath.cornerSize - cornersPath.cornerRadius
+ }
+
+ // Concave arc (bottom-right corner of square, curving inward toward screen center)
+ PathArc {
+ relativeX: -cornersPath.cornerRadius
+ relativeY: cornersPath.cornerRadius
+ radiusX: cornersPath.cornerRadius
+ radiusY: cornersPath.cornerRadius
+ direction: PathArc.Counterclockwise
+ }
+
+ // Bottom edge (moving left)
+ PathLine {
+ relativeX: -(cornersPath.cornerSize - cornersPath.cornerRadius)
+ relativeY: 0
+ }
+
+ // Left edge (moving up) - closes back to start
+ PathLine {
+ relativeX: 0
+ relativeY: -cornersPath.cornerSize
+ }
+
+ // ========== TOP-RIGHT CORNER ==========
+ // Arc is at the bottom-left of this square (inner diagonal)
+ PathMove {
+ x: cornersPath.screenWidth - cornersPath.rightMargin - cornersPath.cornerSize
+ y: cornersPath.topMargin
+ }
+
+ // Top edge (moving right)
+ PathLine {
+ relativeX: cornersPath.cornerSize
+ relativeY: 0
+ }
+
+ // Right edge (moving down)
+ PathLine {
+ relativeX: 0
+ relativeY: cornersPath.cornerSize
+ }
+
+ // Bottom edge (moving left toward arc)
+ PathLine {
+ relativeX: -(cornersPath.cornerSize - cornersPath.cornerRadius)
+ relativeY: 0
+ }
+
+ // Concave arc (bottom-left corner of square, curving inward toward screen center)
+ PathArc {
+ relativeX: -cornersPath.cornerRadius
+ relativeY: -cornersPath.cornerRadius
+ radiusX: cornersPath.cornerRadius
+ radiusY: cornersPath.cornerRadius
+ direction: PathArc.Counterclockwise
+ }
+
+ // Left edge (moving up) - closes back to start
+ PathLine {
+ relativeX: 0
+ relativeY: -(cornersPath.cornerSize - cornersPath.cornerRadius)
+ }
+
+ // ========== BOTTOM-LEFT CORNER ==========
+ // Arc is at the top-right of this square (inner diagonal)
+ PathMove {
+ x: cornersPath.leftMargin
+ y: cornersPath.screenHeight - cornersPath.bottomMargin - cornersPath.cornerSize
+ }
+
+ // Top edge (moving right toward arc)
+ PathLine {
+ relativeX: cornersPath.cornerSize - cornersPath.cornerRadius
+ relativeY: 0
+ }
+
+ // Concave arc (top-right corner of square, curving inward toward screen center)
+ PathArc {
+ relativeX: cornersPath.cornerRadius
+ relativeY: cornersPath.cornerRadius
+ radiusX: cornersPath.cornerRadius
+ radiusY: cornersPath.cornerRadius
+ direction: PathArc.Counterclockwise
+ }
+
+ // Right edge (moving down)
+ PathLine {
+ relativeX: 0
+ relativeY: cornersPath.cornerSize - cornersPath.cornerRadius
+ }
+
+ // Bottom edge (moving left)
+ PathLine {
+ relativeX: -cornersPath.cornerSize
+ relativeY: 0
+ }
+
+ // Left edge (moving up) - closes back to start
+ PathLine {
+ relativeX: 0
+ relativeY: -cornersPath.cornerSize
+ }
+
+ // ========== BOTTOM-RIGHT CORNER ==========
+ // Arc is at the top-left of this square (inner diagonal)
+ // Start at bottom-right of square (different from other corners!)
+ PathMove {
+ x: cornersPath.screenWidth - cornersPath.rightMargin
+ y: cornersPath.screenHeight - cornersPath.bottomMargin
+ }
+
+ // Bottom edge (moving left)
+ PathLine {
+ relativeX: -cornersPath.cornerSize
+ relativeY: 0
+ }
+
+ // Left edge (moving up toward arc)
+ PathLine {
+ relativeX: 0
+ relativeY: -(cornersPath.cornerSize - cornersPath.cornerRadius)
+ }
+
+ // Concave arc (top-left corner of square, curving inward toward screen center)
+ PathArc {
+ relativeX: cornersPath.cornerRadius
+ relativeY: -cornersPath.cornerRadius
+ radiusX: cornersPath.cornerRadius
+ radiusY: cornersPath.cornerRadius
+ direction: PathArc.Counterclockwise
+ }
+
+ // Top edge (moving right)
+ PathLine {
+ relativeX: cornersPath.cornerSize - cornersPath.cornerRadius
+ relativeY: 0
+ }
+
+ // Right edge (moving down) - closes back to start
+ PathLine {
+ relativeX: 0
+ relativeY: cornersPath.cornerSize
+ }
+ }
+ }
+ }
+}
diff --git a/Modules/MainScreen/SmartPanel.qml b/Modules/MainScreen/SmartPanel.qml
new file mode 100644
index 00000000..1d06371f
--- /dev/null
+++ b/Modules/MainScreen/SmartPanel.qml
@@ -0,0 +1,1079 @@
+import QtQuick
+import Quickshell
+import qs.Commons
+import qs.Services.UI
+
+
+/**
+ * SmartPanel for use within MainScreen
+ */
+Item {
+ id: root
+
+ // Screen property provided by MainScreen
+ property ShellScreen screen: null
+
+ // Panel content: Text, icons, etc...
+ property Component panelContent: null
+
+ // Panel size properties
+ property real preferredWidth: 700
+ property real preferredHeight: 900
+ property real preferredWidthRatio
+ property real preferredHeightRatio
+ property color panelBackgroundColor: Color.mSurface
+ property color panelBorderColor: Color.mOutline
+ property var buttonItem: null
+ property bool forceAttachToBar: false
+
+ // Anchoring properties
+ property bool panelAnchorHorizontalCenter: false
+ property bool panelAnchorVerticalCenter: false
+ property bool panelAnchorTop: false
+ property bool panelAnchorBottom: false
+ property bool panelAnchorLeft: false
+ property bool panelAnchorRight: false
+
+ // Button position properties
+ property bool useButtonPosition: false
+ property point buttonPosition: Qt.point(0, 0)
+ property int buttonWidth: 0
+ property int buttonHeight: 0
+
+ // Edge snapping: if panel is within this distance (in pixels) from a screen edge, snap
+ property real edgeSnapDistance: 50
+
+ // Track whether panel is open
+ property bool isPanelOpen: false
+
+ // Track actual visibility (delayed until content is loaded and sized)
+ property bool isPanelVisible: false
+
+ // Track size animation completion for sequential opacity animation
+ property bool sizeAnimationComplete: false
+
+ // Track close animation state: fade opacity first, then shrink size
+ property bool isClosing: false
+ property bool opacityFadeComplete: false
+ property bool closeFinalized: false // Prevent double-finalization
+
+ // Safety: Watchdog timers to prevent stuck states
+ property bool closeWatchdogActive: false
+ property bool openWatchdogActive: false
+
+ // Keyboard event handlers - override these in specific panels to handle shortcuts
+ // These are called from MainScreen's centralized shortcuts
+ function onEscapePressed() {
+ close()
+ }
+ function onTabPressed() {}
+ function onShiftTabPressed() {}
+ function onUpPressed() {}
+ function onDownPressed() {}
+ function onLeftPressed() {}
+ function onRightPressed() {}
+ function onReturnPressed() {}
+ function onHomePressed() {}
+ function onEndPressed() {}
+ function onPageUpPressed() {}
+ function onPageDownPressed() {}
+ function onCtrlJPressed() {}
+ function onCtrlKPressed() {}
+
+ // Expose panel region for click-through mask
+ readonly property var panelRegion: panelContent.maskRegion
+
+ readonly property string barPosition: Settings.data.bar.position
+ readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
+ readonly property bool barFloating: Settings.data.bar.floating
+ readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+
+ // Helper to detect if any anchor is explicitly set
+ readonly property bool hasExplicitHorizontalAnchor: panelAnchorHorizontalCenter || panelAnchorLeft || panelAnchorRight
+ readonly property bool hasExplicitVerticalAnchor: panelAnchorVerticalCenter || panelAnchorTop || panelAnchorBottom
+
+ // Effective anchor properties (depend on allowAttach)
+ // These are true when:
+ // 1. Explicitly anchored, OR
+ // 2. Using button position and bar is on that edge, OR
+ // 3. Attached to bar with no explicit anchors (default centering behavior)
+ readonly property bool effectivePanelAnchorTop: panelAnchorTop || (useButtonPosition && barPosition === "top") || (panelContent.allowAttach && !hasExplicitVerticalAnchor && barPosition === "top" && !barIsVertical)
+ readonly property bool effectivePanelAnchorBottom: panelAnchorBottom || (useButtonPosition && barPosition === "bottom") || (panelContent.allowAttach && !hasExplicitVerticalAnchor && barPosition === "bottom" && !barIsVertical)
+ readonly property bool effectivePanelAnchorLeft: panelAnchorLeft || (useButtonPosition && barPosition === "left") || (panelContent.allowAttach && !hasExplicitHorizontalAnchor && barPosition === "left" && barIsVertical)
+ readonly property bool effectivePanelAnchorRight: panelAnchorRight || (useButtonPosition && barPosition === "right") || (panelContent.allowAttach && !hasExplicitHorizontalAnchor && barPosition === "right" && barIsVertical)
+
+ signal opened
+ signal closed
+
+ Connections {
+ target: Style
+
+ function onUiScaleRatioChanged() {
+ if (root.isPanelOpen && root.isPanelVisible) {
+ root.setPosition()
+ }
+ }
+ }
+
+ // Panel visibility and sizing
+ visible: isPanelVisible
+ width: parent ? parent.width : 0
+ height: parent ? parent.height : 0
+
+ // Panel control functions
+ function toggle(buttonItem, buttonName) {
+ if (!isPanelOpen) {
+ open(buttonItem, buttonName)
+ } else {
+ close()
+ }
+ }
+
+ function open(buttonItem, buttonName) {
+ if (!buttonItem && buttonName) {
+ buttonItem = BarService.lookupWidget(buttonName, screen.name)
+ }
+
+ if (buttonItem) {
+ root.buttonItem = buttonItem
+ // Map button position to screen coordinates
+ var buttonPos = buttonItem.mapToItem(null, 0, 0)
+ root.buttonPosition = Qt.point(buttonPos.x, buttonPos.y)
+ root.buttonWidth = buttonItem.width
+ root.buttonHeight = buttonItem.height
+ root.useButtonPosition = true
+ } else {
+ // No button provided: reset button position mode
+ root.buttonItem = null
+ root.useButtonPosition = false
+ }
+
+ // Set isPanelOpen to trigger content loading, but don't show yet
+ isPanelOpen = true
+
+ // Notify PanelService
+ PanelService.willOpenPanel(root)
+
+ // Position and visibility will be set by Loader.onLoaded
+ // This ensures no flicker from default size to content size
+ }
+
+ function close() {
+ // Start close sequence: fade opacity first
+ isClosing = true
+ sizeAnimationComplete = false
+ closeFinalized = false
+
+ // Stop the open animation timer if it's still running
+ opacityTrigger.stop()
+ openWatchdogActive = false
+ openWatchdogTimer.stop()
+
+ // Start close watchdog timer
+ closeWatchdogActive = true
+ closeWatchdogTimer.restart()
+
+ // If opacity is already 0 (closed during open animation before fade-in),
+ // skip directly to size animation
+ if (root.opacity === 0.0) {
+ opacityFadeComplete = true
+ } else {
+ opacityFadeComplete = false
+ }
+
+ // Opacity will fade out, then size will shrink, then finalizeClose() will complete
+ Logger.d("SmartPanel", "Closing panel", objectName)
+ }
+
+ function finalizeClose() {
+ // Prevent double-finalization
+ if (root.closeFinalized) {
+ Logger.w("SmartPanel", "finalizeClose called but already finalized - ignoring", objectName)
+ return
+ }
+
+ // Complete the close sequence after animations finish
+ root.closeFinalized = true
+ root.closeWatchdogActive = false
+ closeWatchdogTimer.stop()
+
+ root.isPanelVisible = false
+ root.isPanelOpen = false
+ root.isClosing = false
+ root.opacityFadeComplete = false
+ PanelService.closedPanel(root)
+ closed()
+
+ Logger.d("SmartPanel", "Panel close finalized", objectName)
+ }
+
+ function setPosition() {
+ // Don't calculate position if parent dimensions aren't available yet
+ // This prevents centering around (0,0) when width/height are still 0
+ if (!root.width || !root.height) {
+ Logger.d("SmartPanel", "Skipping setPosition - dimensions not ready:", root.width, "x", root.height)
+ // Retry on next frame when dimensions should be available
+ Qt.callLater(setPosition)
+ return
+ }
+
+ // Calculate panel dimensions first (needed for positioning)
+ var w
+ // Priority 1: Content-driven size (dynamic)
+ if (contentLoader.item && contentLoader.item.contentPreferredWidth !== undefined) {
+ w = contentLoader.item.contentPreferredWidth
+ } // Priority 2: Ratio-based size
+ else if (root.preferredWidthRatio !== undefined) {
+ w = Math.round(Math.max(root.width * root.preferredWidthRatio, root.preferredWidth))
+ } // Priority 3: Static preferred width
+ else {
+ w = root.preferredWidth
+ }
+ var panelWidth = Math.min(w, root.width - Style.marginL * 2)
+
+ var h
+ // Priority 1: Content-driven size (dynamic)
+ if (contentLoader.item && contentLoader.item.contentPreferredHeight !== undefined) {
+ h = contentLoader.item.contentPreferredHeight
+ } // Priority 2: Ratio-based size
+ else if (root.preferredHeightRatio !== undefined) {
+ h = Math.round(Math.max(root.height * root.preferredHeightRatio, root.preferredHeight))
+ } // Priority 3: Static preferred height
+ else {
+ h = root.preferredHeight
+ }
+ var panelHeight = Math.min(h, root.height - Style.barHeight - Style.marginL * 2)
+
+ // Update panelBackground target size (will be animated)
+ panelBackground.targetWidth = panelWidth
+ panelBackground.targetHeight = panelHeight
+
+ // Calculate position
+ var calculatedX
+ var calculatedY
+
+ // ===== X POSITIONING =====
+ if (root.useButtonPosition && root.width > 0 && panelWidth > 0) {
+ if (root.barIsVertical) {
+ // For vertical bars
+ if (panelContent.allowAttach) {
+ // Attached panels: align with bar edge (left or right side)
+ if (root.barPosition === "left") {
+ var leftBarEdge = root.barMarginH + Style.barHeight
+ calculatedX = leftBarEdge
+ } else {
+ // right
+ var rightBarEdge = root.width - root.barMarginH - Style.barHeight
+ calculatedX = rightBarEdge - panelWidth
+ }
+ } else {
+ // Detached panels: center on button X position
+ var panelX = root.buttonPosition.x + root.buttonWidth / 2 - panelWidth / 2
+ var minX = Style.marginL
+ var maxX = root.width - panelWidth - Style.marginL
+
+ // Account for vertical bar taking up space
+ if (root.barPosition === "left") {
+ minX = root.barMarginH + Style.barHeight + Style.marginL
+ } else if (root.barPosition === "right") {
+ maxX = root.width - root.barMarginH - Style.barHeight - panelWidth - Style.marginL
+ }
+
+ panelX = Math.max(minX, Math.min(panelX, maxX))
+ calculatedX = panelX
+ }
+ } else {
+ // For horizontal bars, center panel on button X position
+ var panelX = root.buttonPosition.x + root.buttonWidth / 2 - panelWidth / 2
+ if (panelContent.allowAttach) {
+ var cornerInset = root.barFloating ? Style.radiusL * 2 : 0
+ var barLeftEdge = root.barMarginH + cornerInset
+ var barRightEdge = root.width - root.barMarginH - cornerInset
+ panelX = Math.max(barLeftEdge, Math.min(panelX, barRightEdge - panelWidth))
+ } else {
+ panelX = Math.max(Style.marginL, Math.min(panelX, root.width - panelWidth - Style.marginL))
+ }
+ calculatedX = panelX
+ }
+ } else {
+ // Standard anchor positioning
+ if (root.panelAnchorHorizontalCenter) {
+ if (root.barIsVertical) {
+ if (root.barPosition === "left") {
+ var availableStart = root.barMarginH + Style.barHeight
+ var availableWidth = root.width - availableStart
+ calculatedX = availableStart + (availableWidth - panelWidth) / 2
+ } else if (root.barPosition === "right") {
+ var availableWidth = root.width - root.barMarginH - Style.barHeight
+ calculatedX = (availableWidth - panelWidth) / 2
+ } else {
+ calculatedX = (root.width - panelWidth) / 2
+ }
+ } else {
+ calculatedX = (root.width - panelWidth) / 2
+ }
+ } else if (root.effectivePanelAnchorRight) {
+ if (panelContent.allowAttach && root.barIsVertical && root.barPosition === "right") {
+ var rightBarEdge = root.width - root.barMarginH - Style.barHeight
+ calculatedX = rightBarEdge - panelWidth
+ } else if (panelContent.allowAttach) {
+ // Account for corner inset when bar is floating, horizontal, AND panel is on same edge as bar
+ var panelOnSameEdgeAsBar = (root.barPosition === "top" && root.effectivePanelAnchorTop) || (root.barPosition === "bottom" && root.effectivePanelAnchorBottom)
+ if (!root.barIsVertical && root.barFloating && panelOnSameEdgeAsBar) {
+ var rightCornerInset = Style.radiusL * 2
+ calculatedX = root.width - root.barMarginH - rightCornerInset - panelWidth
+ } else {
+ calculatedX = root.width - panelWidth
+ }
+ } else {
+ calculatedX = root.width - panelWidth - Style.marginL
+ }
+ } else if (root.effectivePanelAnchorLeft) {
+ if (panelContent.allowAttach && root.barIsVertical && root.barPosition === "left") {
+ var leftBarEdge = root.barMarginH + Style.barHeight
+ calculatedX = leftBarEdge
+ } else if (panelContent.allowAttach) {
+ // Account for corner inset when bar is floating, horizontal, AND panel is on same edge as bar
+ var panelOnSameEdgeAsBar = (root.barPosition === "top" && root.effectivePanelAnchorTop) || (root.barPosition === "bottom" && root.effectivePanelAnchorBottom)
+ if (!root.barIsVertical && root.barFloating && panelOnSameEdgeAsBar) {
+ var leftCornerInset = Style.radiusL * 2
+ calculatedX = root.barMarginH + leftCornerInset
+ } else {
+ calculatedX = 0
+ }
+ } else {
+ calculatedX = Style.marginL
+ }
+ } else {
+ // No explicit anchor: default to centering on bar
+ if (root.barIsVertical) {
+ if (root.barPosition === "left") {
+ var availableStart = root.barMarginH + Style.barHeight
+ var availableWidth = root.width - availableStart - Style.marginL
+ calculatedX = availableStart + (availableWidth - panelWidth) / 2
+ } else {
+ var availableWidth = root.width - root.barMarginH - Style.barHeight - Style.marginL
+ calculatedX = Style.marginL + (availableWidth - panelWidth) / 2
+ }
+ } else {
+ if (panelContent.allowAttach) {
+ var cornerInset = Style.radiusL + (root.barFloating ? Style.radiusL : 0)
+ var barLeftEdge = root.barMarginH + cornerInset
+ var barRightEdge = root.width - root.barMarginH - cornerInset
+ var centeredX = (root.width - panelWidth) / 2
+ calculatedX = Math.max(barLeftEdge, Math.min(centeredX, barRightEdge - panelWidth))
+ } else {
+ calculatedX = (root.width - panelWidth) / 2
+ }
+ }
+ }
+ }
+
+ // Edge snapping for X
+ if (panelContent.allowAttach && !root.barFloating && root.width > 0 && panelWidth > 0) {
+ var leftEdgePos = root.barMarginH
+ if (root.barPosition === "left") {
+ leftEdgePos = root.barMarginH + Style.barHeight
+ }
+
+ var rightEdgePos = root.width - root.barMarginH - panelWidth
+ if (root.barPosition === "right") {
+ rightEdgePos = root.width - root.barMarginH - Style.barHeight - panelWidth
+ }
+
+ if (Math.abs(calculatedX - leftEdgePos) <= root.edgeSnapDistance) {
+ calculatedX = leftEdgePos
+ } else if (Math.abs(calculatedX - rightEdgePos) <= root.edgeSnapDistance) {
+ calculatedX = rightEdgePos
+ }
+ }
+
+ // ===== Y POSITIONING =====
+ if (root.useButtonPosition && root.height > 0 && panelHeight > 0) {
+ if (root.barPosition === "top") {
+ var topBarEdge = root.barMarginV + Style.barHeight
+ if (panelContent.allowAttach) {
+ calculatedY = topBarEdge
+ } else {
+ calculatedY = topBarEdge + Style.marginM
+ }
+ } else if (root.barPosition === "bottom") {
+ var bottomBarEdge = root.height - root.barMarginV - Style.barHeight
+ if (panelContent.allowAttach) {
+ calculatedY = bottomBarEdge - panelHeight
+ } else {
+ calculatedY = bottomBarEdge - panelHeight - Style.marginM
+ }
+ } else if (root.barIsVertical) {
+ var panelY = root.buttonPosition.y + root.buttonHeight / 2 - panelHeight / 2
+ var extraPadding = (panelContent.allowAttach && root.barFloating) ? Style.radiusL : 0
+ if (panelContent.allowAttach) {
+ var cornerInset = extraPadding + (root.barFloating ? Style.radiusL : 0)
+ var barTopEdge = root.barMarginV + cornerInset
+ var barBottomEdge = root.height - root.barMarginV - cornerInset
+ panelY = Math.max(barTopEdge, Math.min(panelY, barBottomEdge - panelHeight))
+ } else {
+ panelY = Math.max(Style.marginL + extraPadding, Math.min(panelY, root.height - panelHeight - Style.marginL - extraPadding))
+ }
+ calculatedY = panelY
+ }
+ } else {
+ // Standard anchor positioning
+ var barOffset = 0
+ if (!panelContent.allowAttach) {
+ if (root.barPosition === "top") {
+ barOffset = root.barMarginV + Style.barHeight + Style.marginM
+ } else if (root.barPosition === "bottom") {
+ barOffset = root.barMarginV + Style.barHeight + Style.marginM
+ }
+ } else {
+ if (root.effectivePanelAnchorTop && root.barPosition === "top") {
+ calculatedY = root.barMarginV + Style.barHeight
+ } else if (root.effectivePanelAnchorBottom && root.barPosition === "bottom") {
+ calculatedY = root.height - root.barMarginV - Style.barHeight - panelHeight
+ } else if (!root.hasExplicitVerticalAnchor) {
+ if (root.barPosition === "top") {
+ calculatedY = root.barMarginV + Style.barHeight
+ } else if (root.barPosition === "bottom") {
+ calculatedY = root.height - root.barMarginV - Style.barHeight - panelHeight
+ }
+ }
+ }
+
+ if (calculatedY === undefined) {
+ if (root.panelAnchorVerticalCenter) {
+ if (!root.barIsVertical) {
+ if (root.barPosition === "top") {
+ var availableStart = root.barMarginV + Style.barHeight
+ var availableHeight = root.height - availableStart
+ calculatedY = availableStart + (availableHeight - panelHeight) / 2
+ } else if (root.barPosition === "bottom") {
+ var availableHeight = root.height - root.barMarginV - Style.barHeight
+ calculatedY = (availableHeight - panelHeight) / 2
+ } else {
+ calculatedY = (root.height - panelHeight) / 2
+ }
+ } else {
+ calculatedY = (root.height - panelHeight) / 2
+ }
+ } else if (root.effectivePanelAnchorTop) {
+ if (panelContent.allowAttach) {
+ calculatedY = 0
+ } else {
+ var topBarOffset = (root.barPosition === "top") ? barOffset : 0
+ calculatedY = topBarOffset + Style.marginL
+ }
+ } else if (root.effectivePanelAnchorBottom) {
+ if (panelContent.allowAttach) {
+ calculatedY = root.height - panelHeight
+ } else {
+ var bottomBarOffset = (root.barPosition === "bottom") ? barOffset : 0
+ calculatedY = root.height - panelHeight - bottomBarOffset - Style.marginL
+ }
+ } else {
+ if (root.barIsVertical) {
+ if (panelContent.allowAttach) {
+ var cornerInset = root.barFloating ? Style.radiusL * 2 : 0
+ var barTopEdge = root.barMarginV + cornerInset
+ var barBottomEdge = root.height - root.barMarginV - cornerInset
+ var centeredY = (root.height - panelHeight) / 2
+ calculatedY = Math.max(barTopEdge, Math.min(centeredY, barBottomEdge - panelHeight))
+ } else {
+ calculatedY = (root.height - panelHeight) / 2
+ }
+ } else {
+ if (panelContent.allowAttach && !root.barIsVertical) {
+ if (root.barPosition === "top") {
+ calculatedY = root.barMarginV + Style.barHeight
+ } else if (root.barPosition === "bottom") {
+ calculatedY = root.height - root.barMarginV - Style.barHeight - panelHeight
+ }
+ } else {
+ if (root.barPosition === "top") {
+ calculatedY = barOffset + Style.marginL
+ } else if (root.barPosition === "bottom") {
+ calculatedY = Style.marginL
+ } else {
+ calculatedY = Style.marginL
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Edge snapping for Y
+ if (panelContent.allowAttach && !root.barFloating && root.height > 0 && panelHeight > 0) {
+ var topEdgePos = root.barMarginV
+ if (root.barPosition === "top") {
+ topEdgePos = root.barMarginV + Style.barHeight
+ }
+
+ var bottomEdgePos = root.height - root.barMarginV - panelHeight
+ if (root.barPosition === "bottom") {
+ bottomEdgePos = root.height - root.barMarginV - Style.barHeight - panelHeight
+ }
+
+ if (Math.abs(calculatedY - topEdgePos) <= root.edgeSnapDistance) {
+ calculatedY = topEdgePos
+ } else if (Math.abs(calculatedY - bottomEdgePos) <= root.edgeSnapDistance) {
+ calculatedY = bottomEdgePos
+ }
+ }
+
+ // Apply calculated positions (set targets for animation)
+ panelBackground.targetX = calculatedX
+ panelBackground.targetY = calculatedY
+
+ Logger.d("SmartPanel", "Position calculated:", calculatedX, calculatedY)
+ Logger.d("SmartPanel", " Panel size:", panelWidth, "x", panelHeight)
+ Logger.d("SmartPanel", " Parent size:", root.width, "x", root.height)
+ }
+
+ // Watch for changes in content-driven sizes and update position
+ Connections {
+ target: contentLoader.item
+ ignoreUnknownSignals: true
+
+ function onContentPreferredWidthChanged() {
+ if (root.isPanelOpen && root.isPanelVisible) {
+ root.setPosition()
+ }
+ }
+
+ function onContentPreferredHeightChanged() {
+ if (root.isPanelOpen && root.isPanelVisible) {
+ root.setPosition()
+ }
+ }
+ }
+
+ // Opacity animation
+ // Opening: fade in after size animation reaches 75%
+ // Closing: fade out immediately
+ opacity: {
+ if (isClosing)
+ return 0.0 // Fade out when closing
+ if (isPanelVisible && sizeAnimationComplete)
+ return 1.0 // Fade in when opening
+ return 0.0
+ }
+
+ Behavior on opacity {
+ NumberAnimation {
+ id: opacityAnimation
+ duration: root.isClosing ? Style.animationFaster : Style.animationFast
+ easing.type: Easing.OutQuad
+
+ onRunningChanged: {
+ // Safety: If animation didn't run (zero duration), handle immediately
+ if (!running && duration === 0) {
+ if (root.isClosing && root.opacity === 0.0) {
+ root.opacityFadeComplete = true
+ var shouldFinalizeNow = panelContent.maskRegion && !panelContent.maskRegion.shouldAnimateWidth && !panelContent.maskRegion.shouldAnimateHeight
+ if (shouldFinalizeNow) {
+ Logger.d("SmartPanel", "Zero-duration opacity + no size animation - finalizing", root.objectName)
+ Qt.callLater(root.finalizeClose)
+ }
+ } else if (root.isPanelVisible && root.opacity === 1.0) {
+ // Open completed with zero duration
+ root.openWatchdogActive = false
+ openWatchdogTimer.stop()
+ }
+ return
+ }
+
+ // When opacity fade completes during close, trigger size animation
+ if (!running && root.isClosing && root.opacity === 0.0) {
+ root.opacityFadeComplete = true
+ // If no size animation will run (centered attached panels only), finalize immediately
+ // Detached panels (allowAttach === false) should always animate from top
+ var shouldFinalizeNow = panelContent.maskRegion && !panelContent.maskRegion.shouldAnimateWidth && !panelContent.maskRegion.shouldAnimateHeight
+ if (shouldFinalizeNow) {
+ Logger.d("SmartPanel", "No animation - finalizing immediately", root.objectName)
+ Qt.callLater(root.finalizeClose)
+ } else {
+ Logger.d("SmartPanel", "Animation will run - waiting for size animation", root.objectName, "shouldAnimateHeight:", panelContent.maskRegion.shouldAnimateHeight, "shouldAnimateWidth:", panelContent.maskRegion.shouldAnimateWidth)
+ }
+ } // When opacity fade completes during open, stop watchdog
+ else if (!running && root.isPanelVisible && root.opacity === 1.0) {
+ root.openWatchdogActive = false
+ openWatchdogTimer.stop()
+ }
+ }
+ }
+ }
+
+ // Timer to trigger opacity fade at 50% of size animation
+ Timer {
+ id: opacityTrigger
+ interval: Style.animationNormal * 0.5
+ repeat: false
+ onTriggered: {
+ if (root.isPanelVisible) {
+ root.sizeAnimationComplete = true
+ }
+ }
+ }
+
+ // Watchdog timer for open sequence (safety mechanism)
+ Timer {
+ id: openWatchdogTimer
+ interval: Style.animationNormal * 3 // 3x normal animation time
+ repeat: false
+ onTriggered: {
+ if (root.openWatchdogActive) {
+ Logger.w("SmartPanel", "Open watchdog timeout - forcing panel visible state", root.objectName)
+ root.openWatchdogActive = false
+ // Force completion of open sequence
+ if (root.isPanelOpen && !root.isPanelVisible) {
+ root.isPanelVisible = true
+ root.sizeAnimationComplete = true
+ }
+ }
+ }
+ }
+
+ // Watchdog timer for close sequence (safety mechanism)
+ Timer {
+ id: closeWatchdogTimer
+ interval: Style.animationFast * 3 // 3x fast animation time
+ repeat: false
+ onTriggered: {
+ if (root.closeWatchdogActive && !root.closeFinalized) {
+ Logger.w("SmartPanel", "Close watchdog timeout - forcing panel close", root.objectName)
+ // Force finalization
+ Qt.callLater(root.finalizeClose)
+ }
+ }
+ }
+
+ // ------------------------------------------------
+ // Panel Content
+ Item {
+ id: panelContent
+ anchors.fill: parent
+
+ // Screen-dependent attachment properties
+ readonly property bool allowAttach: Settings.data.ui.panelsAttachedToBar || root.forceAttachToBar
+ readonly property bool allowAttachToBar: {
+ if (!(Settings.data.ui.panelsAttachedToBar || root.forceAttachToBar) || Settings.data.bar.backgroundOpacity < 1.0) {
+ return false
+ }
+
+ // A panel can only be attached to a bar if there is a bar on that screen
+ var monitors = Settings.data.bar.monitors || []
+ var result = monitors.length === 0 || monitors.includes(root.screen?.name || "")
+ return result
+ }
+
+ // Edge detection - detect if panel is touching screen edges
+ readonly property bool touchingLeftEdge: allowAttach && panelBackground.x <= 1
+ readonly property bool touchingRightEdge: allowAttach && (panelBackground.x + panelBackground.width) >= (root.width - 1)
+ readonly property bool touchingTopEdge: allowAttach && panelBackground.y <= 1
+ readonly property bool touchingBottomEdge: allowAttach && (panelBackground.y + panelBackground.height) >= (root.height - 1)
+
+ // Bar edge detection - detect if panel is touching bar edges (for cases where centered panels snap to bar due to height constraints)
+ readonly property bool touchingTopBar: allowAttachToBar && root.barPosition === "top" && !root.barIsVertical && Math.abs(panelBackground.y - (root.barMarginV + Style.barHeight)) <= 1
+ readonly property bool touchingBottomBar: allowAttachToBar && root.barPosition === "bottom" && !root.barIsVertical && Math.abs((panelBackground.y + panelBackground.height) - (root.height - root.barMarginV - Style.barHeight)) <= 1
+ readonly property bool touchingLeftBar: allowAttachToBar && root.barPosition === "left" && root.barIsVertical && Math.abs(panelBackground.x - (root.barMarginH + Style.barHeight)) <= 1
+ readonly property bool touchingRightBar: allowAttachToBar && root.barPosition === "right" && root.barIsVertical && Math.abs((panelBackground.x + panelBackground.width) - (root.width - root.barMarginH - Style.barHeight)) <= 1
+
+ // Expose panelBackground for mask region
+ property alias maskRegion: panelBackground
+
+ // The actual panel background - provides geometry for PanelBackground rendering
+ Item {
+ id: panelBackground
+
+ // Store target dimensions (set by setPosition())
+ property real targetWidth: root.preferredWidth
+ property real targetHeight: root.preferredHeight
+ property real targetX: root.x
+ property real targetY: root.y
+
+ property var bezierCurve: [0.05, 0, 0.133, 0.06, 0.166, 0.4, 0.208, 0.82, 0.25, 1, 1, 1]
+
+ // Determine which edges the panel is closest to for animation direction
+ // Use target position (not animated position) to avoid binding loops
+ readonly property bool willTouchTopBar: {
+ if (!isPanelVisible)
+ return false
+ if (!panelContent.allowAttachToBar || root.barPosition !== "top" || root.barIsVertical)
+ return false
+ var targetTopBarY = root.barMarginV + Style.barHeight
+ return Math.abs(panelBackground.targetY - targetTopBarY) <= 1
+ }
+ readonly property bool willTouchBottomBar: {
+ if (!isPanelVisible)
+ return false
+ if (!panelContent.allowAttachToBar || root.barPosition !== "bottom" || root.barIsVertical)
+ return false
+ var targetBottomBarY = root.height - root.barMarginV - Style.barHeight - panelBackground.targetHeight
+ return Math.abs(panelBackground.targetY - targetBottomBarY) <= 1
+ }
+ readonly property bool willTouchLeftBar: {
+ if (!isPanelVisible)
+ return false
+ if (!panelContent.allowAttachToBar || root.barPosition !== "left" || !root.barIsVertical)
+ return false
+ var targetLeftBarX = root.barMarginH + Style.barHeight
+ return Math.abs(panelBackground.targetX - targetLeftBarX) <= 1
+ }
+ readonly property bool willTouchRightBar: {
+ if (!isPanelVisible)
+ return false
+ if (!panelContent.allowAttachToBar || root.barPosition !== "right" || !root.barIsVertical)
+ return false
+ var targetRightBarX = root.width - root.barMarginH - Style.barHeight - panelBackground.targetWidth
+ return Math.abs(panelBackground.targetX - targetRightBarX) <= 1
+ }
+ readonly property bool willTouchTopEdge: isPanelVisible && panelContent.allowAttach && panelBackground.targetY <= 1
+ readonly property bool willTouchBottomEdge: isPanelVisible && panelContent.allowAttach && (panelBackground.targetY + panelBackground.targetHeight) >= (root.height - 1)
+ readonly property bool willTouchLeftEdge: isPanelVisible && panelContent.allowAttach && panelBackground.targetX <= 1
+ readonly property bool willTouchRightEdge: isPanelVisible && panelContent.allowAttach && (panelBackground.targetX + panelBackground.targetWidth) >= (root.width - 1)
+
+ readonly property bool isActuallyAttachedToAnyEdge: {
+ if (!isPanelVisible)
+ return false
+ return willTouchTopBar || willTouchBottomBar || willTouchLeftBar || willTouchRightBar || willTouchTopEdge || willTouchBottomEdge || willTouchLeftEdge || willTouchRightEdge
+ }
+
+ readonly property bool animateFromTop: {
+ // Before panel is visible, default to top animation to avoid diagonal animation on first open
+ if (!isPanelVisible) {
+ return true
+ }
+ // PRIORITY 1: Bar attachment (always takes precedence)
+ // Attached to bar at top
+ if (willTouchTopBar) {
+ return true
+ }
+ // PRIORITY 2: Screen edge attachment (only if not touching bar)
+ // Attached to screen top edge (not bar)
+ if (willTouchTopEdge && !willTouchTopBar && !willTouchBottomBar && !willTouchLeftBar && !willTouchRightBar) {
+ return true
+ }
+ // If panel is not attached to any edge, animate from top by default
+ if (!isActuallyAttachedToAnyEdge) {
+ return true
+ }
+ return false
+ }
+ readonly property bool animateFromBottom: {
+ if (!isPanelVisible) {
+ return false
+ }
+ // PRIORITY 1: Bar attachment (always takes precedence)
+ // Attached to bar at bottom
+ if (willTouchBottomBar) {
+ return true
+ }
+ // PRIORITY 2: Screen edge attachment (only if not touching bar)
+ // Attached to screen bottom edge (not bar)
+ if (willTouchBottomEdge && !willTouchTopBar && !willTouchBottomBar && !willTouchLeftBar && !willTouchRightBar) {
+ return true
+ }
+ return false
+ }
+ readonly property bool animateFromLeft: {
+ if (!isPanelVisible) {
+ return false
+ }
+ // PRIORITY 1: Bar attachment (always takes precedence)
+ // If touching any horizontal bar, don't animate from left
+ if (willTouchTopBar || willTouchBottomBar) {
+ return false
+ }
+ // Attached to bar at left
+ if (willTouchLeftBar) {
+ return true
+ }
+ // PRIORITY 2: Screen edge attachment (only if not touching any bar)
+ // Don't animate from left if also touching top/bottom edge (priority: vertical over horizontal)
+ var touchingTopEdge = isPanelVisible && panelContent.allowAttach && panelBackground.targetY <= 1
+ var touchingBottomEdge = isPanelVisible && panelContent.allowAttach && (panelBackground.targetY + panelBackground.targetHeight) >= (root.height - 1)
+
+ if (touchingTopEdge || touchingBottomEdge) {
+ return false
+ }
+ // Attached to screen left edge (not bar)
+ if (willTouchLeftEdge && !willTouchLeftBar && !willTouchTopBar && !willTouchBottomBar && !willTouchRightBar) {
+ return true
+ }
+ return false
+ }
+ readonly property bool animateFromRight: {
+ if (!isPanelVisible) {
+ return false
+ }
+ // PRIORITY 1: Bar attachment (always takes precedence)
+ // If touching any horizontal bar, don't animate from right
+ if (willTouchTopBar || willTouchBottomBar) {
+ return false
+ }
+ // Attached to bar at right
+ if (willTouchRightBar) {
+ return true
+ }
+ // PRIORITY 2: Screen edge attachment (only if not touching any bar)
+ // Don't animate from right if also touching top/bottom edge (priority: vertical over horizontal)
+ var touchingTopEdge = isPanelVisible && panelContent.allowAttach && panelBackground.targetY <= 1
+ var touchingBottomEdge = isPanelVisible && panelContent.allowAttach && (panelBackground.targetY + panelBackground.targetHeight) >= (root.height - 1)
+
+ if (touchingTopEdge || touchingBottomEdge) {
+ return false
+ }
+ // Attached to screen right edge (not bar)
+ if (willTouchRightEdge && !willTouchLeftBar && !willTouchTopBar && !willTouchBottomBar && !willTouchRightBar) {
+ return true
+ }
+ return false
+ }
+
+ // Determine animation axis based on which edge is closest
+ // Priority: horizontal edges (top/bottom) take precedence over vertical edges (left/right)
+ // This prevents diagonal animations when panel is attached to a corner
+ readonly property bool shouldAnimateWidth: !shouldAnimateHeight && (animateFromLeft || animateFromRight)
+ readonly property bool shouldAnimateHeight: animateFromTop || animateFromBottom
+
+ // Current animated width/height (referenced by x/y for right/bottom positioning)
+ readonly property real currentWidth: {
+ // When closing and opacity fade complete, shrink width if animating horizontally
+ if (isClosing && opacityFadeComplete && shouldAnimateWidth)
+ return 0
+ // When visible or closing (before opacity fade), keep full width
+ if (isClosing || isPanelVisible)
+ return targetWidth
+ // Initial state before visible: always start at 0 for width animation
+ // (will only animate if shouldAnimateWidth becomes true when isPanelVisible changes)
+ return 0
+ }
+ readonly property real currentHeight: {
+ // When closing and opacity fade complete, shrink height if animating vertically
+ if (isClosing && opacityFadeComplete && shouldAnimateHeight)
+ return 0
+ // When visible or closing (before opacity fade), keep full height
+ if (isClosing || isPanelVisible)
+ return targetHeight
+ // Initial state before visible: always start at 0 for height animation
+ // (will only animate if shouldAnimateHeight becomes true when isPanelVisible changes)
+ return 0
+ }
+
+ width: currentWidth
+ height: currentHeight
+
+ x: {
+ // Offset x to make panel grow/shrink from the appropriate edge
+ if (animateFromRight) {
+ // Keep the RIGHT edge fixed at its target position
+ if (isPanelVisible || isClosing) {
+ var targetRightEdge = targetX + targetWidth
+ return targetRightEdge - width
+ }
+ }
+ return targetX
+ }
+ y: {
+ // Offset y to make panel grow/shrink from the appropriate edge
+ if (animateFromBottom) {
+ // Keep the BOTTOM edge fixed at its target position
+ if (isPanelVisible || isClosing) {
+ var targetBottomEdge = targetY + targetHeight
+ return targetBottomEdge - height
+ }
+ }
+ return targetY
+ }
+
+ Behavior on width {
+ NumberAnimation {
+ id: widthAnimation
+ duration: {
+ if (!panelBackground.shouldAnimateWidth)
+ return 0
+ return root.isClosing ? Style.animationFast : Style.animationNormal
+ }
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: panelBackground.bezierCurve
+
+ onRunningChanged: {
+ // Safety: Zero-duration animation handling
+ if (!running && duration === 0) {
+ if (root.isClosing && panelBackground.width === 0 && panelBackground.shouldAnimateWidth) {
+ Logger.d("SmartPanel", "Zero-duration width animation - finalizing", root.objectName)
+ Qt.callLater(root.finalizeClose)
+ }
+ return
+ }
+
+ // When width shrink completes during close, finalize
+ if (!running && root.isClosing && panelBackground.width === 0 && panelBackground.shouldAnimateWidth) {
+ Qt.callLater(root.finalizeClose)
+ }
+ }
+ }
+ }
+
+ Behavior on height {
+ NumberAnimation {
+ id: heightAnimation
+ duration: {
+ if (!panelBackground.shouldAnimateHeight)
+ return 0
+ return root.isClosing ? Style.animationFast : Style.animationNormal
+ }
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: panelBackground.bezierCurve
+
+ onRunningChanged: {
+ // Safety: Zero-duration animation handling
+ if (!running && duration === 0) {
+ if (root.isClosing && panelBackground.height === 0 && panelBackground.shouldAnimateHeight) {
+ Logger.d("SmartPanel", "Zero-duration height animation - finalizing", root.objectName)
+ Qt.callLater(root.finalizeClose)
+ }
+ return
+ }
+
+ // When height shrink completes during close, finalize
+ if (!running && root.isClosing && panelBackground.height === 0 && panelBackground.shouldAnimateHeight) {
+ Qt.callLater(root.finalizeClose)
+ }
+ }
+ }
+ }
+
+ // Corner states for PanelBackground to read
+ // State -1: No radius (flat/square corner)
+ // State 0: Normal (inner curve)
+ // State 1: Horizontal inversion (outer curve on X-axis)
+ // State 2: Vertical inversion (outer curve on Y-axis)
+
+ // Smart corner state calculation based on bar attachment and edge touching
+ property int topLeftCornerState: {
+ var barInverted = panelContent.allowAttachToBar && ((root.barPosition === "top" && !root.barIsVertical && root.effectivePanelAnchorTop) || (root.barPosition === "left" && root.barIsVertical && root.effectivePanelAnchorLeft))
+ var barTouchInverted = panelContent.touchingTopBar || panelContent.touchingLeftBar
+ // Invert if touching either edge that forms this corner (left OR top), regardless of bar position
+ var edgeInverted = panelContent.allowAttach && (panelContent.touchingLeftEdge || panelContent.touchingTopEdge)
+ var oppositeEdgeInverted = panelContent.allowAttach && (panelContent.touchingTopEdge && !root.barIsVertical && root.barPosition !== "top")
+
+ if (barInverted || barTouchInverted || edgeInverted || oppositeEdgeInverted) {
+ // Determine inversion direction based on which edge is touched
+ if (panelContent.touchingLeftEdge && panelContent.touchingTopEdge)
+ return 0 // Both edges: no inversion (normal rounded corner)
+ if (panelContent.touchingLeftEdge)
+ return 2 // Left edge: vertical inversion
+ if (panelContent.touchingTopEdge)
+ return 1 // Top edge: horizontal inversion
+ return root.barIsVertical ? 2 : 1
+ }
+ return 0
+ }
+
+ property int topRightCornerState: {
+ var barInverted = panelContent.allowAttachToBar && ((root.barPosition === "top" && !root.barIsVertical && root.effectivePanelAnchorTop) || (root.barPosition === "right" && root.barIsVertical && root.effectivePanelAnchorRight))
+ var barTouchInverted = panelContent.touchingTopBar || panelContent.touchingRightBar
+ // Invert if touching either edge that forms this corner (right OR top), regardless of bar position
+ var edgeInverted = panelContent.allowAttach && (panelContent.touchingRightEdge || panelContent.touchingTopEdge)
+ var oppositeEdgeInverted = panelContent.allowAttach && (panelContent.touchingTopEdge && !root.barIsVertical && root.barPosition !== "top")
+
+ if (barInverted || barTouchInverted || edgeInverted || oppositeEdgeInverted) {
+ // Determine inversion direction based on which edge is touched
+ if (panelContent.touchingRightEdge && panelContent.touchingTopEdge)
+ return 0 // Both edges: no inversion (normal rounded corner)
+ if (panelContent.touchingRightEdge)
+ return 2 // Right edge: vertical inversion
+ if (panelContent.touchingTopEdge)
+ return 1 // Top edge: horizontal inversion
+ return root.barIsVertical ? 2 : 1
+ }
+ return 0
+ }
+
+ property int bottomLeftCornerState: {
+ var barInverted = panelContent.allowAttachToBar && ((root.barPosition === "bottom" && !root.barIsVertical && root.effectivePanelAnchorBottom) || (root.barPosition === "left" && root.barIsVertical && root.effectivePanelAnchorLeft))
+ var barTouchInverted = panelContent.touchingBottomBar || panelContent.touchingLeftBar
+ // Invert if touching either edge that forms this corner (left OR bottom), regardless of bar position
+ var edgeInverted = panelContent.allowAttach && (panelContent.touchingLeftEdge || panelContent.touchingBottomEdge)
+ var oppositeEdgeInverted = panelContent.allowAttach && (panelContent.touchingBottomEdge && !root.barIsVertical && root.barPosition !== "bottom")
+
+ if (barInverted || barTouchInverted || edgeInverted || oppositeEdgeInverted) {
+ // Determine inversion direction based on which edge is touched
+ if (panelContent.touchingLeftEdge && panelContent.touchingBottomEdge)
+ return 0 // Both edges: no inversion (normal rounded corner)
+ if (panelContent.touchingLeftEdge)
+ return 2 // Left edge: vertical inversion
+ if (panelContent.touchingBottomEdge)
+ return 1 // Bottom edge: horizontal inversion
+ return root.barIsVertical ? 2 : 1
+ }
+ return 0
+ }
+
+ property int bottomRightCornerState: {
+ var barInverted = panelContent.allowAttachToBar && ((root.barPosition === "bottom" && !root.barIsVertical && root.effectivePanelAnchorBottom) || (root.barPosition === "right" && root.barIsVertical && root.effectivePanelAnchorRight))
+ var barTouchInverted = panelContent.touchingBottomBar || panelContent.touchingRightBar
+ // Invert if touching either edge that forms this corner (right OR bottom), regardless of bar position
+ var edgeInverted = panelContent.allowAttach && (panelContent.touchingRightEdge || panelContent.touchingBottomEdge)
+ var oppositeEdgeInverted = panelContent.allowAttach && (panelContent.touchingBottomEdge && !root.barIsVertical && root.barPosition !== "bottom")
+
+ if (barInverted || barTouchInverted || edgeInverted || oppositeEdgeInverted) {
+ // Determine inversion direction based on which edge is touched
+ if (panelContent.touchingRightEdge && panelContent.touchingBottomEdge)
+ return 0 // Both edges: no inversion (normal rounded corner)
+ if (panelContent.touchingRightEdge)
+ return 2 // Right edge: vertical inversion
+ if (panelContent.touchingBottomEdge)
+ return 1 // Bottom edge: horizontal inversion
+ return root.barIsVertical ? 2 : 1
+ }
+ return 0
+ }
+
+ // MouseArea to catch clicks on the panel and prevent them from reaching the background
+ // This prevents closing the panel when clicking inside it
+ MouseArea {
+ anchors.fill: parent
+ z: -1 // Behind content, but on the panel background
+ onClicked: {
+
+ // Accept and ignore - prevents propagation to background
+ }
+ }
+ }
+
+ // Panel top content: Text, icons, etc...
+ Loader {
+ id: contentLoader
+ active: isPanelOpen
+ x: panelBackground.x
+ y: panelBackground.y
+ width: panelBackground.width
+ height: panelBackground.height
+ sourceComponent: root.panelContent
+
+ // When content finishes loading, calculate position then make visible
+ onLoaded: {
+ // Calculate position FIRST so targetX/targetY are set before animation starts
+ // This prevents the panel from animating from (0,0) on first open
+ setPosition()
+
+ // THEN make panel visible on the next frame to ensure all bindings have updated
+ // Qt.callLater defers execution until all current bindings are evaluated
+ Qt.callLater(function () {
+ root.isPanelVisible = true
+ opacityTrigger.start()
+
+ // Start open watchdog timer
+ root.openWatchdogActive = true
+ openWatchdogTimer.start()
+
+ opened()
+ })
+ }
+ }
+ }
+}
diff --git a/Modules/Notification/Notification.qml b/Modules/Notification/Notification.qml
index 5da60b98..f09ef643 100644
--- a/Modules/Notification/Notification.qml
+++ b/Modules/Notification/Notification.qml
@@ -1,11 +1,12 @@
import QtQuick
+import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Services.Notifications
import qs.Commons
-import qs.Services
+import qs.Services.System
import qs.Widgets
// Simple notification popup - displays multiple notifications
@@ -18,7 +19,6 @@ Variants {
required property ShellScreen modelData
- // Access the notification model from the service
property ListModel notificationModel: NotificationService.activeList
// Loader is active when there are notifications
@@ -27,11 +27,10 @@ Variants {
// Keep loader active briefly after last notification to allow animations to complete
Timer {
id: delayTimer
- interval: Style.animationSlow + 200 // Animation duration + buffer
+ interval: Style.animationSlow + 200
repeat: false
}
- // Start delay timer when last notification is removed
Connections {
target: notificationModel
function onCountChanged() {
@@ -42,103 +41,97 @@ Variants {
}
sourceComponent: PanelWindow {
+ id: notifWindow
screen: modelData
- WlrLayershell.namespace: "noctalia-notifications"
- WlrLayershell.layer: (Settings.data.notifications && Settings.data.notifications.overlayLayer) ? WlrLayer.Overlay : WlrLayer.Top
+ WlrLayershell.namespace: "noctalia-notifications-" + (screen?.name || "unknown")
+ WlrLayershell.layer: (Settings.data.notifications?.overlayLayer) ? WlrLayer.Overlay : WlrLayer.Top
+ WlrLayershell.exclusionMode: ExclusionMode.Ignore
color: Color.transparent
- readonly property string location: (Settings.data.notifications && Settings.data.notifications.location) ? Settings.data.notifications.location : "top_right"
- readonly property bool isTop: (location === "top") || (location.length >= 3 && location.substring(0, 3) === "top")
- readonly property bool isBottom: (location === "bottom") || (location.length >= 6 && location.substring(0, 6) === "bottom")
- readonly property bool isLeft: location.indexOf("_left") >= 0
- readonly property bool isRight: location.indexOf("_right") >= 0
- readonly property bool isCentered: (location === "top" || location === "bottom")
+ // Parse location setting
+ readonly property string location: Settings.data.notifications?.location || "top_right"
+ readonly property bool isTop: location.startsWith("top")
+ readonly property bool isBottom: location.startsWith("bottom")
+ readonly property bool isLeft: location.endsWith("_left")
+ readonly property bool isRight: location.endsWith("_right")
+ readonly property bool isCentered: location === "top" || location === "bottom"
- // Store connection for cleanup
- property var animateConnection: null
+ readonly property string barPos: Settings.data.bar.position
+ readonly property bool isFloating: Settings.data.bar.floating
- // Anchor selection based on location (window edges)
+ readonly property int notifWidth: Math.round(400 * Style.uiScaleRatio)
+
+ // Calculate bar offsets for each edge separately
+ readonly property int barOffsetTop: {
+ if (barPos !== "top")
+ return 0
+ const floatMarginV = isFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+ return Style.barHeight + floatMarginV
+ }
+
+ readonly property int barOffsetBottom: {
+ if (barPos !== "bottom")
+ return 0
+ const floatMarginV = isFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+ return Style.barHeight + floatMarginV
+ }
+
+ readonly property int barOffsetLeft: {
+ if (barPos !== "left")
+ return 0
+ const floatMarginH = isFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ return floatMarginH
+ }
+
+ readonly property int barOffsetRight: {
+ if (barPos !== "right")
+ return 0
+ const floatMarginH = isFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ return floatMarginH
+ }
+
+ // Anchoring
anchors.top: isTop
anchors.bottom: isBottom
anchors.left: isLeft
anchors.right: isRight
- // Margins depending on bar position and chosen location
- margins.top: {
- if (!(anchors.top))
- return 0
- var base = Style.marginM
- if (Settings.data.bar.position === "top") {
- var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0
- return (Style.barHeight) + base + floatExtraV
- }
- return base
- }
+ // Margins for PanelWindow - only apply bar offset for the specific edge where the bar is
+ margins.top: isTop ? barOffsetTop : 0
+ margins.bottom: isBottom ? barOffsetBottom : 0
+ margins.left: isLeft ? barOffsetLeft : 0
+ margins.right: isRight ? barOffsetRight : 0
- margins.bottom: {
- if (!(anchors.bottom))
- return 0
- var base = Style.marginM
- if (Settings.data.bar.position === "bottom") {
- var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0
- return (Style.barHeight) + base + floatExtraV
- }
- return base
- }
+ implicitWidth: notifWidth
+ implicitHeight: notificationStack.implicitHeight + Style.marginL
- margins.left: {
- if (!(anchors.left))
- return 0
- var base = Style.marginM
- if (Settings.data.bar.position === "left") {
- var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
- return (Style.barHeight) + base + floatExtraH
- }
- return base
- }
+ property var animateConnection: null
- margins.right: {
- if (!(anchors.right))
- return 0
- var base = Style.marginM
- if (Settings.data.bar.position === "right") {
- var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
- return (Style.barHeight) + base + floatExtraH
- }
- return base
- }
-
- implicitWidth: 360
- implicitHeight: notificationStack.implicitHeight
- WlrLayershell.exclusionMode: ExclusionMode.Ignore
-
- // Connect to animation signal from service
Component.onCompleted: {
- animateConnection = NotificationService.animateAndRemove.connect(function (notificationId) {
- // Find the delegate by notification ID
+ animateConnection = function (notificationId) {
var delegate = null
- if (notificationStack && notificationStack.children && notificationStack.children.length > 0) {
- for (var i = 0; i < notificationStack.children.length; i++) {
- var child = notificationStack.children[i]
- if (child && child.notificationId === notificationId) {
- delegate = child
+ if (notificationRepeater) {
+ for (var i = 0; i < notificationRepeater.count; i++) {
+ var item = notificationRepeater.itemAt(i)
+ if (item?.notificationId === notificationId) {
+ delegate = item
break
}
}
}
- if (delegate && delegate.animateOut) {
+ if (delegate?.animateOut) {
delegate.animateOut()
} else {
- // Force removal without animation as fallback
NotificationService.dismissActiveNotification(notificationId)
}
- })
+ }
+
+ NotificationService.animateAndRemove.connect(animateConnection)
}
- // Disconnect when destroyed to prevent memory leaks
Component.onDestruction: {
if (animateConnection) {
NotificationService.animateAndRemove.disconnect(animateConnection)
@@ -146,20 +139,20 @@ Variants {
}
}
- // Main notification container
ColumnLayout {
id: notificationStack
- // Anchor the stack inside the window based on chosen location
- anchors.top: parent.isTop ? parent.top : undefined
- anchors.bottom: parent.isBottom ? parent.bottom : undefined
- anchors.left: parent.isLeft ? parent.left : undefined
- anchors.right: parent.isRight ? parent.right : undefined
- anchors.horizontalCenter: parent.isCentered ? parent.horizontalCenter : undefined
- spacing: Style.marginS
- width: 360
- visible: true
- // Animate when notifications are added/removed
+ anchors {
+ top: parent.isTop ? parent.top : undefined
+ bottom: parent.isBottom ? parent.bottom : undefined
+ left: parent.isLeft ? parent.left : undefined
+ right: parent.isRight ? parent.right : undefined
+ horizontalCenter: parent.isCentered ? parent.horizontalCenter : undefined
+ }
+
+ spacing: -Style.marginS
+ width: notifWidth
+
Behavior on implicitHeight {
enabled: !Settings.data.general.animationDisabled
SpringAnimation {
@@ -170,90 +163,98 @@ Variants {
}
}
- // Multiple notifications display
Repeater {
+ id: notificationRepeater
model: notificationModel
- delegate: Rectangle {
+
+ delegate: Item {
id: card
- // Store the notification ID and data for reference
property string notificationId: model.id
property var notificationData: model
+ property int hoverCount: 0
+ property bool isRemoving: false
- Layout.preferredWidth: 360
- Layout.preferredHeight: notificationLayout.implicitHeight + (Style.marginL * 2)
+ readonly property int animationDelay: index * 100
+ readonly property int slideDistance: 300
+
+ Layout.preferredWidth: notifWidth
+ Layout.preferredHeight: notificationContent.implicitHeight + Style.marginL * 2
Layout.maximumHeight: Layout.preferredHeight
- radius: Style.radiusL
- border.color: Qt.alpha(Color.mOutline, Settings.data.notifications.backgroundOpacity || 1.0)
- border.width: Style.borderS
- color: Qt.alpha(Color.mSurface, Settings.data.notifications.backgroundOpacity || 1.0)
-
- // Optimized progress bar container
- Rectangle {
- id: progressBarContainer
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.right: parent.right
- height: 2
- color: Color.transparent
- visible: true
-
- // Pre-calculate available width for the progress bar
- readonly property real availableWidth: parent.width - (2 * parent.radius)
-
- // Actual progress bar - centered and symmetric
- Rectangle {
- id: progressBar
- height: parent.height
-
- // Center the bar and make it shrink symmetrically
- x: parent.parent.radius + (parent.availableWidth * (1 - model.progress)) / 2
- width: parent.availableWidth * model.progress
-
- color: {
- var baseColor
- if (model.urgency === NotificationUrgency.Critical || model.urgency === 2)
- baseColor = Color.mError
- else if (model.urgency === NotificationUrgency.Low || model.urgency === 0)
- baseColor = Color.mOnSurface
- else
- baseColor = Color.mPrimary
- return Qt.alpha(baseColor, Settings.data.notifications.backgroundOpacity || 1.0)
- }
-
- antialiasing: true
-
- // Smooth progress animation
- Behavior on width {
- enabled: !card.isRemoving
- NumberAnimation {
- duration: 100 // Quick but smooth
- easing.type: Easing.Linear
- }
- }
-
- Behavior on x {
- enabled: !card.isRemoving
- NumberAnimation {
- duration: 100
- easing.type: Easing.Linear
- }
- }
- }
- }
-
// Animation properties
property real scaleValue: 0.8
property real opacityValue: 0.0
property real slideOffset: 0
- property bool isRemoving: false
- // Staggered animation delay based on index
- readonly property int animationDelay: index * 100
+ scale: scaleValue
+ opacity: opacityValue
+ transform: Translate {
+ y: card.slideOffset
+ }
- property int hoverCount: 0
+ readonly property real slideInOffset: notifWindow.isTop ? -slideDistance : slideDistance
+ readonly property real slideOutOffset: slideInOffset
+ // Background with border
+ Rectangle {
+ id: cardBackground
+ anchors.fill: parent
+ anchors.margins: Style.marginM
+ radius: Style.radiusL
+ border.color: Qt.alpha(Color.mOutline, Settings.data.notifications.backgroundOpacity || 1.0)
+ border.width: Style.borderS
+ color: Qt.alpha(Color.mSurface, Settings.data.notifications.backgroundOpacity || 1.0)
+
+ // Progress bar
+ Rectangle {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 2
+ color: Color.transparent
+
+ readonly property real availableWidth: parent.width - (2 * parent.radius)
+
+ Rectangle {
+ id: progressBar
+ height: parent.height
+ x: parent.parent.radius + (parent.availableWidth * (1 - model.progress)) / 2
+ width: parent.availableWidth * model.progress
+
+ color: {
+ var baseColor = model.urgency === 2 ? Color.mError : model.urgency === 0 ? Color.mOnSurface : Color.mPrimary
+ return Qt.alpha(baseColor, Settings.data.notifications.backgroundOpacity || 1.0)
+ }
+
+ antialiasing: true
+
+ Behavior on width {
+ enabled: !card.isRemoving
+ NumberAnimation {
+ duration: 100
+ easing.type: Easing.Linear
+ }
+ }
+
+ Behavior on x {
+ enabled: !card.isRemoving
+ NumberAnimation {
+ duration: 100
+ easing.type: Easing.Linear
+ }
+ }
+ }
+ }
+ }
+
+ NDropShadows {
+ anchors.fill: cardBackground
+ source: cardBackground
+ autoPaddingEnabled: true
+ }
+
+ // Hover handling
onHoverCountChanged: {
if (hoverCount > 0) {
resumeTimer.stop()
@@ -288,69 +289,49 @@ Variants {
}
}
- // Scale, fade, and slide animation
- scale: scaleValue
- opacity: opacityValue
-
- // Slide animation based on notification position (vertical only for stacking)
- y: slideOffset
-
- // Calculate slide direction based on notification location
- readonly property real slideDistance: 300
- readonly property real slideInOffset: {
- // For vertical stacking, always slide from top
- if (parent.isTop)
- return -slideDistance
- if (parent.isBottom)
- return slideDistance
- return 0
- }
- readonly property real slideOutOffset: {
- // Slide out in the same direction as slide in
- if (parent.isTop)
- return -slideDistance
- if (parent.isBottom)
- return slideDistance
- return 0
- }
-
- // Animate in when the item is created
- Component.onCompleted: {
+ // Animation setup
+ function triggerEntryAnimation() {
+ animInDelayTimer.stop()
+ removalTimer.stop()
+ resumeTimer.stop()
+ isRemoving = false
+ hoverCount = 0
if (Settings.data.general.animationDisabled) {
- // No animation - set to final state immediately
slideOffset = 0
scaleValue = 1.0
opacityValue = 1.0
- } else {
- // Start from slide position
- slideOffset = slideInOffset
- scaleValue = 0.8
- opacityValue = 0.0
-
- // Delay animation based on index for staggered effect
- delayTimer.interval = animationDelay
- delayTimer.start()
+ return
}
+
+ slideOffset = slideInOffset
+ scaleValue = 0.8
+ opacityValue = 0.0
+ animInDelayTimer.interval = animationDelay
+ animInDelayTimer.start()
}
- // Timer for staggered animation start
+ Component.onCompleted: triggerEntryAnimation()
+
+ onNotificationIdChanged: triggerEntryAnimation()
+
Timer {
- id: delayTimer
+ id: animInDelayTimer
interval: 0
repeat: false
onTriggered: {
- // Animate to final position
+ if (card.isRemoving)
+ return
slideOffset = 0
scaleValue = 1.0
opacityValue = 1.0
}
}
- // Animate out when being removed
function animateOut() {
if (isRemoving)
return
- // Prevent multiple animations
+ animInDelayTimer.stop()
+ resumeTimer.stop()
isRemoving = true
if (!Settings.data.general.animationDisabled) {
slideOffset = slideOutOffset
@@ -359,7 +340,6 @@ Variants {
}
}
- // Timer for delayed removal after animation
Timer {
id: removalTimer
interval: Style.animationSlow
@@ -369,14 +349,12 @@ Variants {
}
}
- // Check if this notification is being removed
onIsRemovingChanged: {
if (isRemoving) {
removalTimer.start()
}
}
- // Animation behaviors with spring physics
Behavior on scale {
enabled: !Settings.data.general.animationDisabled
SpringAnimation {
@@ -395,7 +373,7 @@ Variants {
}
}
- Behavior on y {
+ Behavior on slideOffset {
enabled: !Settings.data.general.animationDisabled
SpringAnimation {
spring: 2.5
@@ -405,21 +383,19 @@ Variants {
}
}
+ // Content
ColumnLayout {
- id: notificationLayout
+ id: notificationContent
anchors.fill: parent
anchors.margins: Style.marginM
- anchors.rightMargin: (Style.marginM + 32) // Leave space for close button
spacing: Style.marginM
- // Main content section
RowLayout {
Layout.fillWidth: true
- spacing: Style.marginM
+ spacing: Style.marginL
+ Layout.margins: Style.marginM
ColumnLayout {
- // For real-time notification always show the original image
- // as the cached version is most likely still processing.
NImageCircled {
Layout.preferredWidth: Math.round(40 * Style.uiScaleRatio)
Layout.preferredHeight: Math.round(40 * Style.uiScaleRatio)
@@ -436,12 +412,11 @@ Variants {
}
}
- // Text content
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginS
- // Header section with app name and timestamp
+ // Header with urgency indicator
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
@@ -449,16 +424,9 @@ Variants {
Rectangle {
Layout.preferredWidth: 6
Layout.preferredHeight: 6
- radius: Style.radiusXS
- color: {
- if (model.urgency === NotificationUrgency.Critical || model.urgency === 2)
- return Color.mError
- else if (model.urgency === NotificationUrgency.Low || model.urgency === 0)
- return Color.mOnSurface
- else
- return Color.mPrimary
- }
Layout.alignment: Qt.AlignVCenter
+ radius: Style.radiusXS
+ color: model.urgency === 2 ? Color.mError : model.urgency === 0 ? Color.mOnSurface : Color.mPrimary
}
NText {
@@ -474,15 +442,16 @@ Variants {
NText {
text: model.summary || I18n.tr("general.no-summary")
- pointSize: Style.fontSizeL
+ pointSize: Style.fontSizeM
font.weight: Style.fontWeightMedium
color: Color.mOnSurface
textFormat: Text.PlainText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- Layout.fillWidth: true
maximumLineCount: 3
elide: Text.ElideRight
visible: text.length > 0
+ Layout.fillWidth: true
+ Layout.rightMargin: Style.marginM
}
NText {
@@ -491,25 +460,22 @@ Variants {
color: Color.mOnSurface
textFormat: Text.PlainText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- Layout.fillWidth: true
+
maximumLineCount: 5
elide: Text.ElideRight
visible: text.length > 0
+ Layout.fillWidth: true
+ Layout.rightMargin: Style.marginXL
}
- // Notification actions
+ // Actions
Flow {
Layout.fillWidth: true
spacing: Style.marginS
Layout.topMargin: Style.marginM
-
flow: Flow.LeftToRight
- layoutDirection: Qt.LeftToRight
- // Store the notification ID for access in button delegates
property string parentNotificationId: notificationId
-
- // Parse actions from JSON string
property var parsedActions: {
try {
return model.actionsJson ? JSON.parse(model.actionsJson) : []
@@ -530,7 +496,6 @@ Variants {
text: {
var actionText = actionData.text || "Open"
- // If text contains comma, take the part after the comma (the display text)
if (actionText.includes(",")) {
return actionText.split(",")[1] || actionText
}
@@ -552,15 +517,15 @@ Variants {
}
}
- // Close button positioned absolutely
+ // Close button
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.6
anchors.top: parent.top
- anchors.topMargin: Style.marginM
+ anchors.topMargin: Style.marginXL
anchors.right: parent.right
- anchors.rightMargin: Style.marginM
+ anchors.rightMargin: Style.marginXL
onClicked: {
NotificationService.removeFromHistory(model.id)
diff --git a/Modules/Notification/NotificationHistoryPanel.qml b/Modules/Notification/NotificationHistoryPanel.qml
deleted file mode 100644
index f2336910..00000000
--- a/Modules/Notification/NotificationHistoryPanel.qml
+++ /dev/null
@@ -1,330 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import Quickshell
-import Quickshell.Wayland
-import Quickshell.Services.Notifications
-import qs.Commons
-import qs.Services
-import qs.Widgets
-
-// Notification History panel
-NPanel {
- id: root
-
- preferredWidth: 380
- preferredHeight: 480
- panelKeyboardFocus: true
-
- onOpened: function () {
- NotificationService.updateLastSeenTs()
- }
-
- panelContent: Rectangle {
- id: notificationRect
- color: Color.transparent
-
- ColumnLayout {
- anchors.fill: parent
- anchors.margins: Style.marginL
- spacing: Style.marginM
-
- // Header section
- NBox {
- Layout.fillWidth: true
- implicitHeight: headerRow.implicitHeight + (Style.marginM * 2)
-
- RowLayout {
- id: headerRow
- anchors.fill: parent
- anchors.margins: Style.marginM
- spacing: Style.marginM
-
- NIcon {
- icon: "bell"
- pointSize: Style.fontSizeXXL
- color: Color.mPrimary
- }
-
- NText {
- text: I18n.tr("notifications.panel.title")
- pointSize: Style.fontSizeL
- font.weight: Style.fontWeightBold
- color: Color.mOnSurface
- Layout.fillWidth: true
- }
-
- NIconButton {
- icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell"
- tooltipText: Settings.data.notifications.doNotDisturb ? I18n.tr("tooltips.do-not-disturb-enabled") : I18n.tr("tooltips.do-not-disturb-disabled")
- baseSize: Style.baseWidgetSize * 0.8
- onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
- }
-
- NIconButton {
- icon: "trash"
- tooltipText: I18n.tr("tooltips.clear-history")
- baseSize: Style.baseWidgetSize * 0.8
- onClicked: {
- NotificationService.clearHistory()
- // Close panel as there is nothing more to see.
- root.close()
- }
- }
-
- NIconButton {
- icon: "close"
- tooltipText: I18n.tr("tooltips.close")
- baseSize: Style.baseWidgetSize * 0.8
- onClicked: root.close()
- }
- }
- }
-
- // Empty state when no notifications
- ColumnLayout {
- Layout.fillWidth: true
- Layout.fillHeight: true
- Layout.alignment: Qt.AlignHCenter
- visible: NotificationService.historyList.count === 0
- spacing: Style.marginL
-
- Item {
- Layout.fillHeight: true
- }
-
- NIcon {
- icon: "bell-off"
- pointSize: 64
- color: Color.mOnSurfaceVariant
- Layout.alignment: Qt.AlignHCenter
- }
-
- NText {
- text: I18n.tr("notifications.panel.no-notifications")
- pointSize: Style.fontSizeL
- color: Color.mOnSurfaceVariant
- Layout.alignment: Qt.AlignHCenter
- }
-
- NText {
- text: I18n.tr("notifications.panel.description")
- pointSize: Style.fontSizeS
- color: Color.mOnSurfaceVariant
- Layout.alignment: Qt.AlignHCenter
- Layout.fillWidth: true
- wrapMode: Text.Wrap
- horizontalAlignment: Text.AlignHCenter
- }
-
- Item {
- Layout.fillHeight: true
- }
- }
-
- // Notification list
- NListView {
- id: notificationList
- Layout.fillWidth: true
- Layout.fillHeight: true
- horizontalPolicy: ScrollBar.AlwaysOff
- verticalPolicy: ScrollBar.AsNeeded
-
- model: NotificationService.historyList
- spacing: Style.marginM
- clip: true
- boundsBehavior: Flickable.StopAtBounds
- visible: NotificationService.historyList.count > 0
-
- // Track which notification is expanded
- property string expandedId: ""
-
- delegate: Rectangle {
- property string notificationId: model.id
- property bool isExpanded: notificationList.expandedId === notificationId
-
- width: notificationList.width
- height: notificationLayout.implicitHeight + (Style.marginM * 2)
- radius: Style.radiusM
- color: Color.mSurfaceVariant
- border.color: Qt.alpha(Color.mOutline, Style.opacityMedium)
- border.width: Style.borderS
-
- Behavior on height {
- enabled: !Settings.data.general.animationDisabled
- NumberAnimation {
- duration: Style.animationNormal
- easing.type: Easing.InOutQuad
- }
- }
-
- // Smooth color transition on hover
- Behavior on color {
- enabled: !Settings.data.general.animationDisabled
- ColorAnimation {
- duration: Style.animationFast
- }
- }
-
- // Click to expand/collapse
- MouseArea {
- anchors.fill: parent
- // Don't capture clicks on the delete button
- anchors.rightMargin: 48
- enabled: (summaryText.truncated || bodyText.truncated)
- onClicked: {
- if (notificationList.expandedId === notificationId) {
- notificationList.expandedId = ""
- } else {
- notificationList.expandedId = notificationId
- }
- }
- cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
- }
-
- RowLayout {
- id: notificationLayout
- anchors.fill: parent
- anchors.margins: Style.marginM
- spacing: Style.marginM
-
- ColumnLayout {
- NImageCircled {
- Layout.preferredWidth: Math.round(40 * Style.uiScaleRatio)
- Layout.preferredHeight: Math.round(40 * Style.uiScaleRatio)
- Layout.alignment: Qt.AlignTop
- Layout.topMargin: 20
- imagePath: model.cachedImage || model.originalImage || ""
- borderColor: Color.transparent
- borderWidth: 0
- fallbackIcon: "bell"
- fallbackIconSize: 24
- }
- Item {
- Layout.fillHeight: true
- }
- }
-
- // Notification content column
- ColumnLayout {
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignTop
- spacing: Style.marginXS
- Layout.rightMargin: -(Style.marginM + Style.baseWidgetSize * 0.6)
-
- // Header row with app name and timestamp
- RowLayout {
- Layout.fillWidth: true
- spacing: Style.marginS
-
- // Urgency indicator
- Rectangle {
- Layout.preferredWidth: 6
- Layout.preferredHeight: 6
- Layout.alignment: Qt.AlignVCenter
- radius: 3
- visible: model.urgency !== 1
- color: {
- if (model.urgency === 2)
- return Color.mError
- else if (model.urgency === 0)
- return Color.mOnSurfaceVariant
- else
- return Color.transparent
- }
- }
-
- NText {
- text: model.appName || "Unknown App"
- pointSize: Style.fontSizeXS
- color: Color.mSecondary
- }
-
- NText {
- text: Time.formatRelativeTime(model.timestamp)
- pointSize: Style.fontSizeXS
- color: Color.mSecondary
- }
-
- Item {
- Layout.fillWidth: true
- }
- }
-
- // Summary
- NText {
- id: summaryText
- text: model.summary || I18n.tr("general.no-summary")
- pointSize: Style.fontSizeM
- font.weight: Font.Medium
- color: Color.mOnSurface
- textFormat: Text.PlainText
- wrapMode: Text.Wrap
- Layout.fillWidth: true
- maximumLineCount: isExpanded ? 999 : 2
- elide: Text.ElideRight
- }
-
- // Body
- NText {
- id: bodyText
- text: model.body || ""
- pointSize: Style.fontSizeS
- color: Color.mOnSurfaceVariant
- textFormat: Text.PlainText
- wrapMode: Text.Wrap
- Layout.fillWidth: true
- maximumLineCount: isExpanded ? 999 : 3
- elide: Text.ElideRight
- visible: text.length > 0
- }
-
- // Spacer for expand indicator
- Item {
- Layout.fillWidth: true
- Layout.preferredHeight: (!isExpanded && (summaryText.truncated || bodyText.truncated)) ? (Style.marginS) : 0
- }
-
- // Expand indicator
- RowLayout {
- Layout.fillWidth: true
- visible: !isExpanded && (summaryText.truncated || bodyText.truncated)
- spacing: Style.marginXS
-
- Item {
- Layout.fillWidth: true
- }
-
- NText {
- text: I18n.tr("notifications.panel.click-to-expand") || "Click to expand"
- pointSize: Style.fontSizeXS
- color: Color.mPrimary
- font.weight: Font.Medium
- }
-
- NIcon {
- icon: "chevron-down"
- pointSize: Style.fontSizeS
- color: Color.mPrimary
- }
- }
- }
-
- // Delete button
- NIconButton {
- icon: "trash"
- tooltipText: I18n.tr("tooltips.delete-notification")
- baseSize: Style.baseWidgetSize * 0.7
- Layout.alignment: Qt.AlignTop
-
- onClicked: {
- // Remove from history using the service API
- NotificationService.removeFromHistory(notificationId)
- }
- }
- }
- }
- }
- }
- }
-}
diff --git a/Modules/OSD/OSD.qml b/Modules/OSD/OSD.qml
index c460b672..248f6ac8 100644
--- a/Modules/OSD/OSD.qml
+++ b/Modules/OSD/OSD.qml
@@ -1,11 +1,14 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
+import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Services
import qs.Widgets
+import qs.Services.Hardware
+import qs.Services.Media
+import qs.Services.System
// Unified OSD component
// Loader activates only when showing OSD, deactivates when hidden to save resources
@@ -72,17 +75,29 @@ Variants {
return 0
}
+ // Get maximum value for current OSD type
+ function getMaxValue() {
+ if (currentOSDType === "volume" || currentOSDType === "inputVolume") {
+ return Settings.data.audio.volumeOverdrive ? 1.5 : 1.0
+ } else if (currentOSDType === "brightness") {
+ return 1.0
+ }
+ return 1.0
+ }
+
// Get display percentage
function getDisplayPercentage() {
if (currentOSDType === "volume") {
if (isMuted)
return "0%"
- const pct = Math.round(Math.min(1.0, currentVolume) * 100)
+ const max = getMaxValue()
+ const pct = Math.round(Math.min(max, currentVolume) * 100)
return pct + "%"
} else if (currentOSDType === "inputVolume") {
if (isInputMuted)
return "0%"
- const pct = Math.round(Math.min(1.0, currentInputVolume) * 100)
+ const max = getMaxValue()
+ const pct = Math.round(Math.min(max, currentInputVolume) * 100)
return pct + "%"
} else if (currentOSDType === "brightness") {
const pct = Math.round(Math.min(1.0, currentBrightness) * 100)
@@ -125,8 +140,10 @@ Variants {
readonly property bool isCentered: (location === "top" || location === "bottom")
readonly property bool verticalMode: (location === "left" || location === "right")
readonly property int hWidth: Math.round(320 * Style.uiScaleRatio)
- readonly property int hHeight: Math.round(64 * Style.uiScaleRatio)
- readonly property int vHeight: hWidth // Vertical OSD height (matches horizontal width)
+ readonly property int hHeight: Math.round(72 * Style.uiScaleRatio)
+ readonly property int vWidth: Math.round(72 * Style.uiScaleRatio)
+ readonly property int vHeight: Math.round(280 * Style.uiScaleRatio)
+
// Ensure an even width to keep the vertical bar perfectly centered
readonly property int barThickness: {
const base = Math.max(8, Math.round(8 * Style.uiScaleRatio))
@@ -184,8 +201,8 @@ Variants {
return base
}
- implicitWidth: verticalMode ? hHeight : hWidth
- implicitHeight: osdItem.height
+ implicitWidth: verticalMode ? vWidth : hWidth
+ implicitHeight: verticalMode ? vHeight : hHeight
color: Color.transparent
@@ -193,27 +210,17 @@ Variants {
WlrLayershell.layer: (Settings.data.osd && Settings.data.osd.overlayLayer) ? WlrLayer.Overlay : WlrLayer.Top
exclusionMode: PanelWindow.ExclusionMode.Ignore
- Rectangle {
+ // Rectangle {
+ // anchors.fill: parent
+ // color: "#4400FF00"
+ // }
+ Item {
id: osdItem
-
- width: parent.width
- height: panel.verticalMode ? panel.vHeight : Math.round(64 * Style.uiScaleRatio)
- radius: Style.radiusL
- color: Color.mSurface
- border.color: Color.mOutline
- border.width: {
- const bw = Math.max(2, Style.borderM)
- return (bw % 2 === 0) ? bw : bw + 1
- }
+ anchors.fill: parent
visible: false
opacity: 0
scale: 0.85 // initial scale for a little zoom effect
- // Only horizontally center when the window itself is centered (top/bottom positions)
- // For left/right vertical mode, fill the parent width
- anchors.horizontalCenter: (!panel.verticalMode && panel.isCentered) ? parent.horizontalCenter : undefined
- anchors.verticalCenter: panel.verticalMode ? parent.verticalCenter : undefined
-
Behavior on opacity {
NumberAnimation {
id: opacityAnimation
@@ -248,11 +255,33 @@ Variants {
}
}
+ // Background rectangle (source for the effect)
+ Rectangle {
+ id: background
+ anchors.fill: parent
+ anchors.margins: Style.marginM * 1.5
+ radius: Style.radiusL
+ color: Color.mSurface
+ border.color: Color.mOutline
+ border.width: {
+ const bw = Math.max(2, Style.borderM)
+ return (bw % 2 === 0) ? bw : bw + 1
+ }
+ }
+
+ NDropShadows {
+ anchors.fill: background
+ source: background
+ autoPaddingEnabled: true
+ }
+
+ // Content loader on top of the background (not affected by MultiEffect)
Loader {
id: contentLoader
- anchors.fill: parent
+ anchors.fill: background
+ anchors.margins: Style.marginM
active: true
- sourceComponent: verticalMode ? verticalContent : horizontalContent
+ sourceComponent: panel.verticalMode ? verticalContent : horizontalContent
}
Component {
@@ -287,13 +316,13 @@ Variants {
height: panel.barThickness
radius: Math.round(panel.barThickness / 2)
color: Color.mSurfaceVariant
- Layout.alignment: Qt.AlignVCenter
+ width: parent.width * 0.6
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
- width: parent.width * Math.min(1.0, root.getCurrentValue())
+ width: parent.width * Math.min(1.0, root.getCurrentValue() / root.getMaxValue())
radius: parent.radius
color: root.getProgressColor()
@@ -380,7 +409,7 @@ Variants {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
- height: parent.height * Math.min(1.0, root.getCurrentValue())
+ height: parent.height * Math.min(1.0, root.getCurrentValue() / root.getMaxValue())
radius: parent.radius
color: root.getProgressColor()
diff --git a/Modules/Bar/Audio/AudioPanel.qml b/Modules/Panels/Audio/AudioPanel.qml
similarity index 93%
rename from Modules/Bar/Audio/AudioPanel.qml
rename to Modules/Panels/Audio/AudioPanel.qml
index dc4e1779..6d857791 100644
--- a/Modules/Bar/Audio/AudioPanel.qml
+++ b/Modules/Panels/Audio/AudioPanel.qml
@@ -4,10 +4,11 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Services.Pipewire
import qs.Commons
-import qs.Services
import qs.Widgets
+import qs.Modules.MainScreen
+import qs.Services.Media
-NPanel {
+SmartPanel {
id: root
property real localOutputVolume: AudioService.volume || 0
@@ -16,9 +17,8 @@ NPanel {
property real localInputVolume: AudioService.inputVolume || 0
property bool localInputVolumeChanging: false
- preferredWidth: 380 * Style.uiScaleRatio
- preferredHeight: 500 * Style.uiScaleRatio
- panelKeyboardFocus: true
+ preferredWidth: Math.round(340 * Style.uiScaleRatio)
+ preferredHeight: Math.round(420 * Style.uiScaleRatio)
// Connections to update local volumes when AudioService changes
Connections {
@@ -54,10 +54,13 @@ NPanel {
}
}
- panelContent: Rectangle {
- color: Color.transparent
+ panelContent: Item {
+ // Use implicitHeight from content + margins to avoid binding loops
+ property real contentPreferredHeight: mainColumn.implicitHeight + Style.marginL * 2
+ // property real contentPreferredHeight: Math.min(screen.height * 0.42, mainColumn.implicitHeight) + Style.marginL * 2
ColumnLayout {
+ id: mainColumn
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginM
diff --git a/Modules/Bar/Battery/BatteryPanel.qml b/Modules/Panels/Battery/BatteryPanel.qml
similarity index 51%
rename from Modules/Bar/Battery/BatteryPanel.qml
rename to Modules/Panels/Battery/BatteryPanel.qml
index 8f4d2161..f9544e26 100644
--- a/Modules/Bar/Battery/BatteryPanel.qml
+++ b/Modules/Panels/Battery/BatteryPanel.qml
@@ -4,16 +4,13 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Services
+import qs.Services.Hardware
import qs.Widgets
+import qs.Modules.MainScreen
-NPanel {
+SmartPanel {
id: root
- preferredWidth: 350 * Style.uiScaleRatio
- preferredHeight: 250 * Style.uiScaleRatio
- panelKeyboardFocus: true
-
property var optionsModel: []
function updateOptionsModel() {
@@ -34,11 +31,66 @@ NPanel {
updateOptionsModel()
}
+ ButtonGroup {
+ id: batteryGroup
+ }
+
+ Component {
+ id: optionsComponent
+ ColumnLayout {
+ spacing: Style.marginM
+ Repeater {
+ model: root.optionsModel
+ delegate: NRadioButton {
+ ButtonGroup.group: batteryGroup
+ required property var modelData
+ text: I18n.tr(modelData.label, {
+ "percent": BatteryService.getThresholdValue(modelData.id)
+ })
+ checked: BatteryService.chargingMode === modelData.id
+ onClicked: {
+ BatteryService.setChargingMode(modelData.id)
+ }
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+
+ Component {
+ id: disabledComponent
+
+ ColumnLayout {
+ anchors.centerIn: parent
+ spacing: Style.marginM
+
+ NIcon {
+ icon: "recharging"
+ pointSize: 48
+ color: Color.mOnSurfaceVariant
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ NText {
+ text: I18n.tr("battery.panel.disabled")
+ pointSize: Style.fontSizeL
+ color: Color.mOnSurfaceVariant
+ wrapMode: Text.WordWrap
+ Layout.alignment: Qt.AlignHCenter
+ }
+ }
+ }
+
panelContent: Item {
anchors.fill: parent
+ property real contentPreferredWidth: Math.round(340 * Style.uiScaleRatio)
+ property real contentPreferredHeight: Math.round(mainLayout.implicitHeight + Style.marginM * 2)
+
ColumnLayout {
- anchors.fill: parent
+ id: mainLayout
+ anchors.centerIn: parent
+ width: parent.contentPreferredWidth - Style.marginM * 2
anchors.margins: Style.marginM
spacing: Style.marginM
@@ -53,6 +105,12 @@ NPanel {
anchors.margins: Style.marginM
spacing: Style.marginM
+ NIcon {
+ icon: "battery-4"
+ pointSize: Style.fontSizeXXL
+ color: Color.mPrimary
+ }
+
NText {
text: I18n.tr("battery.panel.title")
pointSize: Style.fontSizeL
@@ -79,57 +137,15 @@ NPanel {
}
}
- ButtonGroup {
- id: batteryGroup
- }
-
NBox {
Layout.fillWidth: true
- Layout.fillHeight: true
+ Layout.preferredHeight: loader.implicitHeight + Style.marginM * 2
- ColumnLayout {
- anchors.fill: parent
- anchors.margins: Style.marginM
- spacing: Style.marginM
-
- Repeater {
- model: optionsModel
-
- NRadioButton {
- visible: BatteryService.chargingMode !== BatteryService.ChargingMode.Disabled
- ButtonGroup.group: batteryGroup
- required property var modelData
- text: I18n.tr(modelData.label, {
- "percent": BatteryService.getThresholdValue(modelData.id)
- })
- checked: BatteryService.chargingMode === modelData.id
- onClicked: {
- BatteryService.setChargingMode(modelData.id)
- }
- Layout.fillWidth: true
- }
- }
- }
-
- ColumnLayout {
- visible: BatteryService.chargingMode === BatteryService.ChargingMode.Disabled
- anchors.fill: parent
- spacing: Style.marginM
-
- Item {
- Layout.fillHeight: true
- }
-
- NText {
- text: I18n.tr("battery.panel.disabled")
- pointSize: Style.fontSizeL
- color: Color.mOnSurfaceVariant
- Layout.alignment: Qt.AlignHCenter
- }
-
- Item {
- Layout.fillHeight: true
- }
+ Loader {
+ id: loader
+ anchors.centerIn: parent
+ width: parent.width - Style.marginM * 2
+ sourceComponent: BatteryService.chargingMode === BatteryService.ChargingMode.Disabled ? disabledComponent : optionsComponent
}
}
}
diff --git a/Modules/Bar/Bluetooth/BluetoothDevicesList.qml b/Modules/Panels/Bluetooth/BluetoothDevicesList.qml
similarity index 95%
rename from Modules/Bar/Bluetooth/BluetoothDevicesList.qml
rename to Modules/Panels/Bluetooth/BluetoothDevicesList.qml
index f2721e6a..147f1042 100644
--- a/Modules/Bar/Bluetooth/BluetoothDevicesList.qml
+++ b/Modules/Panels/Bluetooth/BluetoothDevicesList.qml
@@ -5,7 +5,7 @@ import Quickshell
import Quickshell.Bluetooth
import Quickshell.Wayland
import qs.Commons
-import qs.Services
+import qs.Services.Networking
import qs.Widgets
NBox {
@@ -161,15 +161,15 @@ NBox {
tooltipText: root.tooltipText
text: {
if (modelData.pairing) {
- return "Pairing..."
+ return I18n.tr("bluetooth.panel.pairing")
}
if (modelData.blocked) {
- return "Blocked"
+ return I18n.tr("bluetooth.panel.blocked")
}
if (modelData.connected) {
- return "Disconnect"
+ return I18n.tr("bluetooth.panel.disconnect")
}
- return "Connect"
+ return I18n.tr("bluetooth.panel.connect")
}
icon: (isBusy ? "busy" : null)
onClicked: {
diff --git a/Modules/Bar/Bluetooth/BluetoothPanel.qml b/Modules/Panels/Bluetooth/BluetoothPanel.qml
similarity index 86%
rename from Modules/Bar/Bluetooth/BluetoothPanel.qml
rename to Modules/Panels/Bluetooth/BluetoothPanel.qml
index 18563c0d..d5661867 100644
--- a/Modules/Bar/Bluetooth/BluetoothPanel.qml
+++ b/Modules/Panels/Bluetooth/BluetoothPanel.qml
@@ -3,22 +3,25 @@ import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Bluetooth
-import Quickshell.Wayland
import qs.Commons
-import qs.Services
+import qs.Services.UI
+import qs.Services.Networking
import qs.Widgets
+import qs.Modules.MainScreen
-NPanel {
+SmartPanel {
id: root
- preferredWidth: 420 * Style.uiScaleRatio
- preferredHeight: 500 * Style.uiScaleRatio
- panelKeyboardFocus: true
+ preferredWidth: Math.round(400 * Style.uiScaleRatio)
+ preferredHeight: Math.round(500 * Style.uiScaleRatio)
panelContent: Rectangle {
color: Color.transparent
+ property real contentPreferredHeight: !(BluetoothService.adapter && BluetoothService.adapter.enabled) ? Math.min(preferredHeight, Math.max(280 * Style.uiScaleRatio, mainColumn.implicitHeight + Style.marginL * 2)) : (mainColumn.implicitHeight + Style.marginL * 2)
+
ColumnLayout {
+ id: mainColumn
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginM
@@ -26,13 +29,12 @@ NPanel {
// Header
NBox {
Layout.fillWidth: true
- implicitHeight: headerRow.implicitHeight + (Style.marginM * 2)
+ Layout.preferredHeight: headerRow.implicitHeight + Style.marginM * 2
RowLayout {
id: headerRow
anchors.fill: parent
- anchors.leftMargin: Style.marginM
- anchors.rightMargin: Style.marginM
+ anchors.margins: Style.marginM
spacing: Style.marginM
NIcon {
@@ -87,12 +89,16 @@ NPanel {
// Center the content within this rectangle
ColumnLayout {
- anchors.centerIn: parent
+ anchors.fill: parent
spacing: Style.marginM
+ Item {
+ Layout.fillHeight: true
+ }
+
NIcon {
icon: "bluetooth-off"
- pointSize: 64
+ pointSize: 48
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
@@ -108,7 +114,13 @@ NPanel {
text: I18n.tr("bluetooth.panel.enable-message")
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
- Layout.alignment: Qt.AlignHCenter
+ horizontalAlignment: Text.AlignHCenter
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
+ }
+
+ Item {
+ Layout.fillHeight: true
}
}
}
@@ -220,7 +232,9 @@ NPanel {
text: I18n.tr("bluetooth.panel.pairing-mode")
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
- Layout.alignment: Qt.AlignHCenter
+ horizontalAlignment: Text.AlignHCenter
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
}
}
}
diff --git a/Modules/Bar/Calendar/CalendarPanel.qml b/Modules/Panels/Calendar/CalendarPanel.qml
similarity index 94%
rename from Modules/Bar/Calendar/CalendarPanel.qml
rename to Modules/Panels/Calendar/CalendarPanel.qml
index 7df32214..a01f7836 100644
--- a/Modules/Bar/Calendar/CalendarPanel.qml
+++ b/Modules/Panels/Calendar/CalendarPanel.qml
@@ -4,17 +4,19 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Modules.ControlCenter.Cards
-import qs.Services
+import qs.Modules.MainScreen
+import qs.Modules.Panels.ControlCenter.Cards
+import qs.Services.Location
+import qs.Services.UI
import qs.Widgets
-NPanel {
+SmartPanel {
id: root
- property ShellScreen screen
- readonly property var now: Time.date
+ readonly property var now: Time.now
- panelKeyboardFocus: true
+ preferredWidth: Math.round(440 * Style.uiScaleRatio)
+ preferredHeight: Math.round(700 * Style.uiScaleRatio)
// Helper function to calculate ISO week number
function getISOWeekNumber(date) {
@@ -31,9 +33,12 @@ NPanel {
panelContent: Item {
anchors.fill: parent
- // Dynamic sizing properties that NPanel will bind to
+ // Dynamic sizing properties that SmartPanel will bind to
property real contentPreferredWidth: (Settings.data.location.showWeekNumberInCalendar ? 400 : 380) * Style.uiScaleRatio
+ // Use implicitHeight from content + margins to avoid binding loops
+ property real contentPreferredHeight: content.implicitHeight + Style.marginL * 2
+
property real calendarGridHeight: {
// Calculate number of weeks in the calendar grid
const numWeeks = grid.daysModel ? Math.ceil(grid.daysModel.length / 7) : 5
@@ -43,9 +48,6 @@ NPanel {
return numWeeks * rowHeight
}
-
- property real contentPreferredHeight: banner.height + calendar.height + weatherLoader.height + Style.marginM * 4 + (Settings.data.location.weatherEnabled && Settings.data.location.showCalendarWeather) * Style.marginM
-
ColumnLayout {
id: content
anchors.fill: parent
@@ -58,30 +60,16 @@ NPanel {
readonly property bool weatherReady: Settings.data.location.weatherEnabled && (LocationService.data.weather !== null)
function checkIsCurrentMonth() {
- return (Time.date.getMonth() === grid.month) && (Time.date.getFullYear() === grid.year)
+ return (now.getMonth() === grid.month) && (now.getFullYear() === grid.year)
}
Component.onCompleted: {
isCurrentMonth = checkIsCurrentMonth()
}
- Shortcut {
- sequence: "Escape"
- onActivated: {
- if (timerActive) {
- cancelTimer()
- } else {
- cancelTimer()
- root.close()
- }
- }
- context: Qt.WidgetShortcut
- enabled: root.opened
- }
-
Connections {
target: Time
- function onDateChanged() {
+ function onNowChanged() {
content.isCurrentMonth = content.checkIsCurrentMonth()
}
}
@@ -130,7 +118,7 @@ NPanel {
clip: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
- text: Time.date.getDate()
+ text: now.getDate()
pointSize: Style.fontSizeXXXL * 1.5
font.weight: Style.fontWeightBold
color: Color.mOnPrimary
@@ -213,11 +201,12 @@ NPanel {
}
// Analog clock
- ClockLoader {
+ NClock {
id: clockLoader
anchors.right: parent.right
anchors.rightMargin: Style.marginXL
anchors.verticalCenter: parent.verticalCenter
+ clockStyle: Settings.data.location.analogClockInCalendar ? "analog" : "digital"
progressColor: Color.mOnPrimary
Layout.alignment: Qt.AlignVCenter
now: root.now
@@ -277,8 +266,8 @@ NPanel {
NIconButton {
icon: "calendar"
onClicked: {
- grid.month = Time.date.getMonth()
- grid.year = Time.date.getFullYear()
+ grid.month = now.getMonth()
+ grid.year = now.getFullYear()
content.isCurrentMonth = true
CalendarService.loadEvents()
}
@@ -484,8 +473,8 @@ NPanel {
columnSpacing: Style.marginXXS
rowSpacing: Style.marginXXS
- property int month: Time.date.getMonth()
- property int year: Time.date.getFullYear()
+ property int month: now.getMonth()
+ property int year: now.getFullYear()
Behavior on Layout.preferredHeight {
NumberAnimation {
@@ -622,8 +611,10 @@ NPanel {
onClicked: {
const dateWithSlashes = `${(modelData.month + 1).toString().padStart(2, '0')}/${modelData.day.toString().padStart(2, '0')}/${modelData.year.toString().substring(2)}`
- Quickshell.execDetached(["gnome-calendar", "--date", dateWithSlashes])
- PanelService.getPanel("calendarPanel").toggle(null)
+ if (ProgramCheckerService.gnomeCalendarAvailable) {
+ Quickshell.execDetached(["gnome-calendar", "--date", dateWithSlashes])
+ root.close()
+ }
}
onExited: {
@@ -651,7 +642,7 @@ NPanel {
sourceComponent: WeatherCard {
Layout.fillWidth: true
- forecastDays: 6
+ forecastDays: 5
showLocation: false
}
}
diff --git a/Modules/ControlCenter/Cards/AudioCard.qml b/Modules/Panels/ControlCenter/Cards/AudioCard.qml
similarity index 99%
rename from Modules/ControlCenter/Cards/AudioCard.qml
rename to Modules/Panels/ControlCenter/Cards/AudioCard.qml
index 3370b71f..47c35711 100644
--- a/Modules/ControlCenter/Cards/AudioCard.qml
+++ b/Modules/Panels/ControlCenter/Cards/AudioCard.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Media
import qs.Widgets
// Audio controls card: output and input volume controls
diff --git a/Modules/Panels/ControlCenter/Cards/MediaCard.qml b/Modules/Panels/ControlCenter/Cards/MediaCard.qml
new file mode 100644
index 00000000..7b922fbb
--- /dev/null
+++ b/Modules/Panels/ControlCenter/Cards/MediaCard.qml
@@ -0,0 +1,360 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Effects
+import Quickshell
+import qs.Commons
+import qs.Services.Media
+import qs.Services.UI
+import qs.Widgets
+import qs.Widgets.AudioSpectrum
+
+NBox {
+ id: root
+
+ // Track whether we have an active media player
+ readonly property bool hasActivePlayer: MediaService.currentPlayer && MediaService.canPlay
+
+ // Wrapper - rounded rect clipper
+ Item {
+ anchors.fill: parent
+ layer.enabled: true
+ layer.smooth: true
+ layer.effect: MultiEffect {
+ maskEnabled: true
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 0.0
+ maskSource: ShaderEffectSource {
+ sourceItem: Rectangle {
+ width: root.width
+ height: root.height
+ radius: Style.radiusM
+ color: "white"
+ }
+ }
+ }
+
+ // Background image that covers everything
+ Image {
+ readonly property int dim: Math.round(256 * Style.uiScaleRatio)
+ id: bgImage
+ anchors.fill: parent
+ source: MediaService.trackArtUrl || WallpaperService.getWallpaper(screen.name)
+ sourceSize: Qt.size(dim, dim)
+ fillMode: Image.PreserveAspectCrop
+ }
+
+ // Dark overlay for readability
+ Rectangle {
+ anchors.fill: parent
+ color: Color.mSurfaceVariant
+ opacity: 0.85
+ radius: Style.radiusM
+ }
+
+ // Border
+ Rectangle {
+ anchors.fill: parent
+ color: Color.transparent
+ border.color: Color.mOutline
+ border.width: 1
+ radius: Style.radiusM
+ }
+
+ // Background visualizer on top of the artwork
+ Loader {
+ anchors.fill: parent
+ active: Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
+
+ sourceComponent: {
+ switch (Settings.data.audio.visualizerType) {
+ case "linear":
+ return linearComponent
+ case "mirrored":
+ return mirroredComponent
+ case "wave":
+ return waveComponent
+ default:
+ return null
+ }
+ }
+
+ Component {
+ id: linearComponent
+ NLinearSpectrum {
+ anchors.fill: parent
+ values: CavaService.values
+ fillColor: Color.mPrimary
+ opacity: 0.5
+ }
+ }
+
+ Component {
+ id: mirroredComponent
+ NMirroredSpectrum {
+ anchors.fill: parent
+ values: CavaService.values
+ fillColor: Color.mPrimary
+ opacity: 0.5
+ }
+ }
+
+ Component {
+ id: waveComponent
+ NWaveSpectrum {
+ anchors.fill: parent
+ values: CavaService.values
+ fillColor: Color.mPrimary
+ opacity: 0.5
+ }
+ }
+ }
+ }
+
+ // Player selector
+ Rectangle {
+ id: playerSelectorButton
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.topMargin: Style.marginXS
+ anchors.leftMargin: Style.marginM
+ anchors.rightMargin: Style.marginM
+ height: Style.barHeight
+ visible: MediaService.getAvailablePlayers().length > 1
+ radius: Style.radiusM
+ color: Color.transparent
+
+ property var currentPlayer: MediaService.getAvailablePlayers()[MediaService.selectedPlayerIndex]
+
+ RowLayout {
+ anchors.fill: parent
+ spacing: Style.marginS
+
+ NIcon {
+ icon: "caret-down"
+ pointSize: Style.fontSizeXXL
+ color: Color.mOnSurfaceVariant
+ }
+
+ NText {
+ text: playerSelectorButton.currentPlayer ? playerSelectorButton.currentPlayer.identity : ""
+ pointSize: Style.fontSizeXS
+ color: Color.mOnSurfaceVariant
+ Layout.fillWidth: true
+ }
+ }
+
+ MouseArea {
+ id: playerSelectorMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ var menuItems = []
+ var players = MediaService.getAvailablePlayers()
+ for (var i = 0; i < players.length; i++) {
+ menuItems.push({
+ "label": players[i].identity,
+ "action": i.toString(),
+ "icon": "disc",
+ "enabled": true,
+ "visible": true
+ })
+ }
+ playerContextMenu.model = menuItems
+ playerContextMenu.openAtItem(playerSelectorButton, playerSelectorButton.width - playerContextMenu.width, playerSelectorButton.height)
+ }
+ }
+
+ NContextMenu {
+ id: playerContextMenu
+ parent: root
+ width: 200
+ verticalPolicy: ScrollBar.AlwaysOff
+
+ onTriggered: function (action) {
+ var index = parseInt(action)
+ if (!isNaN(index)) {
+ MediaService.switchToPlayer(index)
+ }
+ }
+ }
+ }
+
+ // Content container that adjusts for player selector
+ Item {
+ anchors.fill: parent
+ anchors.topMargin: playerSelectorButton.visible ? (playerSelectorButton.height + Style.marginXS + Style.marginM) : Style.marginM
+ anchors.leftMargin: Style.marginM
+ anchors.rightMargin: Style.marginM
+ anchors.bottomMargin: Style.marginM
+
+ // No media player detected - centered disc icon
+ NIcon {
+ anchors.centerIn: parent
+ visible: !root.hasActivePlayer
+ icon: "disc"
+ pointSize: Style.fontSizeXXXL * 3
+ color: Color.mOnSurfaceVariant
+ opacity: 1.0
+ }
+
+ // MediaPlayer Main Content - use Loader for performance
+ Loader {
+ id: mainLoader
+ anchors.fill: parent
+ active: root.hasActivePlayer
+
+ sourceComponent: ColumnLayout {
+ id: main
+ spacing: Style.marginS
+
+ // Spacer to push content down
+ Item {
+ Layout.preferredHeight: Style.marginM
+ }
+
+ // Metadata at the bottom left
+ ColumnLayout {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft
+ spacing: Style.marginXS
+
+ NText {
+ visible: MediaService.trackTitle !== ""
+ text: MediaService.trackTitle
+ pointSize: Style.fontSizeL
+ font.weight: Style.fontWeightBold
+ elide: Text.ElideRight
+ wrapMode: Text.Wrap
+ maximumLineCount: 2
+ Layout.fillWidth: true
+ }
+
+ NText {
+ visible: MediaService.trackArtist !== ""
+ text: MediaService.trackArtist
+ color: Color.mPrimary
+ pointSize: Style.fontSizeS
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+
+ NText {
+ visible: MediaService.trackAlbum !== ""
+ text: MediaService.trackAlbum
+ color: Color.mOnSurfaceVariant
+ pointSize: Style.fontSizeM
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+ }
+
+ // Progress slider
+ Item {
+ id: progressWrapper
+ visible: (MediaService.currentPlayer && MediaService.trackLength > 0)
+ Layout.fillWidth: true
+ height: Style.baseWidgetSize * 0.5
+
+ property real localSeekRatio: -1
+ property real lastSentSeekRatio: -1
+ property real seekEpsilon: 0.01
+ property real progressRatio: {
+ if (!MediaService.currentPlayer || MediaService.trackLength <= 0)
+ return 0
+ const r = MediaService.currentPosition / MediaService.trackLength
+ if (isNaN(r) || !isFinite(r))
+ return 0
+ return Math.max(0, Math.min(1, r))
+ }
+ property real effectiveRatio: (MediaService.isSeeking && localSeekRatio >= 0) ? Math.max(0, Math.min(1, localSeekRatio)) : progressRatio
+
+ Timer {
+ id: seekDebounce
+ interval: 75
+ repeat: false
+ onTriggered: {
+ if (MediaService.isSeeking && progressWrapper.localSeekRatio >= 0) {
+ const next = Math.max(0, Math.min(1, progressWrapper.localSeekRatio))
+ if (progressWrapper.lastSentSeekRatio < 0 || Math.abs(next - progressWrapper.lastSentSeekRatio) >= progressWrapper.seekEpsilon) {
+ MediaService.seekByRatio(next)
+ progressWrapper.lastSentSeekRatio = next
+ }
+ }
+ }
+ }
+
+ NSlider {
+ id: progressSlider
+ anchors.fill: parent
+ from: 0
+ to: 1
+ stepSize: 0
+ snapAlways: false
+ enabled: MediaService.trackLength > 0 && MediaService.canSeek
+ heightRatio: 0.6
+
+ onMoved: {
+ progressWrapper.localSeekRatio = value
+ seekDebounce.restart()
+ }
+ onPressedChanged: {
+ if (pressed) {
+ MediaService.isSeeking = true
+ progressWrapper.localSeekRatio = value
+ MediaService.seekByRatio(value)
+ progressWrapper.lastSentSeekRatio = value
+ } else {
+ seekDebounce.stop()
+ MediaService.seekByRatio(value)
+ MediaService.isSeeking = false
+ progressWrapper.localSeekRatio = -1
+ progressWrapper.lastSentSeekRatio = -1
+ }
+ }
+ }
+
+ Binding {
+ target: progressSlider
+ property: "value"
+ value: progressWrapper.progressRatio
+ when: !MediaService.isSeeking
+ }
+ }
+
+ // Spacer to push media controls down
+ Item {
+ Layout.preferredHeight: Style.marginL
+ }
+
+ // Media controls
+ RowLayout {
+ spacing: Style.marginS
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignHCenter
+
+ NIconButton {
+ icon: "media-prev"
+ visible: MediaService.canGoPrevious
+ onClicked: MediaService.canGoPrevious ? MediaService.previous() : {}
+ }
+
+ NIconButton {
+ icon: MediaService.isPlaying ? "media-pause" : "media-play"
+ visible: (MediaService.canPlay || MediaService.canPause)
+ onClicked: (MediaService.canPlay || MediaService.canPause) ? MediaService.playPause() : {}
+ }
+
+ NIconButton {
+ icon: "media-next"
+ visible: MediaService.canGoNext
+ onClicked: MediaService.canGoNext ? MediaService.next() : {}
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Modules/ControlCenter/Cards/ProfileCard.qml b/Modules/Panels/ControlCenter/Cards/ProfileCard.qml
similarity index 84%
rename from Modules/ControlCenter/Cards/ProfileCard.qml
rename to Modules/Panels/ControlCenter/Cards/ProfileCard.qml
index 1eaffb3a..2a75e700 100644
--- a/Modules/ControlCenter/Cards/ProfileCard.qml
+++ b/Modules/Panels/ControlCenter/Cards/ProfileCard.qml
@@ -4,10 +4,10 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
-import qs.Modules.Settings
-import qs.Modules.ControlCenter
import qs.Commons
-import qs.Services
+import qs.Modules.Panels.ControlCenter.Cards
+import qs.Modules.Panels.Settings
+import qs.Services.UI
import qs.Widgets
// Header card with avatar, user and quick actions
@@ -60,8 +60,9 @@ NBox {
icon: "settings"
tooltipText: I18n.tr("tooltips.open-settings")
onClicked: {
- settingsPanel.requestedTab = SettingsPanel.Tab.General
- settingsPanel.open()
+ var panel = PanelService.getPanel("settingsPanel", screen)
+ panel.requestedTab = SettingsPanel.Tab.General
+ panel.open()
}
}
@@ -69,8 +70,8 @@ NBox {
icon: "power"
tooltipText: I18n.tr("tooltips.session-menu")
onClicked: {
- sessionMenuPanel.open()
- controlCenterPanel.close()
+ PanelService.getPanel("sessionMenuPanel", screen)?.open()
+ PanelService.getPanel("controlCenterPanel", screen)?.close()
}
}
@@ -78,7 +79,7 @@ NBox {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
onClicked: {
- controlCenterPanel.close()
+ PanelService.getPanel("controlCenterPanel", screen)?.close()
}
}
}
diff --git a/Modules/ControlCenter/Cards/ShortcutsCard.qml b/Modules/Panels/ControlCenter/Cards/ShortcutsCard.qml
similarity index 80%
rename from Modules/ControlCenter/Cards/ShortcutsCard.qml
rename to Modules/Panels/ControlCenter/Cards/ShortcutsCard.qml
index 2100a76b..91fba4b9 100644
--- a/Modules/ControlCenter/Cards/ShortcutsCard.qml
+++ b/Modules/Panels/ControlCenter/Cards/ShortcutsCard.qml
@@ -3,10 +3,9 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
import qs.Widgets
-import qs.Modules.ControlCenter
-import qs.Modules.ControlCenter.Cards
+import qs.Modules.Panels.ControlCenter
+import qs.Modules.Panels.ControlCenter.Cards
RowLayout {
Layout.fillWidth: true
@@ -28,14 +27,18 @@ RowLayout {
Repeater {
model: Settings.data.controlCenter.shortcuts.left
delegate: ControlCenterWidgetLoader {
+ required property var modelData
+ required property int index
+
Layout.fillWidth: false
widgetId: (modelData.id !== undefined ? modelData.id : "")
+ widgetScreen: root.screen
widgetProps: {
- "screen": root.modelData || null,
"widgetId": modelData.id,
"section": "quickSettings",
"sectionWidgetIndex": index,
- "sectionWidgetsCount": Settings.data.controlCenter.shortcuts.left.length
+ "sectionWidgetsCount": Settings.data.controlCenter.shortcuts.left.length,
+ "widgetSettings": modelData
}
Layout.alignment: Qt.AlignVCenter
}
@@ -63,14 +66,18 @@ RowLayout {
Repeater {
model: Settings.data.controlCenter.shortcuts.right
delegate: ControlCenterWidgetLoader {
+ required property var modelData
+ required property int index
+
Layout.fillWidth: false
widgetId: (modelData.id !== undefined ? modelData.id : "")
+ widgetScreen: root.screen
widgetProps: {
- "screen": root.modelData || null,
"widgetId": modelData.id,
"section": "quickSettings",
"sectionWidgetIndex": index,
- "sectionWidgetsCount": Settings.data.controlCenter.shortcuts.right.length
+ "sectionWidgetsCount": Settings.data.controlCenter.shortcuts.right.length,
+ "widgetSettings": modelData
}
Layout.alignment: Qt.AlignVCenter
}
diff --git a/Modules/ControlCenter/Cards/SystemMonitorCard.qml b/Modules/Panels/ControlCenter/Cards/SystemMonitorCard.qml
similarity index 93%
rename from Modules/ControlCenter/Cards/SystemMonitorCard.qml
rename to Modules/Panels/ControlCenter/Cards/SystemMonitorCard.qml
index 390d4ee3..2aa62105 100644
--- a/Modules/ControlCenter/Cards/SystemMonitorCard.qml
+++ b/Modules/Panels/ControlCenter/Cards/SystemMonitorCard.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.System
import qs.Widgets
// Unified system card: monitors CPU, temp, memory, disk
@@ -46,7 +46,7 @@ NBox {
Layout.alignment: Qt.AlignHCenter
}
NCircleStat {
- value: SystemStatService.diskPercents["/"]
+ value: SystemStatService.diskPercents["/"] ?? 0
icon: "storage"
flat: true
contentScale: 0.8
diff --git a/Modules/ControlCenter/Cards/WeatherCard.qml b/Modules/Panels/ControlCenter/Cards/WeatherCard.qml
similarity index 95%
rename from Modules/ControlCenter/Cards/WeatherCard.qml
rename to Modules/Panels/ControlCenter/Cards/WeatherCard.qml
index e5cb7011..52fcd7bc 100644
--- a/Modules/ControlCenter/Cards/WeatherCard.qml
+++ b/Modules/Panels/ControlCenter/Cards/WeatherCard.qml
@@ -2,14 +2,14 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Location
import qs.Widgets
// Weather overview card (placeholder data)
NBox {
id: root
- property int forecastDays: 7
+ property int forecastDays: 6
property bool showLocation: true
readonly property bool weatherReady: Settings.data.location.weatherEnabled && (LocationService.data.weather !== null)
@@ -134,11 +134,10 @@ NBox {
}
}
- RowLayout {
- visible: !weatherReady
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignHCenter
- NBusyIndicator {}
+ Loader {
+ active: !weatherReady
+ Layout.alignment: Qt.AlignCenter
+ sourceComponent: NBusyIndicator {}
}
}
}
diff --git a/Modules/ControlCenter/ControlCenterPanel.qml b/Modules/Panels/ControlCenter/ControlCenterPanel.qml
similarity index 74%
rename from Modules/ControlCenter/ControlCenterPanel.qml
rename to Modules/Panels/ControlCenter/ControlCenterPanel.qml
index 0e899019..07be9664 100644
--- a/Modules/ControlCenter/ControlCenterPanel.qml
+++ b/Modules/Panels/ControlCenter/ControlCenterPanel.qml
@@ -2,15 +2,35 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
-import qs.Modules.ControlCenter.Cards
import qs.Commons
-import qs.Services
+import qs.Modules.MainScreen
+import qs.Modules.Panels.ControlCenter.Cards
+import qs.Services.Media
+import qs.Services.UI
import qs.Widgets
-NPanel {
+SmartPanel {
id: root
- panelKeyboardFocus: true
+ // Positioning
+ readonly property string controlCenterPosition: Settings.data.controlCenter.position
+
+ // Check if there's a bar on this screen
+ readonly property bool hasBarOnScreen: {
+ var monitors = Settings.data.bar.monitors || []
+ return monitors.length === 0 || monitors.includes(screen?.name)
+ }
+
+ // When position is "close_to_bar_button" but there's no bar, fall back to center
+ readonly property bool shouldCenter: controlCenterPosition === "close_to_bar_button" && !hasBarOnScreen
+
+ panelAnchorHorizontalCenter: shouldCenter || (controlCenterPosition !== "close_to_bar_button" && (controlCenterPosition.endsWith("_center") || controlCenterPosition === "center"))
+ panelAnchorVerticalCenter: shouldCenter || controlCenterPosition === "center"
+ panelAnchorLeft: !shouldCenter && controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.endsWith("_left")
+ panelAnchorRight: !shouldCenter && controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.endsWith("_right")
+ panelAnchorBottom: !shouldCenter && controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.startsWith("bottom_")
+ panelAnchorTop: !shouldCenter && controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.startsWith("top_")
+
preferredWidth: Math.round(460 * Style.uiScaleRatio)
preferredHeight: {
var height = 0
@@ -48,15 +68,6 @@ NPanel {
return height + (count + 1) * Style.marginL
}
- // Positioning
- readonly property string controlCenterPosition: Settings.data.controlCenter.position
- panelAnchorHorizontalCenter: controlCenterPosition !== "close_to_bar_button" && (controlCenterPosition.endsWith("_center") || controlCenterPosition === "center")
- panelAnchorVerticalCenter: controlCenterPosition === "center"
- panelAnchorLeft: controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.endsWith("_left")
- panelAnchorRight: controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.endsWith("_right")
- panelAnchorBottom: controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.startsWith("bottom_")
- panelAnchorTop: controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.startsWith("top_")
-
readonly property int profileHeight: Math.round(64 * Style.uiScaleRatio)
readonly property int shortcutsHeight: Math.round(52 * Style.uiScaleRatio)
readonly property int audioHeight: Math.round(60 * Style.uiScaleRatio)
diff --git a/Modules/ControlCenter/ControlCenterWidgetLoader.qml b/Modules/Panels/ControlCenter/ControlCenterWidgetLoader.qml
similarity index 55%
rename from Modules/ControlCenter/ControlCenterWidgetLoader.qml
rename to Modules/Panels/ControlCenter/ControlCenterWidgetLoader.qml
index e0360d4c..0fb3cd8b 100644
--- a/Modules/ControlCenter/ControlCenterWidgetLoader.qml
+++ b/Modules/Panels/ControlCenter/ControlCenterWidgetLoader.qml
@@ -1,14 +1,15 @@
import QtQuick
import Quickshell
-import qs.Services
+import qs.Services.UI
import qs.Commons
Item {
id: root
- property string widgetId: ""
- property var widgetProps: ({})
- property string screenName: widgetProps && widgetProps.screen ? widgetProps.screen.name : ""
+ required property string widgetId
+ required property var widgetScreen
+ required property var widgetProps
+
property string section: widgetProps && widgetProps.section || ""
property int sectionIndex: widgetProps && widgetProps.sectionWidgetIndex || 0
@@ -23,30 +24,29 @@ Item {
Loader {
id: loader
anchors.fill: parent
- active: widgetId !== ""
asynchronous: false
- sourceComponent: {
- if (!active) {
- return null
- }
- return ControlCenterWidgetRegistry.getWidget(widgetId)
- }
+ sourceComponent: ControlCenterWidgetRegistry.getWidget(widgetId)
onLoaded: {
- if (item && widgetProps) {
- // Apply properties to loaded widget
- for (var prop in widgetProps) {
- if (item.hasOwnProperty(prop)) {
- item[prop] = widgetProps[prop]
- }
+ if (!item)
+ return
+
+ // Apply properties to loaded widget
+ for (var prop in widgetProps) {
+ if (item.hasOwnProperty(prop)) {
+ item[prop] = widgetProps[prop]
}
}
+ // Set screen property
+ if (item.hasOwnProperty("screen")) {
+ item.screen = widgetScreen
+ }
+
+ // Call custom onLoaded if it exists
if (item.hasOwnProperty("onLoaded")) {
item.onLoaded()
}
-
- //Logger.i("ControlCenterWidgetLoader", "Loaded", widgetId, "on screen", item.screen.name)
}
Component.onDestruction: {
@@ -56,8 +56,8 @@ Item {
}
// Error handling
- onWidgetIdChanged: {
- if (widgetId && !ControlCenterWidgetRegistry.hasWidget(widgetId)) {
+ Component.onCompleted: {
+ if (!ControlCenterWidgetRegistry.hasWidget(widgetId)) {
Logger.w("ControlCenterWidgetLoader", "Widget not found in registry:", widgetId)
}
}
diff --git a/Modules/ControlCenter/Widgets/Bluetooth.qml b/Modules/Panels/ControlCenter/Widgets/Bluetooth.qml
similarity index 65%
rename from Modules/ControlCenter/Widgets/Bluetooth.qml
rename to Modules/Panels/ControlCenter/Widgets/Bluetooth.qml
index e4549a31..d3ab79ad 100644
--- a/Modules/ControlCenter/Widgets/Bluetooth.qml
+++ b/Modules/Panels/ControlCenter/Widgets/Bluetooth.qml
@@ -1,7 +1,8 @@
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Networking
+import qs.Services.UI
import qs.Widgets
NIconButtonHot {
@@ -9,5 +10,7 @@ NIconButtonHot {
icon: BluetoothService.enabled ? "bluetooth" : "bluetooth-off"
tooltipText: I18n.tr("quickSettings.bluetooth.tooltip.action")
- onClicked: PanelService.getPanel("bluetoothPanel")?.toggle(this)
+ onClicked: {
+ PanelService.getPanel("bluetoothPanel", screen)?.toggle(this)
+ }
}
diff --git a/Modules/Panels/ControlCenter/Widgets/CustomButton.qml b/Modules/Panels/ControlCenter/Widgets/CustomButton.qml
new file mode 100644
index 00000000..bad214ad
--- /dev/null
+++ b/Modules/Panels/ControlCenter/Widgets/CustomButton.qml
@@ -0,0 +1,161 @@
+import QtQuick
+import Quickshell
+import Quickshell.Io
+import qs.Commons
+import qs.Widgets
+
+Item {
+ id: root
+
+ property string widgetId: "CustomButton"
+ property var widgetSettings: null
+
+ property string onClickedCommand: ""
+ property string onRightClickedCommand: ""
+ property string onMiddleClickedCommand: ""
+ property string stateChecksJson: "[]" // Store state checks as JSON string
+
+ property string generalTooltipText: "Custom Button"
+ property bool enableOnStateLogic: false
+
+ property string _currentIcon: "heart" // Default icon
+ property bool _isHot: false
+ property var _parsedStateChecks: [] // Local array for parsed state checks
+
+ property int _currentStateCheckIndex: -1
+ property string _activeStateIcon: ""
+
+ implicitWidth: button.implicitWidth
+ implicitHeight: button.implicitHeight
+
+ Connections {
+ target: root
+
+ function _updatePropertiesFromSettings() {
+ if (!widgetSettings) {
+ return
+ }
+
+ onClickedCommand = widgetSettings.onClicked || ""
+ onRightClickedCommand = widgetSettings.onRightClicked || ""
+ onMiddleClickedCommand = widgetSettings.onMiddleClicked || ""
+ stateChecksJson = widgetSettings.stateChecksJson || "[]" // Populate from widgetSettings
+ try {
+ _parsedStateChecks = JSON.parse(stateChecksJson)
+ } catch (e) {
+ console.error("CustomButton: Failed to parse stateChecksJson:", e.message)
+ _parsedStateChecks = []
+ }
+ generalTooltipText = widgetSettings.generalTooltipText || "Custom Button"
+ enableOnStateLogic = widgetSettings.enableOnStateLogic || false
+
+ updateState()
+ }
+
+ function onWidgetSettingsChanged() {
+ if (widgetSettings) {
+ _updatePropertiesFromSettings()
+ }
+ }
+ }
+
+ Process {
+ id: stateCheckProcessExecutor
+ running: false
+ command: _currentStateCheckIndex !== -1 && _parsedStateChecks.length > _currentStateCheckIndex ? ["sh", "-c", _parsedStateChecks[_currentStateCheckIndex].command] : []
+ onExited: function (exitCode, stdout, stderr) {
+ var currentCheckItem = _parsedStateChecks[_currentStateCheckIndex]
+ var currentCommand = currentCheckItem.command
+ if (exitCode === 0) {
+ // Command succeeded, this is the active state
+ _isHot = true
+ _activeStateIcon = currentCheckItem.icon || widgetSettings.icon || "heart"
+ } else {
+ // Command failed, try next one
+ _currentStateCheckIndex++
+ _checkNextState()
+ }
+ }
+ }
+
+ Timer {
+ id: stateUpdateTimer
+ interval: 200
+ running: false
+ repeat: false
+ onTriggered: {
+ if (enableOnStateLogic && _parsedStateChecks.length > 0) {
+ _currentStateCheckIndex = 0
+ _checkNextState()
+ } else {
+ _isHot = false
+ _activeStateIcon = widgetSettings.icon || "heart"
+ }
+ }
+ }
+
+ function _checkNextState() {
+ if (_currentStateCheckIndex < _parsedStateChecks.length) {
+ var currentCheckItem = _parsedStateChecks[_currentStateCheckIndex]
+ if (currentCheckItem && currentCheckItem.command) {
+ stateCheckProcessExecutor.running = true
+ } else {
+ _currentStateCheckIndex++
+ _checkNextState()
+ }
+ } else {
+ // All checks failed
+ _isHot = false
+ _activeStateIcon = widgetSettings.icon || "heart"
+ }
+ }
+
+ function updateState() {
+ if (!enableOnStateLogic || _parsedStateChecks.length === 0) {
+ _isHot = false
+ _activeStateIcon = widgetSettings.icon || "heart"
+ return
+ }
+ stateUpdateTimer.restart()
+ }
+
+ function _buildTooltipText() {
+ let tooltip = generalTooltipText
+ if (onClickedCommand) {
+ tooltip += `\nLeft click: ${onClickedCommand}`
+ }
+ if (onRightClickedCommand) {
+ tooltip += `\nRight click: ${onRightClickedCommand}`
+ }
+ if (onMiddleClickedCommand) {
+ tooltip += `\nMiddle click: ${onMiddleClickedCommand}`
+ }
+
+ return tooltip
+ }
+
+ NIconButtonHot {
+ id: button
+ icon: _activeStateIcon
+ hot: _isHot
+ tooltipText: _buildTooltipText()
+ onClicked: {
+ if (onClickedCommand) {
+ Quickshell.execDetached(["sh", "-c", onClickedCommand])
+ updateState()
+ }
+ }
+ onRightClicked: {
+ if (onRightClickedCommand) {
+ Quickshell.execDetached(["sh", "-c", onRightClickedCommand])
+ updateState()
+ }
+ }
+ onMiddleClicked: {
+ if (onMiddleClickedCommand) {
+ Quickshell.execDetached(["sh", "-c", onMiddleClickedCommand])
+ updateState()
+ }
+ }
+ }
+}
diff --git a/Modules/ControlCenter/Widgets/KeepAwake.qml b/Modules/Panels/ControlCenter/Widgets/KeepAwake.qml
similarity index 93%
rename from Modules/ControlCenter/Widgets/KeepAwake.qml
rename to Modules/Panels/ControlCenter/Widgets/KeepAwake.qml
index 96d60733..5b459e7e 100644
--- a/Modules/ControlCenter/Widgets/KeepAwake.qml
+++ b/Modules/Panels/ControlCenter/Widgets/KeepAwake.qml
@@ -1,7 +1,7 @@
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Power
import qs.Widgets
NIconButtonHot {
diff --git a/Modules/ControlCenter/Widgets/NightLight.qml b/Modules/Panels/ControlCenter/Widgets/NightLight.qml
similarity index 86%
rename from Modules/ControlCenter/Widgets/NightLight.qml
rename to Modules/Panels/ControlCenter/Widgets/NightLight.qml
index eb338882..c45f197a 100644
--- a/Modules/ControlCenter/Widgets/NightLight.qml
+++ b/Modules/Panels/ControlCenter/Widgets/NightLight.qml
@@ -1,7 +1,9 @@
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Modules.Panels.Settings
+import qs.Services.System
+import qs.Services.UI
import qs.Widgets
NIconButtonHot {
@@ -25,7 +27,7 @@ NIconButtonHot {
}
onRightClicked: {
- var settingsPanel = PanelService.getPanel("settingsPanel")
+ var settingsPanel = PanelService.getPanel("settingsPanel", screen)
settingsPanel.requestedTab = SettingsPanel.Tab.Display
settingsPanel.open()
}
diff --git a/Modules/Panels/ControlCenter/Widgets/Notifications.qml b/Modules/Panels/ControlCenter/Widgets/Notifications.qml
new file mode 100644
index 00000000..1877130e
--- /dev/null
+++ b/Modules/Panels/ControlCenter/Widgets/Notifications.qml
@@ -0,0 +1,16 @@
+import QtQuick.Layouts
+import Quickshell
+import qs.Commons
+import qs.Services.System
+import qs.Services.UI
+import qs.Widgets
+
+NIconButtonHot {
+ property ShellScreen screen
+
+ icon: NotificationService.doNotDisturb ? "bell-off" : "bell"
+ hot: NotificationService.doNotDisturb
+ tooltipText: I18n.tr("quickSettings.notifications.tooltip.action")
+ onClicked: PanelService.getPanel("notificationHistoryPanel", screen)?.toggle(this)
+ onRightClicked: NotificationService.doNotDisturb = !NotificationService.doNotDisturb
+}
diff --git a/Modules/ControlCenter/Widgets/PowerProfile.qml b/Modules/Panels/ControlCenter/Widgets/PowerProfile.qml
similarity index 61%
rename from Modules/ControlCenter/Widgets/PowerProfile.qml
rename to Modules/Panels/ControlCenter/Widgets/PowerProfile.qml
index d26922ed..1a211c16 100644
--- a/Modules/ControlCenter/Widgets/PowerProfile.qml
+++ b/Modules/Panels/ControlCenter/Widgets/PowerProfile.qml
@@ -2,7 +2,7 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Services.UPower
import qs.Commons
-import qs.Services
+import qs.Services.Power
import qs.Widgets
// Performance
@@ -14,8 +14,6 @@ NIconButtonHot {
enabled: hasPP
icon: PowerProfileService.getIcon()
hot: !PowerProfileService.isDefault()
- tooltipText: hasPP ? I18n.tr("quickSettings.powerProfile.tooltip.action") : I18n.tr("quickSettings.powerProfile.tooltip.disabled")
- onClicked: {
- PowerProfileService.cycleProfile()
- }
+ tooltipText: I18n.tr("quickSettings.powerProfile.tooltip.action")
+ onClicked: PowerProfileService.cycleProfile()
}
diff --git a/Modules/ControlCenter/Widgets/ScreenRecorder.qml b/Modules/Panels/ControlCenter/Widgets/ScreenRecorder.qml
similarity index 76%
rename from Modules/ControlCenter/Widgets/ScreenRecorder.qml
rename to Modules/Panels/ControlCenter/Widgets/ScreenRecorder.qml
index 7a58c8f3..0cbfec0d 100644
--- a/Modules/ControlCenter/Widgets/ScreenRecorder.qml
+++ b/Modules/Panels/ControlCenter/Widgets/ScreenRecorder.qml
@@ -1,7 +1,9 @@
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Media
+import qs.Services.System
+import qs.Services.UI
import qs.Widgets
NIconButtonHot {
@@ -14,8 +16,7 @@ NIconButtonHot {
onClicked: {
ScreenRecorderService.toggleRecording()
if (!ScreenRecorderService.isRecording) {
- var panel = PanelService.getPanel("controlCenterPanel")
- panel?.close()
+ PanelService.getPanel("controlCenterPanel", screen)?.close
}
}
}
diff --git a/Modules/ControlCenter/Widgets/WallpaperSelector.qml b/Modules/Panels/ControlCenter/Widgets/WallpaperSelector.qml
similarity index 77%
rename from Modules/ControlCenter/Widgets/WallpaperSelector.qml
rename to Modules/Panels/ControlCenter/Widgets/WallpaperSelector.qml
index a725974c..6d6e840d 100644
--- a/Modules/ControlCenter/Widgets/WallpaperSelector.qml
+++ b/Modules/Panels/ControlCenter/Widgets/WallpaperSelector.qml
@@ -1,7 +1,7 @@
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
NIconButtonHot {
@@ -10,6 +10,6 @@ NIconButtonHot {
enabled: Settings.data.wallpaper.enabled
icon: "wallpaper-selector"
tooltipText: I18n.tr("quickSettings.wallpaperSelector.tooltip.action")
- onClicked: PanelService.getPanel("wallpaperPanel")?.toggle()
+ onClicked: PanelService.getPanel("wallpaperPanel", screen)?.toggle()
onRightClicked: WallpaperService.setRandomWallpaper()
}
diff --git a/Modules/ControlCenter/Widgets/WiFi.qml b/Modules/Panels/ControlCenter/Widgets/WiFi.qml
similarity index 84%
rename from Modules/ControlCenter/Widgets/WiFi.qml
rename to Modules/Panels/ControlCenter/Widgets/WiFi.qml
index eea64700..e40047f1 100644
--- a/Modules/ControlCenter/Widgets/WiFi.qml
+++ b/Modules/Panels/ControlCenter/Widgets/WiFi.qml
@@ -1,7 +1,8 @@
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Networking
+import qs.Services.UI
import qs.Widgets
NIconButtonHot {
@@ -21,7 +22,7 @@ NIconButtonHot {
break
}
}
- return connected ? NetworkService.signalIcon(signalStrength) : "wifi-off"
+ return connected ? NetworkService.signalIcon(signalStrength, true) : "wifi-off"
} catch (error) {
Logger.e("Wi-Fi", "Error getting icon:", error)
return "signal_wifi_bad"
@@ -29,5 +30,5 @@ NIconButtonHot {
}
tooltipText: I18n.tr("quickSettings.wifi.tooltip.action")
- onClicked: PanelService.getPanel("wifiPanel")?.toggle(this)
+ onClicked: PanelService.getPanel("wifiPanel", screen)?.toggle(this)
}
diff --git a/Modules/Launcher/Launcher.qml b/Modules/Panels/Launcher/Launcher.qml
similarity index 71%
rename from Modules/Launcher/Launcher.qml
rename to Modules/Panels/Launcher/Launcher.qml
index 03c6f0af..9747b3ee 100644
--- a/Modules/Launcher/Launcher.qml
+++ b/Modules/Panels/Launcher/Launcher.qml
@@ -4,29 +4,41 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import qs.Commons
-import qs.Services
+import qs.Modules.MainScreen
+import qs.Services.Keyboard
import qs.Widgets
-NPanel {
+import "Plugins"
+
+SmartPanel {
id: root
// Panel configuration
- preferredWidth: 500 * Style.uiScaleRatio
- preferredHeight: 600 * Style.uiScaleRatio
+ preferredWidth: Math.round(500 * Style.uiScaleRatio)
+ preferredHeight: Math.round(600 * Style.uiScaleRatio)
preferredWidthRatio: 0.3
preferredHeightRatio: 0.5
- panelKeyboardFocus: true
panelBackgroundColor: Qt.alpha(Color.mSurface, Settings.data.appLauncher.backgroundOpacity)
// Positioning
- readonly property string launcherPosition: Settings.data.appLauncher.position
- panelAnchorHorizontalCenter: launcherPosition === "center" || launcherPosition.endsWith("_center")
- panelAnchorVerticalCenter: launcherPosition === "center"
- panelAnchorLeft: launcherPosition !== "center" && launcherPosition.endsWith("_left")
- panelAnchorRight: launcherPosition !== "center" && launcherPosition.endsWith("_right")
- panelAnchorBottom: launcherPosition.startsWith("bottom_")
- panelAnchorTop: launcherPosition.startsWith("top_")
+ readonly property string panelPosition: {
+ if (Settings.data.appLauncher.position === "follow_bar") {
+ if (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") {
+ return `center_${Settings.data.bar.position}`
+ } else {
+ return `${Settings.data.bar.position}_center`
+ }
+ } else {
+ return Settings.data.appLauncher.position
+ }
+ }
+ panelAnchorHorizontalCenter: panelPosition === "center" || panelPosition.endsWith("_center")
+ panelAnchorVerticalCenter: panelPosition === "center"
+ panelAnchorLeft: panelPosition !== "center" && panelPosition.endsWith("_left")
+ panelAnchorRight: panelPosition !== "center" && panelPosition.endsWith("_right")
+ panelAnchorBottom: panelPosition.startsWith("bottom_")
+ panelAnchorTop: panelPosition.startsWith("top_")
// Core state
property string searchText: ""
@@ -40,6 +52,59 @@ NPanel {
readonly property int badgeSize: Math.round(Style.baseWidgetSize * 1.6)
readonly property int entryHeight: Math.round(badgeSize + Style.marginM * 2)
+ // Override keyboard handlers from SmartPanel for navigation
+ function onTabPressed() {
+ selectNextWrapped()
+ }
+
+ function onShiftTabPressed() {
+ selectPreviousWrapped()
+ }
+
+ function onUpPressed() {
+ selectPreviousWrapped()
+ }
+
+ function onDownPressed() {
+ selectNextWrapped()
+ }
+
+ function onReturnPressed() {
+ activate()
+ }
+
+ function onHomePressed() {
+ selectFirst()
+ }
+
+ function onEndPressed() {
+ selectLast()
+ }
+
+ function onPageUpPressed() {
+ selectPreviousPage()
+ }
+
+ function onPageDownPressed() {
+ selectNextPage()
+ }
+
+ function onCtrlJPressed() {
+ selectNextWrapped()
+ }
+
+ function onCtrlKPressed() {
+ selectPreviousWrapped()
+ }
+
+ function onCtrlNPressed() {
+ selectNextWrapped()
+ }
+
+ function onCtrlPPressed() {
+ selectPreviousWrapped()
+ }
+
// Public API for plugins
function setSearchText(text) {
searchText = text
@@ -97,14 +162,14 @@ NPanel {
resultsReady = false
ignoreMouseHover = true
- // Notify plugins
- for (let plugin of plugins) {
- if (plugin.onOpened)
- plugin.onOpened()
- }
- updateResults()
-
+ // Notify plugins and update results
+ // Use Qt.callLater to ensure plugins are registered (Component.onCompleted runs first)
Qt.callLater(() => {
+ for (let plugin of plugins) {
+ if (plugin.onOpened)
+ plugin.onOpened()
+ }
+ updateResults()
resultsReady = true
})
}
@@ -121,33 +186,78 @@ NPanel {
}
}
- // Load plugins
- Component.onCompleted: {
- // Load applications plugin
- const appsPlugin = Qt.createComponent("Plugins/ApplicationsPlugin.qml").createObject(this)
- if (appsPlugin) {
- registerPlugin(appsPlugin)
+ // Plugin components - declared inline so imports work correctly
+ ApplicationsPlugin {
+ id: appsPlugin
+ Component.onCompleted: {
+ registerPlugin(this)
Logger.d("Launcher", "Registered: ApplicationsPlugin")
- } else {
- Logger.e("Launcher", "Failed to load ApplicationsPlugin")
}
+ }
- // Load calculator plugin
- const calcPlugin = Qt.createComponent("Plugins/CalculatorPlugin.qml").createObject(this)
- if (calcPlugin) {
- registerPlugin(calcPlugin)
+ CalculatorPlugin {
+ id: calcPlugin
+ Component.onCompleted: {
+ registerPlugin(this)
Logger.d("Launcher", "Registered: CalculatorPlugin")
- } else {
- Logger.e("Launcher", "Failed to load CalculatorPlugin")
}
+ }
- // Load clipboard history plugin
- const clipboardPlugin = Qt.createComponent("Plugins/ClipboardPlugin.qml").createObject(this)
- if (clipboardPlugin) {
- registerPlugin(clipboardPlugin)
- Logger.d("Launcher", "Registered: ClipboardPlugin")
+ ClipboardPlugin {
+ id: clipPlugin
+ Component.onCompleted: {
+ if (Settings.data.appLauncher.enableClipboardHistory) {
+ registerPlugin(this)
+ Logger.d("Launcher", "Registered: ClipboardPlugin")
+ }
+ }
+ }
+
+ // Navigation functions
+ function selectNextWrapped() {
+ if (results.length > 0) {
+ selectedIndex = (selectedIndex + 1) % results.length
+ }
+ }
+
+ function selectPreviousWrapped() {
+ if (results.length > 0) {
+ selectedIndex = (((selectedIndex - 1) % results.length) + results.length) % results.length
+ }
+ }
+
+ function selectFirst() {
+ selectedIndex = 0
+ }
+
+ function selectLast() {
+ if (results.length > 0) {
+ selectedIndex = results.length - 1
} else {
- Logger.e("Launcher", "Failed to load ClipboardPlugin")
+ selectedIndex = 0
+ }
+ }
+
+ function selectNextPage() {
+ if (results.length > 0) {
+ const page = Math.max(1, Math.floor(600 / entryHeight)) // Use approximate height
+ selectedIndex = Math.min(selectedIndex + page, results.length - 1)
+ }
+ }
+
+ function selectPreviousPage() {
+ if (results.length > 0) {
+ const page = Math.max(1, Math.floor(600 / entryHeight)) // Use approximate height
+ selectedIndex = Math.max(selectedIndex - page, 0)
+ }
+ }
+
+ function activate() {
+ if (results.length > 0 && results[selectedIndex]) {
+ const item = results[selectedIndex]
+ if (item.onActivate) {
+ item.onActivate()
+ }
}
}
@@ -198,6 +308,19 @@ NPanel {
}
}
+ // Focus management
+ Connections {
+ target: root
+ function onOpened() {
+ // Delay focus to ensure window has keyboard focus
+ Qt.callLater(() => {
+ if (searchInput.inputItem) {
+ searchInput.inputItem.forceActiveFocus()
+ }
+ })
+ }
+ }
+
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
@@ -205,102 +328,6 @@ NPanel {
}
}
- // ---------------------
- // Navigation
- function selectNextWrapped() {
- if (results.length > 0) {
- selectedIndex = (selectedIndex + 1) % results.length
- }
- }
-
- function selectPreviousWrapped() {
- if (results.length > 0) {
- selectedIndex = (((selectedIndex - 1) % results.length) + results.length) % results.length
- }
- }
-
- function selectFirst() {
- selectedIndex = 0
- }
-
- function selectLast() {
- if (results.length > 0) {
- selectedIndex = results.length - 1
- } else {
- selectedIndex = 0
- }
- }
-
- function selectNextPage() {
- if (results.length > 0) {
- const page = Math.max(1, Math.floor(resultsList.height / entryHeight))
- selectedIndex = Math.min(selectedIndex + page, results.length - 1)
- }
- }
- function selectPreviousPage() {
- if (results.length > 0) {
- const page = Math.max(1, Math.floor(resultsList.height / entryHeight))
- selectedIndex = Math.max(selectedIndex - page, 0)
- }
- }
-
- function activate() {
- if (results.length > 0 && results[selectedIndex]) {
- const item = results[selectedIndex]
- if (item.onActivate) {
- item.onActivate()
- }
- }
- }
-
- Shortcut {
- sequence: "Ctrl+K"
- onActivated: ui.selectPreviousWrapped()
- enabled: root.opened && searchInput.inputItem && searchInput.inputItem.activeFocus
- }
-
- Shortcut {
- sequence: "Ctrl+J"
- onActivated: ui.selectNextWrapped()
- enabled: root.opened && searchInput.inputItem && searchInput.inputItem.activeFocus
- }
-
- Shortcut {
- sequence: "Tab"
- onActivated: ui.selectNextWrapped()
- enabled: root.opened && searchInput.inputItem && searchInput.inputItem.activeFocus
- }
-
- Shortcut {
- sequence: "Shift+Tab"
- onActivated: ui.selectPreviousWrapped()
- enabled: root.opened && searchInput.inputItem && searchInput.inputItem.activeFocus
- }
-
- Shortcut {
- sequence: "PgDown" // or "PageDown"
- onActivated: ui.selectNextPage()
- enabled: root.opened && searchInput.inputItem && searchInput.inputItem.activeFocus
- }
-
- Shortcut {
- sequence: "PgUp" // or "PageUp"
- onActivated: ui.selectPreviousPage()
- enabled: root.opened && searchInput.inputItem && searchInput.inputItem.activeFocus
- }
-
- Shortcut {
- sequence: "Home"
- onActivated: ui.selectFirst()
- enabled: root.opened && searchInput.inputItem && searchInput.inputItem.activeFocus
- }
-
- Shortcut {
- sequence: "End"
- onActivated: ui.selectLast()
- enabled: root.opened && searchInput.inputItem && searchInput.inputItem.activeFocus
- }
-
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginL
@@ -319,36 +346,8 @@ NPanel {
onTextChanged: searchText = text
Component.onCompleted: {
- if (searchInput.inputItem && searchInput.inputItem.visible) {
+ if (searchInput.inputItem) {
searchInput.inputItem.forceActiveFocus()
-
- // Override the TextField's default Home/End behavior
- searchInput.inputItem.Keys.priority = Keys.BeforeItem
- searchInput.inputItem.Keys.onPressed.connect(function (event) {
- // Intercept Home, End, and Numpad Enter BEFORE the TextField handles them
- if (event.key === Qt.Key_Home) {
- ui.selectFirst()
- event.accepted = true
- return
- } else if (event.key === Qt.Key_End) {
- ui.selectLast()
- event.accepted = true
- return
- } else if (event.key === Qt.Key_Enter) {
- ui.activate()
- event.accepted = true
- return
- }
- })
- searchInput.inputItem.Keys.onDownPressed.connect(function (event) {
- ui.selectNextWrapped()
- })
- searchInput.inputItem.Keys.onUpPressed.connect(function (event) {
- ui.selectPreviousWrapped()
- })
- searchInput.inputItem.Keys.onReturnPressed.connect(function (event) {
- ui.activate()
- })
}
}
}
@@ -588,7 +587,7 @@ NPanel {
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
selectedIndex = index
- ui.activate()
+ root.activate()
mouse.accepted = true
}
}
diff --git a/Modules/Launcher/Plugins/ApplicationsPlugin.qml b/Modules/Panels/Launcher/Plugins/ApplicationsPlugin.qml
similarity index 98%
rename from Modules/Launcher/Plugins/ApplicationsPlugin.qml
rename to Modules/Panels/Launcher/Plugins/ApplicationsPlugin.qml
index 2e07442c..3d3a1e60 100644
--- a/Modules/Launcher/Plugins/ApplicationsPlugin.qml
+++ b/Modules/Panels/Launcher/Plugins/ApplicationsPlugin.qml
@@ -2,8 +2,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
-import "../../../Helpers/FuzzySort.js" as Fuzzysort
+import "../../../../Helpers/FuzzySort.js" as Fuzzysort
Item {
property var launcher: null
@@ -194,7 +193,7 @@ Item {
"icon": app.icon || "application-x-executable",
"isImage": false,
"onActivate": function () {
- // Close the launcher/NPanel immediately without any animations.
+ // Close the launcher/SmartPanel immediately without any animations.
// Ensures we are not preventing the future focusing of the app
launcher.close()
diff --git a/Modules/Launcher/Plugins/CalculatorPlugin.qml b/Modules/Panels/Launcher/Plugins/CalculatorPlugin.qml
similarity index 97%
rename from Modules/Launcher/Plugins/CalculatorPlugin.qml
rename to Modules/Panels/Launcher/Plugins/CalculatorPlugin.qml
index e33152a1..e8aaf5e3 100644
--- a/Modules/Launcher/Plugins/CalculatorPlugin.qml
+++ b/Modules/Panels/Launcher/Plugins/CalculatorPlugin.qml
@@ -1,7 +1,6 @@
import QtQuick
-import qs.Services
import qs.Commons
-import "../../../Helpers/AdvancedMath.js" as AdvancedMath
+import "../../../../Helpers/AdvancedMath.js" as AdvancedMath
Item {
property var launcher: null
diff --git a/Modules/Launcher/Plugins/ClipboardPlugin.qml b/Modules/Panels/Launcher/Plugins/ClipboardPlugin.qml
similarity index 99%
rename from Modules/Launcher/Plugins/ClipboardPlugin.qml
rename to Modules/Panels/Launcher/Plugins/ClipboardPlugin.qml
index 0d5837bd..e6922b87 100644
--- a/Modules/Launcher/Plugins/ClipboardPlugin.qml
+++ b/Modules/Panels/Launcher/Plugins/ClipboardPlugin.qml
@@ -1,7 +1,7 @@
import QtQuick
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Keyboard
Item {
id: root
diff --git a/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml b/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml
new file mode 100644
index 00000000..d0833879
--- /dev/null
+++ b/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml
@@ -0,0 +1,326 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import Quickshell
+import Quickshell.Wayland
+import Quickshell.Services.Notifications
+import qs.Commons
+import qs.Services.System
+import qs.Widgets
+import qs.Modules.MainScreen
+
+// Notification History panel
+SmartPanel {
+ id: root
+
+ preferredWidth: Math.round(340 * Style.uiScaleRatio)
+ preferredHeight: Math.round(420 * Style.uiScaleRatio)
+
+ onOpened: function () {
+ NotificationService.updateLastSeenTs()
+ }
+
+ panelContent: Rectangle {
+ id: notificationRect
+ color: Color.transparent
+
+ ColumnLayout {
+ id: mainColumn
+ anchors.fill: parent
+ anchors.margins: Style.marginL
+ spacing: Style.marginM
+
+ // Header section
+ NBox {
+ Layout.fillWidth: true
+ implicitHeight: headerRow.implicitHeight + (Style.marginM * 2)
+
+ RowLayout {
+ id: headerRow
+ anchors.fill: parent
+ anchors.margins: Style.marginM
+ spacing: Style.marginM
+
+ NIcon {
+ icon: "bell"
+ pointSize: Style.fontSizeXXL
+ color: Color.mPrimary
+ }
+
+ NText {
+ text: I18n.tr("notifications.panel.title")
+ pointSize: Style.fontSizeL
+ font.weight: Style.fontWeightBold
+ color: Color.mOnSurface
+ Layout.fillWidth: true
+ }
+
+ NIconButton {
+ icon: NotificationService.doNotDisturb ? "bell-off" : "bell"
+ tooltipText: NotificationService.doNotDisturb ? I18n.tr("tooltips.do-not-disturb-enabled") : I18n.tr("tooltips.do-not-disturb-disabled")
+ baseSize: Style.baseWidgetSize * 0.8
+ onClicked: NotificationService.doNotDisturb = !NotificationService.doNotDisturb
+ }
+
+ NIconButton {
+ icon: "trash"
+ tooltipText: I18n.tr("tooltips.clear-history")
+ baseSize: Style.baseWidgetSize * 0.8
+ onClicked: {
+ NotificationService.clearHistory()
+ // Close panel as there is nothing more to see.
+ root.close()
+ }
+ }
+
+ NIconButton {
+ icon: "close"
+ tooltipText: I18n.tr("tooltips.close")
+ baseSize: Style.baseWidgetSize * 0.8
+ onClicked: root.close()
+ }
+ }
+ }
+
+ // Empty state when no notifications
+ ColumnLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.alignment: Qt.AlignHCenter
+ visible: NotificationService.historyList.count === 0
+ spacing: Style.marginL
+
+ Item {
+ Layout.fillHeight: true
+ }
+
+ NIcon {
+ icon: "bell-off"
+ pointSize: 48
+ color: Color.mOnSurfaceVariant
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ NText {
+ text: I18n.tr("notifications.panel.no-notifications")
+ pointSize: Style.fontSizeL
+ color: Color.mOnSurfaceVariant
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ NText {
+ text: I18n.tr("notifications.panel.description")
+ pointSize: Style.fontSizeS
+ color: Color.mOnSurfaceVariant
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Item {
+ Layout.fillHeight: true
+ }
+ }
+
+ // Notification list
+ NScrollView {
+ id: scrollView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ ScrollBar.vertical.policy: ScrollBar.AsNeeded
+ clip: true
+ visible: NotificationService.historyList.count > 0
+
+ // Track which notification is expanded
+ property string expandedId: ""
+
+ contentWidth: availableWidth
+
+ Column {
+ width: scrollView.width
+ spacing: Style.marginM
+
+ Repeater {
+ model: NotificationService.historyList
+
+ delegate: Item {
+ id: notificationDelegate
+ width: parent.width
+ height: contentColumn.height + (Style.marginM * 2)
+
+ property string notificationId: model.id
+ property bool isExpanded: scrollView.expandedId === notificationId
+ property bool canExpand: summaryText.truncated || bodyText.truncated
+
+ Rectangle {
+ anchors.fill: parent
+ radius: Style.radiusM
+ color: Color.mSurfaceVariant
+ border.color: Qt.alpha(Color.mOutline, Style.opacityMedium)
+ border.width: Style.borderS
+
+ Behavior on color {
+ enabled: !Settings.data.general.animationDisabled
+ ColorAnimation {
+ duration: Style.animationFast
+ }
+ }
+ }
+
+ // Click to expand/collapse
+ MouseArea {
+ anchors.fill: parent
+ anchors.rightMargin: Style.baseWidgetSize
+ enabled: notificationDelegate.canExpand
+ cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
+ onClicked: {
+ if (scrollView.expandedId === notificationId) {
+ scrollView.expandedId = ""
+ } else {
+ scrollView.expandedId = notificationId
+ }
+ }
+ }
+
+ Column {
+ id: contentColumn
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.margins: Style.marginM
+ spacing: Style.marginM
+
+ Row {
+ width: parent.width
+ spacing: Style.marginM
+
+ // Icon
+ NImageCircled {
+ width: Math.round(40 * Style.uiScaleRatio)
+ height: Math.round(40 * Style.uiScaleRatio)
+ anchors.verticalCenter: parent.verticalCenter
+ imagePath: model.cachedImage || model.originalImage || ""
+ borderColor: Color.transparent
+ borderWidth: 0
+ fallbackIcon: "bell"
+ fallbackIconSize: 24
+ }
+
+ // Content
+ Column {
+ width: parent.width - Math.round(40 * Style.uiScaleRatio) - Style.marginM - Style.baseWidgetSize
+ spacing: Style.marginXS
+
+ // Header row with app name and timestamp
+ Row {
+ width: parent.width
+ spacing: Style.marginS
+
+ // Urgency indicator
+ Rectangle {
+ width: 6
+ height: 6
+ anchors.verticalCenter: parent.verticalCenter
+ radius: 3
+ visible: model.urgency !== 1
+ color: {
+ if (model.urgency === 2)
+ return Color.mError
+ else if (model.urgency === 0)
+ return Color.mOnSurfaceVariant
+ else
+ return Color.transparent
+ }
+ }
+
+ NText {
+ text: model.appName || "Unknown App"
+ pointSize: Style.fontSizeXS
+ color: Color.mSecondary
+ }
+
+ NText {
+ text: Time.formatRelativeTime(model.timestamp)
+ pointSize: Style.fontSizeXS
+ color: Color.mSecondary
+ }
+ }
+
+ // Summary
+ NText {
+ id: summaryText
+ width: parent.width
+ text: model.summary || I18n.tr("general.no-summary")
+ pointSize: Style.fontSizeM
+ font.weight: Font.Medium
+ color: Color.mOnSurface
+ textFormat: Text.PlainText
+ wrapMode: Text.Wrap
+ maximumLineCount: notificationDelegate.isExpanded ? 999 : 2
+ elide: Text.ElideRight
+ }
+
+ // Body
+ NText {
+ id: bodyText
+ width: parent.width
+ text: model.body || ""
+ pointSize: Style.fontSizeS
+ color: Color.mOnSurfaceVariant
+ textFormat: Text.PlainText
+ wrapMode: Text.Wrap
+ maximumLineCount: notificationDelegate.isExpanded ? 999 : 3
+ elide: Text.ElideRight
+ visible: text.length > 0
+ }
+
+ // Expand indicator
+ Row {
+ width: parent.width
+ visible: !notificationDelegate.isExpanded && notificationDelegate.canExpand
+ spacing: Style.marginXS
+
+ Item {
+ width: parent.width - expandText.width - expandIcon.width - Style.marginXS
+ height: 1
+ }
+
+ NText {
+ id: expandText
+ text: I18n.tr("notifications.panel.click-to-expand") || "Click to expand"
+ pointSize: Style.fontSizeXS
+ color: Color.mPrimary
+ font.weight: Font.Medium
+ }
+
+ NIcon {
+ id: expandIcon
+ icon: "chevron-down"
+ pointSize: Style.fontSizeS
+ color: Color.mPrimary
+ }
+ }
+ }
+
+ // Delete button
+ NIconButton {
+ icon: "trash"
+ tooltipText: I18n.tr("tooltips.delete-notification")
+ baseSize: Style.baseWidgetSize * 0.7
+ anchors.verticalCenter: parent.verticalCenter
+
+ onClicked: {
+ NotificationService.removeFromHistory(notificationId)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Modules/SessionMenu/SessionMenu.qml b/Modules/Panels/SessionMenu/SessionMenu.qml
similarity index 85%
rename from Modules/SessionMenu/SessionMenu.qml
rename to Modules/Panels/SessionMenu/SessionMenu.qml
index 623a93c5..942ae471 100644
--- a/Modules/SessionMenu/SessionMenu.qml
+++ b/Modules/Panels/SessionMenu/SessionMenu.qml
@@ -7,17 +7,19 @@ import Quickshell.Io
import Quickshell.Widgets
import Quickshell.Wayland
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.UI
import qs.Widgets
+import qs.Modules.MainScreen
-NPanel {
+SmartPanel {
id: root
- preferredWidth: 400 * Style.uiScaleRatio
- preferredHeight: 340 * Style.uiScaleRatio
+ preferredWidth: Math.round(400 * Style.uiScaleRatio)
+ preferredHeight: Math.round(340 * Style.uiScaleRatio)
+
panelAnchorHorizontalCenter: true
panelAnchorVerticalCenter: true
- panelKeyboardFocus: true
// Timer properties
property int timerDuration: 9000 // 9 seconds
@@ -87,9 +89,9 @@ NPanel {
switch (action) {
case "lock":
- // Access lockScreen directly like IPCManager does
- if (!lockScreen.active) {
- lockScreen.active = true
+ // Access lockScreen via PanelService
+ if (PanelService.lockScreen && !PanelService.lockScreen.active) {
+ PanelService.lockScreen.active = true
}
break
case "suspend":
@@ -148,6 +150,52 @@ NPanel {
}
}
+ // Override keyboard handlers from SmartPanel
+ function onEscapePressed() {
+ if (timerActive) {
+ cancelTimer()
+ } else {
+ cancelTimer()
+ close()
+ }
+ }
+
+ function onTabPressed() {
+ selectNextWrapped()
+ }
+
+ function onShiftTabPressed() {
+ selectPreviousWrapped()
+ }
+
+ function onUpPressed() {
+ selectPreviousWrapped()
+ }
+
+ function onDownPressed() {
+ selectNextWrapped()
+ }
+
+ function onReturnPressed() {
+ activate()
+ }
+
+ function onHomePressed() {
+ selectFirst()
+ }
+
+ function onEndPressed() {
+ selectLast()
+ }
+
+ function onCtrlJPressed() {
+ selectNextWrapped()
+ }
+
+ function onCtrlKPressed() {
+ selectPreviousWrapped()
+ }
+
// Countdown timer
Timer {
id: countdownTimer
@@ -165,81 +213,6 @@ NPanel {
id: ui
color: Color.transparent
- // Keyboard shortcuts
- Shortcut {
- sequence: "Ctrl+K"
- onActivated: ui.selectPreviousWrapped()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Ctrl+J"
- onActivated: ui.selectNextWrapped()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Up"
- onActivated: ui.selectPreviousWrapped()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Down"
- onActivated: ui.selectNextWrapped()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Shift+Tab"
- onActivated: ui.selectPreviousWrapped()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Tab"
- onActivated: ui.selectNextWrapped()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Home"
- onActivated: ui.selectFirst()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "End"
- onActivated: ui.selectLast()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Return"
- onActivated: ui.activate()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Enter"
- onActivated: ui.activate()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Escape"
- onActivated: {
- if (timerActive) {
- cancelTimer()
- } else {
- cancelTimer()
- root.close()
- }
- }
- context: Qt.WidgetShortcut
- enabled: root.opened
- }
-
// Navigation functions
function selectFirst() {
root.selectFirst()
diff --git a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml b/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml
similarity index 96%
rename from Modules/Settings/Bar/BarWidgetSettingsDialog.qml
rename to Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml
index 6e7a644e..0e68800b 100644
--- a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml
+++ b/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml
@@ -4,7 +4,7 @@ import QtQuick.Effects
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
+import qs.Services.UI
// Widget Settings Dialog Component
Popup {
@@ -21,22 +21,16 @@ Popup {
height: content.implicitHeight + padding * 2
padding: Style.marginXL
modal: true
+ dim: false
anchors.centerIn: parent
onOpened: {
- // Mark this popup has opened in the PanelService
- PanelService.willOpenPopup(root)
-
// Load settings when popup opens with data
if (widgetData && widgetId) {
loadWidgetSettings()
}
}
- onClosed: {
- PanelService.willClosePopup(root)
- }
-
background: Rectangle {
id: bgRect
diff --git a/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml
similarity index 99%
rename from Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml
index 514733dd..140fc511 100644
--- a/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/AudioVisualizerSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/AudioVisualizerSettings.qml
similarity index 99%
rename from Modules/Settings/Bar/WidgetSettings/AudioVisualizerSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/AudioVisualizerSettings.qml
index 936260a6..dea5809d 100644
--- a/Modules/Settings/Bar/WidgetSettings/AudioVisualizerSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/AudioVisualizerSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/BatterySettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/BatterySettings.qml
similarity index 98%
rename from Modules/Settings/Bar/WidgetSettings/BatterySettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/BatterySettings.qml
index 8377958d..7a5f046c 100644
--- a/Modules/Settings/Bar/WidgetSettings/BatterySettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/BatterySettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/BluetoothSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/BluetoothSettings.qml
similarity index 100%
rename from Modules/Settings/Bar/WidgetSettings/BluetoothSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/BluetoothSettings.qml
diff --git a/Modules/Settings/Bar/WidgetSettings/BrightnessSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/BrightnessSettings.qml
similarity index 98%
rename from Modules/Settings/Bar/WidgetSettings/BrightnessSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/BrightnessSettings.qml
index bc572776..ecf4869c 100644
--- a/Modules/Settings/Bar/WidgetSettings/BrightnessSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/BrightnessSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/ClockSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/ClockSettings.qml
similarity index 99%
rename from Modules/Settings/Bar/WidgetSettings/ClockSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/ClockSettings.qml
index 024592f1..3e650121 100644
--- a/Modules/Settings/Bar/WidgetSettings/ClockSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/ClockSettings.qml
@@ -3,7 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
+import qs.Services.System
ColumnLayout {
id: root
@@ -25,7 +25,7 @@ ColumnLayout {
property var focusedInput: null
property int focusedLineIndex: -1
- readonly property var now: Time.date
+ readonly property var now: Time.now
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
diff --git a/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/ControlCenterSettings.qml
similarity index 82%
rename from Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/ControlCenterSettings.qml
index 66c30c11..1b423d9c 100644
--- a/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/ControlCenterSettings.qml
@@ -4,7 +4,6 @@ import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
@@ -18,12 +17,14 @@ ColumnLayout {
property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon
property bool valueUseDistroLogo: widgetData.useDistroLogo !== undefined ? widgetData.useDistroLogo : widgetMetadata.useDistroLogo
property string valueCustomIconPath: widgetData.customIconPath !== undefined ? widgetData.customIconPath : ""
+ property bool valueColorizeDistroLogo: widgetData.colorizeDistroLogo !== undefined ? widgetData.colorizeDistroLogo : (widgetMetadata.colorizeDistroLogo !== undefined ? widgetMetadata.colorizeDistroLogo : false)
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.icon = valueIcon
settings.useDistroLogo = valueUseDistroLogo
settings.customIconPath = valueCustomIconPath
+ settings.colorizeDistroLogo = valueColorizeDistroLogo
return settings
}
@@ -40,6 +41,16 @@ ColumnLayout {
}
}
+ NToggle {
+ visible: valueUseDistroLogo
+ label: I18n.tr("bar.widget-settings.control-center.colorize-distro-logo.label")
+ description: I18n.tr("bar.widget-settings.control-center.colorize-distro-logo.description")
+ checked: valueColorizeDistroLogo
+ onToggled: function (checked) {
+ valueColorizeDistroLogo = checked
+ }
+ }
+
RowLayout {
spacing: Style.marginM
diff --git a/Modules/Settings/Bar/WidgetSettings/CustomButtonSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
similarity index 88%
rename from Modules/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
index 129613ab..715e4ef9 100644
--- a/Modules/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
@@ -4,7 +4,6 @@ import QtQuick.Layouts
import QtQuick.Window
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
@@ -16,6 +15,7 @@ ColumnLayout {
property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon
property bool valueTextStream: widgetData.textStream !== undefined ? widgetData.textStream : widgetMetadata.textStream
property bool valueParseJson: widgetData.parseJson !== undefined ? widgetData.parseJson : widgetMetadata.parseJson
+ property bool valueHideTextInVerticalBar: widgetData.hideTextInVerticalBar !== undefined ? widgetData.hideTextInVerticalBar : widgetMetadata.hideTextInVerticalBar
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
@@ -27,6 +27,7 @@ ColumnLayout {
settings.textCollapse = textCollapseInput.text
settings.textStream = valueTextStream
settings.parseJson = valueParseJson
+ settings.hideTextInVerticalBar = valueHideTextInVerticalBar
settings.textIntervalMs = parseInt(textIntervalInput.text || textIntervalInput.placeholderText, 10)
return settings
}
@@ -95,6 +96,13 @@ ColumnLayout {
label: I18n.tr("bar.widget-settings.custom-button.dynamic-text")
}
+ NToggle {
+ label: I18n.tr("bar.widget-settings.custom-button.hide-vertical.label", "Hide text in vertical bar")
+ description: I18n.tr("bar.widget-settings.custom-button.hide-vertical.description", "If enabled, the text from the command output will not be shown when the bar is in a vertical layout (left or right).")
+ checked: valueHideTextInVerticalBar
+ onToggled: checked => valueHideTextInVerticalBar = checked
+ }
+
NToggle {
id: textStreamInput
label: I18n.tr("bar.widget-settings.custom-button.text-stream.label")
diff --git a/Modules/Settings/Bar/WidgetSettings/KeyboardLayoutSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/KeyboardLayoutSettings.qml
similarity index 98%
rename from Modules/Settings/Bar/WidgetSettings/KeyboardLayoutSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/KeyboardLayoutSettings.qml
index 86ada35a..5b2bc230 100644
--- a/Modules/Settings/Bar/WidgetSettings/KeyboardLayoutSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/KeyboardLayoutSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Panels/Settings/Bar/WidgetSettings/LockKeysSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/LockKeysSettings.qml
new file mode 100644
index 00000000..dd9b12ef
--- /dev/null
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/LockKeysSettings.qml
@@ -0,0 +1,133 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import qs.Commons
+import qs.Widgets
+
+ColumnLayout {
+ id: root
+ spacing: Style.marginM
+
+ // Properties to receive data from parent
+ property var widgetData: null
+ property var widgetMetadata: null
+
+ // Local state
+ property bool valueShowCapsLock: widgetData.showCapsLock !== undefined ? widgetData.showCapsLock : widgetMetadata.showCapsLock
+ property bool valueShowNumLock: widgetData.showNumLock !== undefined ? widgetData.showNumLock : widgetMetadata.showNumLock
+ property bool valueShowScrollLock: widgetData.showScrollLock !== undefined ? widgetData.showScrollLock : widgetMetadata.showScrollLock
+
+ property string capsIcon: widgetData.capsLockIcon !== undefined ? widgetData.capsLockIcon : widgetMetadata.capsLockIcon
+ property string numIcon: widgetData.numLockIcon !== undefined ? widgetData.numLockIcon : widgetMetadata.numLockIcon
+ property string scrollIcon: widgetData.scrollLockIcon !== undefined ? widgetData.scrollLockIcon : widgetMetadata.scrollLockIcon
+
+ function saveSettings() {
+ var settings = Object.assign({}, widgetData || {})
+ settings.showCapsLock = valueShowCapsLock
+ settings.showNumLock = valueShowNumLock
+ settings.showScrollLock = valueShowScrollLock
+ settings.capsLockIcon = capsIcon
+ settings.numLockIcon = numIcon
+ settings.scrollLockIcon = scrollIcon
+ return settings
+ }
+
+ RowLayout {
+ spacing: Style.marginM
+
+ NToggle {
+ label: I18n.tr("bar.widget-settings.lock-keys.show-caps-lock.label")
+ description: I18n.tr("bar.widget-settings.lock-keys.show-caps-lock.description")
+ checked: valueShowCapsLock
+ onToggled: checked => valueShowCapsLock = checked
+ }
+
+ NIcon {
+ Layout.alignment: Qt.AlignVCenter
+ icon: capsIcon
+ pointSize: Style.fontSizeXL
+ visible: capsIcon !== ""
+ }
+
+ NButton {
+ text: I18n.tr("bar.widget-settings.lock-keys.browse")
+ onClicked: capsPicker.open()
+ enabled: valueShowCapsLock
+ }
+ }
+
+ NIconPicker {
+ id: capsPicker
+ initialIcon: capsIcon
+ query: "letter-c"
+ onIconSelected: function (iconName) {
+ capsIcon = iconName
+ }
+ }
+
+ RowLayout {
+ spacing: Style.marginM
+
+ NToggle {
+ label: I18n.tr("bar.widget-settings.lock-keys.show-num-lock.label")
+ description: I18n.tr("bar.widget-settings.lock-keys.show-num-lock.description")
+ checked: valueShowNumLock
+ onToggled: checked => valueShowNumLock = checked
+ }
+
+ NIcon {
+ Layout.alignment: Qt.AlignVCenter
+ icon: numIcon
+ pointSize: Style.fontSizeXL
+ visible: numIcon !== ""
+ }
+
+ NButton {
+ text: I18n.tr("bar.widget-settings.lock-keys.browse")
+ onClicked: numPicker.open()
+ enabled: valueShowNumLock
+ }
+ }
+
+ NIconPicker {
+ id: numPicker
+ initialIcon: numIcon
+ query: "letter-n"
+ onIconSelected: function (iconName) {
+ numIcon = iconName
+ }
+ }
+
+ RowLayout {
+ spacing: Style.marginM
+
+ NToggle {
+ label: I18n.tr("bar.widget-settings.lock-keys.show-scroll-lock.label")
+ description: I18n.tr("bar.widget-settings.lock-keys.show-scroll-lock.description")
+ checked: valueShowScrollLock
+ onToggled: checked => valueShowScrollLock = checked
+ }
+
+ NIcon {
+ Layout.alignment: Qt.AlignVCenter
+ icon: scrollIcon
+ pointSize: Style.fontSizeXL
+ visible: scrollIcon !== ""
+ }
+
+ NButton {
+ text: I18n.tr("bar.widget-settings.lock-keys.browse")
+ onClicked: scrollPicker.open()
+ enabled: valueShowScrollLock
+ }
+ }
+
+ NIconPicker {
+ id: scrollPicker
+ initialIcon: scrollIcon
+ query: "letter-s"
+ onIconSelected: function (iconName) {
+ scrollIcon = iconName
+ }
+ }
+}
diff --git a/Modules/Settings/Bar/WidgetSettings/MediaMiniSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/MediaMiniSettings.qml
similarity index 91%
rename from Modules/Settings/Bar/WidgetSettings/MediaMiniSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/MediaMiniSettings.qml
index 5e109feb..808fe3dc 100644
--- a/Modules/Settings/Bar/WidgetSettings/MediaMiniSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/MediaMiniSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
@@ -15,6 +14,8 @@ ColumnLayout {
// Local state
property string valueHideMode: "hidden" // Default to 'Hide When Empty'
+ // Deprecated: hideWhenIdle now folded into hideMode = "idle"
+ property bool valueHideWhenIdle: widgetData.hideWhenIdle !== undefined ? widgetData.hideWhenIdle : (widgetMetadata.hideWhenIdle !== undefined ? widgetMetadata.hideWhenIdle : false)
property bool valueShowAlbumArt: widgetData.showAlbumArt !== undefined ? widgetData.showAlbumArt : widgetMetadata.showAlbumArt
property bool valueShowVisualizer: widgetData.showVisualizer !== undefined ? widgetData.showVisualizer : widgetMetadata.showVisualizer
property string valueVisualizerType: widgetData.visualizerType || widgetMetadata.visualizerType
@@ -31,6 +32,7 @@ ColumnLayout {
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.hideMode = valueHideMode
+ // No longer store hideWhenIdle separately; kept for backward compatibility only
settings.showAlbumArt = valueShowAlbumArt
settings.showVisualizer = valueShowVisualizer
settings.visualizerType = valueVisualizerType
@@ -53,6 +55,9 @@ ColumnLayout {
}, {
"key": "transparent",
"name": I18n.tr("options.hide-modes.transparent")
+ }, {
+ "key": "idle",
+ "name": I18n.tr("options.hide-modes.idle")
}]
currentKey: root.valueHideMode
onSelected: key => root.valueHideMode = key
diff --git a/Modules/Settings/Bar/WidgetSettings/MicrophoneSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/MicrophoneSettings.qml
similarity index 98%
rename from Modules/Settings/Bar/WidgetSettings/MicrophoneSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/MicrophoneSettings.qml
index e62ac1da..3ff2ee58 100644
--- a/Modules/Settings/Bar/WidgetSettings/MicrophoneSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/MicrophoneSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml
similarity index 98%
rename from Modules/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml
index 61c4e10d..ba6475af 100644
--- a/Modules/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/SpacerSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/SpacerSettings.qml
similarity index 97%
rename from Modules/Settings/Bar/WidgetSettings/SpacerSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/SpacerSettings.qml
index 9679c619..423f0d65 100644
--- a/Modules/Settings/Bar/WidgetSettings/SpacerSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/SpacerSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml
similarity index 87%
rename from Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml
index 8133ab18..9ca62f78 100644
--- a/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
@@ -14,6 +13,7 @@ ColumnLayout {
property var widgetMetadata: null
// Local, editable state for checkboxes
+ property bool valueUsePrimaryColor: widgetData.usePrimaryColor !== undefined ? widgetData.usePrimaryColor : widgetMetadata.usePrimaryColor
property bool valueShowCpuUsage: widgetData.showCpuUsage !== undefined ? widgetData.showCpuUsage : widgetMetadata.showCpuUsage
property bool valueShowCpuTemp: widgetData.showCpuTemp !== undefined ? widgetData.showCpuTemp : widgetMetadata.showCpuTemp
property bool valueShowMemoryUsage: widgetData.showMemoryUsage !== undefined ? widgetData.showMemoryUsage : widgetMetadata.showMemoryUsage
@@ -23,6 +23,7 @@ ColumnLayout {
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
+ settings.usePrimaryColor = valueUsePrimaryColor
settings.showCpuUsage = valueShowCpuUsage
settings.showCpuTemp = valueShowCpuTemp
settings.showMemoryUsage = valueShowMemoryUsage
@@ -32,6 +33,14 @@ ColumnLayout {
return settings
}
+ NToggle {
+ Layout.fillWidth: true
+ label: I18n.tr("bar.widget-settings.clock.use-primary-color.label")
+ description: I18n.tr("bar.widget-settings.clock.use-primary-color.description")
+ checked: valueUsePrimaryColor
+ onToggled: checked => valueUsePrimaryColor = checked
+ }
+
NToggle {
id: showCpuUsage
Layout.fillWidth: true
diff --git a/Modules/Settings/Bar/WidgetSettings/TaskbarGroupedSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/TaskbarGroupedSettings.qml
similarity index 100%
rename from Modules/Settings/Bar/WidgetSettings/TaskbarGroupedSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/TaskbarGroupedSettings.qml
diff --git a/Modules/Settings/Bar/WidgetSettings/TaskbarSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/TaskbarSettings.qml
similarity index 99%
rename from Modules/Settings/Bar/WidgetSettings/TaskbarSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/TaskbarSettings.qml
index 9ea560e8..825d32d5 100644
--- a/Modules/Settings/Bar/WidgetSettings/TaskbarSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/TaskbarSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/TraySettings.qml
similarity index 88%
rename from Modules/Settings/Bar/WidgetSettings/TraySettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/TraySettings.qml
index 3f2c7d2c..a628863d 100644
--- a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/TraySettings.qml
@@ -13,6 +13,7 @@ ColumnLayout {
// Local state
property var localBlacklist: widgetData.blacklist || []
property bool valueColorizeIcons: widgetData.colorizeIcons !== undefined ? widgetData.colorizeIcons : widgetMetadata.colorizeIcons
+ property bool valueDrawerEnabled: widgetData.drawerEnabled !== undefined ? widgetData.drawerEnabled : (widgetMetadata.drawerEnabled !== undefined ? widgetMetadata.drawerEnabled : true)
ListModel {
id: blacklistModel
@@ -37,6 +38,14 @@ ColumnLayout {
onToggled: checked => root.valueColorizeIcons = checked
}
+ NToggle {
+ Layout.fillWidth: true
+ label: I18n.tr("bar.widget-settings.tray.drawer-enabled.label")
+ description: I18n.tr("bar.widget-settings.tray.drawer-enabled.description")
+ checked: root.valueDrawerEnabled
+ onToggled: checked => root.valueDrawerEnabled = checked
+ }
+
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginS
@@ -140,6 +149,7 @@ ColumnLayout {
var settings = Object.assign({}, widgetData || {})
settings.blacklist = newBlacklist
settings.colorizeIcons = root.valueColorizeIcons
+ settings.drawerEnabled = root.valueDrawerEnabled
return settings
}
}
diff --git a/Modules/Settings/Bar/WidgetSettings/VolumeSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/VolumeSettings.qml
similarity index 98%
rename from Modules/Settings/Bar/WidgetSettings/VolumeSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/VolumeSettings.qml
index 3b131d38..09032661 100644
--- a/Modules/Settings/Bar/WidgetSettings/VolumeSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/VolumeSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Settings/Bar/WidgetSettings/WiFiSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/WiFiSettings.qml
similarity index 100%
rename from Modules/Settings/Bar/WidgetSettings/WiFiSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/WiFiSettings.qml
diff --git a/Modules/Settings/Bar/WidgetSettings/WorkspaceSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/WorkspaceSettings.qml
similarity index 99%
rename from Modules/Settings/Bar/WidgetSettings/WorkspaceSettings.qml
rename to Modules/Panels/Settings/Bar/WidgetSettings/WorkspaceSettings.qml
index 4beecd41..83d0bbad 100644
--- a/Modules/Settings/Bar/WidgetSettings/WorkspaceSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/WorkspaceSettings.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
ColumnLayout {
id: root
diff --git a/Modules/Panels/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml b/Modules/Panels/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml
new file mode 100644
index 00000000..8ebea72e
--- /dev/null
+++ b/Modules/Panels/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml
@@ -0,0 +1,119 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import qs.Commons
+import qs.Widgets
+import qs.Services.UI
+
+// Widget Settings Dialog Component
+Popup {
+ id: root
+
+ property int widgetIndex: -1
+ property var widgetData: null
+ property string widgetId: ""
+ property string sectionId: ""
+
+ signal updateWidgetSettings(string section, int index, var settings)
+
+ width: Math.max(content.implicitWidth + padding * 2, 500)
+ height: content.implicitHeight + padding * 2
+ padding: Style.marginXL
+ modal: true
+ anchors.centerIn: parent
+
+ onOpened: {
+ if (widgetData && widgetId) {
+ loadWidgetSettings()
+ }
+ }
+
+ background: Rectangle {
+ color: Color.mSurface
+ radius: Style.radiusL
+ border.color: Color.mPrimary
+ border.width: Style.borderM
+ }
+
+ contentItem: ColumnLayout {
+ id: content
+ width: parent.width
+ spacing: Style.marginM
+
+ // Title
+ RowLayout {
+ Layout.fillWidth: true
+
+ NText {
+ text: I18n.tr("system.widget-settings-title", {
+ "widget": root.widgetId
+ })
+ pointSize: Style.fontSizeL
+ font.weight: Style.fontWeightBold
+ color: Color.mPrimary
+ Layout.fillWidth: true
+ }
+
+ NIconButton {
+ icon: "close"
+ tooltipText: I18n.tr("tooltips.close")
+ onClicked: root.close()
+ }
+ }
+
+ // Separator
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 1
+ color: Color.mOutline
+ }
+
+ Loader {
+ id: settingsLoader
+ Layout.fillWidth: true
+ }
+
+ // Action buttons
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.topMargin: Style.marginM
+ spacing: Style.marginM
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ NButton {
+ text: I18n.tr("settings.control-center.shortcuts.dialog.cancel", "Cancel")
+ outlined: true
+ onClicked: root.close()
+ }
+
+ NButton {
+ text: I18n.tr("settings.control-center.shortcuts.dialog.apply", "Apply")
+ icon: "check"
+ onClicked: {
+ if (settingsLoader.item && settingsLoader.item.saveSettings) {
+ var newSettings = settingsLoader.item.saveSettings()
+ root.updateWidgetSettings(root.sectionId, root.widgetIndex, newSettings)
+ root.close()
+ }
+ }
+ }
+ }
+ }
+
+ function loadWidgetSettings() {
+ const widgetSettingsMap = {
+ "CustomButton": "WidgetSettings/CustomButtonSettings.qml"
+ }
+
+ const source = widgetSettingsMap[widgetId]
+ if (source) {
+ settingsLoader.setSource(source, {
+ "widgetData": widgetData,
+ "widgetMetadata": ControlCenterWidgetRegistry.widgetMetadata[widgetId]
+ })
+ }
+ }
+}
diff --git a/Modules/Panels/Settings/ControlCenter/WidgetSettings/CustomButtonSettings.qml b/Modules/Panels/Settings/ControlCenter/WidgetSettings/CustomButtonSettings.qml
new file mode 100644
index 00000000..3cd0cb9b
--- /dev/null
+++ b/Modules/Panels/Settings/ControlCenter/WidgetSettings/CustomButtonSettings.qml
@@ -0,0 +1,244 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQml.Models
+import qs.Commons
+import qs.Widgets
+
+ColumnLayout {
+ id: root
+
+ property var widgetData: null
+ property var widgetMetadata: null
+
+ QtObject {
+ id: _settings
+
+ property string icon: (widgetData && widgetData.icon !== undefined) ? widgetData.icon : widgetMetadata.icon
+ property string onClicked: (widgetData && widgetData.onClicked !== undefined) ? widgetData.onClicked : widgetMetadata.onClicked
+ property string onRightClicked: (widgetData && widgetData.onRightClicked !== undefined) ? widgetData.onRightClicked : widgetMetadata.onRightClicked
+ property string onMiddleClicked: (widgetData && widgetData.onMiddleClicked !== undefined) ? widgetData.onMiddleClicked : widgetMetadata.onMiddleClicked
+ property ListModel _stateChecksListModel: ListModel {}
+ property string stateChecksJson: "[]"
+ property string generalTooltipText: (widgetData && widgetData.generalTooltipText !== undefined) ? widgetData.generalTooltipText : widgetMetadata.generalTooltipText
+ property bool enableOnStateLogic: (widgetData && widgetData.enableOnStateLogic !== undefined) ? widgetData.enableOnStateLogic : widgetMetadata.enableOnStateLogic
+
+ Component.onCompleted: {
+ stateChecksJson = (widgetData && widgetData.stateChecksJson !== undefined) ? widgetData.stateChecksJson : widgetMetadata.stateChecksJson || "[]"
+ try {
+ var initialChecks = JSON.parse(stateChecksJson)
+ if (initialChecks && Array.isArray(initialChecks)) {
+ for (var i = 0; i < initialChecks.length; i++) {
+ var item = initialChecks[i]
+ if (item && typeof item === "object") {
+ _settings._stateChecksListModel.append({
+ "command": item.command || "",
+ "icon": item.icon || ""
+ })
+ } else {
+ console.warn("⚠️ Invalid stateChecks entry at index " + i + ":", item)
+ }
+ }
+ }
+ } catch (e) {
+ console.error("CustomButtonSettings: Failed to parse stateChecksJson:", e.message)
+ }
+ }
+ }
+
+ function saveSettings() {
+ var savedStateChecksArray = []
+ for (var i = 0; i < _settings._stateChecksListModel.count; i++) {
+ savedStateChecksArray.push(_settings._stateChecksListModel.get(i))
+ }
+ _settings.stateChecksJson = JSON.stringify(savedStateChecksArray)
+
+ return {
+ "id": widgetData.id,
+ "icon": _settings.icon,
+ "onClicked": _settings.onClicked,
+ "onRightClicked": _settings.onRightClicked,
+ "onMiddleClicked": _settings.onMiddleClicked,
+ "stateChecksJson": _settings.stateChecksJson,
+ "generalTooltipText": _settings.generalTooltipText,
+ "enableOnStateLogic": _settings.enableOnStateLogic
+ }
+ }
+
+ RowLayout {
+ spacing: Style?.marginM ?? 8
+
+ NLabel {
+ label: I18n.tr("settings.control-center.shortcuts.custom-button.icon.label")
+ description: I18n.tr("settings.control-center.shortcuts.custom-button.icon.description")
+ }
+
+ NIcon {
+ Layout.alignment: Qt.AlignVCenter
+ icon: _settings.icon || widgetMetadata.icon
+ pointSize: Style?.fontSizeXL ?? 24
+ visible: (_settings.icon || widgetMetadata.icon) !== ""
+ }
+
+ NButton {
+ text: I18n.tr("settings.control-center.shortcuts.custom-button.browse")
+ onClicked: iconPicker.open()
+ }
+ }
+
+ NIconPicker {
+ id: iconPicker
+ initialIcon: _settings.icon
+ onIconSelected: function (iconName) {
+ _settings.icon = iconName
+ }
+ }
+
+ NTextInput {
+ Layout.fillWidth: true
+ label: I18n.tr("settings.control-center.shortcuts.custom-button.general-tooltip-text.label")
+ description: I18n.tr("settings.control-center.shortcuts.custom-button.general-tooltip-text.description")
+ placeholderText: I18n.tr("placeholders.enter-tooltip")
+ text: _settings.generalTooltipText
+ onTextChanged: _settings.generalTooltipText = text
+ }
+
+ NTextInput {
+ Layout.fillWidth: true
+ label: I18n.tr("settings.control-center.shortcuts.custom-button.on-clicked.label")
+ description: I18n.tr("settings.control-center.shortcuts.custom-button.on-clicked.description")
+ placeholderText: I18n.tr("placeholders.enter-command")
+ text: _settings.onClicked
+ onTextChanged: _settings.onClicked = text
+ }
+
+ NTextInput {
+ Layout.fillWidth: true
+ label: I18n.tr("settings.control-center.shortcuts.custom-button.on-right-clicked.label")
+ description: I18n.tr("settings.control-center.shortcuts.custom-button.on-right-clicked.description")
+ placeholderText: I18n.tr("placeholders.enter-command")
+ text: _settings.onRightClicked
+ onTextChanged: _settings.onRightClicked = text
+ }
+
+ NTextInput {
+ Layout.fillWidth: true
+ label: I18n.tr("settings.control-center.shortcuts.custom-button.on-middle-clicked.label")
+ description: I18n.tr("settings.control-center.shortcuts.custom-button.on-middle-clicked.description")
+ placeholderText: I18n.tr("placeholders.enter-command")
+ text: _settings.onMiddleClicked
+ onTextChanged: _settings.onMiddleClicked = text
+ }
+
+ NDivider {}
+
+ NToggle {
+ id: enableOnStateLogicToggle
+ Layout.fillWidth: true
+ label: I18n.tr("settings.control-center.shortcuts.custom-button.enable-on-state-logic.label")
+ description: I18n.tr("settings.control-center.shortcuts.custom-button.enable-on-state-logic.description")
+ checked: _settings.enableOnStateLogic
+ onToggled: checked => _settings.enableOnStateLogic = checked
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ visible: _settings.enableOnStateLogic
+ spacing: (Style?.marginM ?? 8) * 2
+
+ NLabel {
+ label: I18n.tr("settings.control-center.shortcuts.custom-button.state-checks.label")
+ }
+
+ Repeater {
+ model: _settings._stateChecksListModel
+ delegate: Item {
+ property int currentIndex: index
+
+ implicitHeight: contentRow.implicitHeight + ((divider.visible) ? divider.height : 0)
+ Layout.fillWidth: true
+
+ RowLayout {
+ id: contentRow
+ anchors.fill: parent
+ spacing: Style?.marginM ?? 8
+
+ NTextInput {
+ Layout.fillWidth: true
+ placeholderText: I18n.tr("settings.control-center.shortcuts.custom-button.state-checks.command")
+ text: model.command
+ onEditingFinished: _settings._stateChecksListModel.set(currentIndex, {
+ "command": text,
+ "icon": model.icon
+ })
+ }
+
+ RowLayout {
+ Layout.alignment: Qt.AlignVCenter
+ spacing: Style?.marginS ?? 4
+
+ NIcon {
+ icon: model.icon
+ pointSize: Style?.fontSizeL ?? 20
+ visible: model.icon !== undefined && model.icon !== ""
+ }
+
+ NIconButton {
+ icon: "folder"
+ tooltipText: I18n.tr("settings.control-center.shortcuts.custom-button.state-checks.browse-icon")
+ baseSize: Style?.buttonSizeS ?? 24
+ onClicked: iconPickerDelegate.open()
+ }
+
+ NIconButton {
+ icon: "close"
+ tooltipText: I18n.tr("settings.control-center.shortcuts.custom-button.state-checks.remove")
+ baseSize: Style?.buttonSizeS ?? 24
+ colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
+ colorBg: Color.mError
+ colorFg: Color.mOnError
+ colorBgHover: Qt.alpha(Color.mError, Style.opacityMedium)
+ colorFgHover: Color.mOnError
+ onClicked: _settings._stateChecksListModel.remove(currentIndex)
+ }
+ }
+ }
+
+ NIconPicker {
+ id: iconPickerDelegate
+ initialIcon: model.icon
+ onIconSelected: function (iconName) {
+ _settings._stateChecksListModel.set(currentIndex, {
+ "command": model.command,
+ "icon": iconName
+ })
+ }
+ }
+
+ NDivider {
+ id: divider
+ anchors.bottom: parent.bottom
+ visible: index < _settings._stateChecksListModel.count - 1 // Only show divider if not the last item
+ }
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Style?.marginM ?? 8
+
+ NButton {
+ text: I18n.tr("settings.control-center.shortcuts.custom-button.state-checks.add")
+ onClicked: _settings._stateChecksListModel.append({
+ "command": "",
+ "icon": ""
+ })
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+ }
+ }
+
+ NDivider {}
+}
diff --git a/Modules/Settings/SettingsPanel.qml b/Modules/Panels/Settings/SettingsPanel.qml
similarity index 71%
rename from Modules/Settings/SettingsPanel.qml
rename to Modules/Panels/Settings/SettingsPanel.qml
index a8fc5bce..9d024e7d 100644
--- a/Modules/Settings/SettingsPanel.qml
+++ b/Modules/Panels/Settings/SettingsPanel.qml
@@ -2,23 +2,61 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
-import Quickshell.Wayland
-import qs.Modules.Settings.Tabs
+import qs.Modules.Panels.Settings.Tabs
import qs.Commons
-import qs.Services
+import qs.Modules.MainScreen
+import qs.Services.System
import qs.Widgets
-NPanel {
+SmartPanel {
id: root
- preferredWidth: 820 * Style.uiScaleRatio
- preferredHeight: 940 * Style.uiScaleRatio
+ preferredWidth: Math.round(820 * Style.uiScaleRatio)
+ preferredHeight: Math.round(910 * Style.uiScaleRatio)
- panelAnchorHorizontalCenter: true
- panelAnchorVerticalCenter: true
- panelKeyboardFocus: true
+ readonly property bool attachToBar: Settings.data.ui.settingsPanelAttachToBar
+ readonly property string barPosition: Settings.data.bar.position
+ readonly property bool barFloating: Settings.data.bar.floating
+ readonly property real barMarginH: barFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ readonly property real barMarginV: barFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
- draggable: !PanelService.hasOpenedPopup
+ forceAttachToBar: attachToBar
+ panelAnchorHorizontalCenter: attachToBar ? (barPosition === "top" || barPosition === "bottom") : true
+ panelAnchorVerticalCenter: attachToBar ? (barPosition === "left" || barPosition === "right") : true
+ panelAnchorTop: attachToBar && barPosition === "top"
+ panelAnchorBottom: attachToBar && barPosition === "bottom"
+ panelAnchorLeft: attachToBar && barPosition === "left"
+ panelAnchorRight: attachToBar && barPosition === "right"
+
+ onAttachToBarChanged: {
+ if (isPanelOpen) {
+ Qt.callLater(root.setPosition)
+ }
+ }
+
+ onBarPositionChanged: {
+ if (isPanelOpen) {
+ Qt.callLater(root.setPosition)
+ }
+ }
+
+ onBarFloatingChanged: {
+ if (isPanelOpen) {
+ Qt.callLater(root.setPosition)
+ }
+ }
+
+ onBarMarginHChanged: {
+ if (isPanelOpen) {
+ Qt.callLater(root.setPosition)
+ }
+ }
+
+ onBarMarginVChanged: {
+ if (isPanelOpen) {
+ Qt.callLater(root.setPosition)
+ }
+ }
// Tabs enumeration, order is NOT relevant
enum Tab {
@@ -287,6 +325,39 @@ NPanel {
}
}
+ // Override keyboard handlers from SmartPanel
+ function onTabPressed() {
+ selectNextTab()
+ }
+
+ function onShiftTabPressed() {
+ selectPreviousTab()
+ }
+
+ function onUpPressed() {
+ scrollUp()
+ }
+
+ function onDownPressed() {
+ scrollDown()
+ }
+
+ function onPageUpPressed() {
+ scrollPageUp()
+ }
+
+ function onPageDownPressed() {
+ scrollPageDown()
+ }
+
+ function onCtrlJPressed() {
+ scrollDown()
+ }
+
+ function onCtrlKPressed() {
+ scrollUp()
+ }
+
panelContent: Rectangle {
color: Color.transparent
@@ -296,62 +367,6 @@ NPanel {
anchors.margins: Style.marginL
spacing: 0
- // Keyboard shortcuts container
- Item {
- Layout.preferredWidth: 0
- Layout.preferredHeight: 0
-
- // Scrolling via keyboard
- Shortcut {
- sequence: "Down"
- onActivated: root.scrollDown()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Up"
- onActivated: root.scrollUp()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Ctrl+J"
- onActivated: root.scrollDown()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Ctrl+K"
- onActivated: root.scrollUp()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "PgDown"
- onActivated: root.scrollPageDown()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "PgUp"
- onActivated: root.scrollPageUp()
- enabled: root.opened
- }
-
- // Changing tab via keyboard
- Shortcut {
- sequence: "Tab"
- onActivated: root.selectNextTab()
- enabled: root.opened
- }
-
- Shortcut {
- sequence: "Shift+Tab"
- onActivated: root.selectPreviousTab()
- enabled: root.opened
- }
- }
-
// Main content area
RowLayout {
Layout.fillWidth: true
@@ -361,6 +376,8 @@ NPanel {
// Sidebar
Rectangle {
id: sidebar
+
+ clip: true
Layout.preferredWidth: 220 * Style.uiScaleRatio
Layout.fillHeight: true
Layout.alignment: Qt.AlignTop
@@ -369,92 +386,93 @@ NPanel {
border.width: Style.borderS
radius: Style.radiusM
- MouseArea {
- anchors.fill: parent
- acceptedButtons: Qt.NoButton // Don't interfere with clicks
- property int wheelAccumulator: 0
- onWheel: wheel => {
- wheelAccumulator += wheel.angleDelta.y
- if (wheelAccumulator >= 120) {
- root.selectPreviousTab()
- wheelAccumulator = 0
- } else if (wheelAccumulator <= -120) {
- root.selectNextTab()
- wheelAccumulator = 0
- }
- wheel.accepted = true
- }
- }
-
- ColumnLayout {
+ NListView {
+ id: sidebarList
anchors.fill: parent
anchors.margins: Style.marginS
+ model: root.tabsModel
spacing: Style.marginXS
+ currentIndex: root.currentTabIndex
+ verticalPolicy: ScrollBar.AsNeeded
- Repeater {
- id: sections
- model: root.tabsModel
- delegate: Rectangle {
- id: tabItem
- Layout.fillWidth: true
- Layout.preferredHeight: tabEntryRow.implicitHeight + Style.marginM * 2
- radius: Style.radiusS
- color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mHover : Color.transparent)
- readonly property bool selected: index === currentTabIndex
- property bool hovering: false
- property color tabTextColor: selected ? Color.mOnPrimary : (tabItem.hovering ? Color.mOnHover : Color.mOnSurface)
+ delegate: Rectangle {
+ id: tabItem
+ width: sidebarList.verticalScrollBarActive ? sidebarList.width - sidebarList.scrollBarWidth - Style.marginXS : sidebarList.width
+ height: tabEntryRow.implicitHeight + Style.marginM * 2
+ radius: Style.radiusS
+ color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mHover : Color.transparent)
+ readonly property bool selected: index === root.currentTabIndex
+ property bool hovering: false
+ property color tabTextColor: selected ? Color.mOnPrimary : (tabItem.hovering ? Color.mOnHover : Color.mOnSurface)
- Behavior on color {
- ColorAnimation {
- duration: Style.animationFast
- }
+ Behavior on width {
+ NumberAnimation {
+ duration: Style.animationFast
+ }
+ }
+
+ Behavior on color {
+ ColorAnimation {
+ duration: Style.animationFast
+ }
+ }
+
+ Behavior on tabTextColor {
+ ColorAnimation {
+ duration: Style.animationFast
+ }
+ }
+
+ RowLayout {
+ id: tabEntryRow
+ anchors.fill: parent
+ anchors.leftMargin: Style.marginS
+ anchors.rightMargin: Style.marginS
+ spacing: Style.marginM
+
+ // Tab icon
+ NIcon {
+ icon: modelData.icon
+ color: tabTextColor
+ pointSize: Style.fontSizeXL
}
- Behavior on tabTextColor {
- ColorAnimation {
- duration: Style.animationFast
- }
+ // Tab label
+ NText {
+ text: I18n.tr(modelData.label)
+ color: tabTextColor
+ pointSize: Style.fontSizeM
+ font.weight: Style.fontWeightBold
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
}
+ }
- RowLayout {
- id: tabEntryRow
- anchors.fill: parent
- anchors.leftMargin: Style.marginS
- anchors.rightMargin: Style.marginS
- spacing: Style.marginM
-
- // Tab icon
- NIcon {
- icon: modelData.icon
- color: tabTextColor
- pointSize: Style.fontSizeXL
- }
-
- // Tab label
- NText {
- text: I18n.tr(modelData.label)
- color: tabTextColor
- pointSize: Style.fontSizeM
- font.weight: Style.fontWeightBold
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignVCenter
- }
- }
-
- MouseArea {
- anchors.fill: parent
- hoverEnabled: true
- acceptedButtons: Qt.LeftButton
- onEntered: tabItem.hovering = true
- onExited: tabItem.hovering = false
- onCanceled: tabItem.hovering = false
- onClicked: currentTabIndex = index
- }
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ acceptedButtons: Qt.LeftButton
+ onEntered: tabItem.hovering = true
+ onExited: tabItem.hovering = false
+ onCanceled: tabItem.hovering = false
+ onClicked: root.currentTabIndex = index
}
}
- Item {
- Layout.fillHeight: true
+ onCurrentIndexChanged: {
+ if (currentIndex !== root.currentTabIndex) {
+ root.currentTabIndex = currentIndex
+ }
+ }
+
+ Connections {
+ target: root
+ function onCurrentTabIndexChanged() {
+ if (sidebarList.currentIndex !== root.currentTabIndex) {
+ sidebarList.currentIndex = root.currentTabIndex
+ sidebarList.positionViewAtIndex(root.currentTabIndex, ListView.Contain)
+ }
+ }
}
}
}
diff --git a/Modules/Settings/Tabs/AboutTab.qml b/Modules/Panels/Settings/Tabs/AboutTab.qml
similarity index 97%
rename from Modules/Settings/Tabs/AboutTab.qml
rename to Modules/Panels/Settings/Tabs/AboutTab.qml
index 087b712b..b3463bc3 100644
--- a/Modules/Settings/Tabs/AboutTab.qml
+++ b/Modules/Panels/Settings/Tabs/AboutTab.qml
@@ -5,7 +5,7 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.Noctalia
import qs.Widgets
ColumnLayout {
@@ -135,7 +135,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXXXL
- Layout.bottomMargin: Style.marginXL
+ Layout.bottomMargin: Style.marginL
}
// Contributors
@@ -179,8 +179,8 @@ ColumnLayout {
}
RowLayout {
- anchors.fill: parent
- anchors.margins: Style.marginS
+ anchors.centerIn: parent
+ width: parent.width - (Style.marginS * 2)
spacing: Style.marginM
Item {
diff --git a/Modules/Settings/Tabs/AudioTab.qml b/Modules/Panels/Settings/Tabs/AudioTab.qml
similarity index 93%
rename from Modules/Settings/Tabs/AudioTab.qml
rename to Modules/Panels/Settings/Tabs/AudioTab.qml
index 1e5098be..d47fe949 100644
--- a/Modules/Settings/Tabs/AudioTab.qml
+++ b/Modules/Panels/Settings/Tabs/AudioTab.qml
@@ -3,7 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Services.Pipewire
import qs.Commons
-import qs.Services
+import qs.Services.Media
import qs.Widgets
ColumnLayout {
@@ -143,8 +143,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// AudioService Devices
@@ -222,8 +222,8 @@ ColumnLayout {
// Divider
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Media Player Preferences
@@ -331,7 +331,6 @@ ColumnLayout {
}
// AudioService Visualizer section
NComboBox {
- id: audioVisualizerCombo
label: I18n.tr("settings.audio.media.visualizer-type.label")
description: I18n.tr("settings.audio.media.visualizer-type.description")
model: [{
@@ -351,6 +350,20 @@ ColumnLayout {
onSelected: key => Settings.data.audio.visualizerType = key
}
+ NComboBox {
+ label: I18n.tr("settings.audio.media.visualizer-quality.label")
+ description: I18n.tr("settings.audio.media.visualizer-quality.description")
+ model: [{
+ "key": "low",
+ "name": I18n.tr("options.visualizer-quality.low")
+ }, {
+ "key": "high",
+ "name": I18n.tr("options.visualizer-quality.high")
+ }]
+ currentKey: Settings.data.audio.visualizerQuality
+ onSelected: key => Settings.data.audio.visualizerQuality = key
+ }
+
NComboBox {
label: I18n.tr("settings.audio.media.frame-rate.label")
description: I18n.tr("settings.audio.media.frame-rate.description")
@@ -397,7 +410,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/BarTab.qml b/Modules/Panels/Settings/Tabs/BarTab.qml
similarity index 90%
rename from Modules/Settings/Tabs/BarTab.qml
rename to Modules/Panels/Settings/Tabs/BarTab.qml
index a9552659..5e0c2fad 100644
--- a/Modules/Settings/Tabs/BarTab.qml
+++ b/Modules/Panels/Settings/Tabs/BarTab.qml
@@ -3,7 +3,8 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.UI
import qs.Widgets
ColumnLayout {
@@ -25,7 +26,7 @@ ColumnLayout {
// Handler for drag start - disables panel background clicks
function handleDragStart() {
- var panel = PanelService.getPanel("settingsPanel")
+ var panel = PanelService.getPanel("settingsPanel", screen)
if (panel && panel.disableBackgroundClick) {
panel.disableBackgroundClick()
}
@@ -33,7 +34,7 @@ ColumnLayout {
// Handler for drag end - re-enables panel background clicks
function handleDragEnd() {
- var panel = PanelService.getPanel("settingsPanel")
+ var panel = PanelService.getPanel("settingsPanel", screen)
if (panel && panel.enableBackgroundClick) {
panel.enableBackgroundClick()
}
@@ -99,7 +100,25 @@ ColumnLayout {
label: I18n.tr("settings.bar.appearance.floating.label")
description: I18n.tr("settings.bar.appearance.floating.description")
checked: Settings.data.bar.floating
- onToggled: checked => Settings.data.bar.floating = checked
+ onToggled: checked => {
+ Settings.data.bar.floating = checked
+ if (checked) {
+ // Disable outer corners when floating is enabled
+ Settings.data.bar.outerCorners = false
+ } else {
+ // Enable outer corners when floating is disabled
+ Settings.data.bar.outerCorners = true
+ }
+ }
+ }
+
+ NToggle {
+ Layout.fillWidth: true
+ label: I18n.tr("settings.bar.appearance.outer-corners.label")
+ description: I18n.tr("settings.bar.appearance.outer-corners.description")
+ checked: Settings.data.bar.outerCorners
+ visible: !Settings.data.bar.floating
+ onToggled: checked => Settings.data.bar.outerCorners = checked
}
// Floating bar options - only show when floating is enabled
@@ -181,8 +200,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Widgets Management Section
@@ -206,7 +225,7 @@ ColumnLayout {
NSectionEditor {
sectionName: "Left"
sectionId: "left"
- settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/Bar/BarWidgetSettingsDialog.qml")
+ settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml")
widgetRegistry: BarWidgetRegistry
widgetModel: Settings.data.bar.widgets.left
availableWidgets: availableWidgets
@@ -223,7 +242,7 @@ ColumnLayout {
NSectionEditor {
sectionName: "Center"
sectionId: "center"
- settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/Bar/BarWidgetSettingsDialog.qml")
+ settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml")
widgetRegistry: BarWidgetRegistry
widgetModel: Settings.data.bar.widgets.center
availableWidgets: availableWidgets
@@ -240,7 +259,7 @@ ColumnLayout {
NSectionEditor {
sectionName: "Right"
sectionId: "right"
- settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/Bar/BarWidgetSettingsDialog.qml")
+ settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml")
widgetRegistry: BarWidgetRegistry
widgetModel: Settings.data.bar.widgets.right
availableWidgets: availableWidgets
@@ -257,8 +276,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Monitor Configuration
@@ -299,8 +318,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Signal functions
diff --git a/Modules/Settings/Tabs/ColorSchemeTab.qml b/Modules/Panels/Settings/Tabs/ColorSchemeTab.qml
similarity index 90%
rename from Modules/Settings/Tabs/ColorSchemeTab.qml
rename to Modules/Panels/Settings/Tabs/ColorSchemeTab.qml
index f08cdb25..d11e6e68 100644
--- a/Modules/Settings/Tabs/ColorSchemeTab.qml
+++ b/Modules/Panels/Settings/Tabs/ColorSchemeTab.qml
@@ -4,7 +4,9 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.Theming
+import qs.Services.System
+import qs.Services.UI
import qs.Widgets
ColumnLayout {
@@ -111,9 +113,9 @@ ColumnLayout {
if (exitCode === 0) {
Settings.data.colorSchemes.useWallpaperColors = true
AppThemeService.generate()
- ToastService.showNotice(I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.label"), I18n.tr("toast.wallpaper-colors.enabled"), "settings-color-scheme")
+ ToastService.showNotice(I18n.tr("toast.wallpaper-colors.label"), I18n.tr("toast.wallpaper-colors.enabled"), "settings-color-scheme")
} else {
- ToastService.showWarning(I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.label"), I18n.tr("toast.wallpaper-colors.not-installed"))
+ ToastService.showWarning(I18n.tr("oast.wallpaper-colors.label"), I18n.tr("toast.wallpaper-colors.not-installed"))
}
}
@@ -128,7 +130,6 @@ ColumnLayout {
Repeater {
model: ColorSchemeService.schemes
-
delegate: Item {
FileView {
path: modelData
@@ -248,7 +249,7 @@ ColumnLayout {
matugenCheck.running = true
} else {
Settings.data.colorSchemes.useWallpaperColors = false
- ToastService.showNotice(I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.label"), I18n.tr("toast.wallpaper-colors.disabled"), "settings-color-scheme")
+ ToastService.showNotice(I18n.tr("toast.wallpaper-colors.label"), I18n.tr("toast.wallpaper-colors.disabled"), "settings-color-scheme")
if (Settings.data.colorSchemes.predefinedScheme) {
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
@@ -301,8 +302,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
visible: !Settings.data.colorSchemes.useWallpaperColors
}
@@ -460,14 +461,15 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Template toggles organized by category
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginL
+ visible: Settings.data.colorSchemes.useWallpaperColors || Settings.data.colorSchemes.generateTemplatesForPredefined
NHeader {
label: I18n.tr("settings.color-scheme.templates.section.label")
@@ -596,6 +598,24 @@ ColumnLayout {
}
}
}
+
+ NCheckbox {
+ label: "Wezterm"
+ description: ProgramCheckerService.weztermAvailable ? I18n.tr("settings.color-scheme.templates.terminal.wezterm.description", {
+ "filepath": "~/.config/wezterm/colors/Noctalia.toml"
+ }) : I18n.tr("settings.color-scheme.templates.terminal.wezterm.description-missing", {
+ "app": "wezterm"
+ })
+ checked: Settings.data.templates.wezterm
+ enabled: ProgramCheckerService.weztermAvailable
+ opacity: ProgramCheckerService.weztermAvailable ? 1.0 : 0.6
+ onToggled: checked => {
+ if (ProgramCheckerService.weztermAvailable) {
+ Settings.data.templates.wezterm = checked
+ AppThemeService.generate()
+ }
+ }
+ }
}
// Applications
@@ -737,8 +757,25 @@ ColumnLayout {
}
}
}
- }
+ NCheckbox {
+ label: "Spicetify"
+ description: ProgramCheckerService.spicetifyAvailable ? I18n.tr("settings.color-scheme.templates.programs.spicetify.description", {
+ "filepath": "~/.config/spicetify/Themes/Comfy/color.ini"
+ }) : I18n.tr("settings.color-scheme.templates.programs.spicetify.description-missing", {
+ "app": "spicetify"
+ })
+ checked: Settings.data.templates.spicetify
+ enabled: ProgramCheckerService.spicetifyAvailable
+ opacity: ProgramCheckerService.spicetifyAvailable ? 1.0 : 0.6
+ onToggled: checked => {
+ if (ProgramCheckerService.spicetifyAvailable) {
+ Settings.data.templates.spicetify = checked
+ AppThemeService.generate()
+ }
+ }
+ }
+ }
// Miscellaneous
NCollapsible {
Layout.fillWidth: true
@@ -753,7 +790,7 @@ ColumnLayout {
onToggled: checked => {
Settings.data.templates.enableUserTemplates = checked
if (checked) {
- MatugenTemplates.writeUserTemplatesToml()
+ TemplateRegistry.writeUserTemplatesToml()
}
AppThemeService.generate()
}
diff --git a/Modules/Settings/Tabs/ControlCenterTab.qml b/Modules/Panels/Settings/Tabs/ControlCenterTab.qml
similarity index 79%
rename from Modules/Settings/Tabs/ControlCenterTab.qml
rename to Modules/Panels/Settings/Tabs/ControlCenterTab.qml
index 372add57..989f56e7 100644
--- a/Modules/Settings/Tabs/ControlCenterTab.qml
+++ b/Modules/Panels/Settings/Tabs/ControlCenterTab.qml
@@ -3,7 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
ColumnLayout {
@@ -40,7 +40,7 @@ ColumnLayout {
// Handler for drag start - disables panel background clicks
function handleDragStart() {
- var panel = PanelService.getPanel("settingsPanel")
+ var panel = PanelService.getPanel("settingsPanel", screen)
if (panel && panel.disableBackgroundClick) {
panel.disableBackgroundClick()
}
@@ -48,7 +48,7 @@ ColumnLayout {
// Handler for drag end - re-enables panel background clicks
function handleDragEnd() {
- var panel = PanelService.getPanel("settingsPanel")
+ var panel = PanelService.getPanel("settingsPanel", screen)
if (panel && panel.enableBackgroundClick) {
panel.enableBackgroundClick()
}
@@ -68,12 +68,13 @@ ColumnLayout {
Component.onCompleted: {
// Fill out availableWidgets ListModel
availableWidgets.clear()
- ControlCenterWidgetRegistry.getAvailableWidgets().forEach(entry => {
- availableWidgets.append({
- "key": entry,
- "name": entry
- })
- })
+ var sortedEntries = ControlCenterWidgetRegistry.getAvailableWidgets().slice().sort()
+ sortedEntries.forEach(entry => {
+ availableWidgets.append({
+ "key": entry,
+ "name": entry
+ })
+ })
// Starts empty
cardsModel = []
@@ -85,6 +86,10 @@ ColumnLayout {
if (settingCard.id === cardsDefault[j].id) {
var card = cardsDefault[j]
card.enabled = settingCard.enabled
+ // Auto-disable weather card if weather is disabled
+ if (card.id === "weather-card" && !Settings.data.location.weatherEnabled) {
+ card.enabled = false
+ }
cardsModel.push(card)
}
}
@@ -101,7 +106,12 @@ ColumnLayout {
}
if (!found) {
- cardsModel.push(cardsDefault[i])
+ var card = cardsDefault[i]
+ // Auto-disable weather card if weather is disabled
+ if (card.id === "weather-card" && !Settings.data.location.weatherEnabled) {
+ card.enabled = false
+ }
+ cardsModel.push(card)
}
}
@@ -125,27 +135,27 @@ ColumnLayout {
model: [{
"key": "close_to_bar_button",
"name": I18n.tr("options.control-center.position.close_to_bar_button")
+ }, {
+ "key": "center",
+ "name": I18n.tr("options.control-center.position.center")
+ }, {
+ "key": "top_center",
+ "name": I18n.tr("options.control-center.position.top_center")
}, {
"key": "top_left",
"name": I18n.tr("options.control-center.position.top_left")
}, {
"key": "top_right",
"name": I18n.tr("options.control-center.position.top_right")
+ }, {
+ "key": "bottom_center",
+ "name": I18n.tr("options.control-center.position.bottom_center")
}, {
"key": "bottom_left",
"name": I18n.tr("options.control-center.position.bottom_left")
}, {
"key": "bottom_right",
"name": I18n.tr("options.control-center.position.bottom_right")
- }, {
- "key": "bottom_center",
- "name": I18n.tr("options.control-center.position.bottom_center")
- }, {
- "key": "top_center",
- "name": I18n.tr("options.control-center.position.top_center")
- }, {
- "key": "center",
- "name": I18n.tr("options.control-center.position.center")
}]
currentKey: Settings.data.controlCenter.position
onSelected: function (key) {
@@ -156,8 +166,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Widgets Management Section
@@ -170,9 +180,28 @@ ColumnLayout {
description: I18n.tr("settings.control-center.cards.section.description")
}
+ Connections {
+ target: Settings.data.location
+ function onWeatherEnabledChanged() {
+ // Auto-disable weather card when weather is disabled
+ var newModel = cardsModel.slice()
+ for (var i = 0; i < newModel.length; i++) {
+ if (newModel[i].id === "weather-card") {
+ newModel[i] = Object.assign({}, newModel[i], {
+ "enabled": Settings.data.location.weatherEnabled
+ })
+ cardsModel = newModel
+ saveCards()
+ break
+ }
+ }
+ }
+ }
+
NReorderCheckboxes {
Layout.fillWidth: true
model: cardsModel
+ disabledIds: Settings.data.location.weatherEnabled ? [] : ["weather-card"]
onDragPotentialStarted: {
root.handleDragStart()
}
@@ -201,8 +230,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Widgets Management Section
@@ -226,7 +255,7 @@ ColumnLayout {
NSectionEditor {
sectionName: I18n.tr("settings.control-center.shortcuts.sectionLeft")
sectionId: "left"
- settingsDialogComponent: ""
+ settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml")
maxWidgets: 5
widgetRegistry: ControlCenterWidgetRegistry
widgetModel: Settings.data.controlCenter.shortcuts["left"]
@@ -245,7 +274,7 @@ ColumnLayout {
NSectionEditor {
sectionName: I18n.tr("settings.control-center.shortcuts.sectionRight")
sectionId: "right"
- settingsDialogComponent: ""
+ settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml")
maxWidgets: 5
widgetRegistry: ControlCenterWidgetRegistry
widgetModel: Settings.data.controlCenter.shortcuts["right"]
@@ -264,8 +293,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// ---------------------------------
@@ -329,8 +358,12 @@ ColumnLayout {
}
function _updateWidgetSettingsInSection(section, index, settings) {
- // Update the widget settings in the Settings data
- Settings.data.controlCenter.shortcuts[section][index] = settings
+ // Create a new array to trigger QML's change detection for persistence.
+ // This is crucial for Settings.data to detect the change and persist it.
+ var newSectionArray = Settings.data.controlCenter.shortcuts[section].slice()
+ newSectionArray[index] = settings
+ Settings.data.controlCenter.shortcuts[section] = newSectionArray
+ Settings.saveImmediate()
}
// Base list model for all combo boxes
diff --git a/Modules/Settings/Tabs/DisplayTab.qml b/Modules/Panels/Settings/Tabs/DisplayTab.qml
similarity index 82%
rename from Modules/Settings/Tabs/DisplayTab.qml
rename to Modules/Panels/Settings/Tabs/DisplayTab.qml
index b835b50d..d9530e28 100644
--- a/Modules/Settings/Tabs/DisplayTab.qml
+++ b/Modules/Panels/Settings/Tabs/DisplayTab.qml
@@ -4,7 +4,9 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.Location
+import qs.Services.Hardware
import qs.Widgets
ColumnLayout {
@@ -113,12 +115,17 @@ ColumnLayout {
to: 1
value: brightnessMonitor ? brightnessMonitor.brightness : 0.5
stepSize: 0.01
+ enabled: brightnessMonitor ? brightnessMonitor.brightnessControlAvailable : false
onMoved: value => {
- if (brightnessMonitor.method === "internal") {
+ if (brightnessMonitor && brightnessMonitor.brightnessControlAvailable) {
brightnessMonitor.setBrightness(value)
}
}
- onPressedChanged: (pressed, value) => brightnessMonitor.setBrightness(value)
+ onPressedChanged: (pressed, value) => {
+ if (brightnessMonitor && brightnessMonitor.brightnessControlAvailable) {
+ brightnessMonitor.setBrightness(value)
+ }
+ }
Layout.fillWidth: true
}
@@ -127,17 +134,29 @@ ColumnLayout {
Layout.preferredWidth: 55
horizontalAlignment: Text.AlignRight
Layout.alignment: Qt.AlignVCenter
+ opacity: brightnessMonitor && !brightnessMonitor.brightnessControlAvailable ? 0.5 : 1.0
}
Item {
Layout.preferredWidth: 30
Layout.fillHeight: true
NIcon {
- icon: brightnessMonitor.method == "internal" ? "device-laptop" : "device-desktop"
+ icon: brightnessMonitor && brightnessMonitor.method == "internal" ? "device-laptop" : "device-desktop"
anchors.centerIn: parent
+ opacity: brightnessMonitor && !brightnessMonitor.brightnessControlAvailable ? 0.5 : 1.0
}
}
}
+
+ // Show message when brightness control is not available
+ NText {
+ visible: brightnessMonitor && !brightnessMonitor.brightnessControlAvailable
+ text: !Settings.data.brightness.enableDdcSupport ? I18n.tr("settings.display.monitors.brightness-unavailable.ddc-disabled") : I18n.tr("settings.display.monitors.brightness-unavailable.generic")
+ pointSize: Style.fontSizeS
+ color: Color.mOnSurfaceVariant
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
+ }
}
}
}
@@ -163,12 +182,24 @@ ColumnLayout {
checked: Settings.data.brightness.enforceMinimum
onToggled: checked => Settings.data.brightness.enforceMinimum = checked
}
+
+ NToggle {
+ Layout.fillWidth: true
+ label: I18n.tr("settings.display.monitors.external-brightness.label")
+ description: I18n.tr("settings.display.monitors.external-brightness.description")
+ checked: Settings.data.brightness.enableDdcSupport
+ onToggled: checked => {
+ Settings.data.brightness.enableDdcSupport = checked
+ // DDC detection will run on next monitor change when enabled
+ // Monitors will stop using DDC immediately when disabled
+ }
+ }
}
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Night Light Section
@@ -338,7 +369,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/DockTab.qml b/Modules/Panels/Settings/Tabs/DockTab.qml
similarity index 96%
rename from Modules/Settings/Tabs/DockTab.qml
rename to Modules/Panels/Settings/Tabs/DockTab.qml
index 632a3826..8afe2e59 100644
--- a/Modules/Settings/Tabs/DockTab.qml
+++ b/Modules/Panels/Settings/Tabs/DockTab.qml
@@ -3,7 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
import qs.Widgets
ColumnLayout {
@@ -137,8 +137,8 @@ ColumnLayout {
NDivider {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Monitor Configuration
@@ -181,7 +181,7 @@ ColumnLayout {
NDivider {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/GeneralTab.qml b/Modules/Panels/Settings/Tabs/GeneralTab.qml
similarity index 89%
rename from Modules/Settings/Tabs/GeneralTab.qml
rename to Modules/Panels/Settings/Tabs/GeneralTab.qml
index e01bb567..c3bdfa99 100644
--- a/Modules/Settings/Tabs/GeneralTab.qml
+++ b/Modules/Panels/Settings/Tabs/GeneralTab.qml
@@ -3,7 +3,8 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.System
+import qs.Services.UI
import qs.Widgets
ColumnLayout {
@@ -61,8 +62,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Fonts
@@ -186,8 +187,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Language selection
@@ -227,16 +228,28 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
NButton {
visible: !DistroService.isNixOS
text: I18n.tr("settings.general.launch-setup-wizard")
onClicked: {
- setupWizardLoader.active = false
- setupWizardLoader.active = true
+ var targetScreen = PanelService.openedPanel ? PanelService.openedPanel.screen : (Quickshell.screens.length > 0 ? Quickshell.screens[0] : null)
+ if (!targetScreen) {
+ return
+ }
+ var setupPanel = PanelService.getPanel("setupWizardPanel", targetScreen)
+ if (setupPanel) {
+ setupPanel.open()
+ } else {
+ Qt.callLater(() => {
+ var sp = PanelService.getPanel("setupWizardPanel", targetScreen)
+ if (sp)
+ sp.open()
+ })
+ }
}
}
}
diff --git a/Modules/Settings/Tabs/HooksTab.qml b/Modules/Panels/Settings/Tabs/HooksTab.qml
similarity index 98%
rename from Modules/Settings/Tabs/HooksTab.qml
rename to Modules/Panels/Settings/Tabs/HooksTab.qml
index 0b664cd3..b129bd7c 100644
--- a/Modules/Settings/Tabs/HooksTab.qml
+++ b/Modules/Panels/Settings/Tabs/HooksTab.qml
@@ -2,7 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
+import qs.Services.Control
+import qs.Services.UI
import qs.Widgets
ColumnLayout {
diff --git a/Modules/Settings/Tabs/LauncherTab.qml b/Modules/Panels/Settings/Tabs/LauncherTab.qml
similarity index 96%
rename from Modules/Settings/Tabs/LauncherTab.qml
rename to Modules/Panels/Settings/Tabs/LauncherTab.qml
index 86cf97ee..e4cc4631 100644
--- a/Modules/Settings/Tabs/LauncherTab.qml
+++ b/Modules/Panels/Settings/Tabs/LauncherTab.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
+import qs.Services.System
import qs.Widgets
ColumnLayout {
@@ -15,13 +15,18 @@ ColumnLayout {
}
NComboBox {
- id: launcherPosition
label: I18n.tr("settings.launcher.settings.position.label")
description: I18n.tr("settings.launcher.settings.position.description")
Layout.fillWidth: true
model: [{
+ "key": "follow_bar",
+ "name": I18n.tr("options.launcher.position.follow_bar")
+ }, {
"key": "center",
"name": I18n.tr("options.launcher.position.center")
+ }, {
+ "key": "top_center",
+ "name": I18n.tr("options.launcher.position.top_center")
}, {
"key": "top_left",
"name": I18n.tr("options.launcher.position.top_left")
@@ -37,9 +42,6 @@ ColumnLayout {
}, {
"key": "bottom_center",
"name": I18n.tr("options.launcher.position.bottom_center")
- }, {
- "key": "top_center",
- "name": I18n.tr("options.launcher.position.top_center")
}]
currentKey: Settings.data.appLauncher.position
onSelected: function (key) {
@@ -145,7 +147,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/LocationTab.qml b/Modules/Panels/Settings/Tabs/LocationTab.qml
similarity index 95%
rename from Modules/Settings/Tabs/LocationTab.qml
rename to Modules/Panels/Settings/Tabs/LocationTab.qml
index 69f8c4fa..b91edeb6 100644
--- a/Modules/Settings/Tabs/LocationTab.qml
+++ b/Modules/Panels/Settings/Tabs/LocationTab.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
+import qs.Services.Location
import qs.Widgets
ColumnLayout {
@@ -57,8 +57,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Weather section
@@ -97,8 +97,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Date & time section
@@ -166,7 +166,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/LockScreenTab.qml b/Modules/Panels/Settings/Tabs/LockScreenTab.qml
similarity index 81%
rename from Modules/Settings/Tabs/LockScreenTab.qml
rename to Modules/Panels/Settings/Tabs/LockScreenTab.qml
index 2b9d0ea7..46b9497d 100644
--- a/Modules/Settings/Tabs/LockScreenTab.qml
+++ b/Modules/Panels/Settings/Tabs/LockScreenTab.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
import qs.Widgets
ColumnLayout {
@@ -13,7 +12,7 @@ ColumnLayout {
label: I18n.tr("settings.lock-screen.lock-on-suspend.label")
description: I18n.tr("settings.lock-screen.lock-on-suspend.description")
checked: Settings.data.general.lockOnSuspend
- onToggled: Settings.data.general.lockOnSuspend = checked
+ onToggled: checked => Settings.data.general.lockOnSuspend = checked
}
NToggle {
@@ -25,7 +24,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/NetworkTab.qml b/Modules/Panels/Settings/Tabs/NetworkTab.qml
similarity index 84%
rename from Modules/Settings/Tabs/NetworkTab.qml
rename to Modules/Panels/Settings/Tabs/NetworkTab.qml
index 90e3216f..9ff4c6d2 100644
--- a/Modules/Settings/Tabs/NetworkTab.qml
+++ b/Modules/Panels/Settings/Tabs/NetworkTab.qml
@@ -2,9 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
-import Quickshell.Bluetooth
import qs.Commons
-import qs.Services
+import qs.Services.Networking
import qs.Widgets
ColumnLayout {
@@ -29,7 +28,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/NotificationsTab.qml b/Modules/Panels/Settings/Tabs/NotificationsTab.qml
similarity index 91%
rename from Modules/Settings/Tabs/NotificationsTab.qml
rename to Modules/Panels/Settings/Tabs/NotificationsTab.qml
index bdfe4d03..189e7038 100644
--- a/Modules/Settings/Tabs/NotificationsTab.qml
+++ b/Modules/Panels/Settings/Tabs/NotificationsTab.qml
@@ -3,7 +3,7 @@ import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
import qs.Widgets
ColumnLayout {
@@ -32,11 +32,18 @@ ColumnLayout {
description: I18n.tr("settings.notifications.settings.section.description")
}
+ NToggle {
+ label: I18n.tr("settings.notifications.settings.enabled.label")
+ description: I18n.tr("settings.notifications.settings.enabled.description")
+ checked: Settings.data.notifications.enabled !== false
+ onToggled: checked => Settings.data.notifications.enabled = checked
+ }
+
NToggle {
label: I18n.tr("settings.notifications.settings.do-not-disturb.label")
description: I18n.tr("settings.notifications.settings.do-not-disturb.description")
- checked: Settings.data.notifications.doNotDisturb
- onToggled: checked => Settings.data.notifications.doNotDisturb = checked
+ checked: NotificationService.doNotDisturb
+ onToggled: checked => NotificationService.doNotDisturb = checked
}
NComboBox {
@@ -98,8 +105,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Duration
@@ -185,8 +192,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Monitor Configuration
@@ -223,7 +230,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/OsdTab.qml b/Modules/Panels/Settings/Tabs/OsdTab.qml
similarity index 95%
rename from Modules/Settings/Tabs/OsdTab.qml
rename to Modules/Panels/Settings/Tabs/OsdTab.qml
index 365b2396..3474ec87 100644
--- a/Modules/Settings/Tabs/OsdTab.qml
+++ b/Modules/Panels/Settings/Tabs/OsdTab.qml
@@ -3,7 +3,7 @@ import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
import qs.Widgets
ColumnLayout {
@@ -62,8 +62,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// General
@@ -108,8 +108,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Monitor Configuration
diff --git a/Modules/Settings/Tabs/ScreenRecorderTab.qml b/Modules/Panels/Settings/Tabs/ScreenRecorderTab.qml
similarity index 97%
rename from Modules/Settings/Tabs/ScreenRecorderTab.qml
rename to Modules/Panels/Settings/Tabs/ScreenRecorderTab.qml
index f1bed075..580af116 100644
--- a/Modules/Settings/Tabs/ScreenRecorderTab.qml
+++ b/Modules/Panels/Settings/Tabs/ScreenRecorderTab.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
import qs.Widgets
ColumnLayout {
@@ -43,8 +42,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Video Settings
@@ -179,8 +178,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Audio Settings
diff --git a/Modules/Settings/Tabs/UserInterfaceTab.qml b/Modules/Panels/Settings/Tabs/UserInterfaceTab.qml
similarity index 62%
rename from Modules/Settings/Tabs/UserInterfaceTab.qml
rename to Modules/Panels/Settings/Tabs/UserInterfaceTab.qml
index 063714e5..abe077e4 100644
--- a/Modules/Settings/Tabs/UserInterfaceTab.qml
+++ b/Modules/Panels/Settings/Tabs/UserInterfaceTab.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
import qs.Widgets
ColumnLayout {
@@ -26,6 +25,13 @@ ColumnLayout {
onToggled: checked => Settings.data.ui.tooltipsEnabled = checked
}
+ NToggle {
+ label: I18n.tr("settings.user-interface.dim-desktop.label")
+ description: I18n.tr("settings.user-interface.dim-desktop.description")
+ checked: Settings.data.general.dimDesktop
+ onToggled: checked => Settings.data.general.dimDesktop = checked
+ }
+
NToggle {
label: I18n.tr("settings.user-interface.panels-attached-to-bar.label")
description: I18n.tr("settings.user-interface.panels-attached-to-bar.description")
@@ -34,10 +40,83 @@ ColumnLayout {
}
NToggle {
- label: I18n.tr("settings.user-interface.panels-overlay.label")
- description: I18n.tr("settings.user-interface.panels-overlay.description")
- checked: Settings.data.ui.panelsOverlayLayer
- onToggled: checked => Settings.data.ui.panelsOverlayLayer = checked
+ label: I18n.tr("settings.user-interface.settings-panel-attached-to-bar.label")
+ description: I18n.tr("settings.user-interface.settings-panel-attached-to-bar.description")
+ checked: Settings.data.ui.settingsPanelAttachToBar
+ enabled: Settings.data.ui.panelsAttachedToBar
+ onToggled: checked => Settings.data.ui.settingsPanelAttachToBar = checked
+ }
+
+ NToggle {
+ label: I18n.tr("settings.user-interface.shadows.label")
+ description: I18n.tr("settings.user-interface.shadows.description")
+ checked: Settings.data.general.enableShadows
+ onToggled: checked => Settings.data.general.enableShadows = checked
+ }
+
+ // Shadow direction
+ NComboBox {
+ visible: Settings.data.general.enableShadows
+ label: I18n.tr("settings.user-interface.shadows.direction.label")
+ description: I18n.tr("settings.user-interface.shadows.direction.description")
+ Layout.fillWidth: true
+
+ readonly property var shadowOptionsMap: ({
+ "top_left": {
+ "name": I18n.tr("options.shadow-direction.top_left"),
+ "p": Qt.point(-2, -2)
+ },
+ "top": {
+ "name": I18n.tr("options.shadow-direction.top"),
+ "p": Qt.point(0, -3)
+ },
+ "top_right": {
+ "name": I18n.tr("options.shadow-direction.top_right"),
+ "p": Qt.point(2, -2)
+ },
+ "left": {
+ "name": I18n.tr("options.shadow-direction.left"),
+ "p": Qt.point(-3, 0)
+ },
+ "center": {
+ "name": I18n.tr("options.shadow-direction.center"),
+ "p": Qt.point(0, 0)
+ },
+ "right": {
+ "name": I18n.tr("options.shadow-direction.right"),
+ "p": Qt.point(3, 0)
+ },
+ "bottom_left": {
+ "name": I18n.tr("options.shadow-direction.bottom_left"),
+ "p": Qt.point(-2, 2)
+ },
+ "bottom": {
+ "name": I18n.tr("options.shadow-direction.bottom"),
+ "p": Qt.point(0, 3)
+ },
+ "bottom_right": {
+ "name": I18n.tr("options.shadow-direction.bottom_right"),
+ "p": Qt.point(2, 3)
+ }
+ })
+
+ model: Object.keys(shadowOptionsMap).map(function (k) {
+ return {
+ "key": k,
+ "name": shadowOptionsMap[k].name
+ }
+ })
+
+ currentKey: Settings.data.general.shadowDirection
+
+ onSelected: function (key) {
+ var opt = shadowOptionsMap[key]
+ if (opt) {
+ Settings.data.general.shadowDirection = key
+ Settings.data.general.shadowOffsetX = opt.p.x
+ Settings.data.general.shadowOffsetY = opt.p.y
+ }
+ }
}
NDivider {
@@ -102,7 +181,7 @@ ColumnLayout {
NValueSlider {
Layout.fillWidth: true
from: 0
- to: 1
+ to: 2
stepSize: 0.01
value: Settings.data.general.radiusRatio
onMoved: value => Settings.data.general.radiusRatio = value
@@ -183,8 +262,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
// Dock
@@ -254,7 +333,7 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
}
diff --git a/Modules/Settings/Tabs/WallpaperTab.qml b/Modules/Panels/Settings/Tabs/WallpaperTab.qml
similarity index 84%
rename from Modules/Settings/Tabs/WallpaperTab.qml
rename to Modules/Panels/Settings/Tabs/WallpaperTab.qml
index 002c6996..ec3995a0 100644
--- a/Modules/Settings/Tabs/WallpaperTab.qml
+++ b/Modules/Panels/Settings/Tabs/WallpaperTab.qml
@@ -4,7 +4,8 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.UI
import qs.Widgets
ColumnLayout {
@@ -27,6 +28,22 @@ ColumnLayout {
Layout.bottomMargin: Style.marginL
}
+ NToggle {
+ visible: Settings.data.wallpaper.enabled && CompositorService.isNiri
+ label: I18n.tr("settings.wallpaper.settings.enable-overview.label")
+ description: I18n.tr("settings.wallpaper.settings.enable-overview.description")
+ checked: Settings.data.wallpaper.overviewEnabled
+ onToggled: checked => Settings.data.wallpaper.overviewEnabled = checked
+ Layout.bottomMargin: Style.marginL
+ }
+
+ NDivider {
+ visible: Settings.data.wallpaper.enabled
+ Layout.fillWidth: true
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
+ }
+
ColumnLayout {
visible: Settings.data.wallpaper.enabled
spacing: Style.marginL
@@ -69,6 +86,7 @@ ColumnLayout {
border.color: Color.mOutline
border.width: Style.borderS
implicitHeight: contentCol.implicitHeight + Style.marginL * 2
+ clip: true
ColumnLayout {
id: contentCol
@@ -103,13 +121,48 @@ ColumnLayout {
}
}
}
+
+ NComboBox {
+ label: I18n.tr("settings.wallpaper.settings.selector-position.label")
+ description: I18n.tr("settings.wallpaper.settings.selector-position.description")
+ Layout.fillWidth: true
+ model: [{
+ "key": "follow_bar",
+ "name": I18n.tr("options.launcher.position.follow_bar")
+ }, {
+ "key": "center",
+ "name": I18n.tr("options.launcher.position.center")
+ }, {
+ "key": "top_center",
+ "name": I18n.tr("options.launcher.position.top_center")
+ }, {
+ "key": "top_left",
+ "name": I18n.tr("options.launcher.position.top_left")
+ }, {
+ "key": "top_right",
+ "name": I18n.tr("options.launcher.position.top_right")
+ }, {
+ "key": "bottom_left",
+ "name": I18n.tr("options.launcher.position.bottom_left")
+ }, {
+ "key": "bottom_right",
+ "name": I18n.tr("options.launcher.position.bottom_right")
+ }, {
+ "key": "bottom_center",
+ "name": I18n.tr("options.launcher.position.bottom_center")
+ }]
+ currentKey: Settings.data.wallpaper.panelPosition
+ onSelected: function (key) {
+ Settings.data.wallpaper.panelPosition = key
+ }
+ }
}
NDivider {
visible: Settings.data.wallpaper.enabled
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
ColumnLayout {
@@ -191,8 +244,8 @@ ColumnLayout {
NDivider {
visible: Settings.data.wallpaper.enabled
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
ColumnLayout {
@@ -342,8 +395,8 @@ ColumnLayout {
NDivider {
Layout.fillWidth: true
- Layout.topMargin: Style.marginXL
- Layout.bottomMargin: Style.marginXL
+ Layout.topMargin: Style.marginL
+ Layout.bottomMargin: Style.marginL
}
NFilePicker {
diff --git a/Modules/SetupWizard/SetupAppearanceStep.qml b/Modules/Panels/SetupWizard/SetupAppearanceStep.qml
similarity index 99%
rename from Modules/SetupWizard/SetupAppearanceStep.qml
rename to Modules/Panels/SetupWizard/SetupAppearanceStep.qml
index 7a2df8c0..56621419 100644
--- a/Modules/SetupWizard/SetupAppearanceStep.qml
+++ b/Modules/Panels/SetupWizard/SetupAppearanceStep.qml
@@ -3,7 +3,8 @@ import QtQuick.Layouts
import QtQuick.Controls
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.System
+import qs.Services.Theming
import qs.Widgets
ColumnLayout {
diff --git a/Modules/SetupWizard/SetupCustomizeStep.qml b/Modules/Panels/SetupWizard/SetupCustomizeStep.qml
similarity index 81%
rename from Modules/SetupWizard/SetupCustomizeStep.qml
rename to Modules/Panels/SetupWizard/SetupCustomizeStep.qml
index 0c9f5853..2526933d 100644
--- a/Modules/SetupWizard/SetupCustomizeStep.qml
+++ b/Modules/Panels/SetupWizard/SetupCustomizeStep.qml
@@ -3,7 +3,6 @@ import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import qs.Commons
-import qs.Services
import qs.Widgets
ColumnLayout {
@@ -442,6 +441,108 @@ ColumnLayout {
}
}
+ // Divider
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 1
+ color: Color.mOutline
+ opacity: 0.2
+ Layout.topMargin: Style.marginS
+ Layout.bottomMargin: Style.marginS
+ }
+
+ // Dim Desktop toggle
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Style.marginM
+ Rectangle {
+ width: 32
+ height: 32
+ radius: Style.radiusM
+ color: Color.mSurface
+ NIcon {
+ icon: "screen-share"
+ pointSize: Style.fontSizeL
+ color: Color.mPrimary
+ anchors.centerIn: parent
+ }
+ }
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 2
+ NText {
+ text: I18n.tr("settings.user-interface.dim-desktop.label")
+ pointSize: Style.fontSizeL
+ font.weight: Style.fontWeightBold
+ color: Color.mOnSurface
+ }
+ NText {
+ text: I18n.tr("settings.user-interface.dim-desktop.description")
+ pointSize: Style.fontSizeS
+ color: Color.mOnSurfaceVariant
+ wrapMode: Text.WordWrap
+ Layout.fillWidth: true
+ }
+ }
+ NToggle {
+ checked: Settings.data.general.dimDesktop
+ onToggled: function (checked) {
+ Settings.data.general.dimDesktop = checked
+ }
+ }
+ }
+
+ // Divider
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 1
+ color: Color.mOutline
+ opacity: 0.2
+ Layout.topMargin: Style.marginS
+ Layout.bottomMargin: Style.marginS
+ }
+
+ // Drop Shadows toggle
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Style.marginM
+ Rectangle {
+ width: 32
+ height: 32
+ radius: Style.radiusM
+ color: Color.mSurface
+ NIcon {
+ icon: "shadow"
+ pointSize: Style.fontSizeL
+ color: Color.mPrimary
+ anchors.centerIn: parent
+ }
+ }
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 2
+ NText {
+ text: I18n.tr("settings.user-interface.shadows.label")
+ pointSize: Style.fontSizeL
+ font.weight: Style.fontWeightBold
+ color: Color.mOnSurface
+ }
+ NText {
+ text: I18n.tr("settings.user-interface.shadows.description")
+ pointSize: Style.fontSizeS
+ color: Color.mOnSurfaceVariant
+ wrapMode: Text.WordWrap
+ Layout.fillWidth: true
+ }
+ }
+ NToggle {
+ checked: Settings.data.general.enableShadows
+ onToggled: function (checked) {
+ Settings.data.general.enableShadows = checked
+ }
+ }
+ }
+
Item {
Layout.fillWidth: true
Layout.preferredHeight: Style.marginL
diff --git a/Modules/SetupWizard/SetupDockStep.qml b/Modules/Panels/SetupWizard/SetupDockStep.qml
similarity index 99%
rename from Modules/SetupWizard/SetupDockStep.qml
rename to Modules/Panels/SetupWizard/SetupDockStep.qml
index 79164869..3b3e3168 100644
--- a/Modules/SetupWizard/SetupDockStep.qml
+++ b/Modules/Panels/SetupWizard/SetupDockStep.qml
@@ -3,7 +3,7 @@ import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
import qs.Widgets
ColumnLayout {
diff --git a/Modules/SetupWizard/SetupWallpaperStep.qml b/Modules/Panels/SetupWizard/SetupWallpaperStep.qml
similarity index 99%
rename from Modules/SetupWizard/SetupWallpaperStep.qml
rename to Modules/Panels/SetupWizard/SetupWallpaperStep.qml
index 4725247d..33b1edef 100644
--- a/Modules/SetupWizard/SetupWallpaperStep.qml
+++ b/Modules/Panels/SetupWizard/SetupWallpaperStep.qml
@@ -5,9 +5,9 @@ import QtQuick.Effects
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
import qs.Widgets
-import "../../Helpers/FuzzySort.js" as FuzzySort
+import "../../../Helpers/FuzzySort.js" as FuzzySort
ColumnLayout {
id: root
diff --git a/Modules/SetupWizard/SetupWizard.qml b/Modules/Panels/SetupWizard/SetupWizard.qml
similarity index 94%
rename from Modules/SetupWizard/SetupWizard.qml
rename to Modules/Panels/SetupWizard/SetupWizard.qml
index 5be6df91..e6955fb3 100644
--- a/Modules/SetupWizard/SetupWizard.qml
+++ b/Modules/Panels/SetupWizard/SetupWizard.qml
@@ -4,27 +4,27 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Services
import qs.Widgets
+import qs.Modules.MainScreen
-NPanel {
+SmartPanel {
id: root
- preferredWidth: 520 * Style.uiScaleRatio
- preferredHeight: 600 * Style.uiScaleRatio
+ preferredWidth: Math.round(preferredWidthRatio * 2560 * Style.uiScaleRatio)
+ preferredHeight: Math.round(preferredHeightRatio * 1440 * Style.uiScaleRatio)
preferredWidthRatio: 0.4
preferredHeightRatio: 0.6
+
panelAnchorHorizontalCenter: true
panelAnchorVerticalCenter: true
- panelKeyboardFocus: true
-
- // Prevent closing during setup
- backgroundClickEnabled: false
- draggable: false
property int currentStep: 0
property int totalSteps: 5
+ // Override Escape handler to prevent closing the setup wizard
+ function onEscapePressed() {// Do nothing - prevent ESC from closing the setup wizard
+ }
+
// Setup wizard data
property string selectedWallpaperDirectory: Settings.defaultWallpapersDirectory
property string selectedWallpaper: ""
@@ -42,22 +42,11 @@ NPanel {
anchors.margins: Style.marginXL
spacing: Style.marginL
- // Override ESC key to prevent closing during setup
- Shortcut {
- sequences: ["Escape"]
- enabled: root.active
- onActivated: {
-
- // Do nothing - prevent ESC from closing the setup wizard
- }
- context: Qt.WindowShortcut
- }
-
// Step content - takes most of the space
Item {
Layout.fillWidth: true
Layout.fillHeight: true
- Layout.minimumHeight: 300
+ Layout.minimumHeight: Math.round(300 * Style.uiScaleRatio)
StackLayout {
id: stepStack
@@ -68,7 +57,7 @@ NPanel {
Item {
ColumnLayout {
anchors.centerIn: parent
- width: Math.min(parent.width - Style.marginXL * 2, 420)
+ width: Math.round(Math.min(parent.width - Style.marginXL * 2, 420))
spacing: Style.marginXL
// Logo with subtle glow effect
@@ -193,7 +182,12 @@ NPanel {
}
}
- // Step 2: UI Configuration
+ // Step 2: Appearance - Dark mode and color source
+ SetupAppearanceStep {
+ id: step3
+ }
+
+ // Step 3: UI Configuration
SetupCustomizeStep {
id: step2
selectedScaleRatio: root.selectedScaleRatio
@@ -208,15 +202,10 @@ NPanel {
}
}
- // Step 3: Dock Setup
+ // Step 4: Dock Setup
SetupDockStep {
id: stepDock
}
-
- // Step 4: Appearance - Dark mode and color source
- SetupAppearanceStep {
- id: step3
- }
}
}
@@ -244,15 +233,15 @@ NPanel {
}, {
"icon": "image",
"label": "Wallpaper"
+ }, {
+ "icon": "palette",
+ "label": "Appearance"
}, {
"icon": "settings",
"label": "Customize"
}, {
"icon": "device-desktop",
"label": "Dock"
- }, {
- "icon": "palette",
- "label": "Appearance"
}]
delegate: RowLayout {
spacing: Style.marginS
diff --git a/Modules/Panels/Tray/TrayDrawerPanel.qml b/Modules/Panels/Tray/TrayDrawerPanel.qml
new file mode 100644
index 00000000..20db163a
--- /dev/null
+++ b/Modules/Panels/Tray/TrayDrawerPanel.qml
@@ -0,0 +1,181 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Widgets
+import Quickshell.Services.SystemTray
+import qs.Commons
+import qs.Services.UI
+import qs.Widgets
+import qs.Modules.MainScreen
+
+// A compact grid panel listing all tray items, opened from the Tray widget
+SmartPanel {
+ id: root
+
+ // Widget info for menu functionality
+ property string widgetSection: ""
+ property int widgetIndex: -1
+
+ // Trigger refresh when settings change
+ property int settingsVersion: 0
+
+ // Read widget settings for reactivity
+ readonly property var widgetSettings: {
+ // Reference settingsVersion to force recalculation when it changes
+ var _ = root.settingsVersion
+ if (widgetSection === "" || widgetIndex < 0)
+ return {}
+ var widgets = Settings.data.bar.widgets[widgetSection]
+ if (!widgets || widgetIndex >= widgets.length)
+ return {}
+ var settings = widgets[widgetIndex]
+ if (!settings || settings.id !== "Tray")
+ return {}
+ return settings
+ }
+
+ // Read favorites directly from settings for reactivity
+ readonly property var favoritesList: widgetSettings.favorites || []
+
+ function wildCardMatch(str, rule) {
+ if (!str || !rule)
+ return false
+ let escaped = rule.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ let pattern = '^' + escaped.replace(/\\\*/g, '.*') + '$'
+ try {
+ return new RegExp(pattern, 'i').test(str)
+ } catch (e) {
+ return false
+ }
+ }
+
+ function isFavorite(item) {
+ if (!favoritesList || favoritesList.length === 0)
+ return false
+ const title = item?.tooltipTitle || item?.name || item?.id || ""
+ for (var i = 0; i < favoritesList.length; i++) {
+ if (wildCardMatch(title, favoritesList[i]))
+ return true
+ }
+ return false
+ }
+
+ // Dynamic sizing based on item count
+ // Show items that are NOT favorites (unpinned items go to drawer)
+ readonly property var trayValuesAll: (SystemTray.items && SystemTray.items.values) ? SystemTray.items.values : []
+ readonly property var trayValues: trayValuesAll.filter(function (it) {
+ return !root.isFavorite(it)
+ })
+ readonly property int itemCount: trayValues.length
+ readonly property int maxColumns: 8
+ readonly property real cellSize: Math.round(Style.capsuleHeight * 0.65)
+ readonly property real outerPadding: Style.marginM
+ readonly property real innerSpacing: Style.marginM
+ readonly property int columns: Math.max(1, Math.min(maxColumns, itemCount))
+ readonly property int rows: Math.max(1, Math.ceil(itemCount / Math.max(1, columns)))
+
+ // Add 2*gap margins around the grid
+ preferredWidth: (columns * cellSize) + ((columns - 1) * innerSpacing) + (2 * outerPadding)
+ preferredHeight: (rows * cellSize) + ((rows - 1) * innerSpacing) + (2 * outerPadding)
+
+ // Positioning is handled automatically by SmartPanel when toggle(buttonItem) is called
+
+ // Watch for settings changes to refresh the dropdown
+ Connections {
+ target: Settings
+ function onSettingsSaved() {
+ // Force refresh by incrementing settingsVersion, which triggers recalculation of favoritesList
+ root.settingsVersion++
+ }
+ }
+
+ panelContent: Item {
+ id: content
+
+ Grid {
+ id: grid
+ anchors.fill: parent
+ anchors.margins: outerPadding
+ spacing: innerSpacing
+ columns: root.columns
+ rowSpacing: innerSpacing
+ columnSpacing: innerSpacing
+
+ Repeater {
+ id: repeater
+ model: root.trayValues
+
+ delegate: Item {
+ width: root.cellSize
+ height: root.cellSize
+
+ IconImage {
+ id: trayIcon
+ anchors.fill: parent
+ asynchronous: true
+ backer.fillMode: Image.PreserveAspectFit
+ source: {
+ let icon = modelData?.icon || ""
+ if (!icon)
+ return ""
+ if (icon.includes("?path=")) {
+ const chunks = icon.split("?path=")
+ const name = chunks[0]
+ const path = chunks[1]
+ const fileName = name.substring(name.lastIndexOf("/") + 1)
+ return `file://${path}/${fileName}`
+ }
+ return icon
+ }
+
+ layer.enabled: root.widgetSettings.colorizeIcons !== false
+ layer.effect: ShaderEffect {
+ property color targetColor: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mSurfaceVariant
+ property real colorizeMode: 1.0
+ fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb")
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ hoverEnabled: true
+ acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
+
+ onClicked: mouse => {
+ if (!modelData)
+ return
+ if (mouse.button === Qt.RightButton && modelData.hasMenu && modelData.menu) {
+ const panel = PanelService.getPanel("trayMenuPanel", root.screen)
+ if (panel) {
+ panel.menu = modelData.menu
+ panel.trayItem = modelData
+ panel.widgetSection = root.widgetSection
+ panel.widgetIndex = root.widgetIndex
+ panel.openAt(trayIcon)
+ }
+ } else if (mouse.button === Qt.LeftButton) {
+ modelData.activate()
+ // Close the drawer after activation
+ PanelService.getPanel("trayDrawerPanel", root.screen)?.close()
+ } else if (mouse.button === Qt.MiddleButton) {
+ modelData.secondaryActivate()
+ PanelService.getPanel("trayDrawerPanel", root.screen)?.close()
+ }
+ }
+
+ onWheel: wheel => {
+ if (wheel.angleDelta.y > 0)
+ modelData?.scrollUp()
+ else if (wheel.angleDelta.y < 0)
+ modelData?.scrollDown()
+ }
+
+ onEntered: TooltipService.show(Screen, trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection())
+ onExited: TooltipService.hide()
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Modules/Panels/Tray/TrayMenuPanel.qml b/Modules/Panels/Tray/TrayMenuPanel.qml
new file mode 100644
index 00000000..2a3f31cb
--- /dev/null
+++ b/Modules/Panels/Tray/TrayMenuPanel.qml
@@ -0,0 +1,710 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+import qs.Commons
+import qs.Widgets
+import qs.Modules.MainScreen
+import qs.Services.UI
+
+SmartPanel {
+ id: root
+
+ // Inputs
+ property QsMenuHandle menu
+ property var trayItem: null
+ property string widgetSection: ""
+ property int widgetIndex: -1
+
+ // Internal
+ property int menuWidth: 280
+ readonly property int minMenuWidth: 280
+ readonly property int maxMenuWidth: 600
+ preferredWidth: menuWidth
+ // Height is content-driven via panelContent
+
+ // Open positioned relative to button
+ function openAt(buttonItem) {
+ open(buttonItem)
+ }
+
+ panelContent: Item {
+ id: content
+
+ // Track currently open submenu
+ property var activeSubMenu: null
+ property var activeSubMenuEntry: null
+ // Only show submenu in place (replace main menu)
+ property bool inPlaceSubmenu: true
+
+ // Let Panel size to our content
+ readonly property real contentPreferredWidth: root.menuWidth
+ readonly property real contentPreferredHeight: {
+ // If showing submenu in-place, size to the submenu's flickable content height
+ if (activeSubMenu && inPlaceSubmenu) {
+ const subFlickable = inPlaceSubMenuLoader.item?.children[1] // Flickable is the second child (after MouseArea)
+ const subHeight = subFlickable?.contentHeight || 0
+ return Math.min(root.screen ? root.screen.height * 0.9 : Screen.height * 0.9, subHeight + (Style.marginS * 2))
+ }
+
+ const mainHeight = mainFlickable.contentHeight
+ return Math.min(root.screen ? root.screen.height * 0.9 : Screen.height * 0.9, mainHeight + (Style.marginS * 2))
+ }
+
+ QsMenuOpener {
+ id: opener
+ menu: root.menu
+ onChildrenChanged: Qt.callLater(() => calculateMenuWidth(opener))
+ }
+
+ // Text metrics for measuring text widths
+ TextMetrics {
+ id: textMetrics
+ font.pointSize: Style.fontSizeS
+ }
+
+ // Watch for submenu changes
+ onActiveSubMenuChanged: {
+ if (activeSubMenu && inPlaceSubmenu) {
+ Qt.callLater(() => {
+ // Find the subMenuOpener in the loaded component
+ if (inPlaceSubMenuLoader.item) {
+ const flickable = inPlaceSubMenuLoader.item.children[1] // Flickable
+ if (flickable && flickable.children.length > 0) {
+ const columnLayout = flickable.children[0]
+ if (columnLayout && columnLayout.children.length > 0) {
+ const subOpener = columnLayout.children[0]
+ if (subOpener) {
+ calculateMenuWidth(subOpener)
+ }
+ }
+ }
+ }
+ })
+ } else if (!activeSubMenu) {
+ // Reset to main menu width when submenu closes
+ Qt.callLater(() => calculateMenuWidth(opener))
+ }
+ }
+
+ // Calculate required menu width based on menu entries
+ function calculateMenuWidth(menuOpener) {
+ let maxWidth = root.minMenuWidth
+
+ // For submenus, also measure the "Back" button
+ if (menuOpener !== opener && content.inPlaceSubmenu) {
+ textMetrics.text = I18n.tr("settings.bar.tray.back")
+ const backWidth = (Style.marginM * 4) + Style.fontSizeS + Style.marginS + textMetrics.width
+ maxWidth = Math.max(maxWidth, backWidth)
+ }
+
+ // Check all menu entries
+ if (menuOpener && menuOpener.children) {
+ try {
+ const entries = menuOpener.children.values ? [...menuOpener.children.values] : []
+
+ for (var i = 0; i < entries.length; i++) {
+ const entry = entries[i]
+ if (!entry || entry.isSeparator)
+ continue
+
+ const text = entry.text || ""
+ if (text === "")
+ continue
+
+ // Measure the text
+ textMetrics.text = text.replace(/[\n\r]+/g, ' ')
+ const textWidth = textMetrics.width
+
+ // Calculate total width:
+ // - Outer inset: Style.marginM * 2 (parent width reduction)
+ // - RowLayout margins: Style.marginM * 2 (left + right)
+ // - Text width
+ // - Spacing: Style.marginS
+ // - Icon: Style.marginL (if present)
+ // - Submenu arrow: ~20px (if present)
+ let requiredWidth = (Style.marginM * 4) + textWidth
+
+ if (entry.icon && entry.icon !== "") {
+ requiredWidth += Style.marginS + Style.marginL
+ }
+
+ if (entry.hasChildren) {
+ requiredWidth += Style.marginS + 20
+ }
+
+ maxWidth = Math.max(maxWidth, requiredWidth)
+ }
+ } catch (e) {
+
+ // Silently ignore errors during width calculation
+ }
+ }
+
+ // Check pin/unpin button (only for main menu)
+ if (menuOpener === opener && root.trayItem !== null && root.widgetSection !== "" && root.widgetIndex >= 0) {
+ textMetrics.text = I18n.tr("settings.bar.tray.pin-application")
+ let pinWidth = (Style.marginM * 4) + textMetrics.width + Style.marginS + 20
+ maxWidth = Math.max(maxWidth, pinWidth)
+
+ textMetrics.text = I18n.tr("settings.bar.tray.unpin-application")
+ let unpinWidth = (Style.marginM * 4) + textMetrics.width + Style.marginS + 20
+ maxWidth = Math.max(maxWidth, unpinWidth)
+ }
+
+ // Clamp to min/max and apply
+ const finalWidth = Math.min(root.maxMenuWidth, Math.max(root.minMenuWidth, Math.ceil(maxWidth)))
+ root.menuWidth = finalWidth
+ }
+
+ Component.onCompleted: Qt.callLater(() => calculateMenuWidth(opener))
+
+ Component {
+ id: subMenuComponent
+
+ Item {
+ id: subMenuContainer
+
+ // MouseArea to track hover for submenu (covers entire submenu area)
+ MouseArea {
+ id: subMenuMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ enabled: content.activeSubMenu !== null && !content.inPlaceSubmenu
+ visible: !content.inPlaceSubmenu
+ z: 1
+ acceptedButtons: Qt.NoButton // Don't intercept clicks, just track hover
+ onExited: {
+ if (content.inPlaceSubmenu)
+ return
+ Qt.callLater(() => {
+ if (!subMenuMouseArea.containsMouse && content.activeSubMenuEntry) {
+ // Find the mouseArea in the entry (it's in the second Rectangle's MouseArea)
+ let entryMouseArea = null
+ for (var i = 0; i < content.activeSubMenuEntry.children.length; i++) {
+ const child = content.activeSubMenuEntry.children[i]
+ if (child && child.children && child.children.length > 0) {
+ for (var j = 0; j < child.children.length; j++) {
+ const grandchild = child.children[j]
+ if (grandchild && grandchild.hoverEnabled !== undefined) {
+ entryMouseArea = grandchild
+ break
+ }
+ }
+ }
+ if (entryMouseArea)
+ break
+ }
+ if (!entryMouseArea || !entryMouseArea.containsMouse) {
+ content.activeSubMenu = null
+ content.activeSubMenuEntry = null
+ }
+ }
+ })
+ }
+ }
+
+ Flickable {
+ id: subMenuFlickable
+ anchors.fill: parent
+ contentHeight: subMenuColumnLayout.implicitHeight
+ interactive: false
+ z: 0
+
+ ColumnLayout {
+ id: subMenuColumnLayout
+ width: subMenuFlickable.width
+ spacing: 0
+
+ QsMenuOpener {
+ id: subMenuOpener
+ menu: content.activeSubMenu
+ onChildrenChanged: Qt.callLater(() => content.calculateMenuWidth(subMenuOpener))
+ }
+
+ // Back button (only shown when submenu is in-place)
+ Rectangle {
+ id: backEntry
+ visible: content.inPlaceSubmenu
+ Layout.preferredWidth: parent.width
+ Layout.preferredHeight: visible ? 28 : 0
+ color: Color.transparent
+
+ Rectangle {
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.topMargin: 0
+ anchors.bottomMargin: 0
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width - (Style.marginM * 2)
+ color: backMouseArea.containsMouse ? Color.mHover : Color.transparent
+ radius: Style.radiusS
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: Style.marginM
+ anchors.rightMargin: Style.marginM
+ spacing: Style.marginS
+
+ NIcon {
+ icon: "arrow-left"
+ pointSize: Style.fontSizeS
+ applyUiScale: false
+ verticalAlignment: Text.AlignVCenter
+ color: backMouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface
+ }
+
+ NText {
+ Layout.fillWidth: true
+ color: backMouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface
+ text: I18n.tr("settings.bar.tray.back")
+ pointSize: Style.fontSizeS
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+ }
+
+ MouseArea {
+ id: backMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+
+ onClicked: {
+ content.activeSubMenu = null
+ content.activeSubMenuEntry = null
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ visible: content.inPlaceSubmenu
+ Layout.preferredWidth: parent.width
+ Layout.preferredHeight: visible ? 8 : 0
+ color: Color.transparent
+
+ NDivider {
+ anchors.centerIn: parent
+ width: parent.width - (Style.marginM * 2)
+ visible: parent.visible
+ }
+ }
+
+ Repeater {
+ model: subMenuOpener.children ? [...subMenuOpener.children.values] : []
+
+ delegate: Rectangle {
+ id: subEntry
+ required property var modelData
+
+ Layout.preferredWidth: parent.width
+ Layout.preferredHeight: {
+ if (modelData?.isSeparator) {
+ return 8
+ } else {
+ return 28
+ }
+ }
+
+ color: Color.transparent
+
+ NDivider {
+ anchors.centerIn: parent
+ width: parent.width - (Style.marginM * 2)
+ visible: modelData?.isSeparator ?? false
+ }
+
+ Rectangle {
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.topMargin: 0
+ anchors.bottomMargin: 0
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width - (Style.marginM * 2)
+ color: subMouseArea.containsMouse ? Color.mHover : Color.transparent
+ radius: Style.radiusS
+ visible: !(modelData?.isSeparator ?? false)
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: Style.marginM
+ anchors.rightMargin: Style.marginM
+ spacing: Style.marginS
+
+ NText {
+ Layout.fillWidth: true
+ color: (modelData?.enabled ?? true) ? (subMouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface) : Color.mOnSurfaceVariant
+ text: modelData?.text !== "" ? modelData?.text.replace(/[\n\r]+/g, ' ') : "..."
+ pointSize: Style.fontSizeS
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ Image {
+ Layout.preferredWidth: Style.marginL
+ Layout.preferredHeight: Style.marginL
+ source: modelData?.icon ?? ""
+ visible: (modelData?.icon ?? "") !== ""
+ fillMode: Image.PreserveAspectFit
+ }
+
+ NIcon {
+ icon: modelData?.hasChildren ? "menu" : ""
+ pointSize: Style.fontSizeS
+ applyUiScale: false
+ verticalAlignment: Text.AlignVCenter
+ visible: modelData?.hasChildren ?? false
+ Layout.rightMargin: Style.marginL
+ color: (subMouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface)
+ }
+ }
+
+ MouseArea {
+ id: subMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ enabled: (modelData?.enabled ?? true) && !(modelData?.isSeparator ?? false)
+
+ onClicked: {
+ if (!modelData || modelData.isSeparator)
+ return
+
+ if (modelData.hasChildren) {
+ // Toggle nested submenu on click
+ if (content.activeSubMenu === modelData) {
+ // If already open, close it on second click
+ content.activeSubMenu = null
+ content.activeSubMenuEntry = null
+ return
+ }
+
+ // Open nested submenu in place
+ content.activeSubMenu = modelData
+ content.activeSubMenuEntry = null // No entry for nested submenus
+ content.inPlaceSubmenu = true
+ } else {
+ modelData.triggered()
+ root.close()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Main menu and in-place submenu
+ RowLayout {
+ id: rowLayout
+ anchors.fill: parent
+ anchors.topMargin: Style.marginS
+ spacing: 0
+
+ // Submenu replacing main menu (in-place)
+ Loader {
+ id: inPlaceSubMenuLoader
+ Layout.preferredWidth: (content.activeSubMenu && content.inPlaceSubmenu) ? root.menuWidth : 0
+ Layout.fillHeight: true
+ visible: content.activeSubMenu !== null && content.inPlaceSubmenu
+ sourceComponent: subMenuComponent
+ }
+
+ // Main menu
+ Flickable {
+ id: mainFlickable
+ Layout.preferredWidth: root.menuWidth
+ Layout.fillHeight: true
+ contentHeight: mainColumnLayout.implicitHeight
+ interactive: false
+ visible: !(content.activeSubMenu !== null && content.inPlaceSubmenu)
+
+ ColumnLayout {
+ id: mainColumnLayout
+ width: mainFlickable.width
+ spacing: 0
+
+ Repeater {
+ model: opener.children ? [...opener.children.values] : []
+
+ delegate: Rectangle {
+ id: entry
+ required property var modelData
+
+ Layout.preferredWidth: parent.width
+ Layout.preferredHeight: {
+ if (modelData?.isSeparator) {
+ return 8
+ } else {
+ return 28
+ }
+ }
+
+ color: Color.transparent
+
+ NDivider {
+ anchors.centerIn: parent
+ width: parent.width - Style.marginL
+ visible: modelData?.isSeparator ?? false
+ }
+
+ Rectangle {
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.topMargin: 0
+ anchors.bottomMargin: 0
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width - (Style.marginM * 2)
+ color: mouseArea.containsMouse ? Color.mHover : Color.transparent
+ radius: Style.radiusS
+ visible: !(modelData?.isSeparator ?? false)
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: Style.marginM
+ anchors.rightMargin: Style.marginM
+ spacing: Style.marginS
+
+ NText {
+ id: text
+ Layout.fillWidth: true
+ color: (modelData?.enabled ?? true) ? (mouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface) : Color.mOnSurfaceVariant
+ text: modelData?.text !== "" ? modelData?.text.replace(/[\n\r]+/g, ' ') : "..."
+ pointSize: Style.fontSizeS
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ Image {
+ Layout.preferredWidth: Style.marginL
+ Layout.preferredHeight: Style.marginL
+ source: modelData?.icon ?? ""
+ visible: (modelData?.icon ?? "") !== ""
+ fillMode: Image.PreserveAspectFit
+ }
+
+ NIcon {
+ icon: modelData?.hasChildren ? "menu" : ""
+ pointSize: Style.fontSizeS
+ applyUiScale: false
+ verticalAlignment: Text.AlignVCenter
+ visible: modelData?.hasChildren ?? false
+ Layout.rightMargin: Style.marginS
+ color: (mouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface)
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ enabled: (modelData?.enabled ?? true) && !(modelData?.isSeparator ?? false)
+
+ onClicked: {
+ if (!modelData || modelData.isSeparator)
+ return
+
+ if (modelData.hasChildren) {
+ // Toggle submenu on click
+ if (content.activeSubMenuEntry === entry && content.inPlaceSubmenu) {
+ // If already open in-place, close it on second click
+ content.activeSubMenu = null
+ content.activeSubMenuEntry = null
+ return
+ }
+
+ // Close any other open submenu
+ if (content.activeSubMenuEntry && content.activeSubMenuEntry !== entry) {
+ content.activeSubMenu = null
+ content.activeSubMenuEntry = null
+ }
+
+ // Open submenu in place (replace main menu)
+ content.activeSubMenu = modelData
+ content.activeSubMenuEntry = entry
+ content.inPlaceSubmenu = true
+ } else {
+ modelData.triggered()
+ root.close()
+ }
+ }
+
+ onExited: {
+
+ }
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ visible: root.trayItem !== null && root.widgetSection !== "" && root.widgetIndex >= 0
+ Layout.preferredWidth: parent.width
+ Layout.preferredHeight: visible ? 8 : 0
+ color: Color.transparent
+
+ NDivider {
+ anchors.centerIn: parent
+ width: parent.width - Style.marginL
+ visible: parent.visible
+ }
+ }
+
+ Rectangle {
+ id: addToFavoriteEntry
+ visible: root.trayItem !== null && root.widgetSection !== "" && root.widgetIndex >= 0
+ Layout.preferredWidth: parent.width
+ Layout.preferredHeight: visible ? 28 : 0
+ color: Color.transparent
+
+ readonly property bool isFavorite: {
+ if (!root.trayItem || root.widgetSection === "" || root.widgetIndex < 0)
+ return false
+ const itemName = root.trayItem.tooltipTitle || root.trayItem.name || root.trayItem.id || ""
+ if (!itemName)
+ return false
+ var widgets = Settings.data.bar.widgets[root.widgetSection]
+ if (!widgets || root.widgetIndex >= widgets.length)
+ return false
+ var widgetSettings = widgets[root.widgetIndex]
+ if (!widgetSettings || widgetSettings.id !== "Tray")
+ return false
+ var favorites = widgetSettings.favorites || []
+ for (var i = 0; i < favorites.length; i++) {
+ if (favorites[i] === itemName)
+ return true
+ }
+ return false
+ }
+
+ Rectangle {
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.topMargin: 0
+ anchors.bottomMargin: 0
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width - (Style.marginM * 2)
+ color: addToFavoriteMouseArea.containsMouse ? Qt.alpha(Color.mPrimary, 0.2) : Qt.alpha(Color.mPrimary, 0.08)
+ radius: Style.radiusS
+ border.color: Qt.alpha(Color.mPrimary, addToFavoriteMouseArea.containsMouse ? 0.4 : 0.2)
+ border.width: Style.borderS
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: Style.marginM
+ anchors.rightMargin: Style.marginM
+ spacing: Style.marginS
+
+ NIcon {
+ icon: addToFavoriteEntry.isFavorite ? "unpin" : "pin"
+ pointSize: Style.fontSizeS
+ applyUiScale: false
+ verticalAlignment: Text.AlignVCenter
+ color: Color.mPrimary
+ }
+
+ NText {
+ Layout.fillWidth: true
+ color: Color.mPrimary
+ text: addToFavoriteEntry.isFavorite ? I18n.tr("settings.bar.tray.unpin-application") : I18n.tr("settings.bar.tray.pin-application")
+ pointSize: Style.fontSizeS
+ font.weight: Font.Medium
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+ }
+
+ MouseArea {
+ id: addToFavoriteMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+
+ onClicked: {
+ if (addToFavoriteEntry.isFavorite) {
+ root.removeFromFavorites()
+ } else {
+ root.addToFavorites()
+ }
+ root.close()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Keys.onEscapePressed: root.close()
+ }
+
+ function addToFavorites() {
+ if (!trayItem || widgetSection === "" || widgetIndex < 0) {
+ Logger.w("TrayMenu", "Cannot add as favorite: missing tray item or widget info")
+ return
+ }
+ const itemName = trayItem.tooltipTitle || trayItem.name || trayItem.id || ""
+ if (!itemName) {
+ Logger.w("TrayMenu", "Cannot add as favorite: tray item has no name")
+ return
+ }
+ var widgets = Settings.data.bar.widgets[widgetSection]
+ if (!widgets || widgetIndex >= widgets.length) {
+ Logger.w("TrayMenu", "Cannot add as favorite: invalid widget index")
+ return
+ }
+ var widgetSettings = widgets[widgetIndex]
+ if (!widgetSettings || widgetSettings.id !== "Tray") {
+ Logger.w("TrayMenu", "Cannot add as favorite: widget is not a Tray widget")
+ return
+ }
+ var favorites = widgetSettings.favorites || []
+ var newFavorites = favorites.slice()
+ newFavorites.push(itemName)
+ var newSettings = Object.assign({}, widgetSettings)
+ newSettings.favorites = newFavorites
+ widgets[widgetIndex] = newSettings
+ Settings.data.bar.widgets[widgetSection] = widgets
+ Settings.saveImmediate()
+ if (root.screen) {
+ const panel = PanelService.getPanel("trayDrawerPanel", root.screen)
+ if (panel)
+ panel.close()
+ }
+ }
+
+ function removeFromFavorites() {
+ if (!trayItem || widgetSection === "" || widgetIndex < 0) {
+ Logger.w("TrayMenu", "Cannot remove from favorites: missing tray item or widget info")
+ return
+ }
+ const itemName = trayItem.tooltipTitle || trayItem.name || trayItem.id || ""
+ if (!itemName) {
+ Logger.w("TrayMenu", "Cannot remove from favorites: tray item has no name")
+ return
+ }
+ var widgets = Settings.data.bar.widgets[widgetSection]
+ if (!widgets || widgetIndex >= widgets.length) {
+ Logger.w("TrayMenu", "Cannot remove from favorites: invalid widget index")
+ return
+ }
+ var widgetSettings = widgets[widgetIndex]
+ if (!widgetSettings || widgetSettings.id !== "Tray") {
+ Logger.w("TrayMenu", "Cannot remove from favorites: widget is not a Tray widget")
+ return
+ }
+ var favorites = widgetSettings.favorites || []
+ var newFavorites = []
+ for (var i = 0; i < favorites.length; i++) {
+ if (favorites[i] !== itemName) {
+ newFavorites.push(favorites[i])
+ }
+ }
+ var newSettings = Object.assign({}, widgetSettings)
+ newSettings.favorites = newFavorites
+ widgets[widgetIndex] = newSettings
+ Settings.data.bar.widgets[widgetSection] = widgets
+ Settings.saveImmediate()
+ }
+}
diff --git a/Modules/Wallpaper/WallpaperPanel.qml b/Modules/Panels/Wallpaper/WallpaperPanel.qml
similarity index 72%
rename from Modules/Wallpaper/WallpaperPanel.qml
rename to Modules/Panels/Wallpaper/WallpaperPanel.qml
index 2a1f6d57..d2fc547f 100644
--- a/Modules/Wallpaper/WallpaperPanel.qml
+++ b/Modules/Panels/Wallpaper/WallpaperPanel.qml
@@ -2,25 +2,103 @@ import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
-import Quickshell.Wayland
import qs.Commons
-import qs.Modules.Settings
-import qs.Services
+import qs.Modules.MainScreen
+import qs.Modules.Panels.Settings
+import qs.Services.UI
import qs.Widgets
-import "../../Helpers/FuzzySort.js" as FuzzySort
+import "../../../Helpers/FuzzySort.js" as FuzzySort
-NPanel {
+SmartPanel {
id: root
- preferredWidth: 640 * Style.uiScaleRatio
- preferredHeight: 480 * Style.uiScaleRatio
- preferredWidthRatio: 0.4
- preferredHeightRatio: 0.52
- panelAnchorHorizontalCenter: true
- panelAnchorVerticalCenter: true
- panelKeyboardFocus: true
+ preferredWidth: 800 * Style.uiScaleRatio
+ preferredHeight: 600 * Style.uiScaleRatio
+ preferredWidthRatio: 0.5
+ preferredHeightRatio: 0.45
- draggable: !PanelService.hasOpenedPopup
+ // Positioning
+ readonly property string panelPosition: {
+ if (Settings.data.wallpaper.panelPosition === "follow_bar") {
+ if (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") {
+ return `center_${Settings.data.bar.position}`
+ } else {
+ return `${Settings.data.bar.position}_center`
+ }
+ } else {
+ return Settings.data.wallpaper.panelPosition
+ }
+ }
+ panelAnchorHorizontalCenter: panelPosition === "center" || panelPosition.endsWith("_center")
+ panelAnchorVerticalCenter: panelPosition === "center"
+ panelAnchorLeft: panelPosition !== "center" && panelPosition.endsWith("_left")
+ panelAnchorRight: panelPosition !== "center" && panelPosition.endsWith("_right")
+ panelAnchorBottom: panelPosition.startsWith("bottom_")
+ panelAnchorTop: panelPosition.startsWith("top_")
+
+ // Store direct reference to content for instant access
+ property var contentItem: null
+
+ // Override keyboard handlers to enable grid navigation
+ function onDownPressed() {
+ if (!contentItem)
+ return
+ let view = contentItem.screenRepeater.itemAt(contentItem.currentScreenIndex)
+ if (view?.gridView) {
+ if (!view.gridView.activeFocus) {
+ view.gridView.forceActiveFocus()
+ if (view.gridView.currentIndex < 0) {
+ view.gridView.currentIndex = 0
+ }
+ } else {
+ view.gridView.moveCurrentIndexDown()
+ }
+ }
+ }
+
+ function onUpPressed() {
+ if (!contentItem)
+ return
+ let view = contentItem.screenRepeater.itemAt(contentItem.currentScreenIndex)
+ if (view?.gridView?.activeFocus) {
+ view.gridView.moveCurrentIndexUp()
+ }
+ }
+
+ function onLeftPressed() {
+ if (!contentItem)
+ return
+ let view = contentItem.screenRepeater.itemAt(contentItem.currentScreenIndex)
+ if (view?.gridView?.activeFocus) {
+ view.gridView.moveCurrentIndexLeft()
+ }
+ }
+
+ function onRightPressed() {
+ if (!contentItem)
+ return
+ let view = contentItem.screenRepeater.itemAt(contentItem.currentScreenIndex)
+ if (view?.gridView?.activeFocus) {
+ view.gridView.moveCurrentIndexRight()
+ }
+ }
+
+ function onReturnPressed() {
+ if (!contentItem)
+ return
+ let view = contentItem.screenRepeater.itemAt(contentItem.currentScreenIndex)
+ if (view?.gridView?.activeFocus) {
+ let gridView = view.gridView
+ if (gridView.currentIndex >= 0 && gridView.currentIndex < gridView.model.length) {
+ let path = gridView.model[gridView.currentIndex]
+ if (Settings.data.wallpaper.setWallpaperOnAllMonitors) {
+ WallpaperService.changeWallpaper(path, undefined)
+ } else {
+ WallpaperService.changeWallpaper(path, view.targetScreen.name)
+ }
+ }
+ }
+ }
panelContent: Rectangle {
id: wallpaperPanel
@@ -37,9 +115,31 @@ NPanel {
}
property var currentScreen: Quickshell.screens[currentScreenIndex]
property string filterText: ""
+ property alias screenRepeater: screenRepeater
+
+ Component.onCompleted: {
+ root.contentItem = wallpaperPanel
+ }
color: Color.transparent
+ // Focus management
+ Connections {
+ target: root
+ function onOpened() {
+ // Ensure contentItem is set
+ if (!root.contentItem) {
+ root.contentItem = wallpaperPanel
+ }
+ // Give initial focus to search input
+ Qt.callLater(() => {
+ if (searchInput.inputItem) {
+ searchInput.inputItem.forceActiveFocus()
+ }
+ })
+ }
+ }
+
// Debounce timer for search
Timer {
id: searchDebounceTimer
@@ -85,7 +185,7 @@ NPanel {
tooltipText: I18n.tr("settings.wallpaper.settings.section.label")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
- var settingsPanel = PanelService.getPanel("settingsPanel")
+ var settingsPanel = PanelService.getPanel("settingsPanel", screen)
settingsPanel.requestedTab = SettingsPanel.Tab.Wallpaper
settingsPanel.open()
}
@@ -119,7 +219,7 @@ NPanel {
}
// Monitor tabs
- TabBar {
+ NTabBar {
id: screenTabBar
visible: !Settings.data.wallpaper.setWallpaperOnAllMonitors || Settings.data.wallpaper.enableMultiMonitorDirectories
Layout.fillWidth: true
@@ -127,60 +227,17 @@ NPanel {
onCurrentIndexChanged: currentScreenIndex = currentIndex
spacing: Style.marginM
- background: Rectangle {
- color: Color.transparent
- }
-
Repeater {
model: Quickshell.screens
- delegate: TabButton {
+ NTabButton {
+ required property var modelData
+ required property int index
text: modelData.name || `Screen ${index + 1}`
- width: implicitWidth + Style.marginS * 2
-
- background: Rectangle {
- color: screenTabBar.currentIndex === index ? Color.mSecondary : Color.transparent
- radius: Style.radiusS
- border.width: screenTabBar.currentIndex === index ? 0 : Style.borderS
- border.color: Color.mOutline
-
- Behavior on color {
- ColorAnimation {
- duration: Style.animationFast
- }
- }
- }
-
- contentItem: NText {
- text: parent.text
- pointSize: Style.fontSizeL
- font.weight: screenTabBar.currentIndex === index ? Style.fontWeightBold : Style.fontWeightRegular
- family: Settings.data.ui.fontDefault
- color: screenTabBar.currentIndex === index ? Color.mOnSecondary : Color.mOnSurfaceVariant
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
-
- // Add hover effect
- HoverHandler {
- id: tabHover
- }
-
- Rectangle {
- anchors.fill: parent
- color: Color.mOnSurface
- opacity: tabHover.hovered && screenTabBar.currentIndex !== index ? 0.08 : 0
- radius: Style.radiusS
-
- Behavior on opacity {
- NumberAnimation {
- duration: Style.animationFast
- }
- }
- }
+ tabIndex: index
+ checked: screenTabBar.currentIndex === index
}
}
}
-
// StackLayout for each screen's wallpaper content
StackLayout {
id: screenStack
@@ -291,7 +348,7 @@ NPanel {
return
}
wallpapersList = WallpaperService.getWallpapersList(targetScreen.name)
- Logger.i("WallpaperPanel", "Got", wallpapersList.length, "wallpapers for screen", targetScreen.name)
+ Logger.d("WallpaperPanel", "Got", wallpapersList.length, "wallpapers for screen", targetScreen.name)
// Pre-compute basenames once for better performance
wallpapersWithNames = wallpapersList.map(function (p) {
@@ -324,7 +381,7 @@ NPanel {
model: filteredWallpapers
- property int columns: 4
+ property int columns: (screen.width > 1920) ? 5 : 4
property int itemSize: cellWidth
cellWidth: Math.floor((width - leftMargin - rightMargin) / columns)
@@ -368,6 +425,50 @@ NPanel {
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded
+ parent: wallpaperGridView
+ x: wallpaperGridView.mirrored ? 0 : wallpaperGridView.width - width
+ y: 0
+ height: wallpaperGridView.height
+
+ property color handleColor: Qt.alpha(Color.mHover, 0.8)
+ property color handleHoverColor: handleColor
+ property color handlePressedColor: handleColor
+ property real handleWidth: 6
+ property real handleRadius: Style.radiusM
+
+ contentItem: Rectangle {
+ implicitWidth: parent.handleWidth
+ implicitHeight: 100
+ radius: parent.handleRadius
+ color: parent.pressed ? parent.handlePressedColor : parent.hovered ? parent.handleHoverColor : parent.handleColor
+ opacity: parent.policy === ScrollBar.AlwaysOn || parent.active ? 1.0 : 0.0
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: Style.animationFast
+ }
+ }
+
+ Behavior on color {
+ ColorAnimation {
+ duration: Style.animationFast
+ }
+ }
+ }
+
+ background: Rectangle {
+ implicitWidth: parent.handleWidth
+ implicitHeight: 100
+ color: Color.transparent
+ opacity: parent.policy === ScrollBar.AlwaysOn || parent.active ? 0.3 : 0.0
+ radius: parent.handleRadius / 2
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: Style.animationFast
+ }
+ }
+ }
}
delegate: ColumnLayout {
diff --git a/Modules/Bar/WiFi/WiFiPanel.qml b/Modules/Panels/WiFi/WiFiPanel.qml
similarity index 97%
rename from Modules/Bar/WiFi/WiFiPanel.qml
rename to Modules/Panels/WiFi/WiFiPanel.qml
index 281ace59..178e010f 100644
--- a/Modules/Bar/WiFi/WiFiPanel.qml
+++ b/Modules/Panels/WiFi/WiFiPanel.qml
@@ -2,17 +2,16 @@ import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
-import Quickshell.Wayland
import qs.Commons
-import qs.Services
+import qs.Modules.MainScreen
+import qs.Services.Networking
import qs.Widgets
-NPanel {
+SmartPanel {
id: root
- preferredWidth: 420 * Style.uiScaleRatio
- preferredHeight: 500 * Style.uiScaleRatio
- panelKeyboardFocus: true
+ preferredWidth: Math.round(400 * Style.uiScaleRatio)
+ preferredHeight: Math.round(500 * Style.uiScaleRatio)
property string passwordSsid: ""
property string passwordInput: ""
@@ -23,7 +22,10 @@ NPanel {
panelContent: Rectangle {
color: Color.transparent
+ property real contentPreferredHeight: Math.min(preferredHeight, Math.max(280 * Style.uiScaleRatio, mainColumn.implicitHeight + Style.marginL * 2))
+
ColumnLayout {
+ id: mainColumn
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginM
@@ -131,7 +133,7 @@ NPanel {
NIcon {
icon: "wifi-off"
- pointSize: 64
+ pointSize: 48
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
@@ -147,7 +149,9 @@ NPanel {
text: I18n.tr("wifi.panel.enable-message")
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
- Layout.alignment: Qt.AlignHCenter
+ horizontalAlignment: Text.AlignHCenter
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
}
Item {
@@ -244,7 +248,7 @@ NPanel {
spacing: Style.marginS
NIcon {
- icon: NetworkService.signalIcon(modelData.signal)
+ icon: NetworkService.signalIcon(modelData.signal, modelData.connected)
pointSize: Style.fontSizeXXL
color: modelData.connected ? Color.mPrimary : Color.mOnSurface
}
diff --git a/Modules/Settings/Bar/WidgetSettings/LockKeysSettings.qml b/Modules/Settings/Bar/WidgetSettings/LockKeysSettings.qml
deleted file mode 100644
index 541ce9d8..00000000
--- a/Modules/Settings/Bar/WidgetSettings/LockKeysSettings.qml
+++ /dev/null
@@ -1,84 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import qs.Commons
-import qs.Widgets
-import qs.Services
-
-ColumnLayout {
- id: root
- spacing: Style.marginM
-
- // Properties to receive data from parent
- property var widgetData: null
- property var widgetMetadata: null
-
- // Local state
- property string valueIndicatorStyle: widgetData.indicatorStyle !== undefined ? widgetData.indicatorStyle : widgetMetadata.indicatorStyle
- property bool valueShowCapsLock: widgetData.showCapsLock !== undefined ? widgetData.showCapsLock : widgetMetadata.showCapsLock
- property bool valueShowNumLock: widgetData.showNumLock !== undefined ? widgetData.showNumLock : widgetMetadata.showNumLock
- property bool valueShowScrollLock: widgetData.showScrollLock !== undefined ? widgetData.showScrollLock : widgetMetadata.showScrollLock
-
- function saveSettings() {
- var settings = Object.assign({}, widgetData || {})
- settings.indicatorStyle = valueIndicatorStyle
- settings.showCapsLock = valueShowCapsLock
- settings.showNumLock = valueShowNumLock
- settings.showScrollLock = valueShowScrollLock
- return settings
- }
-
- NComboBox {
- Layout.fillWidth: true
- label: I18n.tr("bar.widget-settings.lock-keys.indicator-style.label")
- description: I18n.tr("bar.widget-settings.lock-keys.indicator-style.description")
- model: [{
- "key": "large",
- "name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.large")
- }, {
- "key": "small",
- "name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.small")
- }, {
- "key": "square",
- "name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.square")
- }, {
- "key": "square-round",
- "name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.square-round")
- }, {
- "key": "circle",
- "name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.circle")
- }, {
- "key": "circle-dash",
- "name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.circle-dash")
- }, {
- "key": "circle-dot",
- "name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.circle-dot")
- }, {
- "key": "hex",
- "name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.hex")
- }]
- currentKey: valueIndicatorStyle
- onSelected: key => valueIndicatorStyle = key
- }
-
- NToggle {
- label: I18n.tr("bar.widget-settings.lock-keys.show-caps-lock.label")
- description: I18n.tr("bar.widget-settings.lock-keys.show-caps-lock.description")
- checked: valueShowCapsLock
- onToggled: checked => valueShowCapsLock = checked
- }
-
- NToggle {
- label: I18n.tr("bar.widget-settings.lock-keys.show-num-lock.label")
- description: I18n.tr("bar.widget-settings.lock-keys.show-num-lock.description")
- checked: valueShowNumLock
- onToggled: checked => valueShowNumLock = checked
- }
-
- NToggle {
- label: I18n.tr("bar.widget-settings.lock-keys.show-scroll-lock.label")
- description: I18n.tr("bar.widget-settings.lock-keys.show-scroll-lock.description")
- checked: valueShowScrollLock
- onToggled: checked => valueShowScrollLock = checked
- }
-}
diff --git a/Modules/Settings/DirectWidgetSettingsPanel.qml b/Modules/Settings/DirectWidgetSettingsPanel.qml
deleted file mode 100644
index cd2cde58..00000000
--- a/Modules/Settings/DirectWidgetSettingsPanel.qml
+++ /dev/null
@@ -1,70 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import Quickshell
-import Quickshell.Wayland
-import qs.Modules.Settings.Tabs
-import qs.Commons
-import qs.Services
-import qs.Widgets
-
-NPanel {
- id: root
-
- preferredWidth: 0
- preferredHeight: 0
- panelBackgroundColor: Color.transparent
- panelBorderColor: Color.transparent
-
- property var requestedWidgetSettings: []
-
- panelContent: Item {
-
- Component.onCompleted: {
- Qt.callLater(() => {
- var component = Qt.createComponent(Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/Bar/BarWidgetSettingsDialog.qml"))
-
- function instantiateAndOpen() {
- var dialog = component.createObject(Overlay.overlay, {
- "widgetIndex": requestedWidgetSettings.widgetIndex,
- "widgetData": requestedWidgetSettings.widgetData,
- "widgetId": requestedWidgetSettings.widgetId,
- "sectionId": requestedWidgetSettings.sectionId
- })
- if (dialog) {
- dialog.updateWidgetSettings.connect(updateWidgetSettingsInSection)
- dialog.onClosed.connect(closeWidgetSettings)
- dialog.open()
- }
- }
-
- if (component.status === Component.Ready) {
- instantiateAndOpen()
- } else {
- component.statusChanged.connect(instantiateAndOpen)
- }
-
- // Clear the request after handling
- requestedWidgetSettings = []
- })
- }
- }
-
- function openWidgetSettings(section, widgetIndex, widgetId, widgetData) {
- requestedWidgetSettings = {
- "sectionId": section,
- "widgetIndex": widgetIndex,
- "widgetId": widgetId,
- "widgetData": widgetData
- }
- root.open()
- }
-
- function updateWidgetSettingsInSection(section, index, settings) {
- Settings.data.bar.widgets[section][index] = settings
- }
-
- function closeWidgetSettings() {
- root.close()
- }
-}
diff --git a/Modules/Toast/SimpleToast.qml b/Modules/Toast/SimpleToast.qml
index 0845b11d..9dbd62be 100644
--- a/Modules/Toast/SimpleToast.qml
+++ b/Modules/Toast/SimpleToast.qml
@@ -4,7 +4,7 @@ import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-Rectangle {
+Item {
id: root
property string message: ""
@@ -16,26 +16,39 @@ Rectangle {
signal hidden
- width: parent.width
- height: Math.round(contentLayout.implicitHeight + Style.marginL * 2)
- radius: Style.radiusL
- visible: false
+ width: Math.round(420 * Style.uiScaleRatio + Style.marginM * 1.5 * 2)
+ height: Math.round(contentLayout.implicitHeight + Style.marginM * 3 * 2)
+ visible: true
opacity: 0
scale: initialScale
- color: Color.mSurface
- // Colored border based on type
- border.color: {
- switch (type) {
- case "warning":
- return Color.mPrimary
- case "error":
- return Color.mError
- default:
- return Color.mOutline
+ // Background rectangle (apply shadows here)
+ Rectangle {
+ id: background
+ anchors.fill: parent
+ anchors.margins: Style.marginM * 1.5
+ radius: Style.radiusL
+ color: Color.mSurface
+
+ // Colored border based on type
+ border.width: Math.max(2, Style.borderM)
+ border.color: {
+ switch (root.type) {
+ case "warning":
+ return Color.mPrimary
+ case "error":
+ return Color.mError
+ default:
+ return Color.mOutline
+ }
}
}
- border.width: Math.max(2, Style.borderM)
+
+ NDropShadows {
+ anchors.fill: background
+ source: background
+ autoPaddingEnabled: true
+ }
Behavior on opacity {
NumberAnimation {
@@ -75,7 +88,10 @@ Rectangle {
RowLayout {
id: contentLayout
anchors.fill: parent
- anchors.margins: Style.marginL
+ anchors.topMargin: Style.marginL
+ anchors.bottomMargin: Style.marginL
+ anchors.leftMargin: Style.marginL * 2
+ anchors.rightMargin: Style.marginL * 2
spacing: Style.marginL
// Icon
@@ -132,7 +148,7 @@ Rectangle {
// Click anywhere dismiss the toast
MouseArea {
- anchors.fill: parent
+ anchors.fill: background
acceptedButtons: Qt.LeftButton
onClicked: root.hide()
cursorShape: Qt.PointingHandCursor
diff --git a/Modules/Toast/ToastOverlay.qml b/Modules/Toast/ToastOverlay.qml
index 3051108d..db5dbe56 100644
--- a/Modules/Toast/ToastOverlay.qml
+++ b/Modules/Toast/ToastOverlay.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Services
import qs.Widgets
Variants {
diff --git a/Modules/Toast/ToastScreen.qml b/Modules/Toast/ToastScreen.qml
index 0bd20693..e782a3ce 100644
--- a/Modules/Toast/ToastScreen.qml
+++ b/Modules/Toast/ToastScreen.qml
@@ -1,10 +1,8 @@
import QtQuick
-import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
-import qs.Services
-import qs.Widgets
+import qs.Services.UI
Item {
id: root
@@ -45,11 +43,11 @@ Item {
function enqueueToast(toastData) {
// Safe logging - fix the substring bug
var descPreview = (toastData.description || "").substring(0, 100).replace(/\n/g, " ")
- Logger.i("ToastScreen", "Queuing", toastData.type, ":", toastData.message, descPreview)
+ Logger.d("ToastScreen", "Queuing", toastData.type, ":", toastData.message, descPreview)
// Bounded queue to prevent unbounded memory growth
if (messageQueue.length >= maxQueueSize) {
- Logger.i("ToastScreen", "Queue full, dropping oldest toast")
+ Logger.d("ToastScreen", "Queue full, dropping oldest toast")
messageQueue.shift()
}
@@ -135,73 +133,68 @@ Item {
screen: root.screen
- readonly property string location: (Settings.data.notifications && Settings.data.notifications.location) ? Settings.data.notifications.location : "top_right"
- readonly property bool isTop: (location === "top") || (location.length >= 3 && location.substring(0, 3) === "top")
- readonly property bool isBottom: (location === "bottom") || (location.length >= 6 && location.substring(0, 6) === "bottom")
- readonly property bool isLeft: location.indexOf("_left") >= 0
- readonly property bool isRight: location.indexOf("_right") >= 0
- readonly property bool isCentered: (location === "top" || location === "bottom")
+ // Parse location setting
+ readonly property string location: Settings.data.notifications?.location || "top_right"
+ readonly property bool isTop: location.startsWith("top")
+ readonly property bool isBottom: location.startsWith("bottom")
+ readonly property bool isLeft: location.endsWith("_left")
+ readonly property bool isRight: location.endsWith("_right")
+ readonly property bool isCentered: location === "top" || location === "bottom"
- // Anchor selection based on location (window edges)
+ readonly property string barPos: Settings.data.bar.position
+ readonly property bool isFloating: Settings.data.bar.floating
+
+ // Calculate bar offsets for each edge separately
+ readonly property int barOffsetTop: {
+ if (barPos !== "top")
+ return 0
+ const floatMarginV = isFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+ return Style.barHeight + floatMarginV
+ }
+
+ readonly property int barOffsetBottom: {
+ if (barPos !== "bottom")
+ return 0
+ const floatMarginV = isFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0
+ return Style.barHeight + floatMarginV
+ }
+
+ readonly property int barOffsetLeft: {
+ if (barPos !== "left")
+ return 0
+ const floatMarginH = isFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ return floatMarginH
+ }
+
+ readonly property int barOffsetRight: {
+ if (barPos !== "right")
+ return 0
+ const floatMarginH = isFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
+ return floatMarginH
+ }
+
+ // Anchoring
anchors.top: isTop
anchors.bottom: isBottom
anchors.left: isLeft
anchors.right: isRight
- // Margins depending on bar position and chosen location
- margins.top: {
- if (!(anchors.top))
- return 0
- var base = Style.marginM
- if (Settings.data.bar.position === "top") {
- var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0
- return (Style.barHeight) + base + floatExtraV
- }
- return base
- }
+ // Margins for PanelWindow - only apply bar offset for the specific edge where the bar is
+ margins.top: isTop ? barOffsetTop : 0
+ margins.bottom: isBottom ? barOffsetBottom : 0
+ margins.left: isLeft ? barOffsetLeft : 0
+ margins.right: isRight ? barOffsetRight : 0
- margins.bottom: {
- if (!(anchors.bottom))
- return 0
- var base = Style.marginM
- if (Settings.data.bar.position === "bottom") {
- var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0
- return (Style.barHeight) + base + floatExtraV
- }
- return base
- }
-
- margins.left: {
- if (!(anchors.left))
- return 0
- var base = Style.marginM
- if (Settings.data.bar.position === "left") {
- var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
- return (Style.barHeight) + base + floatExtraH
- }
- return base
- }
-
- margins.right: {
- if (!(anchors.right))
- return 0
- var base = Style.marginM
- if (Settings.data.bar.position === "right") {
- var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
- return (Style.barHeight) + base + floatExtraH
- }
- return base
- }
-
- implicitWidth: 420
- implicitHeight: toastItem.height
+ implicitWidth: Math.round(toastItem.width)
+ implicitHeight: Math.round(toastItem.height)
color: Color.transparent
WlrLayershell.layer: (Settings.data.notifications && Settings.data.notifications.overlayLayer) ? WlrLayer.Overlay : WlrLayer.Top
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
- exclusionMode: PanelWindow.ExclusionMode.Ignore
+ WlrLayershell.exclusionMode: ExclusionMode.Ignore
+ //mask: Region {}
function showToast(message, description, icon, type, duration) {
toastItem.show(message, description, icon, type, duration)
}
@@ -212,8 +205,6 @@ Item {
SimpleToast {
id: toastItem
-
- anchors.horizontalCenter: parent.horizontalCenter
onHidden: root.onToastHidden()
}
}
diff --git a/Modules/Tooltip/Tooltip.qml b/Modules/Tooltip/Tooltip.qml
index f30306d3..584c1529 100644
--- a/Modules/Tooltip/Tooltip.qml
+++ b/Modules/Tooltip/Tooltip.qml
@@ -1,8 +1,7 @@
import QtQuick
-import QtQuick.Layouts
+// import QtQuick.Layouts
import Quickshell
import qs.Commons
-import qs.Services
import qs.Widgets
PopupWindow {
diff --git a/README.md b/README.md
index 6a95fd22..1b7bec4c 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,12 @@ https://github.com/user-attachments/assets/72c6d6dc-48b0-48a0-bd8b-c7e70990edc4
---
+## ⚠️ BREAKING CHANGE ⚠️
+
+With the release of `v3.0.0` we introduced a small breaking change. This is solely for niri users, we reworked how we handle the wallpaper / overview. Please read [the docs](https://docs.noctalia.dev/getting-started/compositor-settings/#niri-wallpaper-and-overview-setup) to adapt your niri config.
+
+---
+
## 🚀 Getting Started
**New to Noctalia?**
@@ -99,7 +105,11 @@ While all donations are greatly appreciated, they are completely voluntary.
* PikaOS
* LionHeartP
* Nyxion ツ
+* RockDuck
* MrDowntempo
+* Tempus Thales
+* Raine
+* JustCurtis
---
diff --git a/Services/AppThemeService.qml b/Services/AppThemeService.qml
deleted file mode 100644
index 4ad64e3a..00000000
--- a/Services/AppThemeService.qml
+++ /dev/null
@@ -1,501 +0,0 @@
-pragma Singleton
-
-import QtQuick
-import Quickshell
-import Quickshell.Io
-import qs.Commons
-import qs.Services
-import "../Helpers/ColorsConvert.js" as ColorsConvert
-
-Singleton {
- id: root
-
- readonly property string colorsApplyScript: Quickshell.shellDir + '/Bin/colors-apply.sh'
- readonly property string dynamicConfigPath: Settings.cacheDir + "matugen.dynamic.toml"
- readonly property var terminalPaths: ({
- "foot": "~/.config/foot/themes/noctalia",
- "ghostty": "~/.config/ghostty/themes/noctalia",
- "kitty": "~/.config/kitty/themes/noctalia.conf",
- "alacritty": "~/.config/alacritty/themes/noctalia.toml"
- })
-
- readonly property var schemeNameMap: ({
- "Noctalia (default)": "Noctalia-default",
- "Noctalia (legacy)": "Noctalia-legacy",
- "Tokyo Night": "Tokyo-Night"
- })
- readonly property var predefinedTemplateConfigs: ({
- "gtk": {
- "input": "gtk.css",
- "outputs": [{
- "path": "~/.config/gtk-3.0/gtk.css"
- }, {
- "path": "~/.config/gtk-4.0/gtk.css"
- }],
- "postProcess": mode => `gsettings set org.gnome.desktop.interface color-scheme prefer-${mode}\n`
- },
- "qt": {
- "input": "qtct.conf",
- "outputs": [{
- "path": "~/.config/qt5ct/colors/noctalia.conf"
- }, {
- "path": "~/.config/qt6ct/colors/noctalia.conf"
- }]
- },
- "kcolorscheme": {
- "input": "kcolorscheme.colors",
- "outputs": [{
- "path": "~/.local/share/color-schemes/noctalia.colors"
- }]
- },
- "fuzzel": {
- "input": "fuzzel.conf",
- "outputs": [{
- "path": "~/.config/fuzzel/themes/noctalia"
- }],
- "postProcess": () => `${colorsApplyScript} fuzzel\n`
- },
- "pywalfox": {
- "input": "pywalfox.json",
- "outputs": [{
- "path": "~/.cache/wal/colors.json"
- }],
- "postProcess": () => `${colorsApplyScript} pywalfox\n`
- },
- "discord_vesktop": {
- "input": "vesktop.css",
- "outputs": [{
- "path": "~/.config/vesktop/themes/noctalia.theme.css"
- }]
- },
- "discord_webcord": {
- "input": "vesktop.css",
- "outputs": [{
- "path": "~/.config/webcord/themes/noctalia.theme.css"
- }]
- },
- "discord_armcord": {
- "input": "vesktop.css",
- "outputs": [{
- "path": "~/.config/armcord/themes/noctalia.theme.css"
- }]
- },
- "discord_equibop": {
- "input": "vesktop.css",
- "outputs": [{
- "path": "~/.config/equibop/themes/noctalia.theme.css"
- }]
- },
- "discord_lightcord": {
- "input": "vesktop.css",
- "outputs": [{
- "path": "~/.config/lightcord/themes/noctalia.theme.css"
- }]
- },
- "discord_dorion": {
- "input": "vesktop.css",
- "outputs": [{
- "path": "~/.config/dorion/themes/noctalia.theme.css"
- }]
- },
- "discord_vencord": {
- "input": "vesktop.css",
- "outputs": [{
- "path": "~/.config/discord/themes/noctalia.theme.css"
- }]
- },
- "vicinae": {
- "input": "vicinae.toml",
- "outputs": [{
- "path": "~/.local/share/vicinae/themes/matugen.toml"
- }],
- "postProcess": () => `cp -n ${Quickshell.shellDir}/Assets/noctalia.svg ~/.local/share/vicinae/themes/noctalia.svg && ${colorsApplyScript} vicinae\n`
- },
- "walker": {
- "input": "walker.css",
- "outputs": [{
- "path": "~/.config/walker/themes/noctalia/style.css"
- }],
- "postProcess": () => `${colorsApplyScript} walker\n`,
- "strict": true
- },
- "code": {
- "input": "code.json",
- "outputs": [{
- "path": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
- }]
- }
- })
-
- Connections {
- target: WallpaperService
- function onWallpaperChanged(screenName, path) {
- if (screenName === Screen.name && Settings.data.colorSchemes.useWallpaperColors) {
- generateFromWallpaper()
- }
- }
- }
-
- Connections {
- target: Settings.data.colorSchemes
- function onDarkModeChanged() {
- Logger.i("AppThemeService", "Detected dark mode change")
- AppThemeService.generate()
- }
- }
-
- // --------------------------------------------------------------------------------
- function init() {
- Logger.i("AppThemeService", "Service started")
- }
-
- // --------------------------------------------------------------------------------
- function generate() {
- if (Settings.data.colorSchemes.useWallpaperColors) {
- generateFromWallpaper()
- } else {
- // Re-apply the scheme, this is the best way to regenerate all templates too.
- ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
- }
- }
-
- // --------------------------------------------------------------------------------
- // Wallpaper Colors Generation
- // --------------------------------------------------------------------------------
- function generateFromWallpaper() {
-
- // Logger.i("AppThemeService", "Generating from wallpaper on screen:", Screen.name)
- const wp = WallpaperService.getWallpaper(Screen.name).replace(/'/g, "'\\''")
- if (!wp) {
- Logger.e("AppThemeService", "No wallpaper found")
- return
- }
-
- const content = MatugenTemplates.buildConfigToml()
- if (!content)
- return
-
- const mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
- const script = buildMatugenScript(content, wp, mode)
-
- generateProcess.command = ["bash", "-lc", script]
- generateProcess.running = true
- }
-
- function buildMatugenScript(content, wallpaper, mode) {
- const delimiter = "MATUGEN_CONFIG_EOF_" + Math.random().toString(36).substr(2, 9)
- const pathEsc = dynamicConfigPath.replace(/'/g, "'\\''")
-
- let script = `cat > '${pathEsc}' << '${delimiter}'\n${content}\n${delimiter}\n`
- script += `matugen image '${wallpaper}' --config '${pathEsc}' --mode ${mode} --type ${Settings.data.colorSchemes.matugenSchemeType}`
- script += buildUserTemplateCommand(wallpaper, mode)
-
- return script + "\n"
- }
-
- // --------------------------------------------------------------------------------
- // Predefined Scheme Generation
- // For predefined color schemes, we bypass matugen's generation which do not gives good results.
- // Instead, we use 'sed' to apply a custom palette to the existing matugen templates.
- // --------------------------------------------------------------------------------
- function generateFromPredefinedScheme(schemeData) {
- Logger.i("AppThemeService", "Generating templates from predefined color scheme")
-
- handleTerminalThemes()
-
- const mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
- const colors = schemeData[mode]
- let script = processAllTemplates(colors, mode)
-
- // Add user templates if enabled
- script += buildUserTemplateCommandForPredefined(schemeData, mode)
-
- generateProcess.command = ["bash", "-lc", script]
- generateProcess.running = true
- }
-
- function generatePalette(colors, isDarkMode, isStrict) {
- const c = hex => ({
- "default": {
- "hex": hex,
- "hex_stripped": hex.replace(/^#/, "")
- }
- })
-
- // Generate container colors
- const primaryContainer = ColorsConvert.generateContainerColor(colors.mPrimary, isDarkMode)
- const secondaryContainer = ColorsConvert.generateContainerColor(colors.mSecondary, isDarkMode)
- const tertiaryContainer = ColorsConvert.generateContainerColor(colors.mTertiary, isDarkMode)
-
- // Generate "on" colors
- const onPrimary = ColorsConvert.generateOnColor(colors.mPrimary, isDarkMode)
- const onSecondary = ColorsConvert.generateOnColor(colors.mSecondary, isDarkMode)
- const onTertiary = ColorsConvert.generateOnColor(colors.mTertiary, isDarkMode)
-
- const onPrimaryContainer = ColorsConvert.generateOnColor(primaryContainer, isDarkMode)
- const onSecondaryContainer = ColorsConvert.generateOnColor(secondaryContainer, isDarkMode)
- const onTertiaryContainer = ColorsConvert.generateOnColor(tertiaryContainer, isDarkMode)
-
- // Generate error colors (standard red-based)
- const errorContainer = ColorsConvert.generateContainerColor(colors.mError, isDarkMode)
- const onError = ColorsConvert.generateOnColor(colors.mError, isDarkMode)
- const onErrorContainer = ColorsConvert.generateOnColor(errorContainer, isDarkMode)
-
- // Surface is same as background in Material Design 3
- const surface = colors.mSurface
- const onSurface = isStrict ? colors.mOnSurface : ColorsConvert.generateOnColor(colors.mSurface, isDarkMode)
-
- // Generate surface variant (slightly different tone)
- const surfaceVariant = isStrict ? colors.mSurfaceVariant : ColorsConvert.adjustLightness(colors.mSurface, isDarkMode ? 5 : -3)
- const onSurfaceVariant = isStrict ? colors.mOnSurfaceVariant : ColorsConvert.generateOnColor(surfaceVariant, isDarkMode)
-
- // Generate surface containers (progressive elevation)
- const surfaceContainerLowest = ColorsConvert.generateSurfaceVariant(surface, 0, isDarkMode)
- const surfaceContainerLow = ColorsConvert.generateSurfaceVariant(surface, 1, isDarkMode)
- const surfaceContainer = ColorsConvert.generateSurfaceVariant(surface, 2, isDarkMode)
- const surfaceContainerHigh = ColorsConvert.generateSurfaceVariant(surface, 3, isDarkMode)
- const surfaceContainerHighest = ColorsConvert.generateSurfaceVariant(surface, 4, isDarkMode)
-
- // Generate outline colors (for borders/dividers)
- const outline = isStrict ? colors.mOutline : ColorsConvert.adjustLightnessAndSaturation(colors.mOnSurface, isDarkMode ? -30 : 30, isDarkMode ? -30 : 30)
- const outlineVariant = ColorsConvert.adjustLightness(outline, isDarkMode ? -20 : 20)
-
- // Shadow is always pitch black
- const shadow = "#000000"
-
- return {
- "primary": c(colors.mPrimary),
- "on_primary": c(onPrimary),
- "primary_container": c(primaryContainer),
- "on_primary_container": c(onPrimaryContainer),
- "secondary": c(colors.mSecondary),
- "on_secondary": c(onSecondary),
- "secondary_container": c(secondaryContainer),
- "on_secondary_container": c(onSecondaryContainer),
- "tertiary": c(colors.mTertiary),
- "on_tertiary": c(onTertiary),
- "tertiary_container": c(tertiaryContainer),
- "on_tertiary_container": c(onTertiaryContainer),
- "error": c(colors.mError),
- "on_error": c(onError),
- "error_container": c(errorContainer),
- "on_error_container": c(onErrorContainer),
- "background": c(surface),
- "on_background": c(onSurface),
- "surface": c(surface),
- "on_surface": c(onSurface),
- "surface_variant": c(surfaceVariant),
- "on_surface_variant": c(onSurfaceVariant),
- "surface_container_lowest": c(surfaceContainerLowest),
- "surface_container_low": c(surfaceContainerLow),
- "surface_container": c(surfaceContainer),
- "surface_container_high": c(surfaceContainerHigh),
- "surface_container_highest": c(surfaceContainerHighest),
- "outline": c(outline),
- "outline_variant": c(outlineVariant),
- "shadow": c(shadow)
- }
- }
-
- function processAllTemplates(colors, mode) {
- let script = ""
- const homeDir = Quickshell.env("HOME")
-
- Object.keys(predefinedTemplateConfigs).forEach(appName => {
- if (Settings.data.templates[appName]) {
- script += processTemplate(appName, colors, mode, homeDir)
- }
- })
-
- return script
- }
-
- function processTemplate(appName, colors, mode, homeDir) {
- const config = predefinedTemplateConfigs[appName]
- const templatePath = `${Quickshell.shellDir}/Assets/MatugenTemplates/${config.input}`
- let script = ""
-
- const palette = generatePalette(colors, Settings.data.colorSchemes.darkMode, config.strict || false)
-
- config.outputs.forEach(output => {
-
- const outputPath = output.path.replace("~", homeDir)
- const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
-
- // For Discord clients, check if the base config directory exists
- if (appName.startsWith("discord_")) {
- const baseConfigDir = outputDir.replace("/themes", "")
- script += `if [ -d "${baseConfigDir}" ]; then\n`
- script += ` mkdir -p ${outputDir}\n`
- script += ` cp '${templatePath}' '${outputPath}'\n`
- script += ` ${replaceColorsInFile(outputPath, palette)}\n`
- script += `else\n`
- script += ` echo "Discord client ${appName} not found at ${baseConfigDir}, skipping theme generation"\n`
- script += `fi\n`
- } else {
- script += `mkdir -p ${outputDir}\n`
- script += `cp '${templatePath}' '${outputPath}'\n`
- script += replaceColorsInFile(outputPath, palette)
- }
- })
-
- if (config.postProcess) {
- script += config.postProcess(mode)
- }
-
- return script
- }
-
- function replaceColorsInFile(filePath, colors) {
- // This handle both ".hex" and ".hex_stripped" the EXACT same way. Our predefined color schemes are
- // always RRGGBB without alpha so this is fine and keeps compatibility with matugen.
- let script = ""
- Object.keys(colors).forEach(colorKey => {
- const colorValue = colors[colorKey].default.hex
- const escapedColor = colorValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
- script += `sed -i 's/{{colors\\.${colorKey}\\.default\\.hex\\(_stripped\\)\\?}}/${escapedColor}/g' '${filePath}'\n`
- })
- return script
- }
-
- // --------------------------------------------------------------------------------
- // Terminal Themes
- // --------------------------------------------------------------------------------
- function handleTerminalThemes() {
- const commands = []
-
- Object.keys(terminalPaths).forEach(terminal => {
- if (Settings.data.templates[terminal]) {
- const outputPath = terminalPaths[terminal]
- const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
- const templatePath = getTerminalColorsTemplate(terminal)
-
- commands.push(`mkdir -p ${outputDir}`)
- commands.push(`cp -f ${templatePath} ${outputPath}`)
- commands.push(`${colorsApplyScript} ${terminal}`)
- }
- })
-
- if (commands.length > 0) {
- copyProcess.command = ["bash", "-lc", commands.join('; ')]
- copyProcess.running = true
- }
- }
-
- function getTerminalColorsTemplate(terminal) {
- let colorScheme = Settings.data.colorSchemes.predefinedScheme
- const mode = Settings.data.colorSchemes.darkMode ? 'dark' : 'light'
-
- colorScheme = schemeNameMap[colorScheme] || colorScheme
- const extension = terminal === 'kitty' ? ".conf" : ""
-
- return `${Quickshell.shellDir}/Assets/ColorScheme/${colorScheme}/terminal/${terminal}/${colorScheme}-${mode}${extension}`
- }
-
- // --------------------------------------------------------------------------------
- // User Templates
- // --------------------------------------------------------------------------------
- function buildUserTemplateCommand(input, mode) {
- if (!Settings.data.templates.enableUserTemplates) {
- return ""
- }
-
- const userConfigPath = getUserConfigPath()
- let script = "\n# Execute user config if it exists\n"
- script += `if [ -f '${userConfigPath}' ]; then\n`
- script += ` matugen image '${input}' --config '${userConfigPath}' --mode ${mode} --type ${Settings.data.colorSchemes.matugenSchemeType}\n`
- script += "fi"
-
- return script
- }
-
- function buildUserTemplateCommandForPredefined(schemeData, mode) {
- if (!Settings.data.templates.enableUserTemplates) {
- return ""
- }
-
- const userConfigPath = getUserConfigPath()
- const isDarkMode = Settings.data.colorSchemes.darkMode
- const colors = schemeData[mode]
-
- // Generate the matugen palette JSON
- const palette = generatePalette(colors, isDarkMode, false)
-
- // Create a temporary JSON file with the color palette
- const tempJsonPath = Settings.cacheDir + "predefined-colors.json"
- const homeDir = Quickshell.env("HOME")
- const tempJsonPathEsc = tempJsonPath.replace(/'/g, "'\\''")
-
- let script = "\n# Execute user templates with predefined scheme colors\n"
- script += `if [ -f '${userConfigPath}' ]; then\n`
-
- // Write the color palette to a temp JSON file
- script += ` cat > '${tempJsonPathEsc}' << 'EOF'\n`
- script += JSON.stringify({
- "colors": palette
- }, null, 2) + "\n"
- script += "EOF\n"
-
- // Use matugen json subcommand with the color palette
- script += ` matugen json '${tempJsonPathEsc}' --config '${userConfigPath}' --mode ${mode}\n`
- script += "fi"
-
- return script
- }
-
- function getUserConfigPath() {
- return (Settings.configDir + "user-templates.toml").replace(/'/g, "'\\''")
- }
-
- // --------------------------------------------------------------------------------
- // Processes
- // --------------------------------------------------------------------------------
- Process {
- id: generateProcess
- workingDirectory: Quickshell.shellDir
- running: false
-
- onExited: function (exitCode) {
- if (exitCode !== 0) {
- // Capture error output from stderr first, then stdout, or use fallback message
- const errorMsg = (stderr.text && stderr.text.trim() !== "") ? stderr.text.trim() : ((stdout.text && stdout.text.trim() !== "") ? stdout.text.trim() : I18n.tr("toast.matugen.failed-general"))
- Logger.e("AppThemeService", "Matugen process failed with exit code:", exitCode)
- if (stderr.text && stderr.text.trim() !== "") {
- Logger.e("AppThemeService", "Matugen stderr:", stderr.text)
- }
- if (stdout.text && stdout.text.trim() !== "") {
- Logger.e("AppThemeService", "Matugen stdout:", stdout.text)
- }
- ToastService.showError(I18n.tr("toast.matugen.failed"), errorMsg, 6000)
- }
- }
-
- stdout: StdioCollector {
- onStreamFinished: {
- if (this.text) {
- Logger.d("AppThemeService", "GenerateProcess stdout:", this.text)
- }
- }
- }
- stderr: StdioCollector {
- onStreamFinished: {
- if (this.text) {
- Logger.d("AppThemeService", "GenerateProcess stderr:", this.text)
- }
- }
- }
- }
-
- Process {
- id: copyProcess
- workingDirectory: Quickshell.shellDir
- running: false
- stderr: StdioCollector {
- onStreamFinished: {
- if (this.text) {
- Logger.d("AppThemeService", "CopyProcess stderr:", this.text)
- }
- }
- }
- }
-}
diff --git a/Services/CavaService.qml b/Services/CavaService.qml
deleted file mode 100644
index 30fccea1..00000000
--- a/Services/CavaService.qml
+++ /dev/null
@@ -1,79 +0,0 @@
-pragma Singleton
-
-import QtQuick
-import Quickshell
-import Quickshell.Io
-import qs.Commons
-
-Singleton {
- id: root
-
- property bool shouldRun: BarService.hasAudioVisualizer || (PanelService.getPanel("controlCenterPanel") === PanelService.openedPanel) || PanelService.lockScreen.active
- property var values: Array(barsCount).fill(0)
- property int barsCount: 48
- property var config: ({
- "general": {
- "bars": barsCount,
- "framerate": Settings.data.audio.cavaFrameRate,
- "autosens": 1,
- "sensitivity": 100,
- "lower_cutoff_freq": 50,
- "higher_cutoff_freq": 12000
- },
- "smoothing": {
- "monstercat": 0,
- "noise_reduction": 77
- },
- "output": {
- "method": "raw",
- "data_format": "ascii",
- "ascii_max_range": 100,
- "bit_format": "8bit",
- "channels": "mono",
- "mono_option": "average"
- }
- })
-
- Process {
- id: process
- stdinEnabled: true
- running: root.shouldRun
- command: ["cava", "-p", "/dev/stdin"]
- onRunningChanged: {
- Logger.d("Cava", "Process running:", running)
- }
- onExited: {
- Logger.i("Cava", "Process exited")
- stdinEnabled = true
- values = Array(barsCount).fill(0)
- }
- onStarted: {
- Logger.i("Cava", "Process started")
- for (const k in config) {
- if (typeof config[k] !== "object") {
- write(k + "=" + config[k] + "\n")
- continue
- }
- write("[" + k + "]\n")
- const obj = config[k]
- for (const k2 in obj) {
- write(k2 + "=" + obj[k2] + "\n")
- }
- }
- stdinEnabled = false
- values = Array(barsCount).fill(0)
- }
- stdout: SplitParser {
- onRead: data => {
- root.values = data.slice(0, -1).split(";").map(v => parseInt(v, 10) / 100)
- }
- }
- stderr: StdioCollector {
- onStreamFinished: {
- if (text.trim()) {
- Logger.w("Cava", "Error", text)
- }
- }
- }
- }
-}
diff --git a/Services/CompositorService.qml b/Services/Compositor/CompositorService.qml
similarity index 99%
rename from Services/CompositorService.qml
rename to Services/Compositor/CompositorService.qml
index cd4b16b1..0ce10ffa 100644
--- a/Services/CompositorService.qml
+++ b/Services/Compositor/CompositorService.qml
@@ -4,7 +4,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
diff --git a/Services/HyprlandService.qml b/Services/Compositor/HyprlandService.qml
similarity index 99%
rename from Services/HyprlandService.qml
rename to Services/Compositor/HyprlandService.qml
index b62d642c..46838bbf 100644
--- a/Services/HyprlandService.qml
+++ b/Services/Compositor/HyprlandService.qml
@@ -45,7 +45,7 @@ Item {
queryDisplayScales()
})
initialized = true
- Logger.i("HyprlandService", "Initialized successfully")
+ Logger.i("HyprlandService", "Service started")
} catch (e) {
Logger.e("HyprlandService", "Failed to initialize:", e)
}
diff --git a/Services/NiriService.qml b/Services/Compositor/NiriService.qml
similarity index 99%
rename from Services/NiriService.qml
rename to Services/Compositor/NiriService.qml
index ab89d366..7eb3a6e0 100644
--- a/Services/NiriService.qml
+++ b/Services/Compositor/NiriService.qml
@@ -28,7 +28,7 @@ Item {
updateWorkspaces()
updateWindows()
queryDisplayScales()
- Logger.i("NiriService", "Initialized successfully")
+ Logger.i("NiriService", "Service started")
}
// Update workspaces
diff --git a/Services/SwayService.qml b/Services/Compositor/SwayService.qml
similarity index 99%
rename from Services/SwayService.qml
rename to Services/Compositor/SwayService.qml
index 1ca2e638..e5ac8f6e 100644
--- a/Services/SwayService.qml
+++ b/Services/Compositor/SwayService.qml
@@ -43,7 +43,7 @@ Item {
queryDisplayScales()
})
initialized = true
- Logger.i("SwayService", "Initialized successfully")
+ Logger.i("SwayService", "Service started")
} catch (e) {
Logger.e("SwayService", "Failed to initialize:", e)
}
diff --git a/Services/HooksService.qml b/Services/Control/HooksService.qml
similarity index 95%
rename from Services/HooksService.qml
rename to Services/Control/HooksService.qml
index 6f6db9d3..646cfbfd 100644
--- a/Services/HooksService.qml
+++ b/Services/Control/HooksService.qml
@@ -3,7 +3,7 @@ pragma Singleton
import QtQuick
import Quickshell
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
@@ -66,6 +66,6 @@ Singleton {
// Initialize the service
function init() {
- Logger.i("HooksService", "Service initialized")
+ Logger.i("HooksService", "Service started")
}
}
diff --git a/Services/IPCService.qml b/Services/Control/IPCService.qml
similarity index 54%
rename from Services/IPCService.qml
rename to Services/Control/IPCService.qml
index 7a569485..eea7eea0 100644
--- a/Services/IPCService.qml
+++ b/Services/Control/IPCService.qml
@@ -2,8 +2,16 @@ import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
+import Quickshell.Widgets
+
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
+import qs.Services.Hardware
+import qs.Services.Media
+import qs.Services.Power
+import qs.Services.System
+import qs.Services.Theming
+import qs.Services.UI
Item {
id: root
@@ -27,7 +35,20 @@ Item {
IpcHandler {
target: "settings"
function toggle() {
- settingsPanel.toggle()
+ root.withTargetScreen(screen => {
+ var settingsPanel = PanelService.getPanel("settingsPanel", screen)
+ settingsPanel?.toggle()
+ })
+ }
+ }
+
+ IpcHandler {
+ target: "calendar"
+ function toggle() {
+ root.withTargetScreen(screen => {
+ var calendarPanel = PanelService.getPanel("calendarPanel", screen)
+ calendarPanel?.toggle(null, "Clock")
+ })
}
}
@@ -35,10 +56,19 @@ Item {
target: "notifications"
function toggleHistory() {
// Will attempt to open the panel next to the bar button if any.
- notificationHistoryPanel.toggle(null, "NotificationHistory")
+ root.withTargetScreen(screen => {
+ var notificationHistoryPanel = PanelService.getPanel("notificationHistoryPanel", screen)
+ notificationHistoryPanel.toggle(null, "NotificationHistory")
+ })
}
function toggleDND() {
- Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
+ NotificationService.doNotDisturb = !NotificationService.doNotDisturb
+ }
+ function enableDND() {
+ NotificationService.doNotDisturb = true
+ }
+ function disableDND() {
+ NotificationService.doNotDisturb = false
}
function clear() {
NotificationService.clearHistory()
@@ -63,15 +93,24 @@ Item {
IpcHandler {
target: "launcher"
function toggle() {
- launcherPanel.toggle()
+ root.withTargetScreen(screen => {
+ var launcherPanel = PanelService.getPanel("launcherPanel", screen)
+ launcherPanel?.toggle()
+ })
}
function clipboard() {
- launcherPanel.setSearchText(">clip ")
- launcherPanel.toggle()
+ root.withTargetScreen(screen => {
+ var launcherPanel = PanelService.getPanel("launcherPanel", screen)
+ launcherPanel?.setSearchText(">clip ")
+ launcherPanel?.toggle()
+ })
}
function calculator() {
- launcherPanel.setSearchText(">calc ")
- launcherPanel.toggle()
+ root.withTargetScreen(screen => {
+ var launcherPanel = PanelService.getPanel("launcherPanel", screen)
+ launcherPanel?.setSearchText(">calc ")
+ launcherPanel?.toggle()
+ })
}
}
@@ -158,7 +197,10 @@ Item {
IpcHandler {
target: "sessionMenu"
function toggle() {
- sessionMenuPanel.toggle()
+ root.withTargetScreen(screen => {
+ var sessionMenuPanel = PanelService.getPanel("sessionMenuPanel", screen)
+ sessionMenuPanel?.toggle()
+ })
}
function lockAndSuspend() {
@@ -169,8 +211,15 @@ Item {
IpcHandler {
target: "controlCenter"
function toggle() {
- // Will attempt to open the panel next to the bar button if any.
- controlCenterPanel.toggle(null, "ControlCenter")
+ root.withTargetScreen(screen => {
+ var controlCenterPanel = PanelService.getPanel("controlCenterPanel", screen)
+ if (Settings.data.controlCenter.position === "close_to_bar_button") {
+ // Will attempt to open the panel next to the bar button if any.
+ controlCenterPanel?.toggle(null, "ControlCenter")
+ } else {
+ controlCenterPanel?.toggle()
+ }
+ })
}
}
@@ -179,7 +228,10 @@ Item {
target: "wallpaper"
function toggle() {
if (Settings.data.wallpaper.enabled) {
- wallpaperPanel.toggle()
+ root.withTargetScreen(screen => {
+ var wallpaperPanel = PanelService.getPanel("wallpaperPanel", screen)
+ wallpaperPanel?.toggle()
+ })
}
}
@@ -228,6 +280,7 @@ Item {
}
}
}
+
IpcHandler {
target: "powerProfile"
function cycle() {
@@ -247,7 +300,20 @@ Item {
break
}
}
+
+ function toggleNoctaliaPerformance() {
+ PowerProfileService.toggleNoctaliaPerformance()
+ }
+
+ function enableNoctaliaPerformance() {
+ PowerProfileService.setNoctaliaPerformance(true)
+ }
+
+ function disableNoctaliaPerformance() {
+ PowerProfileService.setNoctaliaPerformance(false)
+ }
}
+
IpcHandler {
target: "media"
function playPause() {
@@ -292,4 +358,68 @@ Item {
MediaService.seekByRatio(positionVal)
}
}
+
+ // Queue an IPC panel operation - will execute when screen is detected
+ function withTargetScreen(callback) {
+ if (pendingCallback) {
+ Logger.w("IPC", "Another IPC call is pending, ignoring new call")
+ return
+ }
+
+ // Single monitor setup can execute immediately
+ if (Quickshell.screens.length === 1) {
+ callback(Quickshell.screens[0])
+ } else {
+ // Multi-monitors setup needs to start async detection
+ detectedScreen = null
+ pendingCallback = callback
+ screenDetectorLoader.active = true
+ }
+ }
+
+
+ /**
+ * For IPC calls on multi-monitors setup that will open panels on screen,
+ * we need to open a QS PanelWindow and wait for it's "screen" property to stabilize.
+ */
+ property ShellScreen detectedScreen: null
+ property var pendingCallback: null
+
+ Timer {
+ id: screenDetectorDebounce
+ running: false
+ interval: 20
+ onTriggered: {
+ Logger.d("IPC", "Screen debounced to:", detectedScreen?.name || "null")
+
+ // Execute pending callback if any
+ if (pendingCallback) {
+ Logger.d("IPC", "Executing pending IPC callback on screen:", detectedScreen.name)
+ pendingCallback(detectedScreen)
+ pendingCallback = null
+ }
+
+ // Clean up
+ screenDetectorLoader.active = false
+ }
+ }
+
+ // Invisible dummy PanelWindow to detect which screen should receive IPC calls
+ Loader {
+ id: screenDetectorLoader
+ active: false
+
+ sourceComponent: PanelWindow {
+ implicitWidth: 0
+ implicitHeight: 0
+ color: Color.transparent
+ WlrLayershell.exclusionMode: ExclusionMode.Ignore
+ mask: Region {}
+
+ onScreenChanged: {
+ detectedScreen = screen
+ screenDetectorDebounce.restart()
+ }
+ }
+ }
}
diff --git a/Services/BatteryService.qml b/Services/Hardware/BatteryService.qml
similarity index 98%
rename from Services/BatteryService.qml
rename to Services/Hardware/BatteryService.qml
index 3f942403..a655be64 100644
--- a/Services/BatteryService.qml
+++ b/Services/Hardware/BatteryService.qml
@@ -4,7 +4,7 @@ import Quickshell
import Quickshell.Io
import Quickshell.Services.UPower
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
@@ -62,7 +62,7 @@ Singleton {
} else {
BatteryService.initialSetter = true
ToastService.showNotice(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.uninstall-setup"))
- PanelService.getPanel("batteryPanel")?.toggle(this)
+ PanelService.getPanel("batteryPanel", screen)?.toggle(this)
uninstallerProcess.running = true
}
}
@@ -123,7 +123,7 @@ Singleton {
Settings.data.battery.chargingMode = BatteryService.chargingMode
} else if (exitCode === 2) {
ToastService.showWarning(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.initial-setup"))
- PanelService.getPanel("batteryPanel")?.toggle(this)
+ PanelService.getPanel("batteryPanel", null)?.toggle(this)
BatteryService.runInstaller()
} else {
ToastService.showError(I18n.tr("toast.battery-manager.title"), I18n.tr("toast.battery-manager.set-failed"))
diff --git a/Services/BrightnessService.qml b/Services/Hardware/BrightnessService.qml
similarity index 91%
rename from Services/BrightnessService.qml
rename to Services/Hardware/BrightnessService.qml
index fbaaf7b3..f47d9168 100644
--- a/Services/BrightnessService.qml
+++ b/Services/Hardware/BrightnessService.qml
@@ -21,7 +21,7 @@ Singleton {
function getAvailableMethods(): list {
var methods = []
- if (monitors.some(m => m.isDdc))
+ if (Settings.data.brightness.enableDdcSupport && monitors.some(m => m.isDdc))
methods.push("ddcutil")
if (monitors.some(m => !m.isDdc))
methods.push("internal")
@@ -47,11 +47,30 @@ Singleton {
Component.onCompleted: {
Logger.i("Brightness", "Service started")
+ if (Settings.data.brightness.enableDdcSupport) {
+ ddcProc.running = true
+ }
}
onMonitorsChanged: {
ddcMonitors = []
- ddcProc.running = true
+ if (Settings.data.brightness.enableDdcSupport) {
+ ddcProc.running = true
+ }
+ }
+
+ Connections {
+ target: Settings.data.brightness
+ function onEnableDdcSupportChanged() {
+ if (Settings.data.brightness.enableDdcSupport) {
+ // Re-detect DDC monitors when enabled
+ ddcMonitors = []
+ ddcProc.running = true
+ } else {
+ // Clear DDC monitors when disabled
+ ddcMonitors = []
+ }
+ }
}
Variants {
@@ -101,11 +120,21 @@ Singleton {
id: monitor
required property ShellScreen modelData
- readonly property bool isDdc: root.ddcMonitors.some(m => m.model === modelData.model)
+ readonly property bool isDdc: Settings.data.brightness.enableDdcSupport && root.ddcMonitors.some(m => m.model === modelData.model)
readonly property string busNum: root.ddcMonitors.find(m => m.model === modelData.model)?.busNum ?? ""
readonly property bool isAppleDisplay: root.appleDisplayPresent && modelData.model.startsWith("StudioDisplay")
readonly property string method: isAppleDisplay ? "apple" : (isDdc ? "ddcutil" : "internal")
+ // Check if brightness control is available for this monitor
+ readonly property bool brightnessControlAvailable: {
+ if (isAppleDisplay)
+ return true
+ if (isDdc)
+ return true
+ // For internal displays, check if we have a brightness path
+ return brightnessPath !== ""
+ }
+
property real brightness
property real lastBrightness: 0
property real queuedBrightness: NaN
diff --git a/Services/ClipboardService.qml b/Services/Keyboard/ClipboardService.qml
similarity index 100%
rename from Services/ClipboardService.qml
rename to Services/Keyboard/ClipboardService.qml
diff --git a/Services/KeyboardLayoutService.qml b/Services/Keyboard/KeyboardLayoutService.qml
similarity index 58%
rename from Services/KeyboardLayoutService.qml
rename to Services/Keyboard/KeyboardLayoutService.qml
index ed0b4f8b..a28ff993 100644
--- a/Services/KeyboardLayoutService.qml
+++ b/Services/Keyboard/KeyboardLayoutService.qml
@@ -3,9 +3,8 @@ pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import qs.Commons
-import qs.Services
+import qs.Services.Compositor
Singleton {
id: root
@@ -187,7 +186,7 @@ Singleton {
}
// Check for exact matches or partial matches in language map from Commons
- const entries = Object.entries(KeyboardLayout.languageMap)
+ const entries = Object.entries(languageMap)
for (var i = 0; i < entries.length; i++) {
const lang = entries[i][0]
const code = entries[i][1]
@@ -230,4 +229,202 @@ Singleton {
}
}
}
+
+ // Comprehensive language name to ISO code mapping
+ property var languageMap: {
+ "english"// English variants
+ : "us",
+ "american": "us",
+ "united states": "us",
+ "us english": "us",
+ "british": "gb",
+ "uk": "ua",
+ "united kingdom"// FIXED: Ukrainian language code should map to Ukraine
+ : "gb",
+ "english (uk)": "gb",
+ "canadian": "ca",
+ "canada": "ca",
+ "canadian english": "ca",
+ "australian": "au",
+ "australia": "au",
+ "swedish"// Nordic countries
+ : "se",
+ "svenska": "se",
+ "sweden": "se",
+ "norwegian": "no",
+ "norsk": "no",
+ "norway": "no",
+ "danish": "dk",
+ "dansk": "dk",
+ "denmark": "dk",
+ "finnish": "fi",
+ "suomi": "fi",
+ "finland": "fi",
+ "icelandic": "is",
+ "íslenska": "is",
+ "iceland": "is",
+ "german"// Western/Central European Germanic
+ : "de",
+ "deutsch": "de",
+ "germany": "de",
+ "austrian": "at",
+ "austria": "at",
+ "österreich": "at",
+ "swiss": "ch",
+ "switzerland": "ch",
+ "schweiz": "ch",
+ "suisse": "ch",
+ "dutch": "nl",
+ "nederlands": "nl",
+ "netherlands": "nl",
+ "holland": "nl",
+ "belgian": "be",
+ "belgium": "be",
+ "belgië": "be",
+ "belgique": "be",
+ "french"// Romance languages (Western/Southern Europe)
+ : "fr",
+ "français": "fr",
+ "france": "fr",
+ "canadian french": "ca",
+ "spanish": "es",
+ "español": "es",
+ "spain": "es",
+ "castilian": "es",
+ "italian": "it",
+ "italiano": "it",
+ "italy": "it",
+ "portuguese": "pt",
+ "português": "pt",
+ "portugal": "pt",
+ "catalan": "ad",
+ "català": "ad",
+ "andorra": "ad",
+ "romanian"// Eastern European Romance
+ : "ro",
+ "română": "ro",
+ "romania": "ro",
+ "russian"// Slavic languages (Eastern Europe)
+ : "ru",
+ "русский": "ru",
+ "russia": "ru",
+ "polish": "pl",
+ "polski": "pl",
+ "poland": "pl",
+ "czech": "cz",
+ "čeština": "cz",
+ "czech republic": "cz",
+ "slovak": "sk",
+ "slovenčina": "sk",
+ "slovakia": "sk",
+ "uk": "ua",
+ "ukrainian"// Ukrainian language code
+ : "ua",
+ "українська": "ua",
+ "ukraine": "ua",
+ "bulgarian": "bg",
+ "български": "bg",
+ "bulgaria": "bg",
+ "serbian": "rs",
+ "srpski": "rs",
+ "serbia": "rs",
+ "croatian": "hr",
+ "hrvatski": "hr",
+ "croatia": "hr",
+ "slovenian": "si",
+ "slovenščina": "si",
+ "slovenia": "si",
+ "bosnian": "ba",
+ "bosanski": "ba",
+ "bosnia": "ba",
+ "macedonian": "mk",
+ "македонски": "mk",
+ "macedonia": "mk",
+ "irish"// Celtic languages (Western Europe)
+ : "ie",
+ "gaeilge": "ie",
+ "ireland": "ie",
+ "welsh": "gb",
+ "cymraeg": "gb",
+ "wales": "gb",
+ "scottish": "gb",
+ "gàidhlig": "gb",
+ "scotland": "gb",
+ "estonian"// Baltic languages (Northern Europe)
+ : "ee",
+ "eesti": "ee",
+ "estonia": "ee",
+ "latvian": "lv",
+ "latviešu": "lv",
+ "latvia": "lv",
+ "lithuanian": "lt",
+ "lietuvių": "lt",
+ "lithuania": "lt",
+ "hungarian"// Other European languages
+ : "hu",
+ "magyar": "hu",
+ "hungary": "hu",
+ "greek": "gr",
+ "ελληνικά": "gr",
+ "greece": "gr",
+ "albanian": "al",
+ "shqip": "al",
+ "albania": "al",
+ "maltese": "mt",
+ "malti": "mt",
+ "malta": "mt",
+ "turkish"// West/Southwest Asian languages
+ : "tr",
+ "türkçe": "tr",
+ "turkey": "tr",
+ "arabic": "ar",
+ "العربية": "ar",
+ "arab": "ar",
+ "hebrew": "il",
+ "עברית": "il",
+ "israel": "il",
+ "brazilian"// South American languages
+ : "br",
+ "brazilian portuguese": "br",
+ "brasil": "br",
+ "brazil": "br",
+ "japanese"// East Asian languages
+ : "jp",
+ "日本語": "jp",
+ "japan": "jp",
+ "korean": "kr",
+ "한국어": "kr",
+ "korea": "kr",
+ "south korea": "kr",
+ "chinese": "cn",
+ "中文": "cn",
+ "china": "cn",
+ "simplified chinese": "cn",
+ "traditional chinese": "tw",
+ "taiwan": "tw",
+ "繁體中文": "tw",
+ "thai"// Southeast Asian languages
+ : "th",
+ "ไทย": "th",
+ "thailand": "th",
+ "vietnamese": "vn",
+ "tiếng việt": "vn",
+ "vietnam": "vn",
+ "hindi"// South Asian languages
+ : "in",
+ "हिन्दी": "in",
+ "india": "in",
+ "afrikaans"// African languages
+ : "za",
+ "south africa": "za",
+ "south african": "za",
+ "qwerty"// Layout variants
+ : "us",
+ "dvorak": "us",
+ "colemak": "us",
+ "workman": "us",
+ "azerty": "fr",
+ "norman": "fr",
+ "qwertz": "de"
+ }
}
diff --git a/Services/LockKeysService.qml b/Services/Keyboard/LockKeysService.qml
similarity index 100%
rename from Services/LockKeysService.qml
rename to Services/Keyboard/LockKeysService.qml
diff --git a/Services/CalendarService.qml b/Services/Location/CalendarService.qml
similarity index 99%
rename from Services/CalendarService.qml
rename to Services/Location/CalendarService.qml
index 92dfa9f7..75a9a273 100644
--- a/Services/CalendarService.qml
+++ b/Services/Location/CalendarService.qml
@@ -48,7 +48,7 @@ Singleton {
}
Component.onCompleted: {
- Logger.i("Calendar", "Service initialized")
+ Logger.i("Calendar", "Service started")
loadFromCache()
checkAvailability()
}
diff --git a/Services/DarkModeService.qml b/Services/Location/DarkModeService.qml
similarity index 99%
rename from Services/DarkModeService.qml
rename to Services/Location/DarkModeService.qml
index ad3eef00..e7c3315c 100644
--- a/Services/DarkModeService.qml
+++ b/Services/Location/DarkModeService.qml
@@ -3,7 +3,6 @@ pragma Singleton
import QtQuick
import Quickshell
import qs.Commons
-import qs.Services
Singleton {
id: root
diff --git a/Services/LocationService.qml b/Services/Location/LocationService.qml
similarity index 99%
rename from Services/LocationService.qml
rename to Services/Location/LocationService.qml
index 7c2df5d4..775c9616 100644
--- a/Services/LocationService.qml
+++ b/Services/Location/LocationService.qml
@@ -4,7 +4,6 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
// Weather logic and caching with stable UI properties
Singleton {
diff --git a/Services/NightLightService.qml b/Services/Location/NightLightService.qml
similarity index 99%
rename from Services/NightLightService.qml
rename to Services/Location/NightLightService.qml
index 864ae87a..e25c828a 100644
--- a/Services/NightLightService.qml
+++ b/Services/Location/NightLightService.qml
@@ -4,7 +4,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
diff --git a/Services/MatugenTemplates.qml b/Services/MatugenTemplates.qml
deleted file mode 100644
index 606fa1fb..00000000
--- a/Services/MatugenTemplates.qml
+++ /dev/null
@@ -1,299 +0,0 @@
-pragma Singleton
-
-import QtQuick
-import Quickshell
-import Quickshell.Io
-import qs.Commons
-
-// Central place to define which templates we generate and where they write.
-// Users can extend it by dropping additional templates into:
-// - Assets/MatugenTemplates/ (built-in templates)
-// - ~/.config/matugen/ (user-defined templates when enableUserTemplates is true)
-// User templates are automatically executed after the main matugen command
-// if enableUserTemplates is enabled in settings.
-Singleton {
- id: root
-
- // Build the base TOML using current settings
- function buildConfigToml() {
- var lines = []
- var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
-
- if (Settings.data.colorSchemes.useWallpaperColors) {
- addWallpaperBasedTemplates(lines, mode)
- }
-
- addApplicationTemplates(lines, mode)
-
- if (lines.length > 0) {
- const config = ["[config]"].concat(lines)
- return config.join("\n") + "\n"
- }
-
- return ""
- }
-
- // Build user templates TOML for ~/.config/noctalia/user-templates.toml
- function buildUserTemplatesToml() {
- var lines = []
- lines.push("[config]")
- lines.push("")
- lines.push("# User-defined templates")
- lines.push("# Add your custom templates below")
- lines.push("# Example:")
- lines.push("# [templates.myapp]")
- lines.push("# input_path = \"~/.config/noctalia/templates/myapp.css\"")
- lines.push("# output_path = \"~/.config/myapp/theme.css\"")
- lines.push("# post_hook = \"myapp --reload-theme\"")
- lines.push("")
- lines.push("# Remove this section and add your own templates")
- lines.push("#[templates.placeholder]")
- lines.push("#input_path = \"" + Quickshell.shellDir + "/Assets/MatugenTemplates/noctalia.json\"")
- lines.push("#output_path = \"" + Settings.cacheDir + "placeholder.json\"")
- lines.push("")
-
- return lines.join("\n") + "\n"
- }
-
- // Write user templates TOML to ~/.config/noctalia/user-templates.toml
- function writeUserTemplatesToml() {
- var userConfigPath = Settings.configDir + "user-templates.toml"
-
- // Check if file already exists
- fileCheckProcess.command = ["test", "-f", userConfigPath]
- fileCheckProcess.running = true
- }
-
- function doWriteUserTemplatesToml() {
- var userConfigPath = Settings.configDir + "user-templates.toml"
- var configContent = buildUserTemplatesToml()
-
- // Ensure directory exists (should already exist but just in case)
- Quickshell.execDetached(["mkdir", "-p", Settings.configDir])
-
- // Write the config file
- Quickshell.execDetached(["sh", "-c", `echo '${configContent.replace(/'/g, "'\\''")}' > '${userConfigPath}'`])
-
- Logger.d("MatugenTemplates", "User templates config written to:", userConfigPath)
- }
-
- // --------------------------------
- function addWallpaperBasedTemplates(lines, mode) {
- // Noctalia colors
- lines.push("[templates.noctalia]")
- lines.push('input_path = "' + Quickshell.shellDir + '/Assets/MatugenTemplates/noctalia.json"')
- lines.push('output_path = "' + Settings.configDir + 'colors.json"')
-
- // Terminal templates (only for wallpaper-based colors)
- addTerminalTemplates(lines)
- }
-
- // --------------------------------
- function addTerminalTemplates(lines) {
- var terminals = [{
- "name": "foot",
- "path": "Terminal/foot",
- "output": "~/.config/foot/themes/noctalia"
- }, {
- "name": "ghostty",
- "path": "Terminal/ghostty",
- "output": "~/.config/ghostty/themes/noctalia",
- "post_hook": "pkill -SIGUSR2 ghostty"
- }, {
- "name": "kitty",
- "path": "Terminal/kitty.conf",
- "output": "~/.config/kitty/themes/noctalia.conf"
- }, {
- "name": "alacritty",
- "path": "Terminal/alacritty.toml",
- "output": "~/.config/alacritty/themes/noctalia.toml"
- }]
-
- terminals.forEach(function (terminal) {
- if (Settings.data.templates[terminal.name]) {
- lines.push("\n[templates." + terminal.name + "]")
- lines.push('input_path = "' + Quickshell.shellDir + '/Assets/MatugenTemplates/' + terminal.path + '"')
- lines.push('output_path = "' + terminal.output + '"')
- lines.push('post_hook = "' + AppThemeService.colorsApplyScript + " " + terminal.name + '"')
- }
- })
- }
-
- // Applications configuration
- readonly property var applications: [{
- "name": "gtk",
- "templates": [{
- "version": "gtk3",
- "output": "~/.config/gtk-3.0/gtk.css"
- }, {
- "version": "gtk4",
- "output": "~/.config/gtk-4.0/gtk.css"
- }],
- "input": "gtk.css",
- "postHook": "gsettings set org.gnome.desktop.interface color-scheme prefer-{mode}"
- }, {
- "name": "qt",
- "templates": [{
- "version": "qt5",
- "output": "~/.config/qt5ct/colors/noctalia.conf"
- }, {
- "version": "qt6",
- "output": "~/.config/qt6ct/colors/noctalia.conf"
- }],
- "input": "qtct.conf"
- }, {
- "name": "kcolorscheme",
- "templates": [{
- "version": "kcolorscheme",
- "output": "~/.local/share/color-schemes/noctalia.colors"
- }],
- "input": "kcolorscheme.colors"
- }, {
- "name": "fuzzel",
- "templates": [{
- "version": "fuzzel",
- "output": "~/.config/fuzzel/themes/noctalia"
- }],
- "input": "fuzzel.conf",
- "postHook": AppThemeService.colorsApplyScript + " fuzzel"
- }, {
- "name": "vicinae",
- "templates": [{
- "version": "vicinae",
- "output": "~/.local/share/vicinae/themes/matugen.toml"
- }],
- "input": "vicinae.toml",
- "postHook": "cp -n " + Quickshell.shellDir + "/Assets/noctalia.svg ~/.local/share/vicinae/themes/noctalia.svg && " + AppThemeService.colorsApplyScript + " vicinae"
- }, {
- "name": "walker",
- "templates": [{
- "version": "walker",
- "output": "~/.config/walker/themes/noctalia/style.css"
- }],
- "input": "walker.css"
- }, {
- "name": "pywalfox",
- "templates": [{
- "version": "pywalfox",
- "output": "~/.cache/wal/colors.json"
- }],
- "input": "pywalfox.json",
- "postHook": AppThemeService.colorsApplyScript + " pywalfox"
- }, {
- "name": "discord_vesktop",
- "templates": [{
- "version": "discord_vesktop",
- "output": "~/.config/vesktop/themes/noctalia.theme.css"
- }],
- "input": "vesktop.css"
- }, {
- "name": "discord_webcord",
- "templates": [{
- "version": "discord_webcord",
- "output": "~/.config/webcord/themes/noctalia.theme.css"
- }],
- "input": "vesktop.css"
- }, {
- "name": "discord_armcord",
- "templates": [{
- "version": "discord_armcord",
- "output": "~/.config/armcord/themes/noctalia.theme.css"
- }],
- "input": "vesktop.css"
- }, {
- "name": "discord_equibop",
- "templates": [{
- "version": "discord_equibop",
- "output": "~/.config/equibop/themes/noctalia.theme.css"
- }],
- "input": "vesktop.css"
- }, {
- "name": "discord_lightcord",
- "templates": [{
- "version": "discord_lightcord",
- "output": "~/.config/lightcord/themes/noctalia.theme.css"
- }],
- "input": "vesktop.css"
- }, {
- "name": "discord_dorion",
- "templates": [{
- "version": "discord_dorion",
- "output": "~/.config/dorion/themes/noctalia.theme.css"
- }],
- "input": "vesktop.css"
- }, {
- "name": "discord_vencord",
- "templates": [{
- "version": "discord_vencord",
- "output": "~/.config/discord/themes/noctalia.theme.css"
- }],
- "input": "vesktop.css",
- "requiresThemesFolder": true
- }, {
- "name": "code",
- "templates": [{
- "version": "code",
- "output": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
- }],
- "input": "code.json"
- }]
-
- // --------------------------------
- function addApplicationTemplates(lines, mode) {
- applications.forEach(function (app) {
- // Check if app has a condition and if it's met
- var shouldInclude = true
- if (app.condition !== undefined) {
- shouldInclude = app.condition
- }
-
- if (Settings.data.templates[app.name] && shouldInclude) {
- app.templates.forEach(function (template) {
- lines.push("\n[templates." + template.version + "]")
- lines.push('input_path = "' + Quickshell.shellDir + '/Assets/MatugenTemplates/' + app.input + '"')
- lines.push('output_path = "' + template.output + '"')
- if (app.postHook) {
- var postHook = app.postHook.replace("{mode}", mode)
- lines.push('post_hook = "' + postHook + '"')
- }
- })
- }
- })
- }
-
- // Extract Discord clients from applications array
- readonly property var discordClients: {
- var clients = []
- for (var i = 0; i < applications.length; i++) {
- var app = applications[i]
- if (app.name && app.name.startsWith("discord_")) {
- var clientName = app.name.replace("discord_", "")
- var themePath = app.templates[0].output
- var configPath = themePath.replace("/themes/noctalia.theme.css", "")
- clients.push({
- "name": clientName,
- "configPath": configPath,
- "themePath": themePath,
- "requiresThemesFolder": app.requiresThemesFolder || false
- })
- }
- }
- return clients
- }
-
- // Process for checking if user templates file exists
- Process {
- id: fileCheckProcess
- running: false
-
- onExited: function (exitCode) {
- if (exitCode === 0) {
- // File exists, skip creation
- Logger.d("MatugenTemplates", "User templates config already exists, skipping creation")
- } else {
- // File doesn't exist, create it
- doWriteUserTemplatesToml()
- }
- }
- }
-}
diff --git a/Services/AudioService.qml b/Services/Media/AudioService.qml
similarity index 100%
rename from Services/AudioService.qml
rename to Services/Media/AudioService.qml
diff --git a/Services/Media/CavaService.qml b/Services/Media/CavaService.qml
new file mode 100644
index 00000000..8e58c92e
--- /dev/null
+++ b/Services/Media/CavaService.qml
@@ -0,0 +1,151 @@
+pragma Singleton
+
+import QtQuick
+import Quickshell
+import Quickshell.Io
+import qs.Commons
+import qs.Services.UI
+
+Singleton {
+ id: root
+
+
+ /**
+ * Cava runs if:
+ * - Bar has an audio visualizer
+ * - LockScreen is opened
+ * - A control center is open
+ */
+ property bool shouldRun: BarService.hasAudioVisualizer || PanelService.lockScreen?.active || (PanelService.openedPanel && PanelService.openedPanel.objectName.startsWith("controlCenterPanel"))
+
+ property var values: Array(barsCount).fill(0)
+ property int barsCount: 32
+
+ // Idle detection to reduce GPU usage when there's no audio
+ property bool isIdle: true
+ property int idleFrameCount: 0
+ readonly property int idleThreshold: 30 // Frames of silence before considered idle (0.5s at 60fps)
+
+ // Frame skipping for GPU optimization - skip every Nth frame when playing
+ property int frameCounter: 0
+ readonly property int frameSkip: 1 // Update every N frames (1 = no skip, 2 = every other frame, etc)
+
+ // Change detection - only update if values changed significantly
+ property var lastValues: Array(barsCount).fill(0)
+ readonly property real changeThreshold: 0.005 // Minimum change (1%) to trigger update
+ property var config: ({
+ "general": {
+ "bars": barsCount,
+ "framerate": Settings.data.audio.cavaFrameRate,
+ "autosens": 1,
+ "sensitivity": 100,
+ "lower_cutoff_freq": 50,
+ "higher_cutoff_freq": 12000
+ },
+ "smoothing": {
+ "monstercat": 1,
+ "noise_reduction"// Enable monstercat smoothing for less jittery animation
+ : 77
+ },
+ "output": {
+ "method": "raw",
+ "data_format": "ascii",
+ "ascii_max_range": 100,
+ "bit_format": "8bit",
+ "channels": "mono",
+ "mono_option": "average"
+ }
+ })
+
+ Process {
+ id: process
+ stdinEnabled: true
+ running: root.shouldRun
+ command: ["cava", "-p", "/dev/stdin"]
+ onRunningChanged: {
+ Logger.d("Cava", "Process running:", running)
+ }
+ onExited: {
+ Logger.i("Cava", "Process exited")
+ stdinEnabled = true
+ values = Array(barsCount).fill(0)
+ }
+ onStarted: {
+ Logger.i("Cava", "Process started")
+ for (const k in config) {
+ if (typeof config[k] !== "object") {
+ write(k + "=" + config[k] + "\n")
+ continue
+ }
+ write("[" + k + "]\n")
+ const obj = config[k]
+ for (const k2 in obj) {
+ write(k2 + "=" + obj[k2] + "\n")
+ }
+ }
+ stdinEnabled = false
+ values = Array(barsCount).fill(0)
+ }
+ stdout: SplitParser {
+ onRead: data => {
+ const newValues = data.slice(0, -1).split(";").map(v => parseInt(v, 10) / 100)
+
+ // Check if all values are effectively zero (< 0.01)
+ const allZero = newValues.every(v => v < 0.01)
+
+ if (allZero) {
+ root.idleFrameCount++
+ if (root.idleFrameCount >= root.idleThreshold) {
+ // We're idle - stop updating values to save GPU
+ if (!root.isIdle) {
+ root.isIdle = true
+ // Set all values to 0 one final time
+ root.values = Array(root.barsCount).fill(0)
+ root.lastValues = Array(root.barsCount).fill(0)
+ Logger.d("Cava", "Idle detected - stopped rendering")
+ }
+ // Don't update values while idle
+ return
+ }
+ } else {
+ // Audio detected - resume updates
+ root.idleFrameCount = 0
+ if (root.isIdle) {
+ root.isIdle = false
+ Logger.d("Cava", "Audio detected - resumed rendering")
+ }
+ }
+
+ // Frame skipping optimization - skip frames to reduce GPU load
+ root.frameCounter++
+ if (root.frameSkip > 1 && root.frameCounter % root.frameSkip !== 0) {
+ // Skip this frame
+ return
+ }
+
+ // Change detection - only update if values changed significantly
+ let hasSignificantChange = false
+ for (var i = 0; i < newValues.length; i++) {
+ const delta = Math.abs(newValues[i] - root.lastValues[i])
+ if (delta > root.changeThreshold) {
+ hasSignificantChange = true
+ break
+ }
+ }
+
+ // Update values only if there's a significant change
+ if (hasSignificantChange) {
+ root.lastValues = newValues.slice() // Copy array
+ root.values = newValues
+ }
+ }
+ }
+ stderr: StdioCollector {
+ onStreamFinished: {
+ if (text.trim()) {
+ Logger.w("Cava", "Error", text)
+ }
+ }
+ }
+ }
+}
diff --git a/Services/MediaService.qml b/Services/Media/MediaService.qml
similarity index 98%
rename from Services/MediaService.qml
rename to Services/Media/MediaService.qml
index 505a945b..be8bab1e 100644
--- a/Services/MediaService.qml
+++ b/Services/Media/MediaService.qml
@@ -4,7 +4,6 @@ import QtQuick
import Quickshell
import Quickshell.Services.Mpris
import qs.Commons
-import qs.Services
Singleton {
id: root
@@ -304,7 +303,7 @@ Singleton {
repeat: true
running: true
onTriggered: {
- Logger.d("MediaService", "playerStateMonitor triggered. autoSwitchingPaused: " + root.autoSwitchingPaused)
+ //Logger.d("MediaService", "playerStateMonitor triggered. autoSwitchingPaused: " + root.autoSwitchingPaused)
if (autoSwitchingPaused)
return
// Only update if we don't have a playing player or if current player is paused
diff --git a/Services/ScreenRecorderService.qml b/Services/Media/ScreenRecorderService.qml
similarity index 93%
rename from Services/ScreenRecorderService.qml
rename to Services/Media/ScreenRecorderService.qml
index 15d41647..7338ccbb 100644
--- a/Services/ScreenRecorderService.qml
+++ b/Services/Media/ScreenRecorderService.qml
@@ -4,7 +4,8 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.System
+import qs.Services.UI
Singleton {
id: root
@@ -85,7 +86,7 @@ Singleton {
return
}
- ToastService.showNotice(I18n.tr("toast.recording.stopping"), outputPath, 2000, "settings-screen-recorder")
+ ToastService.showNotice(I18n.tr("toast.recording.stopping"), outputPath, "settings-screen-recorder")
Quickshell.execDetached(["sh", "-c", "pkill -SIGINT -f 'gpu-screen-recorder' || pkill -SIGINT -f 'com.dec05eba.gpu_screen_recorder'"])
@@ -113,7 +114,7 @@ Singleton {
// Check if gpu-screen-recorder is not installed
const stdout = String(recorderProcess.stdout.text || "").trim()
if (stdout === "GPU_SCREEN_RECORDER_NOT_INSTALLED") {
- ToastService.showError(I18n.tr("toast.recording.not-installed"), I18n.tr("toast.recording.not-installed-desc"), 7000)
+ ToastService.showError(I18n.tr("toast.recording.not-installed"), I18n.tr("toast.recording.not-installed-desc"))
return
}
@@ -121,9 +122,9 @@ Singleton {
if (exitCode !== 0) {
const err = String(recorderProcess.stderr.text || "").trim()
if (err.length > 0)
- ToastService.showError(I18n.tr("toast.recording.failed-start"), err, 7000)
+ ToastService.showError(I18n.tr("toast.recording.failed-start"), err)
else
- ToastService.showError(I18n.tr("toast.recording.failed-start"), I18n.tr("toast.recording.failed-gpu"), 7000)
+ ToastService.showError(I18n.tr("toast.recording.failed-start"), I18n.tr("toast.recording.failed-gpu"))
}
} else if (isRecording) {
// Process ended normally while recording
@@ -131,13 +132,13 @@ Singleton {
monitorTimer.running = false
// Consider successful save if exitCode == 0
if (exitCode === 0) {
- ToastService.showNotice(I18n.tr("toast.recording.saved"), outputPath, 5000, "settings-screen-recorder")
+ ToastService.showNotice(I18n.tr("toast.recording.saved"), outputPath, "settings-screen-recorder")
} else {
const err2 = String(recorderProcess.stderr.text || "").trim()
if (err2.length > 0)
- ToastService.showError(I18n.tr("toast.recording.failed-start"), err2, 7000)
+ ToastService.showError(I18n.tr("toast.recording.failed-start"), err2)
else
- ToastService.showError(I18n.tr("toast.recording.failed-start"), I18n.tr("toast.recording.failed-general"), 7000)
+ ToastService.showError(I18n.tr("toast.recording.failed-start"), I18n.tr("toast.recording.failed-general"))
}
}
}
@@ -153,7 +154,7 @@ Singleton {
} else {
isPending = false
hasActiveRecording = false
- ToastService.showError(I18n.tr("toast.recording.no-portals"), I18n.tr("toast.recording.no-portals-desc"), 8000)
+ ToastService.showError(I18n.tr("toast.recording.no-portals"), I18n.tr("toast.recording.no-portals-desc"))
}
}
}
@@ -171,7 +172,6 @@ Singleton {
hasActiveRecording = true
monitorTimer.running = true
// Don't show a toast when recording starts to avoid having the toast in every video.
- //ToastService.showNotice("Recording started", outputPath, 4000)
} else if (isPending) {
// Process not running anymore - was cancelled or failed
isPending = false
diff --git a/Services/BluetoothService.qml b/Services/Networking/BluetoothService.qml
similarity index 99%
rename from Services/BluetoothService.qml
rename to Services/Networking/BluetoothService.qml
index 245f0aaa..4a7f8745 100644
--- a/Services/BluetoothService.qml
+++ b/Services/Networking/BluetoothService.qml
@@ -5,7 +5,7 @@ import Quickshell
import Quickshell.Bluetooth
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
@@ -44,7 +44,7 @@ Singleton {
}
function init() {
- Logger.i("Bluetooth", "Service initialized")
+ Logger.i("Bluetooth", "Service started")
}
Timer {
diff --git a/Services/NetworkService.qml b/Services/Networking/NetworkService.qml
similarity index 98%
rename from Services/NetworkService.qml
rename to Services/Networking/NetworkService.qml
index b18b3c15..b9bbe84a 100644
--- a/Services/NetworkService.qml
+++ b/Services/Networking/NetworkService.qml
@@ -4,7 +4,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
@@ -67,7 +67,7 @@ Singleton {
}
Component.onCompleted: {
- Logger.i("Network", "Service initialized")
+ Logger.i("Network", "Service started")
syncWifiState()
scan()
}
@@ -222,8 +222,8 @@ Singleton {
}
// Helper functions
- function signalIcon(signal) {
- if (root.internetConnectivity === "limited" || root.internetConnectivity === "portal")
+ function signalIcon(signal, isConnected = false) {
+ if (isConnected && (root.internetConnectivity === "limited" || root.internetConnectivity === "portal"))
return "world-off"
if (signal >= 80)
return "wifi"
@@ -313,7 +313,7 @@ Singleton {
Logger.i("Network", "Internet connectivity:", result)
if (result === "limited" || result === "portal") {
- ToastService.showWarning(cachedLastConnected, "toast.internet.limited")
+ ToastService.showWarning(cachedLastConnected, I18n.tr("toast.internet.limited"))
} else {
scan()
}
@@ -516,9 +516,9 @@ Singleton {
return cmd
}
}
- environment: {
- "LC_ALL": "C"
- }
+ environment: ({
+ "LC_ALL": "C"
+ })
stdout: StdioCollector {
onStreamFinished: {
diff --git a/Services/GitHubService.qml b/Services/Noctalia/GitHubService.qml
similarity index 99%
rename from Services/GitHubService.qml
rename to Services/Noctalia/GitHubService.qml
index 1a2efc8a..6f7c1bf4 100644
--- a/Services/GitHubService.qml
+++ b/Services/Noctalia/GitHubService.qml
@@ -4,7 +4,6 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
// GitHub API logic and caching
Singleton {
diff --git a/Services/UpdateService.qml b/Services/Noctalia/UpdateService.qml
similarity index 93%
rename from Services/UpdateService.qml
rename to Services/Noctalia/UpdateService.qml
index 5de3a9e5..5cbcba5e 100644
--- a/Services/UpdateService.qml
+++ b/Services/Noctalia/UpdateService.qml
@@ -8,7 +8,7 @@ Singleton {
id: root
// Public properties
- property string baseVersion: "2.21.1"
+ property string baseVersion: "3.0.5"
property bool isDevelopment: true
property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}`
diff --git a/Services/IdleInhibitorService.qml b/Services/Power/IdleInhibitorService.qml
similarity index 98%
rename from Services/IdleInhibitorService.qml
rename to Services/Power/IdleInhibitorService.qml
index aa25fb65..80aaba3f 100644
--- a/Services/IdleInhibitorService.qml
+++ b/Services/Power/IdleInhibitorService.qml
@@ -4,7 +4,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
@@ -17,7 +17,7 @@ Singleton {
property string strategy: "systemd" // "systemd", "wayland", or "auto"
function init() {
- Logger.d("IdleInhibitor", "Service started")
+ Logger.i("IdleInhibitor", "Service started")
detectStrategy()
}
diff --git a/Services/PowerProfileService.qml b/Services/Power/PowerProfileService.qml
similarity index 75%
rename from Services/PowerProfileService.qml
rename to Services/Power/PowerProfileService.qml
index 2417b81b..f086e9c1 100644
--- a/Services/PowerProfileService.qml
+++ b/Services/Power/PowerProfileService.qml
@@ -4,7 +4,7 @@ import QtQuick
import Quickshell
import Quickshell.Services.UPower
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
@@ -13,6 +13,9 @@ Singleton {
readonly property bool available: powerProfiles && powerProfiles.hasPerformanceProfile
property int profile: powerProfiles ? powerProfiles.profile : PowerProfile.Balanced
+ // Not a power profile but a volatile property to quickly disable shadows, animations, etc..
+ property bool noctaliaPerformanceMode: false
+
function getName(p) {
if (!available)
return "Unknown"
@@ -94,4 +97,24 @@ Singleton {
}
}
}
+
+ // Noctalia Performance Mode
+ // - Turning shadow off
+ // - Turning animation off
+ // - Do Not Disturb
+ function toggleNoctaliaPerformance() {
+ noctaliaPerformanceMode = !noctaliaPerformanceMode
+ }
+
+ function setNoctaliaPerformance(value) {
+ noctaliaPerformanceMode = value
+ }
+
+ onNoctaliaPerformanceModeChanged: {
+ if (noctaliaPerformanceMode) {
+ ToastService.showNotice(I18n.tr("toast.noctalia-performance.label"), I18n.tr("toast.noctalia-performance.enabled"), "rocket")
+ } else {
+ ToastService.showNotice(I18n.tr("toast.noctalia-performance.label"), I18n.tr("toast.noctalia-performance.disabled"), "rocket-off")
+ }
+ }
}
diff --git a/Services/DistroService.qml b/Services/System/DistroService.qml
similarity index 100%
rename from Services/DistroService.qml
rename to Services/System/DistroService.qml
diff --git a/Services/FontService.qml b/Services/System/FontService.qml
similarity index 100%
rename from Services/FontService.qml
rename to Services/System/FontService.qml
diff --git a/Services/NotificationService.qml b/Services/System/NotificationService.qml
similarity index 56%
rename from Services/NotificationService.qml
rename to Services/System/NotificationService.qml
index 72277372..277938a1 100644
--- a/Services/NotificationService.qml
+++ b/Services/System/NotificationService.qml
@@ -6,8 +6,9 @@ import Quickshell
import Quickshell.Io
import Quickshell.Services.Notifications
import qs.Commons
-import qs.Services
-import "../Helpers/sha256.js" as Checksum
+import qs.Services.Power
+import qs.Services.UI
+import "../../Helpers/sha256.js" as Checksum
Singleton {
id: root
@@ -20,18 +21,18 @@ Singleton {
// State
property real lastSeenTs: 0
+ // Volatile property that doesn't persist to settings (similar to noctaliaPerformanceMode)
+ property bool doNotDisturb: false
// Models
property ListModel activeList: ListModel {}
property ListModel historyList: ListModel {}
// Internal state
- property var activeMap: ({})
+ property var activeNotifications: ({}) // Maps internal ID to {notification, watcher, metadata}
+ property var quickshellIdToInternalId: ({})
property var imageQueue: []
- // Performance optimization: Track notification metadata separately
- property var notificationMetadata: ({}) // Stores timestamp and duration for each notification
-
PanelWindow {
implicitHeight: 1
implicitWidth: 1
@@ -77,58 +78,207 @@ Singleton {
}
// Notification server
- NotificationServer {
- keepOnReload: false
- imageSupported: true
- actionsSupported: true
- onNotification: notification => handleNotification(notification)
+ property var notificationServerLoader: null
+
+ Component {
+ id: notificationServerComponent
+ NotificationServer {
+ keepOnReload: false
+ imageSupported: true
+ actionsSupported: true
+ onNotification: notification => handleNotification(notification)
+ }
+ }
+
+ Component {
+ id: notificationWatcherComponent
+ Connections {
+ property var targetNotification
+ property var targetDataId
+ target: targetNotification
+
+ function onSummaryChanged() {
+ updateNotificationFromObject(targetDataId)
+ }
+ function onBodyChanged() {
+ updateNotificationFromObject(targetDataId)
+ }
+ function onAppNameChanged() {
+ updateNotificationFromObject(targetDataId)
+ }
+ function onUrgencyChanged() {
+ updateNotificationFromObject(targetDataId)
+ }
+ function onAppIconChanged() {
+ updateNotificationFromObject(targetDataId)
+ }
+ function onImageChanged() {
+ updateNotificationFromObject(targetDataId)
+ }
+ function onActionsChanged() {
+ updateNotificationFromObject(targetDataId)
+ }
+ }
+ }
+
+ function updateNotificationServer() {
+ if (notificationServerLoader) {
+ notificationServerLoader.destroy()
+ notificationServerLoader = null
+ }
+
+ if (Settings.isLoaded && Settings.data.notifications.enabled !== false) {
+ notificationServerLoader = notificationServerComponent.createObject(root)
+ }
+ }
+
+ Component.onCompleted: {
+ if (Settings.isLoaded) {
+ updateNotificationServer()
+ }
+ }
+
+ Connections {
+ target: Settings
+ function onSettingsLoaded() {
+ updateNotificationServer()
+ }
+ function onSettingsSaved() {
+ updateNotificationServer()
+ }
+ }
+
+ // Helper function to generate content-based ID for deduplication
+ function getContentId(summary, body, appName) {
+ return Checksum.sha256(JSON.stringify({
+ "summary": summary || "",
+ "body": body || "",
+ "app": appName || ""
+ }))
}
// Main handler
function handleNotification(notification) {
+ const quickshellId = notification.id
const data = createData(notification)
addToHistory(data)
- if (Settings.data.notifications?.doNotDisturb)
+ if (root.doNotDisturb || PowerProfileService.noctaliaPerformanceMode)
return
- activeMap[data.id] = notification
+ // Check if this is a replacement notification
+ const existingInternalId = quickshellIdToInternalId[quickshellId]
+ if (existingInternalId && activeNotifications[existingInternalId]) {
+ updateExistingNotification(existingInternalId, notification, data)
+ return
+ }
+
+ // Check for duplicate content
+ const duplicateId = findDuplicateNotification(data)
+ if (duplicateId) {
+ removeNotification(duplicateId)
+ }
+
+ // Add new notification
+ addNewNotification(quickshellId, notification, data)
+ }
+
+ function updateExistingNotification(internalId, notification, data) {
+ const index = findNotificationIndex(internalId)
+ if (index < 0)
+ return
+
+ const existing = activeList.get(index)
+ const oldTimestamp = existing.timestamp
+ const oldProgress = existing.progress
+
+ // Update properties (keeping original timestamp and progress)
+ activeList.setProperty(index, "summary", data.summary)
+ activeList.setProperty(index, "body", data.body)
+ activeList.setProperty(index, "appName", data.appName)
+ activeList.setProperty(index, "urgency", data.urgency)
+ activeList.setProperty(index, "expireTimeout", data.expireTimeout)
+ activeList.setProperty(index, "originalImage", data.originalImage)
+ activeList.setProperty(index, "cachedImage", data.cachedImage)
+ activeList.setProperty(index, "actionsJson", data.actionsJson)
+ activeList.setProperty(index, "timestamp", oldTimestamp)
+ activeList.setProperty(index, "progress", oldProgress)
+
+ // Update stored notification object
+ const notifData = activeNotifications[internalId]
+ notifData.notification = notification
notification.tracked = true
- notification.closed.connect(() => removeActive(data.id))
+ notification.closed.connect(() => removeNotification(internalId))
- // Store metadata for efficient progress calculation
- const durations = [Settings.data.notifications?.lowUrgencyDuration * 1000 || 3000, Settings.data.notifications?.normalUrgencyDuration * 1000 || 8000, Settings.data.notifications?.criticalUrgencyDuration * 1000 || 15000]
+ // Update metadata
+ notifData.metadata.urgency = data.urgency
+ notifData.metadata.duration = calculateDuration(data)
+ }
- let expire = 0
- if (Settings.data.notifications?.respectExpireTimeout) {
- if (data.expireTimeout === 0) {
- expire = -1 // Never expire
- } else if (data.expireTimeout > 0) {
- expire = data.expireTimeout
- } else {
- expire = durations[data.urgency]
+ function addNewNotification(quickshellId, notification, data) {
+ // Map IDs
+ quickshellIdToInternalId[quickshellId] = data.id
+
+ // Create watcher
+ const watcher = notificationWatcherComponent.createObject(root, {
+ "targetNotification": notification,
+ "targetDataId": data.id
+ })
+
+ // Store notification data
+ activeNotifications[data.id] = {
+ "notification": notification,
+ "watcher": watcher,
+ "metadata": {
+ "timestamp": data.timestamp.getTime(),
+ "duration": calculateDuration(data),
+ "urgency": data.urgency,
+ "paused": false,
+ "pauseTime": 0
}
- } else {
- expire = durations[data.urgency]
}
- notificationMetadata[data.id] = {
- "timestamp": data.timestamp.getTime(),
- "duration": expire,
- "urgency": data.urgency,
- "paused": false,
- "pauseTime": 0
- }
+ notification.tracked = true
+ notification.closed.connect(() => removeNotification(data.id))
+ // Add to list
activeList.insert(0, data)
+
+ // Remove overflow
while (activeList.count > maxVisible) {
const last = activeList.get(activeList.count - 1)
- activeMap[last.id]?.dismiss()
+ activeNotifications[last.id]?.notification?.dismiss()
activeList.remove(activeList.count - 1)
- delete notificationMetadata[last.id]
+ cleanupNotification(last.id)
}
}
+ function findDuplicateNotification(data) {
+ const contentId = getContentId(data.summary, data.body, data.appName)
+
+ for (var i = 0; i < activeList.count; i++) {
+ const existing = activeList.get(i)
+ const existingContentId = getContentId(existing.summary, existing.body, existing.appName)
+ if (existingContentId === contentId) {
+ return existing.id
+ }
+ }
+ return null
+ }
+
+ function calculateDuration(data) {
+ const durations = [Settings.data.notifications?.lowUrgencyDuration * 1000 || 3000, Settings.data.notifications?.normalUrgencyDuration * 1000 || 8000, Settings.data.notifications?.criticalUrgencyDuration * 1000 || 15000]
+
+ if (Settings.data.notifications?.respectExpireTimeout) {
+ if (data.expireTimeout === 0)
+ return -1 // Never expire
+ if (data.expireTimeout > 0)
+ return data.expireTimeout
+ }
+
+ return durations[data.urgency]
+ }
+
function createData(n) {
const time = new Date()
const id = Checksum.sha256(JSON.stringify({
@@ -144,7 +294,7 @@ Singleton {
return {
"id": id,
- "summary": (n.summary || ""),
+ "summary": n.summary || "",
"body": stripTags(n.body || ""),
"appName": getAppName(n.appName || n.desktopEntry || ""),
"urgency": n.urgency < 0 || n.urgency > 2 ? 1 : n.urgency,
@@ -160,6 +310,104 @@ Singleton {
}
}
+ function findNotificationIndex(internalId) {
+ for (var i = 0; i < activeList.count; i++) {
+ if (activeList.get(i).id === internalId) {
+ return i
+ }
+ }
+ return -1
+ }
+
+ function updateNotificationFromObject(internalId) {
+ const notifData = activeNotifications[internalId]
+ if (!notifData)
+ return
+
+ const index = findNotificationIndex(internalId)
+ if (index < 0)
+ return
+
+ const data = createData(notifData.notification)
+ const existing = activeList.get(index)
+
+ // Update properties (keeping timestamp and progress)
+ activeList.setProperty(index, "summary", data.summary)
+ activeList.setProperty(index, "body", data.body)
+ activeList.setProperty(index, "appName", data.appName)
+ activeList.setProperty(index, "urgency", data.urgency)
+ activeList.setProperty(index, "expireTimeout", data.expireTimeout)
+ activeList.setProperty(index, "originalImage", data.originalImage)
+ activeList.setProperty(index, "cachedImage", data.cachedImage)
+ activeList.setProperty(index, "actionsJson", data.actionsJson)
+
+ // Update metadata
+ notifData.metadata.urgency = data.urgency
+ notifData.metadata.duration = calculateDuration(data)
+ }
+
+ function removeNotification(id) {
+ const index = findNotificationIndex(id)
+ if (index >= 0) {
+ activeList.remove(index)
+ }
+ cleanupNotification(id)
+ }
+
+ function cleanupNotification(id) {
+ const notifData = activeNotifications[id]
+ if (notifData) {
+ notifData.watcher?.destroy()
+ delete activeNotifications[id]
+ }
+
+ // Clean up quickshell ID mapping
+ for (const qsId in quickshellIdToInternalId) {
+ if (quickshellIdToInternalId[qsId] === id) {
+ delete quickshellIdToInternalId[qsId]
+ break
+ }
+ }
+ }
+
+ // Progress updates
+ Timer {
+ interval: 50
+ repeat: true
+ running: activeList.count > 0
+ onTriggered: updateAllProgress()
+ }
+
+ function updateAllProgress() {
+ const now = Date.now()
+ const toRemove = []
+
+ for (var i = 0; i < activeList.count; i++) {
+ const notif = activeList.get(i)
+ const notifData = activeNotifications[notif.id]
+ if (!notifData)
+ continue
+
+ const meta = notifData.metadata
+ if (meta.duration === -1 || meta.paused)
+ continue
+
+ const elapsed = now - meta.timestamp
+ const progress = Math.max(1.0 - (elapsed / meta.duration), 0.0)
+
+ if (progress <= 0) {
+ toRemove.push(notif.id)
+ } else if (Math.abs(notif.progress - progress) > 0.005) {
+ activeList.setProperty(i, "progress", progress)
+ }
+ }
+
+ if (toRemove.length > 0) {
+ animateAndRemove(toRemove[0])
+ }
+ }
+
+ // Image handling
function queueImage(path, imageId) {
if (!path || !path.startsWith("image://") || !imageId)
return
@@ -196,64 +444,6 @@ Singleton {
}
}
- function removeActive(id) {
- for (var i = 0; i < activeList.count; i++) {
- if (activeList.get(i).id === id) {
- activeList.remove(i)
- delete activeMap[id]
- delete notificationMetadata[id]
- break
- }
- }
- }
-
- // Optimized batch progress update
- Timer {
- interval: 50 // Reduced from 10ms to 50ms (20 updates/sec instead of 100)
- repeat: true
- running: activeList.count > 0
- onTriggered: updateAllProgress()
- }
-
- function updateAllProgress() {
- const now = Date.now()
- const toRemove = []
- const updates = [] // Batch updates
-
- // Collect all updates first
- for (var i = 0; i < activeList.count; i++) {
- const notif = activeList.get(i)
- const meta = notificationMetadata[notif.id]
-
- if (!meta || meta.duration === -1 || meta.paused)
- continue
-
- // Skip infinite notifications
- const elapsed = now - meta.timestamp
- const progress = Math.max(1.0 - (elapsed / meta.duration), 0.0)
-
- if (progress <= 0) {
- toRemove.push(notif.id)
- } else if (Math.abs(notif.progress - progress) > 0.005) {
- // Only update if change is significant
- updates.push({
- "index": i,
- "progress": progress
- })
- }
- }
-
- // Apply batch updates
- for (const update of updates) {
- activeList.setProperty(update.index, "progress", update.progress)
- }
-
- // Remove expired notifications (one at a time to allow animation)
- if (toRemove.length > 0) {
- animateAndRemove(toRemove[0])
- }
- }
-
// History management
function addToHistory(data) {
historyList.insert(0, data)
@@ -285,7 +475,7 @@ Singleton {
}
}
- // Persistence - State (lastSeenTs, etc.)
+ // Persistence - State
FileView {
id: stateFileView
path: stateFile
@@ -362,7 +552,6 @@ Singleton {
try {
root.lastSeenTs = stateAdapter.lastSeenTs || 0
- // Migration: if state file is empty but settings has lastSeenTs, migrate it
if (root.lastSeenTs === 0 && Settings.data.notifications && Settings.data.notifications.lastSeenTs) {
root.lastSeenTs = Settings.data.notifications.lastSeenTs
saveState()
@@ -387,10 +576,10 @@ Singleton {
saveState()
}
+ // Utility functions
function getAppName(name) {
if (!name || name.trim() === "")
return "Unknown"
-
name = name.trim()
if (name.includes(".") && (name.startsWith("com.") || name.startsWith("org.") || name.startsWith("io.") || name.startsWith("net."))) {
@@ -401,9 +590,8 @@ Singleton {
appPart = parts[parts.length - 2] || parts[0]
}
- if (appPart) {
+ if (appPart)
name = appPart
- }
}
if (name.includes(".")) {
@@ -461,25 +649,25 @@ Singleton {
}
function pauseTimeout(id) {
- const meta = notificationMetadata[id]
- if (meta && !meta.paused) {
- meta.paused = true
- meta.pauseTime = Date.now()
+ const notifData = activeNotifications[id]
+ if (notifData && !notifData.metadata.paused) {
+ notifData.metadata.paused = true
+ notifData.metadata.pauseTime = Date.now()
}
}
function resumeTimeout(id) {
- const meta = notificationMetadata[id]
- if (meta && meta.paused) {
- meta.timestamp += Date.now() - meta.pauseTime
- meta.paused = false
+ const notifData = activeNotifications[id]
+ if (notifData && notifData.metadata.paused) {
+ notifData.metadata.timestamp += Date.now() - notifData.metadata.pauseTime
+ notifData.metadata.paused = false
}
}
// Public API
function dismissActiveNotification(id) {
- activeMap[id]?.dismiss()
- removeActive(id)
+ activeNotifications[id]?.notification?.dismiss()
+ removeNotification(id)
}
function dismissOldestActive() {
@@ -490,18 +678,21 @@ Singleton {
}
function dismissAllActive() {
- Object.values(activeMap).forEach(n => n.dismiss())
+ for (const id in activeNotifications) {
+ activeNotifications[id].notification?.dismiss()
+ activeNotifications[id].watcher?.destroy()
+ }
activeList.clear()
- activeMap = {}
- notificationMetadata = {}
+ activeNotifications = {}
+ quickshellIdToInternalId = {}
}
function invokeAction(id, actionId) {
- const n = activeMap[id]
- if (!n?.actions)
+ const notifData = activeNotifications[id]
+ if (!notifData?.notification?.actions)
return false
- for (const action of n.actions) {
+ for (const action of notifData.notification.actions) {
if (action.identifier === actionId && action.invoke) {
action.invoke()
return true
@@ -536,14 +727,10 @@ Singleton {
saveHistory()
}
- // Signals & connections
+ // Signals
signal animateAndRemove(string notificationId)
- Connections {
- target: Settings.data.notifications
- function onDoNotDisturbChanged() {
- const enabled = Settings.data.notifications.doNotDisturb
- ToastService.showNotice(enabled ? I18n.tr("toast.do-not-disturb.enabled") : I18n.tr("toast.do-not-disturb.disabled"), enabled ? I18n.tr("toast.do-not-disturb.enabled-desc") : I18n.tr("toast.do-not-disturb.disabled-desc"), enabled ? "bell-off" : "bell")
- }
+ onDoNotDisturbChanged: {
+ ToastService.showNotice(doNotDisturb ? I18n.tr("toast.do-not-disturb.enabled") : I18n.tr("toast.do-not-disturb.disabled"), doNotDisturb ? I18n.tr("toast.do-not-disturb.enabled-desc") : I18n.tr("toast.do-not-disturb.disabled-desc"), doNotDisturb ? "bell-off" : "bell")
}
}
diff --git a/Services/ProgramCheckerService.qml b/Services/System/ProgramCheckerService.qml
similarity index 89%
rename from Services/ProgramCheckerService.qml
rename to Services/System/ProgramCheckerService.qml
index 2bbf9939..10a2eff0 100644
--- a/Services/ProgramCheckerService.qml
+++ b/Services/System/ProgramCheckerService.qml
@@ -4,6 +4,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
+import qs.Services.Theming
// Service to check if various programs are available on the system
Singleton {
@@ -16,6 +17,7 @@ Singleton {
property bool kittyAvailable: false
property bool ghosttyAvailable: false
property bool footAvailable: false
+ property bool weztermAvailable: false
property bool fuzzelAvailable: false
property bool vicinaeAvailable: false
property bool walkerAvailable: false
@@ -23,6 +25,8 @@ Singleton {
property bool wlsunsetAvailable: false
property bool app2unitAvailable: false
property bool codeAvailable: false
+ property bool gnomeCalendarAvailable: false
+ property bool spicetifyAvailable: false
// Discord client auto-detection
property var availableDiscordClients: []
@@ -35,8 +39,8 @@ Singleton {
// Build shell script to check each client
var scriptParts = ["available_clients=\"\";"]
- for (var i = 0; i < MatugenTemplates.discordClients.length; i++) {
- var client = MatugenTemplates.discordClients[i]
+ for (var i = 0; i < TemplateRegistry.discordClients.length; i++) {
+ var client = TemplateRegistry.discordClients[i]
var clientName = client.name
// Check if this client requires themes folder to exist
@@ -71,8 +75,8 @@ Singleton {
// Build list of available clients
for (var i = 0; i < detectedClients.length; i++) {
var clientName = detectedClients[i]
- for (var j = 0; j < MatugenTemplates.discordClients.length; j++) {
- var client = MatugenTemplates.discordClients[j]
+ for (var j = 0; j < TemplateRegistry.discordClients.length; j++) {
+ var client = TemplateRegistry.discordClients[j]
if (client.name === clientName) {
availableDiscordClients.push(client)
break
@@ -101,13 +105,16 @@ Singleton {
"kittyAvailable": ["which", "kitty"],
"ghosttyAvailable": ["which", "ghostty"],
"footAvailable": ["which", "foot"],
+ "weztermAvailable": ["which", "wezterm"],
"fuzzelAvailable": ["which", "fuzzel"],
"vicinaeAvailable": ["sh", "-c", "command -v vicinae >/dev/null 2>&1 || (IFS=:; find $PATH -maxdepth 1 -iname 'vicinae*.appimage' -type f -executable 2>/dev/null | grep -q .)"],
"walkerAvailable": ["which", "walker"],
"app2unitAvailable": ["which", "app2unit"],
"gpuScreenRecorderAvailable": ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"],
"wlsunsetAvailable": ["which", "wlsunset"],
- "codeAvailable": ["which", "code"]
+ "codeAvailable": ["which", "code"],
+ "gnomeCalendarAvailable": ["which", "gnome-calendar"],
+ "spicetifyAvailable": ["which", "spicetify"]
})
// Internal tracking
@@ -195,8 +202,8 @@ Singleton {
Logger.d("ProgramChecker", "HOME:", Quickshell.env("HOME"))
// Test each client directory
- for (var i = 0; i < MatugenTemplates.discordClients.length; i++) {
- var client = MatugenTemplates.discordClients[i]
+ for (var i = 0; i < TemplateRegistry.discordClients.length; i++) {
+ var client = TemplateRegistry.discordClients[i]
var configDir = client.configPath.replace("~", Quickshell.env("HOME"))
Logger.d("ProgramChecker", "Checking:", configDir)
}
diff --git a/Services/SystemStatService.qml b/Services/System/SystemStatService.qml
similarity index 100%
rename from Services/SystemStatService.qml
rename to Services/System/SystemStatService.qml
diff --git a/Services/Theming/AppThemeService.qml b/Services/Theming/AppThemeService.qml
new file mode 100644
index 00000000..5bbffc82
--- /dev/null
+++ b/Services/Theming/AppThemeService.qml
@@ -0,0 +1,61 @@
+pragma Singleton
+
+import QtQuick
+import Quickshell
+import qs.Commons
+import qs.Services.UI
+
+Singleton {
+ id: root
+
+ readonly property string colorsApplyScript: Quickshell.shellDir + '/Bin/colors-apply.sh'
+
+ Connections {
+ target: WallpaperService
+
+ // When the wallpaper changes, regenerate with Matugen if necessary
+ function onWallpaperChanged(screenName, path) {
+ if (screenName === Screen.name && Settings.data.colorSchemes.useWallpaperColors) {
+ generateFromWallpaper()
+ }
+ }
+ }
+
+ Connections {
+ target: Settings.data.colorSchemes
+ function onDarkModeChanged() {
+ Logger.i("AppThemeService", "Detected dark mode change")
+ generate()
+ }
+ }
+
+ // PUBLIC FUNCTIONS
+ function init() {
+ Logger.i("AppThemeService", "Service started")
+ }
+
+ function generate() {
+ if (Settings.data.colorSchemes.useWallpaperColors) {
+ generateFromWallpaper()
+ } else {
+ // applyScheme will trigger template generation via schemeReader.onLoaded
+ ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
+ }
+ }
+
+ function generateFromWallpaper() {
+ const wp = WallpaperService.getWallpaper(Screen.name)
+ if (!wp) {
+ Logger.e("AppThemeService", "No wallpaper found")
+ return
+ }
+ const mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
+ TemplateProcessor.processWallpaperColors(wp, mode)
+ }
+
+ function generateFromPredefinedScheme(schemeData) {
+ Logger.i("AppThemeService", "Generating templates from predefined color scheme")
+ const mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
+ TemplateProcessor.processPredefinedScheme(schemeData, mode)
+ }
+}
diff --git a/Services/Theming/ColorPaletteGenerator.qml b/Services/Theming/ColorPaletteGenerator.qml
new file mode 100644
index 00000000..343b1758
--- /dev/null
+++ b/Services/Theming/ColorPaletteGenerator.qml
@@ -0,0 +1,100 @@
+pragma Singleton
+
+import QtQuick
+import Quickshell
+import "../../Helpers/ColorsConvert.js" as ColorsConvert
+
+Singleton {
+ id: root
+
+
+ /**
+ * Generate Material Design 3 color palette from base colors
+ * @param colors - Object with mPrimary, mSecondary, mTertiary, mError, mSurface, etc.
+ * @param isDarkMode - Boolean indicating dark or light mode
+ * @param isStrict - Boolean; if true, use mSurfaceVariant/mOnSurfaceVariant/mOutline directly
+ * @returns Object with all MD3 color roles in matugen format
+ */
+ function generatePalette(colors, isDarkMode, isStrict) {
+ const c = hex => ({
+ "default": {
+ "hex": hex,
+ "hex_stripped": hex.replace(/^#/, "")
+ }
+ })
+
+ // Generate container colors
+ const primaryContainer = ColorsConvert.generateContainerColor(colors.mPrimary, isDarkMode)
+ const secondaryContainer = ColorsConvert.generateContainerColor(colors.mSecondary, isDarkMode)
+ const tertiaryContainer = ColorsConvert.generateContainerColor(colors.mTertiary, isDarkMode)
+
+ // Generate "on" colors
+ const onPrimary = ColorsConvert.generateOnColor(colors.mPrimary, isDarkMode)
+ const onSecondary = ColorsConvert.generateOnColor(colors.mSecondary, isDarkMode)
+ const onTertiary = ColorsConvert.generateOnColor(colors.mTertiary, isDarkMode)
+
+ const onPrimaryContainer = ColorsConvert.generateOnColor(primaryContainer, isDarkMode)
+ const onSecondaryContainer = ColorsConvert.generateOnColor(secondaryContainer, isDarkMode)
+ const onTertiaryContainer = ColorsConvert.generateOnColor(tertiaryContainer, isDarkMode)
+
+ // Generate error colors (standard red-based)
+ const errorContainer = ColorsConvert.generateContainerColor(colors.mError, isDarkMode)
+ const onError = ColorsConvert.generateOnColor(colors.mError, isDarkMode)
+ const onErrorContainer = ColorsConvert.generateOnColor(errorContainer, isDarkMode)
+
+ // Surface is same as background in Material Design 3
+ const surface = colors.mSurface
+ const onSurface = isStrict ? colors.mOnSurface : ColorsConvert.generateOnColor(colors.mSurface, isDarkMode)
+
+ // Generate surface variant (slightly different tone)
+ const surfaceVariant = isStrict ? colors.mSurfaceVariant : ColorsConvert.adjustLightness(colors.mSurface, isDarkMode ? 5 : -3)
+ const onSurfaceVariant = isStrict ? colors.mOnSurfaceVariant : ColorsConvert.generateOnColor(surfaceVariant, isDarkMode)
+
+ // Generate surface containers (progressive elevation)
+ const surfaceContainerLowest = ColorsConvert.generateSurfaceVariant(surface, 0, isDarkMode)
+ const surfaceContainerLow = ColorsConvert.generateSurfaceVariant(surface, 1, isDarkMode)
+ const surfaceContainer = ColorsConvert.generateSurfaceVariant(surface, 2, isDarkMode)
+ const surfaceContainerHigh = ColorsConvert.generateSurfaceVariant(surface, 3, isDarkMode)
+ const surfaceContainerHighest = ColorsConvert.generateSurfaceVariant(surface, 4, isDarkMode)
+
+ // Generate outline colors (for borders/dividers)
+ const outline = isStrict ? colors.mOutline : ColorsConvert.adjustLightnessAndSaturation(colors.mOnSurface, isDarkMode ? -30 : 30, isDarkMode ? -30 : 30)
+ const outlineVariant = ColorsConvert.adjustLightness(outline, isDarkMode ? -20 : 20)
+
+ // Shadow is always pitch black
+ const shadow = "#000000"
+
+ return {
+ "primary": c(colors.mPrimary),
+ "on_primary": c(onPrimary),
+ "primary_container": c(primaryContainer),
+ "on_primary_container": c(onPrimaryContainer),
+ "secondary": c(colors.mSecondary),
+ "on_secondary": c(onSecondary),
+ "secondary_container": c(secondaryContainer),
+ "on_secondary_container": c(onSecondaryContainer),
+ "tertiary": c(colors.mTertiary),
+ "on_tertiary": c(onTertiary),
+ "tertiary_container": c(tertiaryContainer),
+ "on_tertiary_container": c(onTertiaryContainer),
+ "error": c(colors.mError),
+ "on_error": c(onError),
+ "error_container": c(errorContainer),
+ "on_error_container": c(onErrorContainer),
+ "background": c(surface),
+ "on_background": c(onSurface),
+ "surface": c(surface),
+ "on_surface": c(onSurface),
+ "surface_variant": c(surfaceVariant),
+ "on_surface_variant": c(onSurfaceVariant),
+ "surface_container_lowest": c(surfaceContainerLowest),
+ "surface_container_low": c(surfaceContainerLow),
+ "surface_container": c(surfaceContainer),
+ "surface_container_high": c(surfaceContainerHigh),
+ "surface_container_highest": c(surfaceContainerHighest),
+ "outline": c(outline),
+ "outline_variant": c(outlineVariant),
+ "shadow": c(shadow)
+ }
+ }
+}
diff --git a/Services/ColorSchemeService.qml b/Services/Theming/ColorSchemeService.qml
similarity index 94%
rename from Services/ColorSchemeService.qml
rename to Services/Theming/ColorSchemeService.qml
index e14108b7..a853e651 100644
--- a/Services/ColorSchemeService.qml
+++ b/Services/Theming/ColorSchemeService.qml
@@ -5,7 +5,7 @@ import Qt.labs.folderlistmodel
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
+import qs.Services.UI
Singleton {
id: root
@@ -211,14 +211,16 @@ Singleton {
property color mOnSecondary: "#000000"
property color mTertiary: "#000000"
property color mOnTertiary: "#000000"
- property color mError: "#ff0000"
+ property color mError: "#000000"
property color mOnError: "#000000"
- property color mSurface: "#ffffff"
+ property color mSurface: "#000000"
property color mOnSurface: "#000000"
- property color mSurfaceVariant: "#cccccc"
- property color mOnSurfaceVariant: "#333333"
- property color mOutline: "#444444"
+ property color mSurfaceVariant: "#000000"
+ property color mOnSurfaceVariant: "#000000"
+ property color mOutline: "#000000"
property color mShadow: "#000000"
+ property color mHover: "#000000"
+ property color mOnHover: "#000000"
}
}
@@ -240,6 +242,8 @@ Singleton {
out.mOnSurfaceVariant = pick(obj, "mOnSurfaceVariant", "onSurfaceVariant", out.mOnSurfaceVariant)
out.mOutline = pick(obj, "mOutline", "outline", out.mOutline)
out.mShadow = pick(obj, "mShadow", "shadow", out.mShadow)
+ out.mHover = pick(obj, "mHover", "hover", out.mHover)
+ out.mOnHover = pick(obj, "mOnHover", "onHover", out.mOnHover)
// Force a rewrite by updating the path
colorsWriter.path = ""
diff --git a/Services/Theming/TemplateProcessor.qml b/Services/Theming/TemplateProcessor.qml
new file mode 100644
index 00000000..c1f6ae33
--- /dev/null
+++ b/Services/Theming/TemplateProcessor.qml
@@ -0,0 +1,378 @@
+pragma Singleton
+
+import QtQuick
+import Quickshell
+import Quickshell.Io
+import qs.Commons
+import qs.Services.System
+import qs.Services.UI
+
+Singleton {
+ id: root
+
+ readonly property string dynamicConfigPath: Settings.cacheDir + "matugen.dynamic.toml"
+
+ readonly property var schemeNameMap: ({
+ "Noctalia (default)": "Noctalia-default",
+ "Noctalia (legacy)": "Noctalia-legacy",
+ "Tokyo Night": "Tokyo-Night"
+ })
+
+ readonly property var terminalPaths: ({
+ "foot": "~/.config/foot/themes/noctalia",
+ "ghostty": "~/.config/ghostty/themes/noctalia",
+ "kitty": "~/.config/kitty/themes/noctalia.conf",
+ "alacritty": "~/.config/alacritty/themes/noctalia.toml",
+ "wezterm": "~/.config/wezterm/colors/Noctalia.toml"
+ })
+
+
+ /**
+ * Process wallpaper colors using matugen CLI
+ * Dual-path architecture (wallpaper uses matugen CLI)
+ */
+ function processWallpaperColors(wallpaperPath, mode) {
+ const content = buildMatugenConfig()
+ if (!content)
+ return
+
+ const wp = wallpaperPath.replace(/'/g, "'\\''")
+ const script = buildMatugenScript(content, wp, mode)
+
+ generateProcess.generator = "matugen"
+ generateProcess.command = ["bash", "-lc", script]
+ generateProcess.running = true
+ }
+
+
+ /**
+ * Process predefined color scheme using sed scripts
+ * Dual-path architecture (predefined uses sed scripts)
+ */
+ function processPredefinedScheme(schemeData, mode) {
+ handleTerminalThemes(mode)
+
+ const colors = schemeData[mode]
+ let script = processAllTemplates(colors, mode)
+
+ // Add user templates if enabled (requirement #1)
+ script += buildUserTemplateCommandForPredefined(schemeData, mode)
+
+ generateProcess.generator = "predefined"
+ generateProcess.command = ["bash", "-lc", script]
+ generateProcess.running = true
+ }
+
+ // ================================================================================
+ // WALLPAPER-BASED GENERATION (matugen CLI)
+ // ================================================================================
+ function buildMatugenConfig() {
+ var lines = []
+ var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
+
+ if (Settings.data.colorSchemes.useWallpaperColors) {
+ addWallpaperTemplates(lines, mode)
+ }
+
+ addApplicationTemplates(lines, mode)
+
+ if (lines.length > 0) {
+ return ["[config]"].concat(lines).join("\n") + "\n"
+ }
+ return ""
+ }
+
+ function addWallpaperTemplates(lines, mode) {
+ // Noctalia colors JSON
+ lines.push("[templates.noctalia]")
+ lines.push('input_path = "' + Quickshell.shellDir + '/Assets/MatugenTemplates/noctalia.json"')
+ lines.push('output_path = "' + Settings.configDir + 'colors.json"')
+
+ // Terminal templates
+ TemplateRegistry.terminals.forEach(terminal => {
+ if (Settings.data.templates[terminal.id]) {
+ lines.push(`\n[templates.${terminal.id}]`)
+ lines.push(`input_path = "${Quickshell.shellDir}/Assets/MatugenTemplates/${terminal.matugenPath}"`)
+ lines.push(`output_path = "${terminal.outputPath}"`)
+ const postHook = terminal.postHook || `${TemplateRegistry.colorsApplyScript} ${terminal.id}`
+ lines.push(`post_hook = "${postHook}"`)
+ }
+ })
+ }
+
+ function addApplicationTemplates(lines, mode) {
+ TemplateRegistry.applications.forEach(app => {
+ if (app.id === "discord") {
+ // Handle Discord clients specially
+ if (Settings.data.templates.discord) {
+ app.clients.forEach(client => {
+ // Check if this specific client is detected
+ if (isDiscordClientEnabled(client.name)) {
+ lines.push(`\n[templates.discord_${client.name}]`)
+ lines.push(`input_path = "${Quickshell.shellDir}/Assets/MatugenTemplates/${app.input}"`)
+ lines.push(`output_path = "${client.path}/themes/noctalia.theme.css"`)
+ }
+ })
+ }
+ } else {
+ // Handle regular apps
+ if (Settings.data.templates[app.id]) {
+ app.outputs.forEach((output, idx) => {
+ lines.push(`\n[templates.${app.id}_${idx}]`)
+ lines.push(`input_path = "${Quickshell.shellDir}/Assets/MatugenTemplates/${app.input}"`)
+ lines.push(`output_path = "${output.path}"`)
+ if (app.postProcess) {
+ lines.push(`post_hook = "${app.postProcess(mode)}"`)
+ }
+ })
+ }
+ }
+ })
+ }
+
+ function isDiscordClientEnabled(clientName) {
+ // Check ProgramCheckerService to see if client is detected
+ for (var i = 0; i < ProgramCheckerService.availableDiscordClients.length; i++) {
+ if (ProgramCheckerService.availableDiscordClients[i].name === clientName) {
+ return true
+ }
+ }
+ return false
+ }
+
+ function buildMatugenScript(content, wallpaper, mode) {
+ const delimiter = "MATUGEN_CONFIG_EOF_" + Math.random().toString(36).substr(2, 9)
+ const pathEsc = dynamicConfigPath.replace(/'/g, "'\\''")
+
+ let script = `cat > '${pathEsc}' << '${delimiter}'\n${content}\n${delimiter}\n`
+ script += `matugen image '${wallpaper}' --config '${pathEsc}' --mode ${mode} --type ${Settings.data.colorSchemes.matugenSchemeType}`
+ script += buildUserTemplateCommand(wallpaper, mode)
+
+ return script + "\n"
+ }
+
+ // ================================================================================
+ // PREDEFINED SCHEME GENERATION (sed scripts)
+ // ================================================================================
+ function processAllTemplates(colors, mode) {
+ let script = ""
+ const homeDir = Quickshell.env("HOME")
+
+ TemplateRegistry.applications.forEach(app => {
+ if (app.id === "discord") {
+ if (Settings.data.templates.discord) {
+ script += processDiscordClients(app, colors, mode, homeDir)
+ }
+ } else {
+ if (Settings.data.templates[app.id]) {
+ script += processTemplate(app, colors, mode, homeDir)
+ }
+ }
+ })
+ return script
+ }
+
+ function processDiscordClients(discordApp, colors, mode, homeDir) {
+ let script = ""
+ const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, false)
+
+ discordApp.clients.forEach(client => {
+ if (!isDiscordClientEnabled(client.name))
+ return
+
+ const templatePath = `${Quickshell.shellDir}/Assets/MatugenTemplates/${discordApp.input}`
+ const outputPath = `${client.path}/themes/noctalia.theme.css`.replace("~", homeDir)
+ const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
+ const baseConfigDir = outputDir.replace("/themes", "")
+
+ script += `\n`
+ script += `if [ -d "${baseConfigDir}" ]; then\n`
+ script += ` mkdir -p ${outputDir}\n`
+ script += ` cp '${templatePath}' '${outputPath}'\n`
+ script += ` ${replaceColorsInFile(outputPath, palette)}`
+ script += `else\n`
+ script += ` echo "Discord client ${client.name} not found at ${baseConfigDir}, skipping"\n`
+ script += `fi\n`
+ })
+
+ return script
+ }
+
+ function processTemplate(app, colors, mode, homeDir) {
+ const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, app.strict || false)
+ let script = ""
+
+ app.outputs.forEach(output => {
+ const templatePath = `${Quickshell.shellDir}/Assets/MatugenTemplates/${app.input}`
+ const outputPath = output.path.replace("~", homeDir)
+ const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
+
+ script += `\n`
+ script += `mkdir -p ${outputDir}\n`
+ script += `cp '${templatePath}' '${outputPath}'\n`
+ script += replaceColorsInFile(outputPath, palette)
+ script += `\n`
+ })
+
+ if (app.postProcess) {
+ script += app.postProcess(mode)
+ }
+
+ return script
+ }
+
+ function replaceColorsInFile(filePath, colors) {
+ let script = ""
+ Object.keys(colors).forEach(colorKey => {
+ const hexValue = colors[colorKey].default.hex
+ const hexStrippedValue = colors[colorKey].default.hex_stripped
+
+ const escapedHex = hexValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ const escapedHexStripped = hexStrippedValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+
+ // replace hex_stripped
+ script += `sed -i 's/{{colors\\.${colorKey}\\.default\\.hex_stripped}}/${escapedHexStripped}/g' '${filePath}'\n`
+
+ // replace hex
+ script += `sed -i 's/{{colors\\.${colorKey}\\.default\\.hex}}/${escapedHex}/g' '${filePath}'\n`
+ })
+ return script
+ }
+
+ // ================================================================================
+ // TERMINAL THEMES (predefined schemes use pre-rendered files)
+ // ================================================================================
+ function handleTerminalThemes(mode) {
+ const commands = []
+ const homeDir = Quickshell.env("HOME")
+
+ Object.keys(terminalPaths).forEach(terminal => {
+ if (Settings.data.templates[terminal]) {
+ const outputPath = terminalPaths[terminal].replace("~", homeDir)
+ const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
+ const templatePath = getTerminalColorsTemplate(terminal, mode)
+
+ commands.push(`mkdir -p ${outputDir}`)
+ commands.push(`cp -f ${templatePath} ${outputPath}`)
+ commands.push(`${TemplateRegistry.colorsApplyScript} ${terminal}`)
+ }
+ })
+
+ if (commands.length > 0) {
+ copyProcess.command = ["bash", "-lc", commands.join('; ')]
+ copyProcess.running = true
+ }
+ }
+
+ function getTerminalColorsTemplate(terminal, mode) {
+ let colorScheme = Settings.data.colorSchemes.predefinedScheme
+ colorScheme = schemeNameMap[colorScheme] || colorScheme
+
+ let extension = ""
+ if (terminal === 'kitty') {
+ extension = ".conf"
+ } else if (terminal === 'wezterm') {
+ extension = ".toml"
+ }
+
+ return `${Quickshell.shellDir}/Assets/ColorScheme/${colorScheme}/terminal/${terminal}/${colorScheme}-${mode}${extension}`
+ }
+
+ // ================================================================================
+ // USER TEMPLATES, advanced usage
+ // ================================================================================
+ function buildUserTemplateCommand(input, mode) {
+ if (!Settings.data.templates.enableUserTemplates)
+ return ""
+
+ const userConfigPath = getUserConfigPath()
+ let script = "\n# Execute user config if it exists\n"
+ script += `if [ -f '${userConfigPath}' ]; then\n`
+ script += ` matugen image '${input}' --config '${userConfigPath}' --mode ${mode} --type ${Settings.data.colorSchemes.matugenSchemeType}\n`
+ script += "fi"
+
+ return script
+ }
+
+ function buildUserTemplateCommandForPredefined(schemeData, mode) {
+ if (!Settings.data.templates.enableUserTemplates)
+ return ""
+
+ const userConfigPath = getUserConfigPath()
+ const colors = schemeData[mode]
+ const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, false)
+
+ const tempJsonPath = Settings.cacheDir + "predefined-colors.json"
+ const tempJsonPathEsc = tempJsonPath.replace(/'/g, "'\\''")
+
+ let script = "\n# Execute user templates with predefined scheme colors\n"
+ script += `if [ -f '${userConfigPath}' ]; then\n`
+ script += ` cat > '${tempJsonPathEsc}' << 'EOF'\n`
+ script += JSON.stringify({
+ "colors": palette
+ }, null, 2) + "\n"
+ script += "EOF\n"
+ script += ` matugen json '${tempJsonPathEsc}' --config '${userConfigPath}' --mode ${mode}\n`
+ script += "fi"
+
+ return script
+ }
+
+ function getUserConfigPath() {
+ return (Settings.configDir + "user-templates.toml").replace(/'/g, "'\\''")
+ }
+
+ // ================================================================================
+ // PROCESSES
+ // ================================================================================
+ Process {
+ id: generateProcess
+ workingDirectory: Quickshell.shellDir
+ running: false
+
+ // Error reporting helpers
+ property string generator: ""
+ function showWarning() {
+ const description = (stderr.text && stderr.text.trim() !== "") ? stderr.text.trim() : ((stdout.text && stdout.text.trim() !== "") ? stdout.text.trim() : I18n.tr("toast.theming-processor-failed.desc-generic"))
+ const title = I18n.tr(`toast.theming-processor-failed.title-${generator}`)
+
+ // Give a bit more time to the user to read, as it can contains important information for debugging user's templates.
+ ToastService.showWarning(title, description, 8000)
+ return description
+ }
+
+ onExited: function (exitCode) {
+ if (exitCode !== 0) {
+ const description = generateProcess.showWarning()
+ Logger.e("TemplateProcessor", "Process failed with exit code", exitCode, description)
+ }
+ }
+
+ stdout: StdioCollector {
+ onStreamFinished: {
+ if (this.text)
+ Logger.d("TemplateProcessor", "stdout:", this.text)
+ }
+ }
+ stderr: StdioCollector {
+ onStreamFinished: {
+ if (this.text) {
+ const description = generateProcess.showWarning()
+ Logger.e("TemplateProcessor", "Process failed", description)
+ }
+ }
+ }
+ }
+
+ Process {
+ id: copyProcess
+ workingDirectory: Quickshell.shellDir
+ running: false
+ stderr: StdioCollector {
+ onStreamFinished: {
+ if (this.text)
+ Logger.d("TemplateProcessor", "copyProcess stderr:", this.text)
+ }
+ }
+ }
+}
diff --git a/Services/Theming/TemplateRegistry.qml b/Services/Theming/TemplateRegistry.qml
new file mode 100644
index 00000000..ebb67a01
--- /dev/null
+++ b/Services/Theming/TemplateRegistry.qml
@@ -0,0 +1,247 @@
+pragma Singleton
+
+import QtQuick
+import Quickshell
+import Quickshell.Io
+import qs.Commons
+
+Singleton {
+ id: root
+
+ readonly property string colorsApplyScript: Quickshell.shellDir + '/Bin/colors-apply.sh'
+
+ // Terminal configurations (for wallpaper-based matugen templates)
+ readonly property var terminals: [{
+ "id": "foot",
+ "name": "Foot",
+ "matugenPath": "Terminal/foot",
+ "outputPath": "~/.config/foot/themes/noctalia"
+ }, {
+ "id": "ghostty",
+ "name": "Ghostty",
+ "matugenPath": "Terminal/ghostty",
+ "outputPath": "~/.config/ghostty/themes/noctalia",
+ "postHook": "bash -c 'pgrep -f ghostty >/dev/null && pkill -SIGUSR2 ghostty || true'"
+ }, {
+ "id": "kitty",
+ "name": "Kitty",
+ "matugenPath": "Terminal/kitty.conf",
+ "outputPath": "~/.config/kitty/themes/noctalia.conf"
+ }, {
+ "id": "alacritty",
+ "name": "Alacritty",
+ "matugenPath": "Terminal/alacritty.toml",
+ "outputPath": "~/.config/alacritty/themes/noctalia.toml"
+ }, {
+ "id": "wezterm",
+ "name": "Wezterm",
+ "matugenPath": "Terminal/wezterm.toml",
+ "outputPath": "~/.config/wezterm/colors/Noctalia.toml",
+ "postHook": "touch ~/.config/wezterm/wezterm.lua"
+ }]
+
+ // Application configurations - consolidated from MatugenTemplates + AppThemeService
+ readonly property var applications: [{
+ "id": "gtk",
+ "name": "GTK",
+ "category": "ui",
+ "input": "gtk.css",
+ "outputs": [{
+ "path": "~/.config/gtk-3.0/gtk.css"
+ }, {
+ "path": "~/.config/gtk-4.0/gtk.css"
+ }],
+ "postProcess": mode => `gsettings set org.gnome.desktop.interface color-scheme prefer-${mode}`
+ }, {
+ "id": "qt",
+ "name": "Qt",
+ "category": "ui",
+ "input": "qtct.conf",
+ "outputs": [{
+ "path": "~/.config/qt5ct/colors/noctalia.conf"
+ }, {
+ "path": "~/.config/qt6ct/colors/noctalia.conf"
+ }]
+ }, {
+ "id": "kcolorscheme",
+ "name": "KColorScheme",
+ "category": "ui",
+ "input": "kcolorscheme.colors",
+ "outputs": [{
+ "path": "~/.local/share/color-schemes/noctalia.colors"
+ }]
+ }, {
+ "id": "fuzzel",
+ "name": "Fuzzel",
+ "category": "launchers",
+ "input": "fuzzel.conf",
+ "outputs": [{
+ "path": "~/.config/fuzzel/themes/noctalia"
+ }],
+ "postProcess": () => `${colorsApplyScript} fuzzel`
+ }, {
+ "id": "vicinae",
+ "name": "Vicinae",
+ "category": "launchers",
+ "input": "vicinae.toml",
+ "outputs": [{
+ "path": "~/.local/share/vicinae/themes/matugen.toml"
+ }],
+ "postProcess": () => `cp --update=none ${Quickshell.shellDir}/Assets/noctalia.svg ~/.local/share/vicinae/themes/noctalia.svg && ${colorsApplyScript} vicinae`
+ }, {
+ "id": "walker",
+ "name": "Walker",
+ "category": "launchers",
+ "input": "walker.css",
+ "outputs": [{
+ "path": "~/.config/walker/themes/noctalia/style.css"
+ }],
+ "postProcess": () => `${colorsApplyScript} walker`,
+ "strict": true // Use strict mode for palette generation (preserves custom surface/outline values)
+ }, {
+ "id": "pywalfox",
+ "name": "Pywalfox",
+ "category": "applications",
+ "input": "pywalfox.json",
+ "outputs": [{
+ "path": "~/.cache/wal/colors.json"
+ }],
+ "postProcess": () => `${colorsApplyScript} pywalfox`
+ }, // CONSOLIDATED DISCORD CLIENTS
+ {
+ "id": "discord",
+ "name": "Discord",
+ "category": "applications",
+ "input": "vesktop.css",
+ "clients": [{
+ "name": "vesktop",
+ "path": "~/.config/vesktop",
+ "requiresThemesFolder": false
+ }, {
+ "name": "webcord",
+ "path": "~/.config/webcord",
+ "requiresThemesFolder": false
+ }, {
+ "name": "armcord",
+ "path": "~/.config/armcord",
+ "requiresThemesFolder": false
+ }, {
+ "name": "equibop",
+ "path": "~/.config/equibop",
+ "requiresThemesFolder": false
+ }, {
+ "name": "lightcord",
+ "path": "~/.config/lightcord",
+ "requiresThemesFolder": false
+ }, {
+ "name": "dorion",
+ "path": "~/.config/dorion",
+ "requiresThemesFolder": false
+ }, {
+ "name": "vencord",
+ "path": "~/.config/discord",
+ "requiresThemesFolder": true
+ }]
+ }, // VSCode with hardcoded path (requirement #5)
+ {
+ "id": "code",
+ "name": "VSCode",
+ "category": "applications",
+ "input": "code.json",
+ "outputs": [{
+ "path": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
+ }]
+ }, {
+ "id": "spicetify",
+ "name": "Spicetify",
+ "category": "applications",
+ "input": "spicetify.ini",
+ "outputs": [{
+ "path": "~/.config/spicetify/Themes/Comfy/color.ini"
+ }],
+ "postProcess": () => `spicetify -q apply --no-restart`
+ }]
+
+ // Extract Discord clients for ProgramCheckerService compatibility
+ readonly property var discordClients: {
+ var clients = []
+ var discordApp = applications.find(app => app.id === "discord")
+ if (discordApp && discordApp.clients) {
+ discordApp.clients.forEach(client => {
+ clients.push({
+ "name": client.name,
+ "configPath": client.path,
+ "themePath": `${client.path}/themes/noctalia.theme.css`,
+ "requiresThemesFolder": client.requiresThemesFolder || false
+ })
+ })
+ }
+ return clients
+ }
+
+ // Build user templates TOML content
+ function buildUserTemplatesToml() {
+ var lines = []
+ lines.push("[config]")
+ lines.push("")
+ lines.push("[templates]")
+ lines.push("")
+ lines.push("# User-defined templates")
+ lines.push("# Add your custom templates below")
+ lines.push("# Example:")
+ lines.push("# [templates.myapp]")
+ lines.push("# input_path = \"~/.config/noctalia/templates/myapp.css\"")
+ lines.push("# output_path = \"~/.config/myapp/theme.css\"")
+ lines.push("# post_hook = \"myapp --reload-theme\"")
+ lines.push("")
+ lines.push("# Remove this section and add your own templates")
+ lines.push("#[templates.placeholder]")
+ lines.push("#input_path = \"" + Quickshell.shellDir + "/Assets/MatugenTemplates/noctalia.json\"")
+ lines.push("#output_path = \"" + Settings.cacheDir + "placeholder.json\"")
+ lines.push("")
+
+ return lines.join("\n") + "\n"
+ }
+
+ // Write user templates TOML file (moved from MatugenTemplates)
+ function writeUserTemplatesToml() {
+ var userConfigPath = Settings.configDir + "user-templates.toml"
+
+ // Check if file already exists
+ fileCheckProcess.command = ["test", "-f", userConfigPath]
+ fileCheckProcess.running = true
+ }
+
+ function doWriteUserTemplatesToml() {
+ var userConfigPath = Settings.configDir + "user-templates.toml"
+ var configContent = buildUserTemplatesToml()
+ var userConfigPathEsc = userConfigPath.replace(/'/g, "'\\''")
+
+ // Ensure directory exists (should already exist but just in case)
+ Quickshell.execDetached(["mkdir", "-p", Settings.configDir])
+
+ // Write the config file using heredoc to avoid escaping issues
+ var script = `cat > '${userConfigPathEsc}' << 'EOF'\n`
+ script += configContent
+ script += "EOF\n"
+ Quickshell.execDetached(["sh", "-c", script])
+
+ Logger.d("TemplateRegistry", "User templates config written to:", userConfigPath)
+ }
+
+ // Process for checking if user templates file exists
+ Process {
+ id: fileCheckProcess
+ running: false
+
+ onExited: function (exitCode) {
+ if (exitCode === 0) {
+ // File exists, skip creation
+ Logger.d("TemplateRegistry", "User templates config already exists, skipping creation")
+ } else {
+ // File doesn't exist, create it
+ doWriteUserTemplatesToml()
+ }
+ }
+ }
+}
diff --git a/Services/BarService.qml b/Services/UI/BarService.qml
similarity index 99%
rename from Services/BarService.qml
rename to Services/UI/BarService.qml
index fff26f55..51dd733a 100644
--- a/Services/BarService.qml
+++ b/Services/UI/BarService.qml
@@ -46,7 +46,7 @@ Singleton {
}
Component.onCompleted: {
- Logger.d("BarService", "Service started")
+ Logger.i("BarService", "Service started")
}
// Function for the Bar to call when it's ready
diff --git a/Services/BarWidgetRegistry.qml b/Services/UI/BarWidgetRegistry.qml
similarity index 88%
rename from Services/BarWidgetRegistry.qml
rename to Services/UI/BarWidgetRegistry.qml
index 3bccd148..b5632bad 100644
--- a/Services/BarWidgetRegistry.qml
+++ b/Services/UI/BarWidgetRegistry.qml
@@ -12,32 +12,37 @@ Singleton {
property var widgets: ({
"ActiveWindow": activeWindowComponent,
"AudioVisualizer": audioVisualizerComponent,
- "Battery": batteryComponent,
+ "Battery"// HEAVY
+ : batteryComponent,
"Bluetooth": bluetoothComponent,
"Brightness": brightnessComponent,
"Clock": clockComponent,
"ControlCenter": controlCenterComponent,
"CustomButton": customButtonComponent,
- "DarkMode": darkMode,
+ "DarkMode": darkModeComponent,
"KeepAwake": keepAwakeComponent,
"KeyboardLayout": keyboardLayoutComponent,
"LockKeys": lockKeysComponent,
"MediaMini": mediaMiniComponent,
"Microphone": microphoneComponent,
"NightLight": nightLightComponent,
+ "NoctaliaPerformance": noctaliaPerformanceComponent,
"NotificationHistory": notificationHistoryComponent,
- "PowerProfile": powerProfileComponent,
+ "PowerProfile"// HEAVY
+ : powerProfileComponent,
"ScreenRecorder": screenRecorderComponent,
"SessionMenu": sessionMenuComponent,
"Spacer": spacerComponent,
"SystemMonitor": systemMonitorComponent,
- "Taskbar": taskbarComponent,
+ "Taskbar"// HEAVY
+ : taskbarComponent,
"TaskbarGrouped": taskbarGroupedComponent,
"Tray": trayComponent,
- "Volume": volumeComponent,
+ "Volume"// A BIT HEAVY ?
+ : volumeComponent,
"WiFi": wiFiComponent,
"WallpaperSelector": wallpaperSelectorComponent,
- "Workspace": workspaceComponent
+ "Workspace": workspaceComponent // HEAVY
})
property var widgetMetadata: ({
@@ -81,7 +86,8 @@ Singleton {
"allowUserSettings": true,
"useDistroLogo": false,
"icon": "noctalia",
- "customIconPath": ""
+ "customIconPath": "",
+ "colorizeDistroLogo": false
},
"CustomButton": {
"allowUserSettings": true,
@@ -93,7 +99,8 @@ Singleton {
"textStream": false,
"textIntervalMs": 3000,
"textCollapse": "",
- "parseJson": false
+ "parseJson": false,
+ "hideTextInVerticalBar": false
},
"KeyboardLayout": {
"allowUserSettings": true,
@@ -101,10 +108,12 @@ Singleton {
},
"LockKeys": {
"allowUserSettings": true,
- "indicatorStyle": "large",
"showCapsLock": true,
"showNumLock": true,
- "showScrollLock": true
+ "showScrollLock": true,
+ "capsLockIcon": "letter-c",
+ "numLockIcon": "letter-n",
+ "scrollLockIcon": "letter-s"
},
"MediaMini": {
"allowUserSettings": true,
@@ -112,6 +121,7 @@ Singleton {
"scrollingMode": "hover",
"maxWidth": 145,
"useFixedWidth": false,
+ "hideWhenIdle": false,
"showAlbumArt": false,
"showVisualizer": false,
"visualizerType": "linear"
@@ -131,6 +141,7 @@ Singleton {
},
"SystemMonitor": {
"allowUserSettings": true,
+ "usePrimaryColor": false,
"showCpuUsage": true,
"showCpuTemp": true,
"showMemoryUsage": true,
@@ -153,7 +164,9 @@ Singleton {
"Tray": {
"allowUserSettings": true,
"blacklist": [],
- "colorizeIcons": false
+ "colorizeIcons": false,
+ "favorites": [],
+ "drawerEnabled": true
},
"WiFi": {
"allowUserSettings": true,
@@ -193,7 +206,7 @@ Singleton {
property Component customButtonComponent: Component {
CustomButton {}
}
- property Component darkMode: Component {
+ property Component darkModeComponent: Component {
DarkMode {}
}
property Component keyboardLayoutComponent: Component {
@@ -214,6 +227,9 @@ Singleton {
property Component nightLightComponent: Component {
NightLight {}
}
+ property Component noctaliaPerformanceComponent: Component {
+ NoctaliaPerformance {}
+ }
property Component notificationHistoryComponent: Component {
NotificationHistory {}
}
diff --git a/Services/ControlCenterWidgetRegistry.qml b/Services/UI/ControlCenterWidgetRegistry.qml
similarity index 70%
rename from Services/ControlCenterWidgetRegistry.qml
rename to Services/UI/ControlCenterWidgetRegistry.qml
index f00263ed..b5da5473 100644
--- a/Services/ControlCenterWidgetRegistry.qml
+++ b/Services/UI/ControlCenterWidgetRegistry.qml
@@ -3,7 +3,7 @@ pragma Singleton
import QtQuick
import Quickshell
import qs.Commons
-import qs.Modules.ControlCenter.Widgets
+import qs.Modules.Panels.ControlCenter.Widgets
Singleton {
id: root
@@ -17,10 +17,22 @@ Singleton {
"PowerProfile": powerProfileComponent,
"ScreenRecorder": screenRecorderComponent,
"WiFi": wiFiComponent,
- "WallpaperSelector": wallpaperSelectorComponent
+ "WallpaperSelector": wallpaperSelectorComponent,
+ "CustomButton": customButtonComponent
})
- property var widgetMetadata: ({})
+ property var widgetMetadata: ({
+ "CustomButton": {
+ "allowUserSettings": true,
+ "icon": "heart",
+ "onClicked": "",
+ "onRightClicked": "",
+ "onMiddleClicked": "",
+ "stateChecks": [],
+ "generalTooltipText": "Custom Button",
+ "enableOnStateLogic": false
+ }
+ })
// Component definitions - these are loaded once at startup
property Component bluetoothComponent: Component {
@@ -47,6 +59,9 @@ Singleton {
property Component wallpaperSelectorComponent: Component {
WallpaperSelector {}
}
+ property Component customButtonComponent: Component {
+ CustomButton {}
+ }
function init() {
Logger.i("ControlCenterWidgetRegistry", "Service started")
diff --git a/Services/PanelService.qml b/Services/UI/PanelService.qml
similarity index 50%
rename from Services/PanelService.qml
rename to Services/UI/PanelService.qml
index 1ee055c1..79682646 100644
--- a/Services/PanelService.qml
+++ b/Services/UI/PanelService.qml
@@ -14,22 +14,36 @@ Singleton {
property var registeredPanels: ({})
property var openedPanel: null
signal willOpen
+ signal didClose
- // Currently opened popups, can have more than one.
- // ex: when opening an NIconPicker from a widget setting.
- property var openedPopups: []
- property bool hasOpenedPopup: false
- signal popupChanged
-
- // Register this panel
+ // Register this panel (called after panel is loaded)
function registerPanel(panel) {
registeredPanels[panel.objectName] = panel
- Logger.d("PanelService", "Registered:", panel.objectName)
+ Logger.d("PanelService", "Registered panel:", panel.objectName)
}
- // Returns a panel
- function getPanel(name) {
- return registeredPanels[name] || null
+ // Returns a panel (loads it on-demand if not yet loaded)
+ function getPanel(name, screen) {
+ if (!screen) {
+ Logger.d("PanelService", "missing screen for getPanel:", name)
+ // If no screen specified, return the first matching panel
+ for (var key in registeredPanels) {
+ if (key.startsWith(name + "-")) {
+ return registeredPanels[key]
+ }
+ }
+ return null
+ }
+
+ var panelKey = `${name}-${screen.name}`
+
+ // Check if panel is already loaded
+ if (registeredPanels[panelKey]) {
+ return registeredPanels[panelKey]
+ }
+
+ Logger.w("PanelService", "Panel not found:", panelKey)
+ return null
}
// Check if a panel exists
@@ -52,18 +66,8 @@ Singleton {
if (openedPanel && openedPanel === panel) {
openedPanel = null
}
- }
- // Popups
- function willOpenPopup(popup) {
- openedPopups.push(popup)
- hasOpenedPopup = (openedPopups.length !== 0)
- popupChanged()
- }
-
- function willClosePopup(popup) {
- openedPopups = openedPopups.filter(p => p !== popup)
- hasOpenedPopup = (openedPopups.length !== 0)
- popupChanged()
+ // emit signal
+ didClose()
}
}
diff --git a/Services/UI/SystemTrayService.qml b/Services/UI/SystemTrayService.qml
new file mode 100644
index 00000000..4f2e74ce
--- /dev/null
+++ b/Services/UI/SystemTrayService.qml
@@ -0,0 +1,40 @@
+pragma Singleton
+
+import QtQuick
+import Quickshell
+import Quickshell.Services.SystemTray
+import qs.Commons
+
+
+/**
+ * SystemTrayService
+ * This service ensures that Quickshell's SystemTray service is initialized
+ * early in the shell startup to avoid programs that should stay in tray, not having access to one (let's hope this works).
+ */
+Singleton {
+ id: root
+
+ property bool initialized: false
+
+ Component.onCompleted: {
+ if (SystemTray && SystemTray.items) {
+ Logger.i("SystemTrayService", "SystemTray service initialized")
+ initialized = true
+
+ // Monitor for tray items to confirm it's working
+ if (SystemTray.items.valuesChanged) {
+ Logger.d("SystemTrayService", "SystemTray is ready and monitoring for items")
+ }
+ } else {
+ Logger.w("SystemTrayService", "SystemTray service not available")
+ }
+ }
+
+ function init() {
+ // Explicit initialization function
+ if (!initialized && SystemTray && SystemTray.items) {
+ Logger.i("SystemTrayService", "SystemTray service initialized via init()")
+ initialized = true
+ }
+ }
+}
diff --git a/Services/ToastService.qml b/Services/UI/ToastService.qml
similarity index 90%
rename from Services/ToastService.qml
rename to Services/UI/ToastService.qml
index fd39e6ba..5a5a0929 100644
--- a/Services/ToastService.qml
+++ b/Services/UI/ToastService.qml
@@ -18,7 +18,7 @@ Singleton {
notify(message, description, "", "warning", duration)
}
- function showError(message, description = "", duration = 5000) {
+ function showError(message, description = "", duration = 6000) {
notify(message, description, "", "error", duration)
}
}
diff --git a/Services/TooltipService.qml b/Services/UI/TooltipService.qml
similarity index 100%
rename from Services/TooltipService.qml
rename to Services/UI/TooltipService.qml
diff --git a/Services/WallpaperService.qml b/Services/UI/WallpaperService.qml
similarity index 100%
rename from Services/WallpaperService.qml
rename to Services/UI/WallpaperService.qml
diff --git a/Modules/Audio/LinearSpectrum.qml b/Widgets/AudioSpectrum/NLinearSpectrum.qml
similarity index 74%
rename from Modules/Audio/LinearSpectrum.qml
rename to Widgets/AudioSpectrum/NLinearSpectrum.qml
index a0bbd34c..596f7d10 100644
--- a/Modules/Audio/LinearSpectrum.qml
+++ b/Widgets/AudioSpectrum/NLinearSpectrum.qml
@@ -14,9 +14,10 @@ Item {
property real minimumSignalValue: 0.05 // Default to 5% of height
// Pre compute horizontal mirroring
- readonly property int valuesCount: values.length
+ readonly property int valuesCount: (values && Array.isArray(values)) ? values.length : 0
readonly property int totalBars: valuesCount * 2
readonly property real barSlotSize: totalBars > 0 ? (vertical ? height : width) / totalBars : 0
+ readonly property bool highQuality: (Settings.data.audio.visualizerType === "low") ? false : true
Repeater {
model: root.totalBars
@@ -27,18 +28,23 @@ Item {
property int valueIndex: index < root.valuesCount ? root.valuesCount - 1 - index // Mirrored half
: index - root.valuesCount // Normal half
- property real rawAmp: root.values[valueIndex]
+ property real rawAmp: (root.values && root.values[valueIndex] !== undefined) ? root.values[valueIndex] : 0
property real amp: (root.showMinimumSignal && rawAmp === 0) ? root.minimumSignalValue : rawAmp
color: root.fillColor
border.color: root.strokeColor
border.width: root.strokeWidth
- antialiasing: true
+ antialiasing: root.highQuality
+ smooth: root.highQuality
+ // Only update when value actually changes - reduces GPU load
width: vertical ? root.width * amp : root.barSlotSize * 0.5
height: vertical ? root.barSlotSize * 0.5 : root.height * amp
x: vertical ? root.width - width : index * root.barSlotSize + (root.barSlotSize * 0.25)
y: vertical ? index * root.barSlotSize + (root.barSlotSize * 0.25) : root.height - height
+
+ // Disable updates when invisible to save GPU
+ visible: root.visible
}
}
}
diff --git a/Modules/Audio/MirroredSpectrum.qml b/Widgets/AudioSpectrum/NMirroredSpectrum.qml
similarity index 78%
rename from Modules/Audio/MirroredSpectrum.qml
rename to Widgets/AudioSpectrum/NMirroredSpectrum.qml
index dd5c98ee..4da22d6d 100644
--- a/Modules/Audio/MirroredSpectrum.qml
+++ b/Widgets/AudioSpectrum/NMirroredSpectrum.qml
@@ -14,10 +14,10 @@ Item {
property real minimumSignalValue: 0.05 // Default to 5% of height
// Pre-compute mirroring
- readonly property int valuesCount: values.length
+ readonly property int valuesCount: (values && Array.isArray(values)) ? values.length : 0
readonly property int totalBars: valuesCount * 2
readonly property real barSlotSize: totalBars > 0 ? (vertical ? height : width) / totalBars : 0
-
+ readonly property bool highQuality: (Settings.data.audio.visualizerType === "low") ? false : true
readonly property real centerY: height / 2
readonly property real centerX: width / 2
@@ -30,7 +30,7 @@ Item {
property int valueIndex: index < root.valuesCount ? root.valuesCount - 1 - index // Mirrored half
: index - root.valuesCount // Normal half
- property real rawAmp: root.values[valueIndex]
+ property real rawAmp: (root.values && root.values[valueIndex] !== undefined) ? root.values[valueIndex] : 0
property real amp: (root.showMinimumSignal && rawAmp === 0) ? root.minimumSignalValue : rawAmp
property real barSize: (vertical ? root.width : root.height) * amp
@@ -38,12 +38,16 @@ Item {
color: root.fillColor
border.color: root.strokeColor
border.width: root.strokeWidth
- antialiasing: true
+ antialiasing: root.highQuality
+ smooth: root.highQuality
width: vertical ? barSize : root.barSlotSize * 0.8
height: vertical ? root.barSlotSize * 0.8 : barSize
x: vertical ? root.centerX - (barSize / 2) : index * root.barSlotSize + (root.barSlotSize * 0.25)
y: vertical ? index * root.barSlotSize + (root.barSlotSize * 0.25) : root.centerY - (barSize / 2)
+
+ // Disable updates when invisible to save GPU
+ visible: root.visible
}
}
}
diff --git a/Modules/Audio/WaveSpectrum.qml b/Widgets/AudioSpectrum/NWaveSpectrum.qml
similarity index 67%
rename from Modules/Audio/WaveSpectrum.qml
rename to Widgets/AudioSpectrum/NWaveSpectrum.qml
index 556726b4..e2a78fe9 100644
--- a/Modules/Audio/WaveSpectrum.qml
+++ b/Widgets/AudioSpectrum/NWaveSpectrum.qml
@@ -13,26 +13,49 @@ Item {
property bool showMinimumSignal: false
property real minimumSignalValue: 0.05 // Default to 5% of height
- // Redraw when necessary
- onWidthChanged: canvas.requestPaint()
- onHeightChanged: canvas.requestPaint()
- onValuesChanged: canvas.requestPaint()
- onFillColorChanged: canvas.requestPaint()
- onStrokeColorChanged: canvas.requestPaint()
- onShowMinimumSignalChanged: canvas.requestPaint()
- onMinimumSignalValueChanged: canvas.requestPaint()
- onVerticalChanged: canvas.requestPaint()
+ // Rendering active state - only redraw when visible and values are changing
+ property bool renderingActive: visible && values && values.length > 0
+
+ // Redraw when necessary - only if rendering is active
+ onWidthChanged: if (renderingActive)
+ canvas.requestPaint()
+ onHeightChanged: if (renderingActive)
+ canvas.requestPaint()
+ onValuesChanged: if (renderingActive)
+ canvas.requestPaint()
+ onFillColorChanged: if (renderingActive)
+ canvas.requestPaint()
+ onStrokeColorChanged: if (renderingActive)
+ canvas.requestPaint()
+ onShowMinimumSignalChanged: if (renderingActive)
+ canvas.requestPaint()
+ onMinimumSignalValueChanged: if (renderingActive)
+ canvas.requestPaint()
+ onVerticalChanged: if (renderingActive)
+ canvas.requestPaint()
+
+ // Clear canvas when not rendering
+ onRenderingActiveChanged: {
+ if (!renderingActive) {
+ var ctx = canvas.getContext("2d")
+ if (ctx)
+ ctx.reset()
+ canvas.requestPaint()
+ }
+ }
Canvas {
id: canvas
anchors.fill: parent
- antialiasing: true
+ antialiasing: false // Disable for better performance - shape is smooth enough without it
+ renderStrategy: Canvas.Threaded // Render in separate thread to reduce main thread load
+ renderTarget: Canvas.FramebufferObject // Use FBO for better performance
onPaint: {
var ctx = getContext("2d")
ctx.reset()
- if (values.length === 0) {
+ if (!values || !Array.isArray(values) || values.length === 0) {
return
}
diff --git a/Widgets/NBox.qml b/Widgets/NBox.qml
index 10ed50bd..7d775bfb 100644
--- a/Widgets/NBox.qml
+++ b/Widgets/NBox.qml
@@ -1,7 +1,5 @@
import QtQuick
import qs.Commons
-import qs.Commons
-import qs.Services
// Rounded group container using the variant surface color.
// To be used in side panels and settings panes to group fields or buttons.
diff --git a/Widgets/NBusyIndicator.qml b/Widgets/NBusyIndicator.qml
index bf93abe8..e720debe 100644
--- a/Widgets/NBusyIndicator.qml
+++ b/Widgets/NBusyIndicator.qml
@@ -1,6 +1,5 @@
import QtQuick
import qs.Commons
-import qs.Services
Item {
id: root
@@ -14,40 +13,50 @@ Item {
implicitWidth: size
implicitHeight: size
- Canvas {
- id: canvas
+ // GPU-optimized spinner - draw once, rotate with GPU transform
+ Item {
+ id: spinner
anchors.fill: parent
- onPaint: {
- var ctx = getContext("2d")
- ctx.reset()
+ // Static canvas - drawn ONCE, then cached
+ Canvas {
+ id: canvas
+ anchors.fill: parent
+ renderStrategy: Canvas.Cooperative // Better performance than Threaded for simple shapes
+ renderTarget: Canvas.FramebufferObject // GPU texture
- var centerX = width / 2
- var centerY = height / 2
- var radius = Math.min(width, height) / 2 - strokeWidth / 2
+ // Enable layer caching - critical for performance!
+ layer.enabled: true
+ layer.smooth: true
- ctx.strokeStyle = root.color
- ctx.lineWidth = Math.max(1, root.strokeWidth)
- ctx.lineCap = "round"
+ Component.onCompleted: {
+ requestPaint()
+ }
- // Draw arc with gap (270 degrees with 90 degree gap)
- ctx.beginPath()
- ctx.arc(centerX, centerY, radius, -Math.PI / 2 + rotationAngle, -Math.PI / 2 + rotationAngle + Math.PI * 1.5)
- ctx.stroke()
+ onPaint: {
+ var ctx = getContext("2d")
+ ctx.reset()
+
+ var centerX = width / 2
+ var centerY = height / 2
+ var radius = Math.min(width, height) / 2 - strokeWidth / 2
+
+ ctx.strokeStyle = root.color
+ ctx.lineWidth = Math.max(1, root.strokeWidth)
+ ctx.lineCap = "round"
+
+ // Draw arc with gap (270 degrees = 3/4 of circle)
+ ctx.beginPath()
+ ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + Math.PI * 1.5)
+ ctx.stroke()
+ }
}
- property real rotationAngle: 0
-
- onRotationAngleChanged: {
- requestPaint()
- }
-
- NumberAnimation {
- target: canvas
- property: "rotationAngle"
+ // Smooth rotation animation - uses GPU transform, NO canvas repaints!
+ RotationAnimation on rotation {
running: root.running
from: 0
- to: 2 * Math.PI
+ to: 360
duration: root.duration
loops: Animation.Infinite
}
diff --git a/Widgets/NButton.qml b/Widgets/NButton.qml
index aed27ab7..ce6c68cf 100644
--- a/Widgets/NButton.qml
+++ b/Widgets/NButton.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
+import qs.Services.UI
Rectangle {
id: root
diff --git a/Widgets/NCircleStat.qml b/Widgets/NCircleStat.qml
index ad5a6971..9cdb338b 100644
--- a/Widgets/NCircleStat.qml
+++ b/Widgets/NCircleStat.qml
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
-import qs.Services
import qs.Widgets
// Compact circular statistic display using Layout management
@@ -24,8 +23,27 @@ Rectangle {
border.color: flat ? Color.transparent : Color.mSurfaceVariant
border.width: flat ? 0 : Style.borderS
- // Repaint gauge when the bound value changes
- onValueChanged: gauge.requestPaint()
+ // Animated value for smooth transitions - reduces repaint frequency
+ property real animatedValue: value
+
+ Behavior on animatedValue {
+ enabled: !Settings.data.general.animationDisabled
+ NumberAnimation {
+ duration: Style.animationNormal
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ // Repaint gauge when animated value changes (throttled by animation)
+ onAnimatedValueChanged: repaintTimer.restart()
+
+ // Debounce timer to limit repaint frequency during rapid value changes
+ Timer {
+ id: repaintTimer
+ interval: 16 // ~60 FPS max
+ repeat: false
+ onTriggered: gauge.requestPaint()
+ }
ColumnLayout {
id: mainLayout
@@ -45,7 +63,18 @@ Rectangle {
Canvas {
id: gauge
anchors.fill: parent
- renderStrategy: Canvas.Immediate
+
+ // Optimized Canvas settings for better GPU performance
+ renderStrategy: Canvas.Cooperative // Better performance than Immediate
+ renderTarget: Canvas.FramebufferObject // GPU texture rendering
+
+ // Enable layer caching - critical for performance!
+ layer.enabled: true
+ layer.smooth: true
+
+ Component.onCompleted: {
+ requestPaint()
+ }
onPaint: {
const ctx = getContext("2d")
@@ -68,7 +97,7 @@ Rectangle {
ctx.stroke()
// Value arc with gradient starting at 25%
- const ratio = Math.max(0, Math.min(1, root.value / 100))
+ const ratio = Math.max(0, Math.min(1, root.animatedValue / 100))
const end = start + (endBg - start) * ratio
// Calculate gradient start point (25% into the arc)
@@ -97,7 +126,7 @@ Rectangle {
id: valueLabel
anchors.centerIn: parent
anchors.verticalCenterOffset: -4 * contentScale
- text: `${root.value}${root.suffix}`
+ text: `${Math.round(root.value)}${root.suffix}`
pointSize: Style.fontSizeM * contentScale * 0.9
font.weight: Style.fontWeightBold
color: Color.mOnSurface
diff --git a/Widgets/NClock.qml b/Widgets/NClock.qml
new file mode 100644
index 00000000..5615aa45
--- /dev/null
+++ b/Widgets/NClock.qml
@@ -0,0 +1,268 @@
+import QtQuick
+import QtQuick.Layouts
+import qs.Commons
+import qs.Widgets
+import Quickshell
+import "../Helpers/ColorsConvert.js" as ColorsConvert
+
+Item {
+ id: root
+
+ property var now: Time.now
+
+ // Style: "analog" or "digital"
+ property string clockStyle: "analog"
+
+ // Color properties
+ property color backgroundColor: Color.mPrimary
+ property color clockColor: Color.mOnPrimary
+
+ property color secondHandColor: {
+ var defaultColor = Color.mError
+ var bestContrast = 1.0 // 1.0 is "no contrast"
+ var bestColor = defaultColor
+ var candidates = [Color.mSecondary, Color.mTertiary, Color.mError]
+
+ const minContrast = 1.149
+
+ for (var i = 0; i < candidates.length; i++) {
+ var candidate = candidates[i]
+ var contrastClock = ColorsConvert.getContrastRatio(candidate.toString(), clockColor.toString())
+ if (contrastClock < minContrast) {
+ continue
+ }
+ var contrastBg = ColorsConvert.getContrastRatio(candidate.toString(), backgroundColor.toString())
+ if (contrastBg < minContrast) {
+ continue
+ }
+
+ var currentWorstContrast = Math.min(contrastBg, contrastClock)
+
+ if (currentWorstContrast > bestContrast) {
+ bestContrast = currentWorstContrast
+ bestColor = candidate
+ }
+ }
+
+ return bestColor
+ }
+
+ property color progressColor: root.secondHandColor
+
+ height: Math.round((Style.fontSizeXXXL * 1.9) / 2 * Style.uiScaleRatio) * 2
+ width: root.height
+
+ Loader {
+ id: clockLoader
+ anchors.fill: parent
+
+ sourceComponent: root.clockStyle === "analog" ? analogClockComponent : digitalClockComponent
+
+ onLoaded: {
+ item.now = Qt.binding(function () {
+ return root.now
+ })
+ item.backgroundColor = Qt.binding(function () {
+ return root.backgroundColor
+ })
+ item.clockColor = Qt.binding(function () {
+ return root.clockColor
+ })
+ if (item.hasOwnProperty("secondHandColor")) {
+ item.secondHandColor = Qt.binding(function () {
+ return root.secondHandColor
+ })
+ }
+ if (item.hasOwnProperty("progressColor")) {
+ item.progressColor = Qt.binding(function () {
+ return root.progressColor
+ })
+ }
+ }
+ }
+
+ // Analog Clock Component
+ component NClockAnalog: Item {
+ property var now
+ property color backgroundColor: Color.mPrimary
+ property color clockColor: Color.mOnPrimary
+ property color secondHandColor: Color.mError
+ anchors.fill: parent
+
+ Canvas {
+ id: clockCanvas
+ anchors.fill: parent
+
+ Connections {
+ target: Time
+ function onNowChanged() {
+ clockCanvas.requestPaint()
+ }
+ }
+
+ onPaint: {
+ var currentTime = Time.now
+ var hours = currentTime.getHours()
+ var minutes = currentTime.getMinutes()
+ var seconds = currentTime.getSeconds()
+
+ const markAlpha = 0.7
+ var ctx = getContext("2d")
+ ctx.reset()
+ ctx.translate(width / 2, height / 2)
+ var radius = Math.min(width, height) / 2
+
+ // Hour marks
+ ctx.strokeStyle = Qt.alpha(clockColor, markAlpha)
+ ctx.lineWidth = 2 * Style.uiScaleRatio
+ var scaleFactor = 0.7
+
+ for (var i = 0; i < 12; i++) {
+ var scaleFactor = 0.8
+ if (i % 3 === 0) {
+ scaleFactor = 0.65
+ }
+ ctx.save()
+ ctx.rotate(i * Math.PI / 6)
+ ctx.beginPath()
+ ctx.moveTo(0, -radius * scaleFactor)
+ ctx.lineTo(0, -radius)
+ ctx.stroke()
+ ctx.restore()
+ }
+
+ // Hour hand
+ ctx.save()
+ var hourAngle = (hours % 12 + minutes / 60) * Math.PI / 6
+ ctx.rotate(hourAngle)
+ ctx.strokeStyle = clockColor
+ ctx.lineWidth = 3 * Style.uiScaleRatio
+ ctx.lineCap = "round"
+ ctx.beginPath()
+ ctx.moveTo(0, 0)
+ ctx.lineTo(0, -radius * 0.6)
+ ctx.stroke()
+ ctx.restore()
+
+ // Minute hand
+ ctx.save()
+ var minuteAngle = (minutes + seconds / 60) * Math.PI / 30
+ ctx.rotate(minuteAngle)
+ ctx.strokeStyle = clockColor
+ ctx.lineWidth = 2 * Style.uiScaleRatio
+ ctx.lineCap = "round"
+ ctx.beginPath()
+ ctx.moveTo(0, 0)
+ ctx.lineTo(0, -radius * 0.9)
+ ctx.stroke()
+ ctx.restore()
+
+ // Second hand
+ ctx.save()
+ var secondAngle = seconds * Math.PI / 30
+ ctx.rotate(secondAngle)
+ ctx.strokeStyle = secondHandColor
+ ctx.lineWidth = 1.6 * Style.uiScaleRatio
+ ctx.lineCap = "round"
+ ctx.beginPath()
+ ctx.moveTo(0, 0)
+ ctx.lineTo(0, -radius)
+ ctx.stroke()
+ ctx.restore()
+
+ // Center dot
+ ctx.beginPath()
+ ctx.arc(0, 0, 3 * Style.uiScaleRatio, 0, 2 * Math.PI)
+ ctx.fillStyle = clockColor
+ ctx.fill()
+ }
+
+ Component.onCompleted: requestPaint()
+ }
+ }
+
+ // Digital Clock Component
+ component NClockDigital: Item {
+ property var now
+ property color backgroundColor: Color.mPrimary
+ property color clockColor: Color.mOnPrimary
+ property color progressColor: Color.mError
+
+ anchors.fill: parent
+
+ // Digital clock's seconds circular progress
+ Canvas {
+ id: secondsProgress
+ anchors.fill: parent
+ property real progress: now.getSeconds() / 60
+ onProgressChanged: requestPaint()
+ Connections {
+ target: Time
+ function onNowChanged() {
+ const total = now.getSeconds() * 1000 + now.getMilliseconds()
+ secondsProgress.progress = total / 60000
+ }
+ }
+ onPaint: {
+ var ctx = getContext("2d")
+ var centerX = width / 2
+ var centerY = height / 2
+ var radius = Math.min(width, height) / 2 - 3
+ ctx.reset()
+
+ // Background circle
+ ctx.beginPath()
+ ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI)
+ ctx.lineWidth = 2.5
+ ctx.strokeStyle = Qt.alpha(clockColor, 0.15)
+ ctx.stroke()
+
+ // Progress arc
+ ctx.beginPath()
+ ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progress * 2 * Math.PI)
+ ctx.lineWidth = 2.5
+ ctx.strokeStyle = progressColor
+ ctx.lineCap = "round"
+ ctx.stroke()
+ }
+ }
+
+ // Digital clock
+ ColumnLayout {
+ anchors.centerIn: parent
+ spacing: -Style.marginXXS
+
+ NText {
+ text: {
+ var t = Settings.data.location.use12hourFormat ? I18n.locale.toString(now, "hh AP") : I18n.locale.toString(now, "HH")
+ return t.split(" ")[0]
+ }
+
+ pointSize: Style.fontSizeXS
+ font.weight: Style.fontWeightBold
+ color: clockColor
+ family: Settings.data.ui.fontFixed
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ NText {
+ text: Qt.formatTime(now, "mm")
+ pointSize: Style.fontSizeXXS
+ font.weight: Style.fontWeightBold
+ color: clockColor
+ family: Settings.data.ui.fontFixed
+ Layout.alignment: Qt.AlignHCenter
+ }
+ }
+ }
+
+ Component {
+ id: analogClockComponent
+ NClockAnalog {}
+ }
+
+ Component {
+ id: digitalClockComponent
+ NClockDigital {}
+ }
+}
diff --git a/Widgets/NColorPicker.qml b/Widgets/NColorPicker.qml
index a035facd..d25087c4 100644
--- a/Widgets/NColorPicker.qml
+++ b/Widgets/NColorPicker.qml
@@ -2,13 +2,12 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
import qs.Widgets
Rectangle {
id: root
- property color selectedColor: "#000000"
+ property color selectedColor: Color.black
signal colorSelected(color color)
diff --git a/Widgets/NColorPickerDialog.qml b/Widgets/NColorPickerDialog.qml
index adf701a6..4f142f16 100644
--- a/Widgets/NColorPickerDialog.qml
+++ b/Widgets/NColorPickerDialog.qml
@@ -2,13 +2,12 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
import qs.Widgets
Popup {
id: root
- property color selectedColor: "#000000"
+ property color selectedColor: Color.black
property real currentHue: 0
property real currentSaturation: 0
@@ -27,14 +26,6 @@ Popup {
modal: true
- onOpened: {
- PanelService.willOpenPopup(root)
- }
-
- onClosed: {
- PanelService.willClosePopup(root)
- }
-
function rgbToHsv(r, g, b) {
r /= 255
g /= 255
@@ -181,7 +172,7 @@ Popup {
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeL
font.weight: Font.Bold
- color: root.selectedColor.r + root.selectedColor.g + root.selectedColor.b > 1.5 ? "#000000" : "#FFFFFF"
+ color: root.selectedColor.r + root.selectedColor.g + root.selectedColor.b > 1.5 ? Color.black : Color.white
Layout.alignment: Qt.AlignHCenter
}
@@ -189,7 +180,7 @@ Popup {
text: "RGB(" + Math.round(root.selectedColor.r * 255) + ", " + Math.round(root.selectedColor.g * 255) + ", " + Math.round(root.selectedColor.b * 255) + ")"
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeM
- color: root.selectedColor.r + root.selectedColor.g + root.selectedColor.b > 1.5 ? "#000000" : "#FFFFFF"
+ color: root.selectedColor.r + root.selectedColor.g + root.selectedColor.b > 1.5 ? Color.black : Color.white
Layout.alignment: Qt.AlignHCenter
}
@@ -381,7 +372,7 @@ Popup {
flow: Flow.LeftToRight
Repeater {
- model: [Color.mPrimary, Color.mSecondary, Color.mTertiary, Color.mError, Color.mSurface, Color.mSurfaceVariant, Color.mOutline, "#FFFFFF", "#000000"]
+ model: [Color.mPrimary, Color.mSecondary, Color.mTertiary, Color.mError, Color.mSurface, Color.mSurfaceVariant, Color.mOutline, Color.white, Color.black]
Rectangle {
width: 24
@@ -430,7 +421,7 @@ Popup {
flow: Flow.LeftToRight
Repeater {
- model: ["#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3", "#03A9F4", "#00BCD4", "#009688", "#4CAF50", "#8BC34A", "#CDDC39", "#FFEB3B", "#FFC107", "#FF9800", "#FF5722", "#795548", "#9E9E9E", "#E74C3C", "#E67E22", "#F1C40F", "#2ECC71", "#1ABC9C", "#3498DB", "#2980B9", "#9B59B6", "#34495E", "#2C3E50", "#95A5A6", "#7F8C8D", "#FFFFFF", "#000000"]
+ model: ["#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3", "#03A9F4", "#00BCD4", "#009688", "#4CAF50", "#8BC34A", "#CDDC39", "#FFEB3B", "#FFC107", "#FF9800", "#FF5722", "#795548", "#9E9E9E", "#E74C3C", "#E67E22", "#F1C40F", "#2ECC71", "#1ABC9C", "#3498DB", "#2980B9", "#9B59B6", "#34495E", "#2C3E50", "#95A5A6", "#7F8C8D", Color.white, Color.black]
Rectangle {
width: 24
diff --git a/Widgets/NComboBox.qml b/Widgets/NComboBox.qml
index 0104640f..b0c7faed 100644
--- a/Widgets/NComboBox.qml
+++ b/Widgets/NComboBox.qml
@@ -2,13 +2,12 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
import qs.Widgets
RowLayout {
id: root
- property real minimumWidth: 280 * Style.uiScaleRatio
+ property real minimumWidth: 200 * Style.uiScaleRatio
property real popupHeight: 180 * Style.uiScaleRatio
property string label: ""
@@ -109,14 +108,6 @@ RowLayout {
implicitHeight: Math.min(root.popupHeight, contentItem.implicitHeight + Style.marginM * 2)
padding: Style.marginM
- onOpened: {
- PanelService.willOpenPopup(root)
- }
-
- onClosed: {
- PanelService.willClosePopup(root)
- }
-
contentItem: NListView {
model: combo.popup.visible ? root.model : null
implicitHeight: contentHeight
diff --git a/Widgets/NContextMenu.qml b/Widgets/NContextMenu.qml
index cbfca5a0..eed0a423 100644
--- a/Widgets/NContextMenu.qml
+++ b/Widgets/NContextMenu.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
Popup {
id: root
@@ -10,15 +9,14 @@ Popup {
property alias model: listView.model
property real itemHeight: 36
property real itemPadding: Style.marginM
+ property int verticalPolicy: ScrollBar.AsNeeded
+ property int horizontalPolicy: ScrollBar.AsNeeded
signal triggered(string action)
width: 180
padding: Style.marginS
- onOpened: PanelService.willOpenPopup(root)
- onClosed: PanelService.willClosePopup(root)
-
background: Rectangle {
color: Color.mSurfaceVariant
border.color: Color.mOutline
@@ -31,6 +29,8 @@ Popup {
implicitHeight: contentHeight
spacing: Style.marginXXS
interactive: contentHeight > root.height
+ verticalPolicy: root.verticalPolicy
+ horizontalPolicy: root.horizontalPolicy
delegate: ItemDelegate {
id: menuItem
diff --git a/Widgets/NDivider.qml b/Widgets/NDivider.qml
index d11e82c7..737807d7 100644
--- a/Widgets/NDivider.qml
+++ b/Widgets/NDivider.qml
@@ -2,7 +2,6 @@ import QtQuick
import Quickshell
import Quickshell.Widgets
import qs.Commons
-import qs.Services
Rectangle {
width: parent.width
diff --git a/Widgets/NDropShadows.qml b/Widgets/NDropShadows.qml
new file mode 100644
index 00000000..1182f579
--- /dev/null
+++ b/Widgets/NDropShadows.qml
@@ -0,0 +1,26 @@
+import QtQuick
+import QtQuick.Effects
+import qs.Commons
+import qs.Services.Power
+
+// Unified shadow system
+Item {
+ id: root
+
+ required property var source
+
+ property bool autoPaddingEnabled: false
+
+ layer.enabled: Settings.data.general.enableShadows && !PowerProfileService.noctaliaPerformanceMode
+ layer.effect: MultiEffect {
+ source: root.source
+ shadowEnabled: true
+ blurMax: Style.shadowBlurMax
+ shadowBlur: Style.shadowBlur
+ shadowOpacity: Style.shadowOpacity
+ shadowColor: Color.black
+ shadowHorizontalOffset: Settings.data.general.shadowOffsetX
+ shadowVerticalOffset: Settings.data.general.shadowOffsetY
+ autoPaddingEnabled: root.autoPaddingEnabled
+ }
+}
diff --git a/Widgets/NFilePicker.qml b/Widgets/NFilePicker.qml
index 9489d114..84eb473d 100644
--- a/Widgets/NFilePicker.qml
+++ b/Widgets/NFilePicker.qml
@@ -5,7 +5,6 @@ import Qt.labs.folderlistmodel
import Quickshell
import Quickshell.Io
import qs.Commons
-import qs.Services
import qs.Widgets
import "../Helpers/FuzzySort.js" as FuzzySort
@@ -28,14 +27,6 @@ Popup {
signal accepted(var paths)
signal cancelled
- onOpened: {
- PanelService.willOpenPopup(root)
- }
-
- onClosed: {
- PanelService.willClosePopup(root)
- }
-
function openFilePicker() {
if (!root.currentPath)
root.currentPath = root.initialPath
diff --git a/Widgets/NIconButton.qml b/Widgets/NIconButton.qml
index 2fd507e5..c72d7d38 100644
--- a/Widgets/NIconButton.qml
+++ b/Widgets/NIconButton.qml
@@ -2,7 +2,7 @@ import QtQuick
import Quickshell
import Quickshell.Widgets
import qs.Commons
-import qs.Services
+import qs.Services.UI
Rectangle {
id: root
diff --git a/Widgets/NIconButtonHot.qml b/Widgets/NIconButtonHot.qml
index ff80b3ef..481d526f 100644
--- a/Widgets/NIconButtonHot.qml
+++ b/Widgets/NIconButtonHot.qml
@@ -3,7 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Effects
import qs.Commons
-import qs.Services
+import qs.Services.UI
Rectangle {
id: root
diff --git a/Widgets/NIconPicker.qml b/Widgets/NIconPicker.qml
index 2a4916e9..f37e01f7 100644
--- a/Widgets/NIconPicker.qml
+++ b/Widgets/NIconPicker.qml
@@ -4,7 +4,6 @@ import QtQuick.Layouts
import QtQuick.Window
import qs.Commons
import qs.Widgets
-import qs.Services
Popup {
id: root
@@ -15,8 +14,8 @@ Popup {
signal iconSelected(string iconName)
- width: 900 * Style.uiScaleRatio
- height: 700 * Style.uiScaleRatio
+ width: Math.round(900 * Style.uiScaleRatio)
+ height: Math.round(700 * Style.uiScaleRatio)
anchors.centerIn: Overlay.overlay
padding: Style.marginXL
@@ -36,11 +35,6 @@ Popup {
selectedIcon = initialIcon
query = initialIcon
searchInput.forceActiveFocus()
- PanelService.willOpenPopup(root)
- }
-
- onClosed: {
- PanelService.willClosePopup(root)
}
background: Rectangle {
diff --git a/Widgets/NImageCached.qml b/Widgets/NImageCached.qml
index 2645d479..82be77bb 100644
--- a/Widgets/NImageCached.qml
+++ b/Widgets/NImageCached.qml
@@ -31,9 +31,9 @@ Image {
}
onCachePathChanged: {
if (imageHash && cachePath) {
- // Check if cache file exists before trying to load it
- cacheChecker.command = ["test", "-f", cachePath]
- cacheChecker.running = true
+ // Try to load the cached version, failure will be detected below in onStatusChanged
+ // Failure is expected and warnings are ok in the console. Don't try to improve without consulting.
+ source = cachePath
}
}
onStatusChanged: {
@@ -49,19 +49,4 @@ Image {
})
}
}
-
- // Check if cache file exists to avoid warnings
- Process {
- id: cacheChecker
- running: false
- onExited: function (exitCode) {
- if (exitCode === 0 && root.cachePath) {
- // Cache file exists, load it
- root.source = root.cachePath
- } else if (root.imagePath) {
- // Cache doesn't exist, load original directly
- root.source = root.imagePath
- }
- }
- }
}
diff --git a/Widgets/NImageCircled.qml b/Widgets/NImageCircled.qml
index 55ef7568..c97e4440 100644
--- a/Widgets/NImageCircled.qml
+++ b/Widgets/NImageCircled.qml
@@ -3,7 +3,6 @@ import QtQuick.Effects
import Quickshell
import Quickshell.Widgets
import qs.Commons
-import qs.Services
Rectangle {
id: root
diff --git a/Widgets/NImageRounded.qml b/Widgets/NImageRounded.qml
index 32987e28..a7a5789f 100644
--- a/Widgets/NImageRounded.qml
+++ b/Widgets/NImageRounded.qml
@@ -3,7 +3,6 @@ import QtQuick.Effects
import Quickshell
import Quickshell.Widgets
import qs.Commons
-import qs.Services
Rectangle {
id: root
diff --git a/Widgets/NInputAction.qml b/Widgets/NInputAction.qml
index b4b86879..6fac9618 100644
--- a/Widgets/NInputAction.qml
+++ b/Widgets/NInputAction.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
// Input and button row
RowLayout {
diff --git a/Widgets/NListView.qml b/Widgets/NListView.qml
index 9545bd2e..5fc13b90 100644
--- a/Widgets/NListView.qml
+++ b/Widgets/NListView.qml
@@ -13,7 +13,13 @@ Item {
property real handleWidth: 6
property real handleRadius: Style.radiusM
property int verticalPolicy: ScrollBar.AsNeeded
- property int horizontalPolicy: ScrollBar.AsNeeded
+ property int horizontalPolicy: ScrollBar.AlwaysOff
+ readonly property bool verticalScrollBarActive: {
+ if (listView.ScrollBar.vertical.policy === ScrollBar.AlwaysOff)
+ return false
+ return listView.contentHeight > listView.height
+ }
+ readonly property real scrollBarWidth: verticalScrollBarActive ? handleWidth : 0
// Forward ListView properties
property alias model: listView.model
@@ -118,7 +124,6 @@ Item {
x: listView.mirrored ? 0 : listView.width - width
y: 0
height: listView.height
- active: listView.ScrollBar.horizontal.active
policy: root.verticalPolicy
contentItem: Rectangle {
@@ -155,49 +160,5 @@ Item {
}
}
}
-
- ScrollBar.horizontal: ScrollBar {
- id: horizontalScrollBar
- parent: listView
- x: 0
- y: listView.height - height
- width: listView.width
- active: listView.ScrollBar.vertical.active
- policy: root.horizontalPolicy
-
- contentItem: Rectangle {
- implicitWidth: 100
- implicitHeight: root.handleWidth
- radius: root.handleRadius
- color: parent.pressed ? root.handlePressedColor : parent.hovered ? root.handleHoverColor : root.handleColor
- opacity: parent.policy === ScrollBar.AlwaysOn || parent.active ? 1.0 : 0.0
-
- Behavior on opacity {
- NumberAnimation {
- duration: Style.animationFast
- }
- }
-
- Behavior on color {
- ColorAnimation {
- duration: Style.animationFast
- }
- }
- }
-
- background: Rectangle {
- implicitWidth: 100
- implicitHeight: root.handleWidth
- color: root.trackColor
- opacity: parent.policy === ScrollBar.AlwaysOn || parent.active ? 0.3 : 0.0
- radius: root.handleRadius / 2
-
- Behavior on opacity {
- NumberAnimation {
- duration: Style.animationFast
- }
- }
- }
- }
}
}
diff --git a/Widgets/NPanel.qml b/Widgets/NPanel.qml
deleted file mode 100644
index 98dfb453..00000000
--- a/Widgets/NPanel.qml
+++ /dev/null
@@ -1,645 +0,0 @@
-import QtQuick
-import QtQuick.Effects
-import Quickshell
-import Quickshell.Wayland
-import qs.Commons
-import qs.Services
-
-Loader {
- id: root
-
- property ShellScreen screen
-
- readonly property real opacityThreshold: 0.33
- property bool attachedToBar: (Settings.data.ui.panelsAttachedToBar && Settings.data.bar.backgroundOpacity > opacityThreshold)
- property bool useOverlay: Settings.data.ui.panelsOverlayLayer
-
- property Component panelContent: null
-
- // Panel size properties. Can be set directly on NPanel, or dynamically by the content.
- // For dynamic sizing, the content should expose contentPreferredWidth, contentPreferredHeight,
- // contentPreferredWidthRatio, or contentPreferredHeightRatio properties.
- // Changes to these properties will be animated smoothly (except during panel dragging).
- property real preferredWidth: 700
- property real preferredHeight: 900
- property real preferredWidthRatio
- property real preferredHeightRatio
- property color panelBackgroundColor: Color.mSurface
- property color panelBorderColor: Color.mOutline
- property bool draggable: false
- property var buttonItem: null
- property string buttonName: ""
-
- property bool panelAnchorHorizontalCenter: false
- property bool panelAnchorVerticalCenter: false
- property bool panelAnchorTop: false
- property bool panelAnchorBottom: false
- property bool panelAnchorLeft: false
- property bool panelAnchorRight: false
-
- // Properties to support positioning relative to the opener (button)
- property bool useButtonPosition: false
- property point buttonPosition: Qt.point(0, 0)
- property int buttonWidth: 0
- property int buttonHeight: 0
-
- property bool panelKeyboardFocus: false
- property bool backgroundClickEnabled: true
-
- // Animation properties
- property real panelBackgroundOpacity: 0
- property real panelContentOpacity: 0
- property real dimmingOpacity: 0
-
- readonly property string barPosition: Settings.data.bar.position
- readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
- readonly property real verticalBarWidth: Style.barHeight
-
- // Effective anchor properties - combines explicit anchors with implicit anchoring from useButtonPosition
- readonly property bool effectivePanelAnchorTop: panelAnchorTop || (useButtonPosition && barPosition === "top")
- readonly property bool effectivePanelAnchorBottom: panelAnchorBottom || (useButtonPosition && barPosition === "bottom")
- readonly property bool effectivePanelAnchorLeft: panelAnchorLeft || (useButtonPosition && barPosition === "left")
- readonly property bool effectivePanelAnchorRight: panelAnchorRight || (useButtonPosition && barPosition === "right")
-
- signal opened
- signal closed
-
- active: false
- asynchronous: true
-
- Component.onCompleted: {
- PanelService.registerPanel(root)
- }
-
- // -----------------------------------------
- // Functions to control background click behavior
- function disableBackgroundClick() {
- backgroundClickEnabled = false
- }
-
- function enableBackgroundClick() {
- // Add a small delay to prevent immediate close after drag release
- enableBackgroundClickTimer.restart()
- }
-
- Timer {
- id: enableBackgroundClickTimer
- interval: 100
- repeat: false
- onTriggered: backgroundClickEnabled = true
- }
-
- // -----------------------------------------
- function toggle(buttonItem, buttonName) {
- if (!active) {
- open(buttonItem, buttonName)
- } else {
- close()
- }
- }
-
- // -----------------------------------------
- function open(buttonItem, buttonName) {
- root.buttonItem = buttonItem
- root.buttonName = buttonName || ""
-
- setPosition()
-
- PanelService.willOpenPanel(root)
-
- backgroundClickEnabled = true
- active = true
- root.opened()
- }
-
- // -----------------------------------------
- function close() {
- dimmingOpacity = 0
- panelBackgroundOpacity = 0
- panelContentOpacity = 0
- root.closed()
- active = false
- useButtonPosition = false
- backgroundClickEnabled = true
- PanelService.closedPanel(root)
- }
-
- // -----------------------------------------
- function setPosition() {
- // If we have a button name, we are landing here from an IPC call.
- // IPC calls have no idead on which screen they panel will spawn.
- // Resolve the button name to a proper button item now that we have a screen.
- if (buttonName !== "" && root.screen !== null) {
- buttonItem = BarService.lookupWidget(buttonName, root.screen.name)
- }
-
- // Get the button position if provided
- if (buttonItem !== undefined && buttonItem !== null) {
- useButtonPosition = true
- var itemPos = buttonItem.mapToItem(null, 0, 0)
- buttonPosition = Qt.point(itemPos.x, itemPos.y)
- buttonWidth = buttonItem.width
- buttonHeight = buttonItem.height
- } else {
- useButtonPosition = false
- }
- }
-
- // -----------------------------------------
- sourceComponent: Component {
- // PanelWindow has its own screen property inherited of QsWindow
- PanelWindow {
- id: panelWindow
-
- readonly property bool barIsVisible: (screen !== null) && (Settings.data.bar.monitors.includes(screen.name) || (Settings.data.bar.monitors.length === 0))
-
- Component.onCompleted: {
- Logger.d("NPanel", "Opened", root.objectName, "on", screen.name)
- dimmingOpacity = Style.opacityHeavy
- }
-
- Connections {
- target: panelWindow
- function onScreenChanged() {
- root.screen = screen
-
- // If called from IPC always reposition if screen is updated
- if (buttonName) {
- setPosition()
- }
- Logger.d("NPanel", "OnScreenChanged", root.screen.name)
- }
- }
-
- color: Color.transparent
-
- WlrLayershell.exclusionMode: ExclusionMode.Ignore
- WlrLayershell.namespace: "noctalia-panel"
- WlrLayershell.layer: useOverlay ? WlrLayer.Overlay : WlrLayer.Top
- WlrLayershell.keyboardFocus: root.panelKeyboardFocus ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
-
- Region {
- id: maskRegion
- }
-
- Behavior on color {
- ColorAnimation {
- duration: Style.animationNormal
- }
- }
-
- anchors.top: true
- anchors.left: true
- anchors.right: true
- anchors.bottom: true
-
- // Close any panel with Esc without requiring focus
- Shortcut {
- sequences: ["Escape"]
- enabled: root.active
- onActivated: root.close()
- context: Qt.WindowShortcut
- }
-
- // Clicking outside of the rectangle to close
- MouseArea {
- anchors.fill: parent
- enabled: root.backgroundClickEnabled
- acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
- onClicked: root.close()
- }
-
- // The actual panel's content
- NShapedRectangle {
- id: panelBackground
-
- backgroundColor: (attachedToBar && (topLeftInverted || topRightInverted || bottomLeftInverted || bottomRightInverted)) ? Qt.alpha(panelBackgroundColor, Settings.data.bar.backgroundOpacity) : panelBackgroundColor
-
- topLeftRadius: Style.radiusL
- topRightRadius: Style.radiusL
- bottomLeftRadius: Style.radiusL
- bottomRightRadius: Style.radiusL
-
-
- /*// Drop shadow effect
- layer.enabled: true
- layer.effect: MultiEffect {
- shadowEnabled: true
- shadowBlur: 0.85
- shadowOpacity: 0.45
- shadowColor: Color.mShadow
- shadowHorizontalOffset: (barPosition === "left" || barPosition === "top") ? 6 : - 6
- shadowVerticalOffset: (barPosition === "left" || barPosition === "top") ? 6 : - 6
- }*/
-
- // Set inverted corners based on panel anchors and bar position
-
- // Top-left corner
- topLeftInverted: {
- if (!attachedToBar)
- return false
-
- // Inverted if panel is anchored to top edge (bar is at top)
- if (effectivePanelAnchorTop)
- return true
- // Or if panel is anchored to left edge (bar is at left)
- if (effectivePanelAnchorLeft)
- return true
- return false
- }
- topLeftInvertedDirection: effectivePanelAnchorTop ? "horizontal" : "vertical"
-
- // Top-right corner
- topRightInverted: {
- if (!attachedToBar)
- return false
-
- // Inverted if panel is anchored to top edge (bar is at top)
- if (effectivePanelAnchorTop)
- return true
- // Or if panel is anchored to right edge (bar is at right)
- if (effectivePanelAnchorRight)
- return true
- return false
- }
- topRightInvertedDirection: effectivePanelAnchorTop ? "horizontal" : "vertical"
-
- // Bottom-left corner
- bottomLeftInverted: {
- if (!attachedToBar)
- return false
-
- // Inverted if panel is anchored to bottom edge (bar is at bottom)
- if (effectivePanelAnchorBottom)
- return true
- // Or if panel is anchored to left edge (bar is at left)
- if (effectivePanelAnchorLeft)
- return true
- return false
- }
- bottomLeftInvertedDirection: effectivePanelAnchorBottom ? "horizontal" : "vertical"
-
- // Bottom-right corner
- bottomRightInverted: {
- if (!attachedToBar)
- return false
-
- // Inverted if panel is anchored to bottom edge (bar is at bottom)
- if (effectivePanelAnchorBottom)
- return true
- // Or if panel is anchored to right edge (bar is at right)
- if (effectivePanelAnchorRight)
- return true
- return false
- }
- bottomRightInvertedDirection: effectivePanelAnchorBottom ? "horizontal" : "vertical"
-
- // Dragging support
- property bool draggable: root.draggable
- property bool isDragged: false
- property real manualX: 0
- property real manualY: 0
- width: {
- var w
- if (root.preferredWidthRatio !== undefined) {
- w = Math.round(Math.max(screen?.width * root.preferredWidthRatio, root.preferredWidth))
- } else {
- w = root.preferredWidth
- }
- // Clamp width so it is never bigger than the screen
- return Math.min(w, screen?.width - Style.marginL * 2)
- }
- height: {
- var h
- if (root.preferredHeightRatio !== undefined) {
- h = Math.round(Math.max(screen?.height * root.preferredHeightRatio, root.preferredHeight))
- } else {
- h = root.preferredHeight
- }
-
- // Clamp height so it is never bigger than the screen
- return Math.min(h, screen?.height - Style.barHeight - Style.marginL * 2)
- }
-
- opacity: root.panelBackgroundOpacity
- x: isDragged ? manualX : calculatedX
- y: isDragged ? manualY : calculatedY
-
- // Animate width and height changes smoothly
- Behavior on width {
- enabled: !panelBackground.isDragged
- NumberAnimation {
- duration: Style.animationFast
- easing.type: Easing.InOutQuad
- }
- }
-
- Behavior on height {
- enabled: !panelBackground.isDragged
- NumberAnimation {
- duration: Style.animationFast
- easing.type: Easing.InOutQuad
- }
- }
-
- // ---------------------------------------------
- // Does not account for corners are they are negligible and helps keep the code clean.
- // ---------------------------------------------
- property real marginTop: {
- if (!barIsVisible) {
- return 0
- }
-
- switch (barPosition) {
- case "top":
- return (Style.barHeight + (attachedToBar ? 0 : Style.marginS)) + (Settings.data.bar.floating ? Math.round(Settings.data.bar.marginVertical * Style.marginXL) : 0)
- default:
- return attachedToBar ? 0 : Style.marginS
- }
- }
-
- property real marginBottom: {
- if (!barIsVisible) {
- return 0
- }
- switch (barPosition) {
- case "bottom":
- return (Style.barHeight + (attachedToBar ? 0 : Style.marginS)) + (Settings.data.bar.floating ? Math.round(Settings.data.bar.marginVertical * Style.marginXL) : 0)
- default:
- return attachedToBar ? 0 : Style.marginS
- }
- }
-
- property real marginLeft: {
- if (!barIsVisible) {
- return 0
- }
- switch (barPosition) {
- case "left":
- return (Style.barHeight + (attachedToBar ? 0 : Style.marginS)) + (Settings.data.bar.floating ? Math.round(Settings.data.bar.marginHorizontal * Style.marginXL) : 0)
- default:
- return attachedToBar ? 0 : Style.marginS
- }
- }
-
- property real marginRight: {
- if (!barIsVisible) {
- return 0
- }
- switch (barPosition) {
- case "right":
- return (Style.barHeight + (attachedToBar ? 0 : Style.marginS)) + (Settings.data.bar.floating ? Math.round(Settings.data.bar.marginHorizontal * Style.marginXL) : 0)
- default:
- return attachedToBar ? 0 : Style.marginS
- }
- }
-
- // ---------------------------------------------
- property int calculatedX: {
- // Priority to fixed anchoring
- if (panelAnchorHorizontalCenter) {
- // Center horizontally but respect bar margins
- var centerX = Math.round((panelWindow.width - panelBackground.width) / 2)
- var minX = marginLeft
- var maxX = panelWindow.width - panelBackground.width - marginRight
- return Math.round(Math.max(minX, Math.min(centerX, maxX)))
- } else if (panelAnchorLeft) {
- return marginLeft
- } else if (panelAnchorRight) {
- return Math.round(panelWindow.width - panelBackground.width - marginRight)
- }
-
- // No fixed anchoring
- if (barIsVertical) {
- // Vertical bar
- if (barPosition === "right") {
- // To the left of the right bar
- return Math.round(panelWindow.width - panelBackground.width - marginRight)
- } else {
- // To the right of the left bar
- return marginLeft
- }
- } else {
- // Horizontal bar
- if (root.useButtonPosition) {
- // Position panel relative to button
- var targetX = buttonPosition.x + (buttonWidth / 2) - (panelBackground.width / 2)
- // Keep panel within screen bounds
- var maxX = panelWindow.width - panelBackground.width - marginRight
- var minX = marginLeft
-
- if (Settings.data.bar.floating) {
- maxX -= Settings.data.bar.marginHorizontal * Style.marginXL * 10
- minX += Settings.data.bar.marginHorizontal * Style.marginXL * 10
- }
- return Math.round(Math.max(minX, Math.min(targetX, maxX)))
- } else {
- // Fallback to center horizontally
- return Math.round((panelWindow.width - panelBackground.width) / 2)
- }
- }
- }
-
- // ---------------------------------------------
- property int calculatedY: {
- // Priority to fixed anchoring
- if (panelAnchorVerticalCenter) {
- // Center vertically but respect bar margins
- var centerY = Math.round((panelWindow.height - panelBackground.height) / 2)
- var minY = marginTop
- var maxY = panelWindow.height - panelBackground.height - marginBottom
- return Math.round(Math.max(minY, Math.min(centerY, maxY)))
- } else if (panelAnchorTop) {
- return marginTop
- } else if (panelAnchorBottom) {
- return Math.round(panelWindow.height - panelBackground.height - marginBottom)
- }
-
- // No fixed anchoring
- if (barIsVertical) {
- // Vertical bar
- if (useButtonPosition) {
- // Position panel relative to button
- var targetY = buttonPosition.y + (buttonHeight / 2) - (panelBackground.height / 2)
- // Keep panel within screen bounds
- var maxY = panelWindow.height - panelBackground.height - marginBottom
- var minY = marginTop
-
- if (Settings.data.bar.floating) {
- maxY -= Settings.data.bar.marginHorizontal * Style.marginXL * 10
- minY += Settings.data.bar.marginHorizontal * Style.marginXL * 10
- }
-
- return Math.round(Math.max(minY, Math.min(targetY, maxY)))
- } else {
- // Fallback to center vertically
- return Math.round((panelWindow.height - panelBackground.height) / 2)
- }
- } else {
- // Horizontal bar
- if (barPosition === "bottom") {
- // Above the bottom bar
- return Math.round(panelWindow.height - panelBackground.height - marginBottom)
- } else {
- // Below the top bar
- return marginTop
- }
- }
- }
-
- // Animate in when component is completed
- Component.onCompleted: {
- // Start invisible
- // Use a timer to delay the animation start, allowing QML to properly set up initial state
- fadeInTimer.start()
- }
-
- Timer {
- id: fadeInTimer
- interval: 1
- repeat: false
- onTriggered: {
- // Fade in background
- root.panelBackgroundOpacity = 1.0
- }
- }
-
- // Timer to fade in content after slide animation completes
- Timer {
- id: contentFadeInTimer
- interval: Style.animationFast
- repeat: false
- running: true
- onTriggered: root.panelContentOpacity = 1.0
- }
-
- // Reset drag position when panel closes
- Connections {
- target: root
- function onClosed() {
- panelBackground.isDragged = false
- }
- }
-
- // Prevent closing when clicking in the panel bg
- MouseArea {
- anchors.fill: parent
- }
-
- // Animation behavior
- Behavior on opacity {
- NumberAnimation {
- duration: Style.animationFast
- easing.type: Easing.OutQuad
- }
- }
-
- Loader {
- id: panelContentLoader
- anchors.fill: parent
- sourceComponent: root.panelContent
- opacity: root.panelContentOpacity
-
- Behavior on opacity {
- NumberAnimation {
- duration: Style.animationFast
- easing.type: Easing.OutQuad
- }
- }
-
- // Allow content to dynamically resize the panel
- onItemChanged: {
- if (item) {
- // Bind to content's preferredWidth/Height if they exist
- if (item.hasOwnProperty('contentPreferredWidth')) {
- root.preferredWidth = Qt.binding(() => item.contentPreferredWidth)
- }
- if (item.hasOwnProperty('contentPreferredHeight')) {
- root.preferredHeight = Qt.binding(() => item.contentPreferredHeight)
- }
- if (item.hasOwnProperty('contentPreferredWidthRatio')) {
- root.preferredWidthRatio = Qt.binding(() => item.contentPreferredWidthRatio)
- }
- if (item.hasOwnProperty('contentPreferredHeightRatio')) {
- root.preferredHeightRatio = Qt.binding(() => item.contentPreferredHeightRatio)
- }
- }
- }
- }
-
- // Handle drag move on the whole panel area
- DragHandler {
- id: dragHandler
- target: null
- enabled: panelBackground.draggable
- property real dragStartX: 0
- property real dragStartY: 0
- onActiveChanged: {
- if (active) {
- // Capture current position into manual coordinates BEFORE toggling isDragged
- panelBackground.manualX = panelBackground.x
- panelBackground.manualY = panelBackground.y
- dragStartX = panelBackground.x
- dragStartY = panelBackground.y
- panelBackground.isDragged = true
- if (root.enableBackgroundClick)
- root.disableBackgroundClick()
- } else {
- // Keep isDragged true so we continue using the manual x/y after release
- if (root.enableBackgroundClick)
- root.enableBackgroundClick()
- }
- }
- onTranslationChanged: {
- // Proposed new coordinates from fixed drag origin
- var nx = dragStartX + translation.x
- var ny = dragStartY + translation.y
-
- // Calculate gaps so we never overlap the bar on any side
- var baseGap = Style.marginS
- var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * 2 * Style.marginXL : 0
- var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * 2 * Style.marginXL : 0
-
- var insetLeft = baseGap + ((barIsVisible && barPosition === "left") ? (Style.barHeight + floatExtraH) : 0)
- var insetRight = baseGap + ((barIsVisible && barPosition === "right") ? (Style.barHeight + floatExtraH) : 0)
- var insetTop = baseGap + ((barIsVisible && barPosition === "top") ? (Style.barHeight + floatExtraV) : 0)
- var insetBottom = baseGap + ((barIsVisible && barPosition === "bottom") ? (Style.barHeight + floatExtraV) : 0)
-
- // Clamp within screen bounds accounting for insets
- var maxX = panelWindow.width - panelBackground.width - insetRight
- var minX = insetLeft
- var maxY = panelWindow.height - panelBackground.height - insetBottom
- var minY = insetTop
-
- panelBackground.manualX = Math.round(Math.max(minX, Math.min(nx, maxX)))
- panelBackground.manualY = Math.round(Math.max(minY, Math.min(ny, maxY)))
- }
- }
-
- // Drag indicator border
- Rectangle {
- anchors.fill: parent
- anchors.margins: 0
- color: Color.transparent
- border.color: Color.mPrimary
- border.width: Style.borderM
- radius: Style.radiusL
- visible: panelBackground.isDragged && dragHandler.active
- opacity: 0.8
- z: 3000
-
- // Subtle glow effect
- Rectangle {
- anchors.fill: parent
- anchors.margins: 0
- color: Color.transparent
- border.color: Color.mPrimary
- border.width: Style.borderS
- radius: Style.radiusL
- opacity: 0.3
- }
- }
- }
- }
- }
-}
diff --git a/Widgets/NRadioButton.qml b/Widgets/NRadioButton.qml
index d61b0719..ea3cacbd 100644
--- a/Widgets/NRadioButton.qml
+++ b/Widgets/NRadioButton.qml
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Controls
import qs.Commons
-import qs.Services
import qs.Widgets
RadioButton {
diff --git a/Widgets/NReorderCheckboxes.qml b/Widgets/NReorderCheckboxes.qml
index a245c515..078c5e7a 100644
--- a/Widgets/NReorderCheckboxes.qml
+++ b/Widgets/NReorderCheckboxes.qml
@@ -8,6 +8,7 @@ Item {
// Public API
property var model: []
+ property var disabledIds: []
property color activeColor: Color.mPrimary
property color activeOnColor: Color.mOnPrimary
property color dragHandleColor: Color.mOutline
@@ -77,6 +78,7 @@ Item {
property string text: modelData.text || ""
property bool enabled: modelData.enabled || false
property bool required: modelData.required || false
+ readonly property bool isDisabled: (root.disabledIds || []).indexOf(modelData.id) !== -1
property bool dragging: false
property int dragStartY: 0
property int dragStartIndex: -1
@@ -88,6 +90,7 @@ Item {
width: parent.width
spacing: Style.marginS
+ opacity: isDisabled ? 0.5 : 1.0
// Drag handle
Rectangle {
@@ -123,12 +126,16 @@ Item {
id: dragHandleMouseArea
anchors.fill: parent
- cursorShape: Qt.SizeVerCursor
+ cursorShape: (!delegateItem.required && !delegateItem.isDisabled) ? Qt.SizeVerCursor : Qt.ArrowCursor
hoverEnabled: true
preventStealing: false
+ enabled: !delegateItem.required && !delegateItem.isDisabled
z: 1000
onPressed: mouse => {
+ if (delegateItem.required || delegateItem.isDisabled) {
+ return
+ }
delegateItem.dragStartIndex = delegateItem.index
delegateItem.dragTargetIndex = delegateItem.index
delegateItem.dragStartY = delegateItem.y
@@ -221,11 +228,13 @@ Item {
MouseArea {
anchors.fill: parent
- cursorShape: delegateItem.required ? Qt.ForbiddenCursor : Qt.PointingHandCursor
- enabled: !delegateItem.required
+ cursorShape: (!delegateItem.required && !delegateItem.isDisabled) ? Qt.PointingHandCursor : Qt.ArrowCursor
+ enabled: !delegateItem.required && !delegateItem.isDisabled
onClicked: {
- root.toggleItem(delegateItem.index)
+ if (!delegateItem.required && !delegateItem.isDisabled) {
+ root.toggleItem(delegateItem.index)
+ }
}
}
}
diff --git a/Widgets/NSearchableComboBox.qml b/Widgets/NSearchableComboBox.qml
index b4c82b52..bd334124 100644
--- a/Widgets/NSearchableComboBox.qml
+++ b/Widgets/NSearchableComboBox.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
import qs.Widgets
import "../Helpers/FuzzySort.js" as Fuzzysort
@@ -159,14 +158,6 @@ RowLayout {
height: root.popupHeight + 60
padding: Style.marginM
- onOpened: {
- PanelService.willOpenPopup(root)
- }
-
- onClosed: {
- PanelService.willClosePopup(root)
- }
-
contentItem: ColumnLayout {
spacing: Style.marginS
diff --git a/Widgets/NSectionEditor.qml b/Widgets/NSectionEditor.qml
index 65fe857f..a2ecbedf 100644
--- a/Widgets/NSectionEditor.qml
+++ b/Widgets/NSectionEditor.qml
@@ -4,7 +4,6 @@ import QtQuick.Effects
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
-import qs.Services
NBox {
id: root
diff --git a/Widgets/NShapedRectangle.qml b/Widgets/NShapedRectangle.qml
deleted file mode 100644
index 36f76192..00000000
--- a/Widgets/NShapedRectangle.qml
+++ /dev/null
@@ -1,193 +0,0 @@
-import QtQuick
-import qs.Commons
-
-Item {
- id: root
-
- // Corner radius properties
- property real topLeftRadius: 0
- property real topRightRadius: 0
- property real bottomLeftRadius: 0
- property real bottomRightRadius: 0
-
- // Inverted corner properties (concave instead of convex)
- property bool topLeftInverted: false
- property bool topRightInverted: false
- property bool bottomLeftInverted: false
- property bool bottomRightInverted: false
-
- // Direction for inverted corners: "horizontal" or "vertical"
- // horizontal: curves left/right (extends beyond left/right edges)
- // vertical: curves up/down (extends beyond top/bottom edges)
- property string topLeftInvertedDirection: "horizontal" // default: curves left
- property string topRightInvertedDirection: "horizontal" // default: curves right
- property string bottomLeftInvertedDirection: "horizontal" // default: curves left
- property string bottomRightInvertedDirection: "horizontal" // default: curves right
-
- // Background color
- property color backgroundColor: "white"
-
- // Check if any corner is inverted
- readonly property bool hasInvertedCorners: topLeftInverted || topRightInverted || bottomLeftInverted || bottomRightInverted
-
- // Calculate padding needed for inverted corners based on their direction
- readonly property real topPadding: Math.max((topLeftInverted && topLeftInvertedDirection === "vertical") ? topLeftRadius : 0, (topRightInverted && topRightInvertedDirection === "vertical") ? topRightRadius : 0)
- readonly property real bottomPadding: Math.max((bottomLeftInverted && bottomLeftInvertedDirection === "vertical") ? bottomLeftRadius : 0, (bottomRightInverted && bottomRightInvertedDirection === "vertical") ? bottomRightRadius : 0)
- readonly property real leftPadding: Math.max((topLeftInverted && topLeftInvertedDirection === "horizontal") ? topLeftRadius : 0, (bottomLeftInverted && bottomLeftInvertedDirection === "horizontal") ? bottomLeftRadius : 0)
- readonly property real rightPadding: Math.max((topRightInverted && topRightInvertedDirection === "horizontal") ? topRightRadius : 0, (bottomRightInverted && bottomRightInvertedDirection === "horizontal") ? bottomRightRadius : 0)
-
- // Simple rectangle for non-inverted corners (better performance)
- Rectangle {
- id: simpleBackground
- anchors.fill: parent
- color: root.backgroundColor
- radius: topLeftRadius // Use topLeftRadius as default
- border.width: Style.borderS
- border.color: Color.mOutline
- visible: !root.hasInvertedCorners
-
- topLeftRadius: root.topLeftRadius
- topRightRadius: root.topRightRadius
- bottomLeftRadius: root.bottomLeftRadius
- bottomRightRadius: root.bottomRightRadius
- }
-
- // Background with custom corners (for inverted corners)
- Canvas {
- id: background
- anchors.fill: parent
- anchors.topMargin: -root.topPadding
- anchors.bottomMargin: -root.bottomPadding
- anchors.leftMargin: -root.leftPadding
- anchors.rightMargin: -root.rightPadding
- visible: root.hasInvertedCorners
-
- antialiasing: true
- renderTarget: Canvas.FramebufferObject
- smooth: true
-
- onPaint: {
- var ctx = getContext("2d")
- ctx.reset()
-
- // Adjust coordinates to account for inverted corner padding
- var x = root.leftPadding
- var y = root.topPadding
- var w = width - root.leftPadding - root.rightPadding
- var h = height - root.topPadding - root.bottomPadding
-
- ctx.fillStyle = root.backgroundColor
- ctx.beginPath()
-
- // Start from top left
- if (topLeftInverted) {
- if (topLeftInvertedDirection === "vertical") {
- ctx.moveTo(x, y)
- } else {
- ctx.moveTo(x + topLeftRadius, y)
- }
- } else {
- ctx.moveTo(x + topLeftRadius, y)
- }
-
- // Top edge and top right corner
- if (topRightInverted) {
- if (topRightInvertedDirection === "horizontal") {
- // Curves to the right
- ctx.lineTo(x + w, y)
- ctx.lineTo(x + w + topRightRadius, y)
- ctx.quadraticCurveTo(x + w, y, x + w, y + topRightRadius)
- } else {
- // Curves upward
- ctx.lineTo(x + w, y)
- ctx.lineTo(x + w, y - topRightRadius)
- ctx.quadraticCurveTo(x + w, y, x + w - topRightRadius, y)
- ctx.lineTo(x + w, y)
- ctx.lineTo(x + w, y + topRightRadius)
- }
- } else {
- ctx.lineTo(x + w - topRightRadius, y)
- ctx.arcTo(x + w, y, x + w, y + topRightRadius, topRightRadius)
- }
-
- // Right edge and bottom right corner
- if (bottomRightInverted) {
- if (bottomRightInvertedDirection === "horizontal") {
- // Curves to the right
- ctx.lineTo(x + w, y + h - bottomRightRadius)
- ctx.quadraticCurveTo(x + w, y + h, x + w + bottomRightRadius, y + h)
- ctx.lineTo(x + w, y + h)
- ctx.lineTo(x + w - bottomRightRadius, y + h)
- } else {
- // Curves downward
- ctx.lineTo(x + w, y + h)
- ctx.lineTo(x + w, y + h + bottomRightRadius)
- ctx.quadraticCurveTo(x + w, y + h, x + w - bottomRightRadius, y + h)
- }
- } else {
- ctx.lineTo(x + w, y + h - bottomRightRadius)
- ctx.arcTo(x + w, y + h, x + w - bottomRightRadius, y + h, bottomRightRadius)
- }
-
- // Bottom edge and bottom left corner
- if (bottomLeftInverted) {
- if (bottomLeftInvertedDirection === "horizontal") {
- // Curves to the left
- ctx.lineTo(x + bottomLeftRadius, y + h)
- ctx.lineTo(x - bottomLeftRadius, y + h)
- ctx.quadraticCurveTo(x, y + h, x, y + h - bottomLeftRadius)
- } else {
- // Curves downward
- ctx.lineTo(x, y + h)
- ctx.lineTo(x, y + h + bottomLeftRadius)
- ctx.quadraticCurveTo(x, y + h, x + bottomLeftRadius, y + h)
- ctx.lineTo(x, y + h)
- ctx.lineTo(x, y + h - bottomLeftRadius)
- }
- } else {
- ctx.lineTo(x + bottomLeftRadius, y + h)
- ctx.arcTo(x, y + h, x, y + h - bottomLeftRadius, bottomLeftRadius)
- }
-
- // Left edge and back to top left corner
- if (topLeftInverted) {
- if (topLeftInvertedDirection === "horizontal") {
- // Curves to the left
- ctx.lineTo(x, y + topLeftRadius)
- ctx.quadraticCurveTo(x, y, x - topLeftRadius, y)
- ctx.lineTo(x, y)
- ctx.lineTo(x + topLeftRadius, y)
- } else {
- // Curves upward
- ctx.lineTo(x, y + topLeftRadius)
- ctx.lineTo(x, y)
- ctx.lineTo(x, y - topLeftRadius)
- ctx.quadraticCurveTo(x, y, x + topLeftRadius, y)
- }
- } else {
- ctx.lineTo(x, y + topLeftRadius)
- ctx.arcTo(x, y, x + topLeftRadius, y, topLeftRadius)
- }
-
- ctx.closePath()
- ctx.fill()
- }
- }
-
- // Trigger repaint when properties change
- onTopLeftRadiusChanged: background.requestPaint()
- onTopRightRadiusChanged: background.requestPaint()
- onBottomLeftRadiusChanged: background.requestPaint()
- onBottomRightRadiusChanged: background.requestPaint()
- onTopLeftInvertedChanged: background.requestPaint()
- onTopRightInvertedChanged: background.requestPaint()
- onBottomLeftInvertedChanged: background.requestPaint()
- onBottomRightInvertedChanged: background.requestPaint()
- onTopLeftInvertedDirectionChanged: background.requestPaint()
- onTopRightInvertedDirectionChanged: background.requestPaint()
- onBottomLeftInvertedDirectionChanged: background.requestPaint()
- onBottomRightInvertedDirectionChanged: background.requestPaint()
- onBackgroundColorChanged: background.requestPaint()
- onWidthChanged: background.requestPaint()
- onHeightChanged: background.requestPaint()
-}
diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml
index c2570386..793dd7bf 100644
--- a/Widgets/NSlider.qml
+++ b/Widgets/NSlider.qml
@@ -2,11 +2,12 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import qs.Commons
-import qs.Services
+import qs.Services.UI
Slider {
id: root
+ property color fillColor: Color.mPrimary
property var cutoutColor: Color.mSurface
property bool snapAlways: true
property real heightRatio: 0.7
@@ -46,7 +47,7 @@ Slider {
width: parent.height
height: parent.height
radius: width / 2
- color: Qt.darker(Color.mPrimary, 1.2) //starting color of gradient
+ color: Qt.darker(fillColor, 1.2) //starting color of gradient
}
// The main rectangle
@@ -60,35 +61,11 @@ Slider {
orientation: Gradient.Horizontal
GradientStop {
position: 0.0
- color: Qt.darker(Color.mPrimary, 1.2)
- Behavior on color {
- ColorAnimation {
- duration: 300
- }
- }
- }
- GradientStop {
- position: 0.5
- color: Color.mPrimary
- SequentialAnimation on position {
- loops: Animation.Infinite
- NumberAnimation {
- from: 0.3
- to: 0.7
- duration: 2000
- easing.type: Easing.InOutSine
- }
- NumberAnimation {
- from: 0.7
- to: 0.3
- duration: 2000
- easing.type: Easing.InOutSine
- }
- }
+ color: Qt.darker(fillColor, 1.2)
}
GradientStop {
position: 1.0
- color: Qt.lighter(Color.mPrimary, 1.2)
+ color: fillColor
}
}
}
@@ -118,7 +95,7 @@ Slider {
implicitHeight: knobDiameter
radius: width / 2
color: root.pressed ? Color.mHover : Color.mSurface
- border.color: Color.mPrimary
+ border.color: fillColor
border.width: Style.borderL
anchors.centerIn: parent
@@ -134,9 +111,8 @@ Slider {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
- // Pass through mouse events to the slider
+ acceptedButtons: Qt.NoButton // Don't accept any mouse buttons - only hover
propagateComposedEvents: true
- preventStealing: false
onEntered: {
root.hovering = true
@@ -151,23 +127,15 @@ Slider {
TooltipService.hide()
}
}
+ }
- onPressed: function (mouse) {
- if (root.tooltipText) {
+ // Hide tooltip when slider is pressed (anywhere on the slider)
+ Connections {
+ target: root
+ function onPressedChanged() {
+ if (root.pressed && root.tooltipText) {
TooltipService.hide()
}
- // Pass the event through to the slider
- mouse.accepted = false
- }
-
- onReleased: function (mouse) {
- // Pass the event through to the slider
- mouse.accepted = false
- }
-
- onPositionChanged: function (mouse) {
- // Pass the event through to the slider
- mouse.accepted = false
}
}
}
diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml
index a0fdbc16..0b96cfc4 100644
--- a/Widgets/NSpinBox.qml
+++ b/Widgets/NSpinBox.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
import qs.Widgets
RowLayout {
diff --git a/Widgets/NTabBar.qml b/Widgets/NTabBar.qml
new file mode 100644
index 00000000..5c0259b7
--- /dev/null
+++ b/Widgets/NTabBar.qml
@@ -0,0 +1,27 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import qs.Commons
+import qs.Widgets
+
+Rectangle {
+ id: root
+
+ // Public properties
+ property int currentIndex: 0
+ property int spacing: Style.marginS
+ default property alias content: tabRow.children
+
+ // Styling
+ Layout.fillWidth: true
+ implicitHeight: Style.baseWidgetSize + Style.marginXS * 2
+ color: Color.mSurfaceVariant
+ radius: Style.radiusS
+
+ RowLayout {
+ id: tabRow
+ anchors.fill: parent
+ anchors.margins: Style.marginXS
+ spacing: root.spacing
+ }
+}
diff --git a/Widgets/NTabButton.qml b/Widgets/NTabButton.qml
new file mode 100644
index 00000000..a1def44b
--- /dev/null
+++ b/Widgets/NTabButton.qml
@@ -0,0 +1,67 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import qs.Commons
+import qs.Widgets
+
+Rectangle {
+ id: root
+
+ // Public properties
+ property string text: ""
+ property bool checked: false
+ property int tabIndex: 0
+
+ // Internal state
+ property bool isHovered: false
+
+ signal clicked
+
+ // Sizing
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ // Styling
+ radius: Style.radiusXS
+ color: root.checked ? Color.mPrimary : (root.isHovered ? Color.mHover : Color.mSurface)
+
+ Behavior on color {
+ ColorAnimation {
+ duration: Style.animationFast
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ NText {
+ id: tabText
+ anchors.centerIn: parent
+ text: root.text
+ pointSize: Style.fontSizeM
+ font.weight: root.checked ? Style.fontWeightSemiBold : Style.fontWeightRegular
+ color: root.checked ? Color.mOnPrimary : root.isHovered ? Color.mOnHover : Color.mOnSurface
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+
+ Behavior on color {
+ ColorAnimation {
+ duration: Style.animationFast
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onEntered: root.isHovered = true
+ onExited: root.isHovered = false
+ onClicked: {
+ root.clicked()
+ // Update parent NTabBar's currentIndex
+ if (root.parent && root.parent.parent && root.parent.parent.currentIndex !== undefined) {
+ root.parent.parent.currentIndex = root.tabIndex
+ }
+ }
+ }
+}
diff --git a/Widgets/NText.qml b/Widgets/NText.qml
index 4a9060ed..57c20ed0 100644
--- a/Widgets/NText.qml
+++ b/Widgets/NText.qml
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
-import qs.Services
import qs.Widgets
Text {
diff --git a/Widgets/NTextInput.qml b/Widgets/NTextInput.qml
index f9ba4f05..b2a5e0ea 100644
--- a/Widgets/NTextInput.qml
+++ b/Widgets/NTextInput.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
ColumnLayout {
id: root
diff --git a/Widgets/NToggle.qml b/Widgets/NToggle.qml
index 81cc9533..82c54711 100644
--- a/Widgets/NToggle.qml
+++ b/Widgets/NToggle.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
RowLayout {
id: root
@@ -19,7 +18,9 @@ RowLayout {
signal exited
Layout.fillWidth: true
+
opacity: enabled ? 1.0 : 0.6
+ spacing: Style.marginM
NLabel {
label: root.label
@@ -29,6 +30,8 @@ RowLayout {
Rectangle {
id: switcher
+ Layout.alignment: Qt.AlignVCenter
+
implicitWidth: Math.round(root.baseSize * .85) * 2
implicitHeight: Math.round(root.baseSize * .5) * 2
radius: height * 0.5
diff --git a/Widgets/NValueSlider.qml b/Widgets/NValueSlider.qml
index 19cb8e4c..62006780 100644
--- a/Widgets/NValueSlider.qml
+++ b/Widgets/NValueSlider.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
-import qs.Services
import qs.Widgets
RowLayout {
diff --git a/flake.nix b/flake.nix
index 5f985ddd..5fba4116 100644
--- a/flake.nix
+++ b/flake.nix
@@ -52,10 +52,12 @@
...
}: {
imports = [./nix/home-module.nix];
- programs.noctalia-shell.package = lib.mkDefault self.packages.${pkgs.system}.default;
+ programs.noctalia-shell.package =
+ lib.mkDefault
+ self.packages.${pkgs.stdenv.hostPlatform.system}.default;
programs.noctalia-shell.app2unit.package =
lib.mkDefault
- nixpkgs.legacyPackages.${pkgs.system}.app2unit;
+ nixpkgs.legacyPackages.${pkgs.stdenv.hostPlatform.system}.app2unit;
};
nixosModules.default = {
@@ -64,7 +66,9 @@
...
}: {
imports = [./nix/nixos-module.nix];
- services.noctalia-shell.package = lib.mkDefault self.packages.${pkgs.system}.default;
+ services.noctalia-shell.package =
+ lib.mkDefault
+ self.packages.${pkgs.stdenv.hostPlatform.system}.default;
};
};
}
diff --git a/nix/home-module.nix b/nix/home-module.nix
index 713c8f65..c27e6725 100644
--- a/nix/home-module.nix
+++ b/nix/home-module.nix
@@ -16,6 +16,8 @@ in {
options.programs.noctalia-shell = {
enable = lib.mkEnableOption "Noctalia shell configuration";
+ systemd.enable = lib.mkEnableOption "Noctalia shell systemd integration";
+
package = lib.mkOption {
type = lib.types.nullOr lib.types.package;
description = "The noctalia-shell package to use";
@@ -23,13 +25,13 @@ in {
settings = lib.mkOption {
type = with lib.types;
- nullOr (oneOf [
+ oneOf [
attrs
str
path
- ]);
+ ];
default = {};
- apply = x: lib.recursiveUpdate defaultSettings (extractAttrs x);
+ apply = x: if x == {} then x else lib.recursiveUpdate defaultSettings (extractAttrs x);
example = lib.literalExpression ''
{
bar = {
@@ -103,18 +105,48 @@ in {
useApp2Unit = cfg.settings.appLauncher.useApp2Unit or false;
in
lib.mkIf cfg.enable {
- home.packages = lib.optional useApp2Unit cfg.app2unit.package
+ systemd.user.services.noctalia-shell = lib.mkIf cfg.systemd.enable {
+ Unit = {
+ Description = "Noctalia Shell - Wayland desktop shell";
+ Documentation = "https://docs.noctalia.dev/docs";
+ PartOf = [ config.wayland.systemd.target ];
+ After = [ config.wayland.systemd.target ];
+ X-Restart-Triggers =
+ lib.optional (cfg.settings != {}) "${config.xdg.configFile."noctalia/settings.json".source}"
+ ++ lib.optional (cfg.colors != {}) "${config.xdg.configFile."noctalia/colors.json".source}";
+ };
+
+ Service = {
+ ExecStart = lib.getExe cfg.package;
+ Restart = "on-failure";
+ Environment = [
+ "NOCTALIA_SETTINGS_FALLBACK=%h/.config/noctalia/gui-settings.json"
+ ];
+ };
+
+ Install.WantedBy = [ config.wayland.systemd.target ];
+ };
+
+ home.packages =
+ lib.optional useApp2Unit cfg.app2unit.package
++ lib.optional (cfg.package != null) cfg.package;
xdg.configFile = {
- "noctalia/settings.json" = {
- onChange = restart;
+ "noctalia/settings.json" = lib.mkIf (cfg.settings != {}) {
+ onChange = lib.mkIf (!cfg.systemd.enable) restart;
text = builtins.toJSON cfg.settings;
};
"noctalia/colors.json" = lib.mkIf (cfg.colors != {}) {
- onChange = restart;
+ onChange = lib.mkIf (!cfg.systemd.enable) restart;
text = builtins.toJSON cfg.colors;
};
};
+
+ assertions = [
+ {
+ assertion = !cfg.systemd.enable || cfg.package != null;
+ message = "noctalia-shell: The package option must not be null when systemd service is enabled.";
+ }
+ ];
};
}
diff --git a/nix/package.nix b/nix/package.nix
index a5d07917..cacec580 100644
--- a/nix/package.nix
+++ b/nix/package.nix
@@ -1,25 +1,16 @@
{
version ? "dirty",
lib,
- stdenv,
+ stdenvNoCC,
# build
- gcc,
qt6,
quickshell,
- xkeyboard_config,
# runtime deps
- bash,
- bluez,
brightnessctl,
cava,
cliphist,
- coreutils,
ddcutil,
- file,
- findutils,
- libnotify,
matugen,
- networkmanager,
wlsunset,
wl-clipboard,
gpu-screen-recorder, # optional
@@ -33,6 +24,7 @@
filter = path: type:
!(builtins.any (prefix: lib.path.hasPrefix (../. + prefix) (/. + path)) [
/.github
+ /.gitignore
/Assets/Screenshots
/Assets/Wallpaper
/Bin/dev
@@ -41,60 +33,55 @@
/README.md
/flake.nix
/flake.lock
+ /shell.nix
+ /lefthook.yml
+ /CLAUDE.md
]);
};
runtimeDeps =
[
- bash
- bluez
brightnessctl
cava
cliphist
- coreutils
ddcutil
- file
- findutils
- libnotify
matugen
- networkmanager
wlsunset
wl-clipboard
]
- ++ lib.optionals (stdenv.hostPlatform.isx86_64) [gpu-screen-recorder];
+ ++ lib.optionals (stdenvNoCC.hostPlatform.system == "x86_64-linux") [
+ gpu-screen-recorder
+ ];
- fontconfig = makeFontsConf {
+ fontsConf = makeFontsConf {
fontDirectories = [
roboto
inter-nerdfont
];
};
in
- stdenv.mkDerivation {
+ stdenvNoCC.mkDerivation {
pname = "noctalia-shell";
inherit version src;
nativeBuildInputs = [
- gcc
qt6.wrapQtAppsHook
];
+
buildInputs = [
- quickshell
- xkeyboard_config
qt6.qtbase
];
- propagatedBuildInputs = runtimeDeps;
installPhase = ''
mkdir -p $out/share/noctalia-shell $out/bin
- cp -r ./* $out/share/noctalia-shell
- cp ${quickshell}/bin/qs $out/bin/noctalia-shell
+ cp -r . $out/share/noctalia-shell
+ ln -s ${quickshell}/bin/qs $out/bin/noctalia-shell
'';
preFixup = ''
qtWrapperArgs+=(
--prefix PATH : ${lib.makeBinPath runtimeDeps}
- --set FONTCONFIG_FILE ${fontconfig}
+ --set FONTCONFIG_FILE ${fontsConf}
--add-flags "-p $out/share/noctalia-shell"
)
'';
diff --git a/shell.qml b/shell.qml
index 04ade2a6..35152cd7 100644
--- a/shell.qml
+++ b/shell.qml
@@ -9,39 +9,28 @@
// Qt & Quickshell Core
import QtQuick
import Quickshell
-import Quickshell.Io
-import Quickshell.Services.Pipewire
-import Quickshell.Widgets
+import Quickshell.Services.SystemTray
// Commons & Services
import qs.Commons
-import qs.Services
-import qs.Widgets
+import qs.Services.Control
+import qs.Services.Theming
+import qs.Services.Hardware
+import qs.Services.Location
+import qs.Services.Networking
+import qs.Services.Power
+import qs.Services.System
+import qs.Services.UI
-// Core Modules
+// Modules
import qs.Modules.Background
-import qs.Modules.Dock
-import qs.Modules.LockScreen
-import qs.Modules.SessionMenu
-
-// Bar & Bar Components
import qs.Modules.Bar
-import qs.Modules.Bar.Extras
-import qs.Modules.Bar.Audio
-import qs.Modules.Bar.Bluetooth
-import qs.Modules.Bar.Battery
-import qs.Modules.Bar.Calendar
-import qs.Modules.Bar.WiFi
-
-// Panels & UI Components
-import qs.Modules.ControlCenter
-import qs.Modules.Launcher
+import qs.Modules.Dock
+import qs.Modules.MainScreen
+import qs.Modules.LockScreen
import qs.Modules.Notification
import qs.Modules.OSD
-import qs.Modules.Settings
import qs.Modules.Toast
-import qs.Modules.Wallpaper
-import qs.Modules.SetupWizard
ShellRoot {
id: shellRoot
@@ -74,38 +63,39 @@ ShellRoot {
settingsLoaded = true
}
}
-
Loader {
active: i18nLoaded && settingsLoaded
sourceComponent: Item {
Component.onCompleted: {
Logger.i("Shell", "---------------------------")
+ SystemTrayService.init()
WallpaperService.init()
AppThemeService.init()
ColorSchemeService.init()
- BarWidgetRegistry.init()
LocationService.init()
NightLightService.apply()
DarkModeService.init()
- FontService.init()
HooksService.init()
BluetoothService.init()
BatteryService.init()
IdleInhibitorService.init()
PowerProfileService.init()
DistroService.init()
+ FontService.init()
+
+ // Only open the setup wizard for new users
+ if (!Settings.data.setupCompleted) {
+ checkSetupWizard()
+ }
}
- Background {}
Overview {}
- ScreenCorners {}
- Bar {}
+ Background {}
Dock {}
-
- Notification {
- id: notification
- }
+ ToastOverlay {}
+ OSD {}
+ Notification {}
LockScreen {
id: lockScreen
@@ -115,98 +105,24 @@ ShellRoot {
}
}
- ToastOverlay {}
- OSD {}
-
- // IPCService is treated as a service
- // but it's actually an Item that needs to exists in the shell.
+ // IPCService is treated as a service but it's actually an
+ // Item that needs to exists in the shell.
IPCService {}
- // ------------------------------
- // All the NPanels
- Launcher {
- id: launcherPanel
- objectName: "launcherPanel"
- }
-
- ControlCenterPanel {
- id: controlCenterPanel
- objectName: "controlCenterPanel"
- }
-
- CalendarPanel {
- id: calendarPanel
- objectName: "calendarPanel"
- }
-
- SettingsPanel {
- id: settingsPanel
- objectName: "settingsPanel"
- }
-
- DirectWidgetSettingsPanel {
- id: directWidgetSettingsPanel
- objectName: "directWidgetSettingsPanel"
- }
-
- NotificationHistoryPanel {
- id: notificationHistoryPanel
- objectName: "notificationHistoryPanel"
- }
-
- SessionMenu {
- id: sessionMenuPanel
- objectName: "sessionMenuPanel"
- }
-
- WiFiPanel {
- id: wifiPanel
- objectName: "wifiPanel"
- }
-
- BluetoothPanel {
- id: bluetoothPanel
- objectName: "bluetoothPanel"
- }
-
- AudioPanel {
- id: audioPanel
- objectName: "audioPanel"
- }
-
- WallpaperPanel {
- id: wallpaperPanel
- objectName: "wallpaperPanel"
- }
-
- BatteryPanel {
- id: batteryPanel
- objectName: "batteryPanel"
- }
+ // MainScreen for each screen
+ AllScreens {}
}
}
- // ------------------------------
+ // ---------------------------------------------
// Setup Wizard
- Loader {
- id: setupWizardLoader
- active: false
- asynchronous: true
- sourceComponent: SetupWizard {}
- onLoaded: {
- if (setupWizardLoader.item && setupWizardLoader.item.open) {
- setupWizardLoader.item.open()
- }
- }
- }
-
- Connections {
- target: Settings
- function onSettingsLoaded() {
- // Only open the setup wizard for new users
- if (!Settings.data.setupCompleted) {
- checkSetupWizard()
- }
+ // ---------------------------------------------
+ Timer {
+ id: setupWizardTimer
+ running: false
+ interval: 1000
+ onTriggered: {
+ showSetupWizard()
}
}
@@ -224,9 +140,23 @@ ShellRoot {
}
if (Settings.data.settingsVersion >= Settings.settingsVersion) {
- setupWizardLoader.active = true
+ setupWizardTimer.start()
} else {
Settings.data.setupCompleted = true
}
}
+
+ function showSetupWizard() {
+ // Open Setup Wizard as a panel in the same windowing system as Settings/ControlCenter
+ if (Quickshell.screens.length > 0) {
+ var targetScreen = Quickshell.screens[0]
+ var setupPanel = PanelService.getPanel("setupWizardPanel", targetScreen)
+ if (setupPanel) {
+ setupPanel.open()
+ } else {
+ // If not yet loaded, ensure it loads and try again shortly
+ setupWizardTimer.restart()
+ }
+ }
+ }
}