Compare commits

...

36 Commits

Author SHA1 Message Date
Ly-sec
899595ec5c Release v2.9.2 2025-09-15 14:52:09 +02:00
Ly-sec
dbf1020636 CustomButton: add script execution/polling support with text display 2025-09-15 14:37:29 +02:00
LemmyCook
7def695c0e AboutTab: Fix hover on contributos 2025-09-15 08:36:18 -04:00
LemmyCook
b51a87a981 NSlider: slightly more discrete bg track 2025-09-15 08:36:08 -04:00
LemmyCook
3da2682111 Settings: fix about tab alignment of download button 2025-09-15 07:58:33 -04:00
Ly-sec
758f2f2e55 SystemMonitor: fix network stats, move text above storage icon (vertical bar) 2025-09-15 13:43:58 +02:00
Lysec
26a27c3393 Merge pull request #282 from Mtendekuyokwa19/everforest
Everforest
2025-09-15 09:39:42 +02:00
Mtende Kuyokwa
9d9bfb54e1 everforest light 2025-09-15 09:30:26 +02:00
Mtende Kuyokwa
ab5b1e4d82 everforest dark complete 2025-09-15 08:40:11 +02:00
Ly-sec
8cb9f04a22 ScreenCorners: add solid black option 2025-09-15 08:28:57 +02:00
Ly-sec
22bc5a3bff Move ScreenCorners to the actualy screen edges when bar is floating,
edit vesktop template
ScreenCorners: move to screen corners on floating bar
BarTab: mention the ScreenCorner changes
vesktop: make read channels have less visible text
2025-09-15 08:08:32 +02:00
Ly-sec
f5982f41a2 Remove noctalia.svg 2025-09-15 07:59:39 +02:00
Ly-sec
9a80d51b10 Vesktop: replace old theme with midnight from refact0r for better looks 2025-09-15 07:59:06 +02:00
Ly-sec
fa838ecdb1 Cleaned up ColorSchemeTab, added program checks, added firefox template
Matugen: added firefox (pywalfox) template
SidePanelToggle: use ProgramCheckerService for gpu-screen-recorder
ColorSchemeTab: use NCollapsible for matugen templates, use
ProgramCheckerService to detect available programs (for matugen
templates)
NCollapsible: create collapsible category
2025-09-15 07:44:31 +02:00
ItsLemmy
c0d6780c3d Volume/Brightness/Microphone: fixed tooltips to new mapping 2025-09-14 23:45:17 -04:00
ItsLemmy
8935f9a0f9 NPill: fix broken mouse-wheel control 2025-09-14 23:42:21 -04:00
LemmyCook
b8b97c46a0 DistroLogo: respect original colors, and avoid changing bg color when hovering to compensate. 2025-09-14 23:07:00 -04:00
Lemmy
f2bbf70f93 Merge pull request #279 from MrDowntempo/better-cats
Updated Catppuccin to use more of the official colors.
2025-09-14 22:54:44 -04:00
Corey Woodworth
ecf468f78f Updated Catppuccin to use more of the official colors. Also changed Green to Teal in Mocha and a few minor tweaks for readability. 2025-09-14 22:50:29 -04:00
Lemmy
0b35fc1d2d Merge pull request #278 from kevindiaz314/main
feat: update tokyo night light color scheme with refined color values
2025-09-14 22:47:03 -04:00
LemmyCook
94c5d73a61 NPill: fix icon bg hover color 2025-09-14 22:46:40 -04:00
LemmyCook
f399a6d9f5 TrayMenu: improve tray opening direction in vertical bar more 2025-09-14 22:44:27 -04:00
Kevin Diaz
44fd859aec feat: update tokyo night light color scheme with refined color values 2025-09-14 22:33:13 -04:00
LemmyCook
53d0c3943d TrayMenu: Fix submenu burger icon color when hovered. 2025-09-14 22:24:39 -04:00
Lemmy
d80f923802 Merge pull request #229 from matejc/main
Fix for fingerprint flow on lock screen
2025-09-14 22:18:14 -04:00
Lemmy
1b861d7b7b Merge pull request #277 from kevindiaz314/main
feat: update tokyo night color scheme with refined color values
2025-09-14 22:07:10 -04:00
LemmyCook
65933208ec NPillVertical: match NHorizontal on margins and color 2025-09-14 22:03:44 -04:00
LemmyCook
5df218a789 Bar Widgets: Removed 3 unecessary anchors 2025-09-14 21:56:55 -04:00
LemmyCook
2f7a834b55 Bar: fix centering (againg) 2025-09-14 21:55:21 -04:00
LemmyCook
eca301553e Bar: better vertical centering on horizontal bar. 2025-09-14 21:51:43 -04:00
LemmyCook
91efa38101 HorizontalPill: different color for forceOpen + better margins 2025-09-14 21:49:29 -04:00
Kevin Diaz
acfe94f736 feat: update tokyo night color scheme with refined color values 2025-09-14 21:24:23 -04:00
LemmyCook
97bfcbb9e8 Clock: height calculation similar to NPill to avoid discrepancies 2025-09-14 21:15:27 -04:00
LemmyCook
9e47d91be2 v2.9.1-dev / git 2025-09-14 21:03:21 -04:00
Matej Cotman
b9ae772987 feat(Modules/LockScreen): divert PAM messages to user (eg: to notify the user about fingerprint reader) 2025-09-12 18:26:15 +03:00
Matej Cotman
4265290a0f fix(fingerprint): better fingerprint integration by removing the check for empty password 2025-09-12 18:26:11 +03:00
38 changed files with 963 additions and 933 deletions

View File

@@ -4,7 +4,7 @@
"mOnPrimary": "#11111b",
"mSecondary": "#fab387",
"mOnSecondary": "#11111b",
"mTertiary": "#a6e3a1",
"mTertiary": "#94e2d5",
"mOnTertiary": "#11111b",
"mError": "#f38ba8",
"mOnError": "#11111b",
@@ -16,19 +16,19 @@
"mShadow": "#11111b"
},
"light": {
"mPrimary": "#9349ef",
"mPrimary": "#8839ef",
"mOnPrimary": "#eff1f5",
"mSecondary": "#f67525",
"mSecondary": "#fe640b",
"mOnSecondary": "#eff1f5",
"mTertiary": "#40b635",
"mTertiary": "#40a02b",
"mOnTertiary": "#eff1f5",
"mError": "#f38ba8",
"mOnError": "#11111b",
"mError": "#d20f39",
"mOnError": "#dce0e8",
"mSurface": "#eff1f5",
"mOnSurface": "#4c4f69",
"mSurfaceVariant": "#ccd0da",
"mOnSurfaceVariant": "#6c6f85",
"mOutline": "#aeb5c4",
"mOutline": "#a5adcb",
"mShadow": "#dce0e8"
}
}

View File

@@ -0,0 +1,34 @@
{
"dark": {
"mPrimary": "#D3C6AA",
"mOnPrimary": "#232A2E",
"mSecondary": "#D3C6AA",
"mOnSecondary": "#232A2E",
"mTertiary": "#9DA9A0",
"mOnTertiary": "#232A2E",
"mError": "#E67E80",
"mOnError": "#232A2E",
"mSurface": "#232A2E",
"mOnSurface": "#859289",
"mSurfaceVariant": "#2D353B",
"mOnSurfaceVariant": "#D3C6AA",
"mOutline": "#D3C6AA",
"mShadow": "#475258"
},
"light": {
"mPrimary": "#434F55",
"mOnPrimary": "#D3C6AA",
"mSecondary": "#232a2e",
"mOnSecondary": "#D3C6AA",
"mTertiary": "#333c43",
"mOnTertiary": "#9DA9A0",
"mError": "#E66868",
"mOnError": "#9DA9A0",
"mSurface": "#BEC5B2",
"mOnSurface": "#333C43",
"mSurfaceVariant": "#9DA9A0",
"mOnSurfaceVariant": "#232A2E",
"mOutline": "#232A2E",
"mShadow": "#ECF5ED"
}
}

View File

@@ -1,34 +1,34 @@
{
"dark": {
"mPrimary": "#ff9e64",
"mOnPrimary": "#1a1b26",
"mSecondary": "#e0af68",
"mOnSecondary": "#1a1b26",
"mTertiary": "#7aa2f7",
"mOnTertiary": "#1a1b26",
"mPrimary": "#7aa2f7",
"mOnPrimary": "#16161e",
"mSecondary": "#bb9af7",
"mOnSecondary": "#16161e",
"mTertiary": "#9ece6a",
"mOnTertiary": "#16161e",
"mError": "#f7768e",
"mOnError": "#1a1b26",
"mOnError": "#16161e",
"mSurface": "#1a1b26",
"mOnSurface": "#a9b1d6",
"mSurfaceVariant": "#292e42",
"mOnSurfaceVariant": "#787c99",
"mOutline": "#3d4462",
"mShadow": "#1a1b26"
"mOnSurface": "#c0caf5",
"mSurfaceVariant": "#24283b",
"mOnSurfaceVariant": "#9aa5ce",
"mOutline": "#565f89",
"mShadow": "#15161e"
},
"light": {
"mPrimary": "#fd5d00",
"mOnPrimary": "#e6e7ed",
"mSecondary": "#bb8027",
"mOnSecondary": "#e6e7ed",
"mTertiary": "#4a80f4",
"mOnTertiary": "#e6e7ed",
"mError": "#965027",
"mOnError": "#e6e7ed",
"mSurface": "#e6e7ed",
"mOnSurface": "#343b58",
"mSurfaceVariant": "#d5d6db",
"mOnSurfaceVariant": "#40434f",
"mOutline": "#babbc3",
"mShadow": "#c0caf5"
"mPrimary": "#2e7de9",
"mOnPrimary": "#e1e2e7",
"mSecondary": "#9854f1",
"mOnSecondary": "#e1e2e7",
"mTertiary": "#587539",
"mOnTertiary": "#e1e2e7",
"mError": "#f52a65",
"mOnError": "#e1e2e7",
"mSurface": "#e1e2e7",
"mOnSurface": "#3760bf",
"mSurfaceVariant": "#d0d5e3",
"mOnSurfaceVariant": "#6172b0",
"mOutline": "#b4b5b9",
"mShadow": "#a8aecb"
}
}

View File

@@ -70,6 +70,12 @@ Singleton {
lines.push('input_path = "' + Quickshell.shellDir + '/Assets/Matugen/templates/vesktop.css"')
lines.push('output_path = "~/.config/vesktop/themes/noctalia.theme.css"')
}
if (Settings.data.matugen.pywalfox) {
lines.push("\n[templates.pywalfox]")
lines.push('input_path = "' + Quickshell.shellDir + '/Assets/Matugen/templates/pywalfox.json"')
lines.push('output_path = "~/.cache/wal/colors.json"')
lines.push('post_hook = "pywalfox update"')
}
return lines.join("\n") + "\n"
}

View File

@@ -0,0 +1,22 @@
{
"wallpaper": "{{image}}",
"alpha": "100",
"colors": {
"color0": "{{colors.background.default.hex}}",
"color1": "",
"color2": "",
"color3": "",
"color4": "",
"color5": "",
"color6": "",
"color7": "",
"color8": "",
"color9": "",
"color10": "{{colors.primary.default.hex}}",
"color11": "",
"color12": "",
"color13": "{{colors.surface_bright.default.hex}}",
"color14": "",
"color15": "{{colors.on_surface.default.hex}}"
}
}

View File

@@ -1,572 +1,113 @@
/*
* Vesktop Theme
* Generated with Matugen
* Base was taken from https://github.com/catppuccin/discord <3
/**
* @name noctalia
* @description Original theme: midnight | A dark, rounded discord theme.
* @author refact0r
* @version 1.6.2
* @invite nz87hXyvcy
* @website https://github.com/refact0r/midnight-discord
* @source https://github.com/refact0r/midnight-discord/blob/master/midnight.theme.css
* @authorId 508863359777505290
* @authorLink https://www.refact0r.dev
*/
/* IMPORTANT: make sure to enable dark mode in discord settings for the theme to apply properly!!! */
/* Dark Theme */
.visual-refresh.theme-dark,
.visual-refresh .theme-dark {
/* Brand Colors */
--brand-experiment: {{colors.primary.default.hex}};
--bg-brand: {{colors.primary.default.hex}};
--brand-500: {{colors.primary.default.hex}} !important;
--text-link: {{colors.primary.default.hex}} !important;
--text-brand: {{colors.primary.default.hex}};
--control-brand-foreground: {{colors.primary.default.hex}};
--control-brand-foreground-new: {{colors.primary.default.hex}};
--mention-foreground: {{colors.primary.default.hex}};
--mention-background: {{colors.primary.default.hex}}20;
--focus-primary: {{colors.primary.default.hex}};
--logo-primary: {{colors.on_surface.default.hex}};
--badge-brand-bg: {{colors.primary.default.hex}};
--badge-brand-text: {{colors.on_primary.default.hex}};
@import url('https://refact0r.github.io/midnight-discord/build/midnight.css');
/* Text Colors */
--header-primary: {{colors.on_surface.default.hex}} !important;
--header-secondary: {{colors.on_surface_variant.default.hex}} !important;
--text-normal: {{colors.on_surface.default.hex}} !important;
--text-default: {{colors.on_surface.default.hex}};
--text-muted: {{colors.on_surface_variant.default.hex}} !important;
--text-primary: {{colors.on_surface.default.hex}};
--text-secondary: {{colors.on_surface_variant.default.hex}};
--text-tertiary: {{colors.on_surface_variant.default.hex}} !important;
--interactive-normal: {{colors.on_surface.default.hex}} !important;
--interactive-muted: {{colors.on_surface_variant.default.hex}};
--interactive-hover: {{colors.on_surface.default.hex}};
--interactive-active: {{colors.on_surface.default.hex}};
/* customize things here */
:root {
/* font, change to 'gg sans' for default discord font*/
--font: 'figtree';
/* Main Background Colors - Bar color (mSurface) colors.surface.default.hex*/
--background-primary: {{colors.surface_variant.default.hex}} !important;
--background-floating: {{colors.surface_variant.default.hex}} !important;
--background-surface-high: {{colors.surface_variant.default.hex}} !important;
--modal-background: {{colors.surface_variant.default.hex}} !important;
--app-background-frame: {{colors.surface_variant.default.hex}} !important;
--home-background: {{colors.surface_variant.default.hex}} !important;
--chat-background: {{colors.surface_variant.default.hex}} !important;
--chat-background-default: {{colors.surface_variant.default.hex}} !important;
--chat-input-container-background: {{colors.surface_container.default.hex}} !important;
/* Secondary Background Colors - Workspace color (mSurfaceVariant) */
--background-secondary: {{colors.surface.default.hex}} !important;
--background-secondary-alt: {{colors.surface.default.hex}} !important;
--background-surface-higher: {{colors.surface.default.hex}} !important;
--background-base-low: {{colors.surface.default.hex}} !important;
--background-base-lower: {{colors.surface.default.hex}} !important;
--channeltextarea-background: {{colors.surface_container.default.hex}} !important;
--modal-footer-background: {{colors.surface.default.hex}} !important;
/* New Messages Banner */
--background-mentioned: {{colors.primary.default.hex}}15 !important;
--background-mentioned-hover: {{colors.primary.default.hex}}20 !important;
--text-mentioned: {{colors.on_surface.default.hex}} !important;
--text-mentioned-hover: {{colors.on_surface.default.hex}} !important;
--text-mentioned-link: {{colors.primary.default.hex}} !important;
/* Additional Discord-specific variables for new messages banner */
--background-message-automod: {{colors.primary.default.hex}}15 !important;
--background-message-automod-hover: {{colors.primary.default.hex}}20 !important;
--background-message-highlight: {{colors.primary.default.hex}}15 !important;
--background-message-highlight-hover: {{colors.primary.default.hex}}20 !important;
/* Discord unread messages banner specific variables */
--background-mentioned: {{colors.primary.default.hex}}15 !important;
--background-mentioned-hover: {{colors.primary.default.hex}}20 !important;
--text-mentioned: {{colors.on_surface.default.hex}} !important;
--text-mentioned-hover: {{colors.on_surface.default.hex}} !important;
--text-mentioned-link: {{colors.primary.default.hex}} !important;
/* Additional Discord banner text variables */
--text-normal: {{colors.on_surface.default.hex}} !important;
--text-default: {{colors.on_surface.default.hex}} !important;
--text-primary: {{colors.on_surface.default.hex}} !important;
--text-secondary: {{colors.on_surface_variant.default.hex}} !important;
--text-tertiary: {{colors.on_surface_variant.default.hex}} !important;
--text-muted: {{colors.on_surface_variant.default.hex}} !important;
--interactive-normal: {{colors.on_surface.default.hex}} !important;
--interactive-muted: {{colors.on_surface_variant.default.hex}} !important;
/* Additional Discord banner variables */
--background-message-automod: {{colors.primary.default.hex}}15 !important;
--background-message-automod-hover: {{colors.primary.default.hex}}20 !important;
--background-message-highlight: {{colors.primary.default.hex}}15 !important;
--background-message-highlight-hover: {{colors.primary.default.hex}}20 !important;
--background-message-hover: {{colors.surface_variant.default.hex}}50 !important;
--background-modifier-hover: {{colors.surface_variant.default.hex}}80 !important;
--background-modifier-selected: {{colors.primary.default.hex}}20 !important;
--background-modifier-accent: {{colors.primary.default.hex}}30 !important;
--background-modifier-active: {{colors.primary.default.hex}}25 !important;
/* Chat Input Improvements */
--text-input-background: {{colors.surface_container.default.hex}} !important;
--text-input-border: {{colors.outline.default.hex}} !important;
--text-input-border-hover: {{colors.primary.default.hex}} !important;
/* Additional Discord-specific input variables */
--deprecated-text-input-bg: {{colors.surface_container.default.hex}} !important;
--deprecated-text-input-border: {{colors.outline.default.hex}} !important;
--deprecated-text-input-border-hover: {{colors.primary.default.hex}} !important;
--input-background: {{colors.surface_container.default.hex}} !important;
--input-border: {{colors.outline.default.hex}} !important;
--input-placeholder-text: {{colors.on_surface_variant.default.hex}} !important;
/* Elevated/Container Backgrounds */
--background-tertiary: {{colors.surface_container.default.hex}} !important;
--background-accent: {{colors.surface_container.default.hex}} !important;
--background-surface-highest: {{colors.surface_container_high.default.hex}} !important;
--background-base-lowest: {{colors.surface_container.default.hex}} !important;
/* top left corner text */
--corner-text: 'Midnight';
/* Border Colors */
--border-faint: {{colors.outline_variant.default.hex}};
--border-strong: {{colors.surface_container.default.hex}};
--border-normal: {{colors.surface_container_high.default.hex}};
--border-subtle: {{colors.surface.default.hex}} !important;
--chat-border: {{colors.surface_container_high.default.hex}};
/* color of status indicators and window controls */
--online-indicator: {{colors.inverse_primary.default.hex}}; /* change to #23a55a for default green */
--dnd-indicator: {{colors.error.default.hex}}; /* change to #f13f43 for default red */
--idle-indicator: {{colors.tertiary_container.default.hex}}; /* change to #f0b232 for default yellow */
--streaming-indicator: {{colors.on_primary.default.hex}}; /* change to #593695 for default purple */
/* Status Colors */
--status-positive: {{colors.tertiary.default.hex}};
--status-positive-background: {{colors.tertiary.default.hex}};
--status-positive-text: {{colors.on_tertiary.default.hex}};
--text-positive: {{colors.tertiary.default.hex}};
--text-feedback-positive: {{colors.tertiary.default.hex}};
--background-feedback-positive: {{colors.tertiary.default.hex}}20;
--info-positive-background: {{colors.tertiary.default.hex}}20;
--info-positive-foreground: {{colors.tertiary.default.hex}};
--info-positive-text: {{colors.on_surface.default.hex}};
/* accent colors */
--accent-1: {{colors.tertiary.default.hex}}; /* links */
--accent-2: {{colors.primary.default.hex}}; /* general unread/mention elements, some icons when active */
--accent-3: {{colors.primary.default.hex}}; /* accent buttons */
--accent-4: {{colors.surface_bright.default.hex}}; /* accent buttons when hovered */
--accent-5: {{colors.primary_fixed_dim.default.hex}}; /* accent buttons when clicked */
--mention: {{colors.surface.default.hex}}; /* mentions & mention messages */
--mention-hover: {{colors.surface_bright.default.hex}}; /* mentions & mention messages when hovered */
--status-warning: {{colors.secondary.default.hex}};
--status-warning-background: {{colors.secondary.default.hex}};
--status-warning-text: {{colors.on_secondary.default.hex}};
--text-warning: {{colors.secondary.default.hex}};
--text-feedback-warning: {{colors.secondary.default.hex}};
--background-feedback-warning: {{colors.secondary.default.hex}}20;
--info-warning-background: {{colors.secondary.default.hex}}20;
--info-warning-foreground: {{colors.secondary.default.hex}};
--info-warning-text: {{colors.on_surface.default.hex}};
/* text colors */
--text-0: {{colors.surface.default.hex}}; /* text on colored elements */
--text-1: {{colors.on_surface.default.hex}}; /* other normally white text */
--text-2: {{colors.on_surface.default.hex}}; /* headings and important text */
--text-3: {{colors.on_surface_variant.default.hex}}; /* normal text */
--text-4: {{colors.on_surface_variant.default.hex}}; /* icon buttons and channels */
--text-5: {{colors.outline.default.hex}}; /* muted channels/chats and timestamps */
--status-danger: {{colors.error.default.hex}};
--status-danger-background: {{colors.error.default.hex}};
--status-danger-text: {{colors.on_error.default.hex}};
--text-danger: {{colors.error.default.hex}};
--text-feedback-critical: {{colors.error.default.hex}};
--background-feedback-critical: {{colors.error.default.hex}}20;
--info-danger-background: {{colors.error.default.hex}}20;
--info-danger-foreground: {{colors.error.default.hex}};
--info-danger-text: {{colors.on_surface.default.hex}};
/* background and dark colors */
--bg-1: {{colors.primary.default.hex}}; /* dark buttons when clicked */
--bg-2: {{colors.surface_container_high.default.hex}}; /* dark buttons */
--bg-3: {{colors.surface_container_low.default.hex}}; /* spacing, secondary elements */
--bg-4: {{colors.surface.default.hex}}; /* main background color */
--hover: {{colors.surface_bright.default.hex}}; /* channels and buttons when hovered */
--active: {{colors.surface_bright.default.hex}}; /* channels and buttons when clicked or selected */
--message-hover: {{colors.surface_bright.default.hex}}; /* messages when hovered */
/* Button Colors */
--button-secondary-background: {{colors.surface_variant.default.hex}} !important;
--button-secondary-background-hover: {{colors.surface_container.default.hex}};
--button-secondary-background-active: {{colors.surface_container.default.hex}};
--button-secondary-background-disabled: {{colors.surface_variant.default.hex}};
--button-secondary-text: {{colors.on_surface.default.hex}} !important;
/* amount of spacing and padding */
--spacing: 12px;
--button-filled-brand-text: {{colors.on_primary.default.hex}};
--button-filled-brand-background: {{colors.primary.default.hex}};
--button-filled-brand-background-hover: {{colors.primary.default.hex}};
--button-filled-brand-background-active: {{colors.primary.default.hex}};
/* animations */
/* ALL ANIMATIONS CAN BE DISABLED WITH REDUCED MOTION IN DISCORD SETTINGS */
--list-item-transition: 0.2s ease; /* channels/members/settings hover transition */
--unread-bar-transition: 0.2s ease; /* unread bar moving into view transition */
--moon-spin-transition: 0.4s ease; /* moon icon spin */
--icon-spin-transition: 1s ease; /* round icon button spin (settings, emoji, etc.) */
/* Input Colors */
--input-background: {{colors.surface_container.default.hex}};
--input-border: {{colors.outline.default.hex}};
--input-placeholder-text: {{colors.on_surface_variant.default.hex}};
/* corner roundness (border-radius) */
--roundness-xl: 22px; /* roundness of big panel outer corners */
--roundness-l: 20px; /* popout panels */
--roundness-m: 16px; /* smaller panels, images, embeds */
--roundness-s: 12px; /* members, settings inputs */
--roundness-xs: 10px; /* channels, buttons */
--roundness-xxs: 8px; /* searchbar, small elements */
/* Scrollbar Colors */
--scrollbar-thin-thumb: {{colors.primary.default.hex}};
--scrollbar-thin-track: transparent;
--scrollbar-auto-thumb: {{colors.primary.default.hex}};
--scrollbar-auto-track: {{colors.surface_container_high.default.hex}};
--scrollbar-auto-scrollbar-color-thumb: {{colors.primary.default.hex}};
--scrollbar-auto-scrollbar-color-track: {{colors.surface_container_high.default.hex}};
/* direct messages moon icon */
/* change to block to show, none to hide */
--discord-icon: none; /* discord icon */
--moon-icon: block; /* moon icon */
--moon-icon-url: url('https://upload.wikimedia.org/wikipedia/commons/c/c4/Font_Awesome_5_solid_moon.svg'); /* custom icon url */
--moon-icon-size: auto;
/* Icon Colors */
--icon-muted: {{colors.on_surface_variant.default.hex}};
--icon-default: {{colors.on_surface.default.hex}};
--icon-primary: {{colors.on_surface.default.hex}};
--icon-secondary: {{colors.on_surface_variant.default.hex}};
--icon-tertiary: {{colors.on_surface_variant.default.hex}} !important;
/* Channel Colors */
--channels-default: {{colors.on_surface_variant.default.hex}} !important;
--channel-icon: {{colors.on_surface_variant.default.hex}} !important;
--channel-text-area-placeholder: {{colors.on_surface.default.hex}}80;
/* Selection and Hover States */
--background-modifier-hover: {{colors.surface_variant.default.hex}}80;
--background-modifier-selected: {{colors.primary.default.hex}}20 !important;
--background-modifier-accent: {{colors.primary.default.hex}}30;
--background-modifier-active: {{colors.primary.default.hex}}25 !important;
--background-message-hover: {{colors.surface_variant.default.hex}}50 !important;
--background-message-highlight: {{colors.primary.default.hex}}15;
--background-message-highlight-hover: {{colors.primary.default.hex}}20;
/* Code Block - Use workspace background */
--background-code: {{colors.surface_container.default.hex}};
--textbox-markdown-syntax: {{colors.on_surface_variant.default.hex}};
/* Spoiler */
--spoiler-revealed-background: {{colors.surface_container.default.hex}};
--spoiler-hidden-background: {{colors.surface_variant.default.hex}};
/* White/Black Overrides */
--white: {{colors.on_surface.default.hex}};
--white-400: {{colors.on_surface.default.hex}};
--white-500: {{colors.on_surface.default.hex}};
--white-600: {{colors.on_surface_variant.default.hex}};
--white-700: {{colors.on_surface_variant.default.hex}};
--black-500: {{colors.surface_container_high.default.hex}};
/* Force styling for Discord unread messages banner */
--unread-bar-background: {{colors.primary.default.hex}}15 !important;
--unread-bar-text: {{colors.on_surface.default.hex}} !important;
--unread-bar-hover: {{colors.primary.default.hex}}20 !important;
/* Additional Discord unread bar variables */
--background-mentioned: {{colors.primary.default.hex}}15 !important;
--background-mentioned-hover: {{colors.primary.default.hex}}20 !important;
--text-mentioned: {{colors.on_surface.default.hex}} !important;
--text-mentioned-hover: {{colors.on_surface.default.hex}} !important;
--text-mentioned-link: {{colors.primary.default.hex}} !important;
/* Discord banner specific variables */
--background-message-automod: {{colors.primary.default.hex}}15 !important;
--background-message-automod-hover: {{colors.primary.default.hex}}20 !important;
--background-message-highlight: {{colors.primary.default.hex}}15 !important;
--background-message-highlight-hover: {{colors.primary.default.hex}}20 !important;
/* Discord unread bar specific variables */
--background-mentioned: {{colors.primary.default.hex}}15 !important;
--background-mentioned-hover: {{colors.primary.default.hex}}20 !important;
--text-mentioned: {{colors.on_surface.default.hex}} !important;
--text-mentioned-hover: {{colors.on_surface.default.hex}} !important;
--text-mentioned-link: {{colors.primary.default.hex}} !important;
/* Additional Discord text variables that might affect the banner */
--text-normal: {{colors.on_surface.default.hex}} !important;
--text-default: {{colors.on_surface.default.hex}} !important;
--text-primary: {{colors.on_surface.default.hex}} !important;
--text-secondary: {{colors.on_surface_variant.default.hex}} !important;
--text-tertiary: {{colors.on_surface_variant.default.hex}} !important;
--text-muted: {{colors.on_surface_variant.default.hex}} !important;
--interactive-normal: {{colors.on_surface.default.hex}} !important;
--interactive-muted: {{colors.on_surface_variant.default.hex}} !important;
/* Force styling for Discord chat input */
--chat-input-background: {{colors.surface_container.default.hex}} !important;
--chat-input-placeholder: {{colors.on_surface_variant.default.hex}} !important;
/* Discord unread messages banner specific variables */
--new-messages-bar-background: {{colors.surface_container.default.hex}} !important;
--new-messages-bar-text: {{colors.on_surface.default.hex}} !important;
--new-messages-bar-hover: {{colors.surface_container_high.default.hex}} !important;
--bar-button-background: {{colors.surface_container.default.hex}} !important;
--bar-button-text: {{colors.on_surface.default.hex}} !important;
--bar-button-hover: {{colors.surface_container_high.default.hex}} !important;
/* filter uncolorable elements to fit theme */
/* (just set to none, they're too much work to configure) */
--login-bg-filter: saturate(0.3) hue-rotate(-15deg) brightness(0.4); /* login background artwork */
--green-to-accent-3-filter: hue-rotate(56deg) saturate(1.43); /* add friend page explore icon */
--blurple-to-accent-3-filter: hue-rotate(304deg) saturate(0.84) brightness(1.2); /* add friend page school icon */
}
.visual-refresh.theme-dark ::selection,
.visual-refresh .theme-dark ::selection {
background-color: {{colors.primary.default.hex}};
/* Selected chat/friend text */
.selected_f5eb4b,
.selected_f6f816 .link_d8bfb3 {
color: var(--text-0) !important;
background: var(--accent-3) !important;
}
/* Force Discord unread messages banner styling */
.visual-refresh.theme-dark .newMessagesBar__0f481,
.visual-refresh.theme-dark .barButtonMain__0f481,
.visual-refresh.theme-dark .barButtonBase__0f481,
.visual-refresh.theme-dark .span__0f481 {
background-color: {{colors.surface_container.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
.selected_f6f816 .link_d8bfb3 * {
color: var(--text-0) !important;
fill: var(--text-0) !important;
}
.visual-refresh.theme-dark .newMessagesBar__0f481:hover,
.visual-refresh.theme-dark .barButtonMain__0f481:hover,
.visual-refresh.theme-dark .barButtonBase__0f481:hover {
background-color: {{colors.surface_container_high.default.hex}} !important;
/* Make channel name text less visible (darker) */
.name__2ea32 {
color: var(--text-5) !important;
opacity: 0.7 !important;
}
/* Force Discord chat input styling */
.visual-refresh.theme-dark .channelTextArea-rNsIhG,
.visual-refresh.theme-dark .channelTextArea-rNsIhG *,
.visual-refresh.theme-dark .scrollableContainer-2NUZem,
.visual-refresh.theme-dark [data-slate-editor="true"] {
background-color: {{colors.surface_container.default.hex}} !important;
/* Make unread channel names brighter */
.link__2ea32[aria-label*="unread"] .name__2ea32 {
color: var(--text-2) !important;
opacity: 1 !important;
font-weight: 600 !important;
}
.visual-refresh.theme-dark [data-slate-editor="true"]::placeholder,
.visual-refresh.theme-dark .channelTextArea-rNsIhG [data-slate-editor="true"]::placeholder {
color: {{colors.on_surface_variant.default.hex}} !important;
}
/* Discord Emoji Picker Theming */
.visual-refresh.theme-dark .contentWrapper__08434,
.visual-refresh.theme-dark .emojiPicker_c0e32c,
.visual-refresh.theme-dark .wrapper_c0e32c {
background-color: {{colors.surface.default.hex}} !important;
}
.visual-refresh.theme-dark .nav__08434,
.visual-refresh.theme-dark .navList__08434 {
background-color: {{colors.surface.default.hex}} !important;
}
.visual-refresh.theme-dark .navButton__08434 {
background-color: {{colors.surface.default.hex}} !important;
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .navButtonActive__08434 {
background-color: {{colors.surface.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .searchBar_c0e32c,
.visual-refresh.theme-dark .input_a45028 {
background-color: {{colors.surface.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .input_a45028::placeholder {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .header_c656ac,
.visual-refresh.theme-dark .header__14245,
.visual-refresh.theme-dark .wrapper__14245 {
background-color: {{colors.surface_variant.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .headerLabel__14245 {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .interactive__14245 {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .header__14245 {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .header__14245 * {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .headerIcon__14245 svg,
.visual-refresh.theme-dark .headerCollapseIcon__14245 svg {
color: {{colors.on_surface.default.hex}} !important;
fill: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .emojiItem_fc7141 {
background-color: transparent !important;
}
.visual-refresh.theme-dark .emojiItem_fc7141:hover {
background-color: {{colors.surface_container.default.hex}} !important;
}
.visual-refresh.theme-dark .emojiItemSelected_fc7141 {
background-color: {{colors.primary.default.hex}}20 !important;
}
.visual-refresh.theme-dark .inspector_aeaaeb {
background-color: {{colors.surface_container.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .categoryList_c0e32c {
background-color: {{colors.surface.default.hex}} !important;
}
.visual-refresh.theme-dark .categoryItem_b9ee0c {
background-color: transparent !important;
}
.visual-refresh.theme-dark .categoryItem_b9ee0c:hover {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .categoryItemDefaultCategorySelected_b9ee0c {
background-color: {{colors.surface_variant.default.hex}} !important;
}
/* Additional Discord emoji picker elements */
.visual-refresh.theme-dark .navItem__08434 {
background-color: {{colors.surface_variant.default.hex}} !important;
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .navItem__08434:hover {
background-color: {{colors.surface_container.default.hex}} !important;
}
.visual-refresh.theme-dark .stickersNavItem__08434 {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .wrapper__14245 {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .headerLabel__14245 {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .headerIcon__14245 svg,
.visual-refresh.theme-dark .headerCollapseIcon__14245 svg {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .interactive__14245:hover {
background-color: {{colors.surface_container.default.hex}} !important;
}
/* Chat input styling */
.visual-refresh.theme-dark .scrollableContainer__74017,
.visual-refresh.theme-dark .themedBackground__74017,
.visual-refresh.theme-dark .inner__74017,
.visual-refresh.theme-dark .textArea__74017,
.visual-refresh.theme-dark .slateContainer_ec4baf,
.visual-refresh.theme-dark .markup__75297,
.visual-refresh.theme-dark .editor__1b31f,
.visual-refresh.theme-dark .slateTextArea_ec4baf {
background-color: {{colors.surface_container.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .emptyText__1464f {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .placeholder__1b31f {
color: {{colors.on_surface_variant.default.hex}} !important;
}
/* Message content styling */
.visual-refresh.theme-dark .messageContent_c19a55 {
color: {{colors.on_surface.default.hex}} !important;
background-color: {{colors.surface.default.hex}} !important;
}
.visual-refresh.theme-dark .messageContent_c19a55 .markup__75297 {
color: {{colors.on_surface.default.hex}} !important;
background-color: {{colors.surface.default.hex}} !important;
}
/* Message background styling */
.visual-refresh.theme-dark .message__5126c,
.visual-refresh.theme-dark .cozyMessage__5126c,
.visual-refresh.theme-dark .wrapper_c19a55,
.visual-refresh.theme-dark .contents_c19a55 {
background-color: {{colors.surface.default.hex}} !important;
}
/* Message hover effects */
.visual-refresh.theme-dark .message__5126c:hover {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .message__5126c:hover * {
color: {{colors.on_surface.default.hex}} !important;
}
/* Remove Discord's native quote/reply bar */
.visual-refresh.theme-dark .message__5126c::before {
display: none !important;
}
.visual-refresh.theme-dark .message__5126c.hasReply_c19a55::before {
display: none !important;
}
/* Channel styling - darker text for read channels */
.visual-refresh.theme-dark .link__2ea32 .name__2ea32 {
color: {{colors.outline.default.hex}} !important;
}
/* Unread channels keep normal color */
.visual-refresh.theme-dark .link__2ea32[aria-label*="unread"] .name__2ea32 {
color: {{colors.on_surface.default.hex}} !important;
}
/* Search input styling */
.visual-refresh.theme-dark .inner_a45028 {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .input_a45028 {
background-color: transparent !important;
}
.visual-refresh.theme-dark .input_a45028::placeholder {
color: {{colors.on_surface_variant.default.hex}} !important;
}
/* Chat input placeholder styling */
.visual-refresh.theme-dark .emptyText__1464f {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .slateTextArea_ec4baf > div:first-child .emptyText__1464f::before {
content: "send a message" !important;
color: {{colors.on_surface_variant.default.hex}} !important;
}
/* Hide placeholder when input is focused */
.visual-refresh.theme-dark .slateTextArea_ec4baf:focus .emptyText__1464f::before,
.visual-refresh.theme-dark .markup__75297:focus .emptyText__1464f::before {
display: none !important;
}
.visual-refresh.theme-dark .message__5126c:hover .messageContent_c19a55,
.visual-refresh.theme-dark .message__5126c:hover .markup__75297,
.visual-refresh.theme-dark .message__5126c:hover .header_c19a55,
.visual-refresh.theme-dark .message__5126c:hover .headerText_c19a55,
.visual-refresh.theme-dark .message__5126c:hover .username_c19a55,
.visual-refresh.theme-dark .message__5126c:hover .timestamp_c19a55 {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .categoryIcon_b9ee0c svg {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .unicodeShortcut_b9ee0c {
background-color: {{colors.surface_container.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .unicodeShortcut_b9ee0c:hover {
background-color: {{colors.surface_container_high.default.hex}} !important;
}
.visual-refresh.theme-dark .unicodeShortcut_b9ee0c svg {
color: {{colors.on_surface.default.hex}} !important;
}
/* Number badge styling */
.visual-refresh.theme-dark .numberBadge__2b1f5 {
color: {{colors.surface.default.hex}} !important;
background-color: {{colors.primary.default.hex}} !important;
}
/* New badge styling */
.visual-refresh.theme-dark .newBadge__4ed1a {
color: {{colors.surface.default.hex}} !important;
background-color: {{colors.primary.default.hex}} !important;
}

View File

@@ -322,6 +322,7 @@ Singleton {
property string avatarImage: defaultAvatar
property bool dimDesktop: false
property bool showScreenCorners: false
property bool forceBlackScreenCorners: false
property real radiusRatio: 1.0
property real screenRadiusRatio: 1.0
// Animation speed multiplier (0.1x - 2.0x)
@@ -449,6 +450,7 @@ Singleton {
property bool foot: false
property bool fuzzel: false
property bool vesktop: false
property bool pywalfox: false
property bool enableUserTemplates: false
}

View File

@@ -7,7 +7,7 @@ import qs.Services
import qs.Widgets
Loader {
active: Settings.data.general.showScreenCorners && !Settings.data.bar.floating
active: Settings.data.general.showScreenCorners
sourceComponent: Variants {
model: Quickshell.screens
@@ -19,7 +19,7 @@ Loader {
property real scaling: ScalingService.getScreenScale(screen)
screen: modelData
property color cornerColor: Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity)
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 * scaling
property real cornerSize: Style.screenRadius * scaling
@@ -46,10 +46,12 @@ Loader {
}
margins {
top: ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "top" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
bottom: ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "bottom" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
left: ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "left" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
right: ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "right" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
// 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.floating && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "top" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
bottom: !Settings.data.bar.floating && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "bottom" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
left: !Settings.data.bar.floating && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "left" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
right: !Settings.data.bar.floating && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "right" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
}
mask: Region {}

View File

@@ -91,7 +91,7 @@ Variants {
anchors.fill: parent
// Top section (left widgets)
Column {
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Style.marginM * root.scaling
@@ -109,13 +109,13 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.left.length
}
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
}
}
}
// Center section (center widgets)
Column {
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
spacing: Style.marginS * root.scaling
@@ -132,13 +132,13 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.center.length
}
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
}
}
}
// Bottom section (right widgets)
Column {
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.marginM * root.scaling
@@ -156,7 +156,7 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.right.length
}
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
}
}
}
@@ -189,6 +189,7 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.left.length
}
Layout.alignment: Qt.AlignVCenter
}
}
}
@@ -213,6 +214,7 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.center.length
}
Layout.alignment: Qt.AlignVCenter
}
}
}
@@ -238,6 +240,7 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.right.length
}
Layout.alignment: Qt.AlignVCenter
}
}
}

View File

@@ -178,7 +178,7 @@ PopupWindow {
font.pointSize: Style.fontSizeS * scaling
verticalAlignment: Text.AlignVCenter
visible: modelData?.hasChildren ?? false
color: Color.mOnSurface
color: (mouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface)
}
}
@@ -220,9 +220,32 @@ PopupWindow {
const submenuWidth = menuWidth * scaling // Assuming a similar width as the parent
const overlap = 4 * scaling // A small overlap to bridge the mouse path
// Check if there's enough space on the right
// 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.mapToGlobal(0, 0)
const openLeft = (globalPos.x + entry.width + submenuWidth > (screen ? screen.width : Screen.width))
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 ? screen.width : 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 ? screen.width : Screen.width)) {
// Would open off the right edge, force left opening
openLeft = true
}
}
// Position with overlap
const anchorX = openLeft ? -submenuWidth + overlap : entry.width - overlap

View File

@@ -90,7 +90,7 @@ Item {
var monitor = getMonitor()
if (!monitor)
return ""
return "Brightness: " + Math.round(monitor.brightness * 100) + "%\nMethod: " + monitor.method + "\nLeft click for advanced settings.\nScroll up/down to change brightness."
return "Brightness: " + Math.round(monitor.brightness * 100) + "%\nRight click for settings.\nScroll to modify brightness."
}
onWheel: function (angle) {
@@ -104,6 +104,12 @@ Item {
}
}
onClicked: {
var settingsPanel = PanelService.getPanel("settingsPanel")
settingsPanel.requestedTab = SettingsPanel.Tab.Display
settingsPanel.open()
}
onRightClicked: {
var settingsPanel = PanelService.getPanel("settingsPanel")
settingsPanel.requestedTab = SettingsPanel.Tab.Display

View File

@@ -36,10 +36,10 @@ Rectangle {
readonly property string displayFormat: widgetSettings.displayFormat !== undefined ? widgetSettings.displayFormat : widgetMetadata.displayFormat
// Use compact mode for vertical bars
readonly property bool useCompactMode: barPosition === "left" || barPosition === "right"
readonly property bool verticalMode: barPosition === "left" || barPosition === "right"
implicitWidth: useCompactMode ? Math.round(Style.capsuleHeight * scaling) : Math.round(layout.implicitWidth + Style.marginM * 2 * scaling)
implicitHeight: useCompactMode ? Math.round(Style.capsuleHeight * 2.5 * scaling) : Math.round(Style.capsuleHeight * scaling)
implicitWidth: verticalMode ? Math.round(Style.capsuleHeight * scaling) : Math.round(layout.implicitWidth + Style.marginM * 2 * scaling)
implicitHeight: verticalMode ? Math.round(Style.capsuleHeight * 2.5 * scaling) : Math.round(Style.baseWidgetSize * 0.8 * scaling) // Match NPill
radius: Math.round(Style.radiusS * scaling)
color: Color.mSurfaceVariant
@@ -52,18 +52,18 @@ Rectangle {
ColumnLayout {
id: layout
anchors.centerIn: parent
spacing: useCompactMode ? -2 * scaling : -3 * scaling
spacing: verticalMode ? -2 * scaling : -3 * scaling
// Compact mode for vertical bars - Time section (HH, MM)
Repeater {
model: useCompactMode ? 2 : 1
model: verticalMode ? 2 : 1
NText {
readonly property bool showSeconds: (displayFormat === "time-seconds")
readonly property bool inlineDate: (displayFormat === "time-date")
readonly property var now: Time.date
text: {
if (useCompactMode) {
if (verticalMode) {
// Compact mode: time section (first 2 lines)
switch (index) {
case 0:
@@ -126,7 +126,7 @@ Rectangle {
}
font.family: Settings.data.ui.fontFixed
font.pointSize: useCompactMode ? Style.fontSizeXXS * scaling : Style.fontSizeXS * scaling
font.pointSize: verticalMode ? Style.fontSizeXXS * scaling : Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
@@ -135,7 +135,7 @@ Rectangle {
// Separator line for compact mode (between time and date)
Rectangle {
visible: useCompactMode
visible: verticalMode
Layout.preferredWidth: 20 * scaling
Layout.preferredHeight: 2 * scaling
Layout.alignment: Qt.AlignHCenter
@@ -148,12 +148,12 @@ Rectangle {
// Compact mode for vertical bars - Date section (DD, MM)
Repeater {
model: useCompactMode ? 2 : 0
model: verticalMode ? 2 : 0
NText {
readonly property var now: Time.date
text: {
if (useCompactMode) {
if (verticalMode) {
// Compact mode: date section (last 2 lines)
switch (index) {
case 0:
@@ -178,7 +178,7 @@ Rectangle {
// Second line for normal mode (date)
NText {
visible: !useCompactMode && (displayFormat === "time-date-short")
visible: !verticalMode && (displayFormat === "time-date-short")
text: {
const now = Time.date
const day = now.getDate().toString().padStart(2, '0')

View File

@@ -1,12 +1,13 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services
import qs.Widgets
import qs.Modules.SettingsPanel
NIconButton {
Item {
id: root
// Widget properties passed from Bar.qml
@@ -35,33 +36,80 @@ NIconButton {
readonly property string leftClickExec: widgetSettings.leftClickExec || widgetMetadata.leftClickExec
readonly property string rightClickExec: widgetSettings.rightClickExec || widgetMetadata.rightClickExec
readonly property string middleClickExec: widgetSettings.middleClickExec || widgetMetadata.middleClickExec
readonly property string textCommand: widgetSettings.textCommand !== undefined ? widgetSettings.textCommand : (widgetMetadata.textCommand || "")
readonly property int textIntervalMs: widgetSettings.textIntervalMs !== undefined ? widgetSettings.textIntervalMs : (widgetMetadata.textIntervalMs || 3000)
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
enabled: hasExec
allowClickWhenDisabled: true // we want to be able to open config with left click when its not setup properly
colorBorder: Color.transparent
colorBorderHover: Color.transparent
sizeRatio: 0.8
icon: customIcon
tooltipText: {
if (!hasExec) {
return "Custom Button - Configure in settings"
} else {
var lines = []
if (leftClickExec !== "") {
lines.push(`Left click: <i>${leftClickExec}</i>.`)
implicitWidth: pill.width
implicitHeight: pill.height
NPill {
id: pill
rightOpen: BarWidgetRegistry.getNPillDirection(root)
icon: customIcon
text: _dynamicText
autoHide: false
forceOpen: _dynamicText !== ""
forceClose: false
disableOpen: true
tooltipText: {
if (!hasExec) {
return "Custom Button - Configure in settings"
} else {
var lines = []
if (leftClickExec !== "") {
lines.push(`Left click: ${leftClickExec}.`)
}
if (rightClickExec !== "") {
lines.push(`Right click: ${rightClickExec}.`)
}
if (middleClickExec !== "") {
lines.push(`Middle click: ${middleClickExec}.`)
}
return lines.join("\n")
}
if (rightClickExec !== "") {
lines.push(`Right click: <i>${rightClickExec}</i>.`)
}
if (middleClickExec !== "") {
lines.push(`Middle click: <i>${middleClickExec}</i>.`)
}
return lines.join("<br/>")
}
onClicked: root.onClicked()
onRightClicked: root.onRightClicked()
onMiddleClicked: root.onMiddleClicked()
}
// Internal state for dynamic text
property string _dynamicText: ""
// Periodically run the text command (if set)
Timer {
id: refreshTimer
interval: Math.max(250, textIntervalMs)
repeat: true
running: (textCommand && textCommand.length > 0)
triggeredOnStart: true
onTriggered: {
if (!textCommand || textCommand.length === 0)
return
if (textProc.running)
return
textProc.command = ["sh", "-lc", textCommand]
textProc.running = true
}
}
onClicked: {
Process {
id: textProc
stdout: StdioCollector {}
stderr: StdioCollector {}
onExited: (exitCode, exitStatus) => {
var out = String(stdout.text || "").trim()
if (out.indexOf("\n") !== -1) {
out = out.split("\n")[0]
}
_dynamicText = out
}
}
function onClicked() {
if (leftClickExec) {
Quickshell.execDetached(["sh", "-c", leftClickExec])
Logger.log("CustomButton", `Executing command: ${leftClickExec}`)
@@ -73,14 +121,14 @@ NIconButton {
}
}
onRightClicked: {
function onRightClicked() {
if (rightClickExec) {
Quickshell.execDetached(["sh", "-c", rightClickExec])
Logger.log("CustomButton", `Executing command: ${rightClickExec}`)
}
}
onMiddleClicked: {
function onMiddleClicked() {
if (middleClickExec) {
Quickshell.execDetached(["sh", "-c", middleClickExec])
Logger.log("CustomButton", `Executing command: ${middleClickExec}`)

View File

@@ -17,7 +17,5 @@ NIconButton {
colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
colorBorder: Color.transparent
colorBorderHover: Color.transparent
anchors.verticalCenter: parent.verticalCenter
onClicked: Settings.data.colorSchemes.darkMode = !Settings.data.colorSchemes.darkMode
}

View File

@@ -95,7 +95,7 @@ Item {
suffix: "%"
forceOpen: displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide"
tooltipText: "Microphone: " + Math.round(AudioService.inputVolume * 100) + "%\nLeft click for advanced settings.\nScroll up/down to change volume.\nRight click to toggle mute."
tooltipText: "Microphone: " + Math.round(AudioService.inputVolume * 100) + "%\nLeft click to toggle mute.\nRight click for settings.\nScroll to modify volume."
onWheel: function (delta) {
wheelAccumulator += delta

View File

@@ -16,6 +16,5 @@ NIconButton {
sizeRatio: 0.8
colorBg: Color.mPrimary
colorFg: Color.mOnPrimary
anchors.verticalCenter: parent.verticalCenter
onClicked: ScreenRecorderService.toggleRecording()
}

View File

@@ -33,34 +33,23 @@ NIconButton {
icon: useDistroLogo ? "" : "noctalia"
tooltipText: "Open side panel."
sizeRatio: 0.8
sizeRatio: 0.85
colorBg: Color.mSurfaceVariant
colorFg: Color.mOnSurface
colorBgHover: useDistroLogo ? Color.mSurfaceVariant : Color.mTertiary
colorBorder: Color.transparent
colorBorderHover: Color.transparent
anchors.verticalCenter: parent.verticalCenter
colorBorderHover: useDistroLogo ? Color.mTertiary : Color.transparent
onClicked: PanelService.getPanel("sidePanel")?.toggle(this)
onRightClicked: PanelService.getPanel("settingsPanel")?.toggle()
IconImage {
id: logo
anchors.centerIn: parent
width: root.width * 0.6
width: root.width * 0.85
height: width
source: useDistroLogo ? DistroLogoService.osLogo : ""
visible: useDistroLogo && source !== ""
smooth: true
}
MultiEffect {
anchors.fill: logo
source: logo
//visible: logo.visible
colorization: 1
brightness: 1
saturation: 1
colorizationColor: root.hovering ? Color.mSurfaceVariant : Color.mOnSurface
}
}

View File

@@ -43,6 +43,26 @@ Rectangle {
radius: Math.round(Style.radiusM * scaling)
color: Color.mSurfaceVariant
// Compact speed formatter for vertical bar display
function formatCompactSpeed(bytesPerSecond) {
if (!bytesPerSecond || bytesPerSecond <= 0)
return "0"
const units = ["", "k", "M", "G"]
let value = bytesPerSecond
let unitIndex = 0
while (value >= 1024 && unitIndex < units.length - 1) {
value = value / 1024.0
unitIndex++
}
// Promote at ~100 of current unit (e.g., 100k -> ~0.1M shown as 0.1M or 0M if rounded)
if (unitIndex < units.length - 1 && value >= 100) {
value = value / 1024.0
unitIndex++
}
const display = (value >= 10) ? Math.round(value).toString() : value.toFixed(1)
return display + units[unitIndex]
}
// Horizontal layout for top/bottom bars
RowLayout {
id: horizontalLayout
@@ -347,21 +367,21 @@ Rectangle {
anchors.centerIn: parent
spacing: Style.marginXXS * scaling
NText {
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
text: formatCompactSpeed(SystemStatService.rxSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
color: Color.mPrimary
}
NIcon {
icon: "download-speed"
font.pointSize: Style.fontSizeS * scaling
anchors.horizontalCenter: parent.horizontalCenter
}
NText {
text: SystemStatService.formatSpeed(SystemStatService.rxSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
color: Color.mPrimary
}
}
}
@@ -377,21 +397,21 @@ Rectangle {
anchors.centerIn: parent
spacing: Style.marginXXS * scaling
NText {
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
text: formatCompactSpeed(SystemStatService.txSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
color: Color.mPrimary
}
NIcon {
icon: "upload-speed"
font.pointSize: Style.fontSizeS * scaling
anchors.horizontalCenter: parent.horizontalCenter
}
NText {
text: SystemStatService.formatSpeed(SystemStatService.txSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
color: Color.mPrimary
}
}
}
@@ -407,12 +427,6 @@ Rectangle {
anchors.centerIn: parent
spacing: Style.marginXXS * scaling
NIcon {
icon: "storage"
font.pointSize: Style.fontSizeS * scaling
Layout.alignment: Qt.AlignHCenter
}
NText {
text: `${SystemStatService.diskPercent}%`
font.family: Settings.data.ui.fontFixed
@@ -422,6 +436,12 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter
color: Color.mPrimary
}
NIcon {
icon: "storage"
font.pointSize: Style.fontSizeS * scaling
Layout.alignment: Qt.AlignHCenter
}
}
}
}

View File

@@ -81,7 +81,7 @@ Item {
suffix: "%"
forceOpen: displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide"
tooltipText: "Volume: " + Math.round(AudioService.volume * 100) + "%\nLeft click for advanced settings.\nScroll up/down to change volume.\nRight click to toggle mute."
tooltipText: "Volume: " + Math.round(AudioService.volume * 100) + "%\nLeft click to toggle mute.\nRight click for settings.\nScroll to modify volume."
onWheel: function (delta) {
wheelAccumulator += delta

View File

@@ -12,6 +12,7 @@ Scope {
property bool unlockInProgress: false
property bool showFailure: false
property string errorMessage: ""
property string infoMessage: ""
property bool pamAvailable: typeof PamContext !== "undefined"
onCurrentTextChanged: {
@@ -28,12 +29,6 @@ Scope {
return
}
if (currentText === "") {
errorMessage = "Password required"
showFailure = true
return
}
root.unlockInProgress = true
errorMessage = ""
showFailure = false
@@ -52,6 +47,8 @@ Scope {
if (messageIsError) {
errorMessage = message
} else {
infoMessage = message
}
if (responseRequired) {

View File

@@ -511,6 +511,7 @@ Loader {
width: 0
height: 0
visible: false
enabled: !lockContext.unlockInProgress
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
color: Color.mOnSurface
@@ -540,7 +541,7 @@ Loader {
color: Color.mOnSurface
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
visible: passwordInput.activeFocus
visible: passwordInput.activeFocus && !lockContext.unlockInProgress
SequentialAnimation {
id: typingEffect
@@ -584,7 +585,7 @@ Loader {
NText {
text: {
if (lockContext.unlockInProgress)
return "Authenticating..."
return lockContext.infoMessage || "Authenticating..."
if (lockContext.showFailure && lockContext.errorMessage)
return lockContext.errorMessage
if (lockContext.showFailure)

View File

@@ -19,6 +19,8 @@ ColumnLayout {
settings.leftClickExec = leftClickExecInput.text
settings.rightClickExec = rightClickExecInput.text
settings.middleClickExec = middleClickExecInput.text
settings.textCommand = textCommandInput.text
settings.textIntervalMs = parseInt(textIntervalInput.text || textIntervalInput.placeholderText, 10)
return settings
}
@@ -228,4 +230,33 @@ ColumnLayout {
placeholderText: "Enter command to execute (app or custom script)"
text: widgetData.middleClickExec || widgetMetadata.middleClickExec
}
NDivider {
Layout.fillWidth: true
}
NText {
text: "Dynamic Text"
font.pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
}
NTextInput {
id: textCommandInput
Layout.fillWidth: true
label: "Text Command"
description: "Shell command to run periodically (first line becomes the text)."
placeholderText: "echo \"Hello World\""
text: widgetData?.textCommand || widgetMetadata.textCommand
}
NTextInput {
id: textIntervalInput
Layout.fillWidth: true
label: "Refresh Interval"
description: "Interval in milliseconds."
placeholderText: String(widgetMetadata.textIntervalMs || 3000)
text: widgetData && widgetData.textIntervalMs !== undefined ? String(widgetData.textIntervalMs) : ""
}
}

View File

@@ -59,7 +59,7 @@ ColumnLayout {
// Update button
Rectangle {
Layout.alignment: Qt.alignmentRight
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: Math.round(updateRow.implicitWidth + (Style.marginL * scaling * 2))
Layout.preferredHeight: Math.round(Style.barHeight * scaling)
radius: Style.radiusL * scaling
@@ -189,7 +189,7 @@ ColumnLayout {
NText {
text: modelData.login || "Unknown"
font.weight: Style.fontWeightBold
color: contributorArea.containsMouse ? Color.mSurface : Color.mOnSurface
color: contributorArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface
elide: Text.ElideRight
Layout.fillWidth: true
}
@@ -197,7 +197,7 @@ ColumnLayout {
NText {
text: (modelData.contributions || 0) + " " + ((modelData.contributions || 0) === 1 ? "commit" : "commits")
font.pointSize: Style.fontSizeXS * scaling
color: contributorArea.containsMouse ? Color.mSurface : Color.mOnSurface
color: contributorArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface
}
}
}

View File

@@ -95,7 +95,7 @@ ColumnLayout {
NToggle {
Layout.fillWidth: true
label: "Floating Bar"
description: "Make the bar float with rounded corners and margins. This will hide screen corners."
description: "Make the bar float with rounded corners and margins. Screen corners will move to screen edges."
checked: Settings.data.bar.floating
onToggled: checked => Settings.data.bar.floating = checked
}

View File

@@ -318,146 +318,187 @@ ColumnLayout {
visible: Settings.data.colorSchemes.useWallpaperColors
}
// Matugen template toggles (moved from MatugenTab)
// Matugen template toggles organized by category
ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
visible: Settings.data.colorSchemes.useWallpaperColors
spacing: Style.marginL * scaling
ColumnLayout {
spacing: Style.marginS * scaling
// UI Components
NCollapsible {
Layout.fillWidth: true
label: "UI"
description: "Desktop environment and UI toolkit theming."
defaultExpanded: false
NText {
text: "Matugen Templates"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
NCheckbox {
label: "GTK 4 (libadwaita)"
description: "Write ~/.config/gtk-4.0/gtk.css"
checked: Settings.data.matugen.gtk4
onToggled: checked => {
Settings.data.matugen.gtk4 = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NText {
text: "Select which external components Matugen should apply theming to."
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
Layout.fillWidth: true
wrapMode: Text.WordWrap
NCheckbox {
label: "GTK 3"
description: "Write ~/.config/gtk-3.0/gtk.css"
checked: Settings.data.matugen.gtk3
onToggled: checked => {
Settings.data.matugen.gtk3 = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Qt6ct"
description: "Write ~/.config/qt6ct/colors/noctalia.conf"
checked: Settings.data.matugen.qt6
onToggled: checked => {
Settings.data.matugen.qt6 = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Qt5ct"
description: "Write ~/.config/qt5ct/colors/noctalia.conf"
checked: Settings.data.matugen.qt5
onToggled: checked => {
Settings.data.matugen.qt5 = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
}
NCheckbox {
label: "GTK 4 (libadwaita)"
description: "Write ~/.config/gtk-4.0/gtk.css"
checked: Settings.data.matugen.gtk4
onToggled: checked => {
Settings.data.matugen.gtk4 = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "GTK 3"
description: "Write ~/.config/gtk-3.0/gtk.css"
checked: Settings.data.matugen.gtk3
onToggled: checked => {
Settings.data.matugen.gtk3 = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Qt6ct"
description: "Write ~/.config/qt6ct/colors/noctalia.conf"
checked: Settings.data.matugen.qt6
onToggled: checked => {
Settings.data.matugen.qt6 = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Qt5ct"
description: "Write ~/.config/qt5ct/colors/noctalia.conf"
checked: Settings.data.matugen.qt5
onToggled: checked => {
Settings.data.matugen.qt5 = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Kitty"
description: "Write ~/.config/kitty/themes/noctalia.conf and reload"
checked: Settings.data.matugen.kitty
onToggled: checked => {
Settings.data.matugen.kitty = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Ghostty"
description: "Write ~/.config/ghostty/themes/noctalia and reload"
checked: Settings.data.matugen.ghostty
onToggled: checked => {
Settings.data.matugen.ghostty = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Foot"
description: "Write ~/.config/foot/themes/noctalia and reload"
checked: Settings.data.matugen.foot
onToggled: checked => {
Settings.data.matugen.foot = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Fuzzel"
description: "Write ~/.config/fuzzel/themes/noctalia and reload"
checked: Settings.data.matugen.fuzzel
onToggled: checked => {
Settings.data.matugen.fuzzel = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NCheckbox {
label: "Vesktop"
description: "Write ~/.config/vesktop/themes/noctalia.theme.css"
checked: Settings.data.matugen.vesktop
onToggled: checked => {
Settings.data.matugen.vesktop = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
NDivider {
// Terminal Emulators
NCollapsible {
Layout.fillWidth: true
Layout.topMargin: Style.marginM * scaling
Layout.bottomMargin: Style.marginM * scaling
label: "Terminal"
description: "Terminal emulator theming."
defaultExpanded: false
NCheckbox {
label: "Kitty"
description: ProgramCheckerService.kittyAvailable ? "Write ~/.config/kitty/themes/noctalia.conf and reload" : "Requires kitty terminal to be installed"
checked: Settings.data.matugen.kitty
enabled: ProgramCheckerService.kittyAvailable
opacity: ProgramCheckerService.kittyAvailable ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.kittyAvailable) {
Settings.data.matugen.kitty = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
}
NCheckbox {
label: "Ghostty"
description: ProgramCheckerService.ghosttyAvailable ? "Write ~/.config/ghostty/themes/noctalia and reload" : "Requires ghostty terminal to be installed"
checked: Settings.data.matugen.ghostty
enabled: ProgramCheckerService.ghosttyAvailable
opacity: ProgramCheckerService.ghosttyAvailable ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.ghosttyAvailable) {
Settings.data.matugen.ghostty = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
}
NCheckbox {
label: "Foot"
description: ProgramCheckerService.footAvailable ? "Write ~/.config/foot/themes/noctalia and reload" : "Requires foot terminal to be installed"
checked: Settings.data.matugen.foot
enabled: ProgramCheckerService.footAvailable
opacity: ProgramCheckerService.footAvailable ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.footAvailable) {
Settings.data.matugen.foot = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
}
}
NCheckbox {
label: "User Templates"
description: "Enable user-defined Matugen config from ~/.config/matugen/config.toml"
checked: Settings.data.matugen.enableUserTemplates
onToggled: checked => {
Settings.data.matugen.enableUserTemplates = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
// Applications
NCollapsible {
Layout.fillWidth: true
label: "Programs"
description: "Application-specific theming."
defaultExpanded: false
NCheckbox {
label: "Fuzzel"
description: ProgramCheckerService.fuzzelAvailable ? "Write ~/.config/fuzzel/themes/noctalia and reload" : "Requires fuzzel launcher to be installed"
checked: Settings.data.matugen.fuzzel
enabled: ProgramCheckerService.fuzzelAvailable
opacity: ProgramCheckerService.fuzzelAvailable ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.fuzzelAvailable) {
Settings.data.matugen.fuzzel = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
}
NCheckbox {
label: "Vesktop"
description: ProgramCheckerService.vesktopAvailable ? "Write ~/.config/vesktop/themes/noctalia.theme.css" : "Requires vesktop Discord client to be installed"
checked: Settings.data.matugen.vesktop
enabled: ProgramCheckerService.vesktopAvailable
opacity: ProgramCheckerService.vesktopAvailable ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.vesktopAvailable) {
Settings.data.matugen.vesktop = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
}
NCheckbox {
label: "Pywalfox (Firefox)"
description: ProgramCheckerService.pywalfoxAvailable ? "Write ~/.cache/wal/colors.json and run pywalfox update" : "Requires pywalfox package to be installed"
checked: Settings.data.matugen.pywalfox
enabled: ProgramCheckerService.pywalfoxAvailable
opacity: ProgramCheckerService.pywalfoxAvailable ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.pywalfoxAvailable) {
Settings.data.matugen.pywalfox = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
}
}
// Miscellaneous
NCollapsible {
Layout.fillWidth: true
label: "Misc"
description: "Additional configuration options."
defaultExpanded: false
NCheckbox {
label: "User Templates"
description: "Enable user-defined Matugen config from ~/.config/matugen/config.toml"
checked: Settings.data.matugen.enableUserTemplates
onToggled: checked => {
Settings.data.matugen.enableUserTemplates = checked
if (Settings.data.colorSchemes.useWallpaperColors)
MatugenService.generateFromWallpaper()
}
}
}
}
}

View File

@@ -129,6 +129,13 @@ ColumnLayout {
onToggled: checked => Settings.data.general.showScreenCorners = checked
}
NToggle {
label: "Solid Black Corners"
description: "Force screen corners to always render as solid black."
checked: Settings.data.general.forceBlackScreenCorners
onToggled: checked => Settings.data.general.forceBlackScreenCorners = checked
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true

View File

@@ -19,7 +19,6 @@ ColumnLayout {
ColumnLayout {
spacing: Style.marginS * scaling
Layout.fillWidth: true
Layout.topMargin: Style.marginS * scaling
NTextInput {
label: "Output Directory"

View File

@@ -61,7 +61,9 @@ Singleton {
"icon": "heart",
"leftClickExec": "",
"rightClickExec": "",
"middleClickExec": ""
"middleClickExec": "",
"textCommand": "",
"textIntervalMs": 3000
},
"Microphone": {
"allowUserSettings": true,

View File

@@ -0,0 +1,118 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
// Service to check if various programs are available on the system
Singleton {
id: root
// Program availability properties
property bool matugenAvailable: false
property bool pywalfoxAvailable: false
property bool kittyAvailable: false
property bool ghosttyAvailable: false
property bool footAvailable: false
property bool fuzzelAvailable: false
property bool vesktopAvailable: false
property bool gpuScreenRecorderAvailable: false
// Signal emitted when all checks are complete
signal checksCompleted
// Programs to check - maps property names to commands
readonly property var programsToCheck: ({
"matugenAvailable": ["which", "matugen"],
"pywalfoxAvailable": ["which", "pywalfox"],
"kittyAvailable": ["which", "kitty"],
"ghosttyAvailable": ["which", "ghostty"],
"footAvailable": ["which", "foot"],
"fuzzelAvailable": ["which", "fuzzel"],
"vesktopAvailable": ["which", "vesktop"],
"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')"]
})
// Internal tracking
property int completedChecks: 0
property int totalChecks: Object.keys(programsToCheck).length
// Single reusable Process object
Process {
id: checker
running: false
property string currentProperty: ""
onExited: function (exitCode) {
// Set the availability property
root[currentProperty] = (exitCode === 0)
// Stop the process to free resources
running = false
// Track completion
root.completedChecks++
// Check next program or emit completion signal
if (root.completedChecks >= root.totalChecks) {
root.checksCompleted()
} else {
root.checkNextProgram()
}
}
stdout: StdioCollector {}
stderr: StdioCollector {}
}
// Queue of programs to check
property var checkQueue: []
property int currentCheckIndex: 0
// Function to check the next program in the queue
function checkNextProgram() {
if (currentCheckIndex >= checkQueue.length)
return
var propertyName = checkQueue[currentCheckIndex]
var command = programsToCheck[propertyName]
checker.currentProperty = propertyName
checker.command = command
checker.running = true
currentCheckIndex++
}
// Function to run all program checks
function checkAllPrograms() {
// Reset state
completedChecks = 0
currentCheckIndex = 0
checkQueue = Object.keys(programsToCheck)
// Start first check
if (checkQueue.length > 0) {
checkNextProgram()
}
}
// Function to check a specific program
function checkProgram(programProperty) {
if (!programsToCheck.hasOwnProperty(programProperty)) {
Logger.warn("ProgramChecker", "Unknown program property:", programProperty)
return
}
checker.currentProperty = programProperty
checker.command = programsToCheck[programProperty]
checker.running = true
}
// Initialize checks when service is created
Component.onCompleted: {
checkAllPrograms()
}
}

View File

@@ -13,16 +13,13 @@ Singleton {
property bool isRecording: false
property bool isPending: false
property string outputPath: ""
property bool isAvailable: false
property bool isAvailable: ProgramCheckerService.gpuScreenRecorderAvailable
Component.onCompleted: {
checkAvailability()
}
function checkAvailability() {
// Detect native or Flatpak gpu-screen-recorder
availabilityCheckProcess.command = ["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')"]
availabilityCheckProcess.running = true
// Update availability when ProgramCheckerService completes its checks
Connections {
target: ProgramCheckerService
function onChecksCompleted() {// Availability is now automatically updated via property binding
}
}
// Start or Stop recording
@@ -101,18 +98,6 @@ Singleton {
}
}
// Availability check process
Process {
id: availabilityCheckProcess
command: ["sh", "-c", "true"]
onExited: function (exitCode, exitStatus) {
// exitCode 0 means available, non-zero means unavailable
root.isAvailable = (exitCode === 0)
}
stdout: StdioCollector {}
stderr: StdioCollector {}
}
Timer {
id: pendingTimer
interval: 2000 // Wait 2 seconds to see if process stays alive

View File

@@ -8,7 +8,7 @@ Singleton {
id: root
// Public properties
property string baseVersion: "2.9.1"
property string baseVersion: "2.9.2"
property bool isDevelopment: false
property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}`

193
Widgets/NCollapsible.qml Normal file
View File

@@ -0,0 +1,193 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
ColumnLayout {
id: root
property string label: ""
property string description: ""
property bool expanded: false
property bool defaultExpanded: false
property real contentSpacing: Style.marginM * scaling
signal toggled(bool expanded)
Layout.fillWidth: true
spacing: 0
// Default property to accept children
default property alias content: contentLayout.children
// Header with clickable area
Rectangle {
id: headerContainer
Layout.fillWidth: true
Layout.preferredHeight: headerContent.implicitHeight + (Style.marginL * scaling * 2)
// Material 3 style background
color: root.expanded ? Color.mSecondary : Color.mSurfaceVariant
radius: Style.radiusL * scaling
// Subtle border
border.color: root.expanded ? Color.mOnSecondary : Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
// Smooth color transitions
Behavior on color {
ColorAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
Behavior on border.color {
ColorAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
MouseArea {
id: headerArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
root.expanded = !root.expanded
root.toggled(root.expanded)
}
// Hover effect overlay
Rectangle {
anchors.fill: parent
color: headerArea.containsMouse ? Color.mOnSurface : Color.transparent
opacity: headerArea.containsMouse ? 0.08 : 0
radius: headerContainer.radius // Reference the container's radius directly
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
}
}
}
}
RowLayout {
id: headerContent
anchors.fill: parent
anchors.margins: Style.marginL * scaling
spacing: Style.marginM * scaling
// Expand/collapse icon with rotation animation
NIcon {
id: chevronIcon
icon: "chevron-right"
font.pointSize: Style.fontSizeL * scaling
color: root.expanded ? Color.mOnSecondary : Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
rotation: root.expanded ? 90 : 0
Behavior on rotation {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
Behavior on color {
ColorAnimation {
duration: Style.animationNormal
}
}
}
// Header text content - properly contained
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: Style.marginXXS * scaling
NText {
text: root.label
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightSemiBold
color: root.expanded ? Color.mOnSecondary : Color.mOnSurface
Layout.fillWidth: true
wrapMode: Text.WordWrap
Behavior on color {
ColorAnimation {
duration: Style.animationNormal
}
}
}
NText {
text: root.description
font.pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightRegular
color: root.expanded ? Color.mOnSecondary : Color.mOnSurfaceVariant
Layout.fillWidth: true
wrapMode: Text.WordWrap
visible: root.description !== ""
opacity: 0.87
Behavior on color {
ColorAnimation {
duration: Style.animationNormal
}
}
}
}
}
}
// Collapsible content with Material 3 styling
Rectangle {
id: contentContainer
Layout.fillWidth: true
Layout.topMargin: Style.marginS * scaling
visible: root.expanded
color: Color.mSurface
radius: Style.radiusL * scaling
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
// Dynamic height based on content
Layout.preferredHeight: visible ? contentLayout.implicitHeight + (Style.marginL * scaling * 2) : 0
// Smooth height animation
Behavior on Layout.preferredHeight {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
// Content layout
ColumnLayout {
id: contentLayout
anchors.fill: parent
anchors.margins: Style.marginL * scaling
spacing: root.contentSpacing
// Clip content during animation
clip: true
}
// Fade in animation for content
opacity: root.expanded ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
}
// Initialize expanded state
Component.onCompleted: {
root.expanded = root.defaultExpanded
}
}

View File

@@ -60,7 +60,7 @@ Item {
onClicked: root.clicked()
onRightClicked: root.rightClicked()
onMiddleClicked: root.middleClicked()
onWheel: root.wheel
onWheel: delta => root.wheel(delta)
}
}
@@ -85,7 +85,7 @@ Item {
onClicked: root.clicked()
onRightClicked: root.rightClicked()
onMiddleClicked: root.middleClicked()
onWheel: root.wheel
onWheel: delta => root.wheel(delta)
}
}
}

View File

@@ -37,7 +37,7 @@ Item {
// Exposed width logic
readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling)
readonly property int pillHeight: iconSize
readonly property int pillPaddingHorizontal: Style.marginS * scaling
readonly property int pillPaddingHorizontal: 3 * 2 * scaling // Very precise adjustment don't replace by Style.margin
readonly property int pillOverlap: iconSize * 0.5
readonly property int maxPillWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 2 + pillOverlap)
@@ -70,7 +70,7 @@ Item {
var offset = rightOpen ? Style.marginXS * scaling : -Style.marginXS * scaling
if (forceOpen) {
// If its force open, the icon disc background is the same color as the bg pill move text slightly
offset += rightOpen ? -Style.marginXXS * scaling : Style.marginXS * scaling
offset += rightOpen ? -Style.marginXXS * scaling : Style.marginXXS * scaling
}
return centerX + offset
}
@@ -78,7 +78,7 @@ Item {
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
color: forceOpen ? Color.mOnSurface : Color.mPrimary
visible: revealed
}
@@ -118,7 +118,7 @@ Item {
NIcon {
icon: root.icon
font.pointSize: Style.fontSizeM * scaling
color: hovered && !forceOpen ? Color.mOnTertiary : Color.mOnSurface
color: hovered ? Color.mOnTertiary : Color.mOnSurface
// Center horizontally
x: (iconCircle.width - width) / 2
// Center vertically accounting for font metrics
@@ -243,9 +243,7 @@ Item {
root.middleClicked()
}
}
onWheel: wheel => {
root.wheel(wheel.angleDelta.y)
}
onWheel: wheel => root.wheel(wheel.angleDelta.y)
}
function show() {

View File

@@ -45,8 +45,7 @@ Item {
// Sizing logic for vertical bars
readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling)
readonly property int pillHeight: iconSize
readonly property int pillPaddingHorizontal: Style.marginS * scaling
readonly property int pillPaddingVertical: Style.marginS * scaling
readonly property int pillPaddingVertical: 3 * 2 * scaling // Very precise adjustment don't replace by Style.margin
readonly property int pillOverlap: iconSize * 0.5
readonly property int maxPillWidth: iconSize
readonly property int maxPillHeight: Math.max(1, textItem.implicitHeight + pillPaddingVertical * 4)
@@ -83,7 +82,7 @@ Item {
var offset = openDownward ? pillPaddingVertical * 0.75 : -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 += rightOpen ? -Style.marginXXS * scaling : Style.marginXS * scaling
offset += rightOpen ? -Style.marginXXS * scaling : Style.marginXXS * scaling
}
return offset
}
@@ -93,7 +92,7 @@ Item {
font.weight: Style.fontWeightMedium
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: Color.mPrimary
color: forceOpen ? Color.mOnSurface : Color.mPrimary
visible: revealed
}
@@ -142,7 +141,7 @@ Item {
NIcon {
icon: root.icon
font.pointSize: Style.fontSizeM * scaling
color: hovered && !forceOpen ? Color.mOnTertiary : Color.mOnSurface
color: hovered ? Color.mOnTertiary : Color.mOnSurface
// Center horizontally
x: (iconCircle.width - width) / 2
// Center vertically accounting for font metrics
@@ -285,9 +284,7 @@ Item {
root.middleClicked()
}
}
onWheel: wheel => {
root.wheel(wheel.angleDelta.y)
}
onWheel: wheel => root.wheel(wheel.angleDelta.y)
}
function show() {

View File

@@ -26,7 +26,9 @@ Slider {
width: root.availableWidth
height: implicitHeight
radius: 0
color: Color.mSurface
color: Qt.alpha(Color.mSurface, 0.5)
border.color: Qt.alpha(Color.mOutline, 0.5)
border.width: Math.max(1, Style.borderS * scaling)
// Animated gradient active track
Rectangle {
@@ -34,25 +36,39 @@ Slider {
width: root.visualPosition * parent.width
height: parent.height
radius: parent.radius
// Animated gradient fill
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
GradientStop {
position: 0.0
color: Qt.darker(Color.mPrimary, 1.2)
Behavior on color { ColorAnimation { duration: 300 } }
Behavior on color {
ColorAnimation {
duration: 300
}
}
}
GradientStop {
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 }
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
}
}
}
GradientStop {
GradientStop {
position: 1.0
color: Qt.lighter(Color.mPrimary, 1.2)
}
@@ -94,4 +110,4 @@ Slider {
}
}
}
}
}

View File

@@ -1,26 +0,0 @@
import QtQuick
import qs.Commons
import qs.Services
Column {
id: root
property string text: ""
property real fontSize: Style.fontSizeXS
property color color: Color.mOnSurface
property int fontWeight: Style.fontWeightBold
spacing: -2 * scaling
Repeater {
model: root.text.split("")
NText {
text: modelData
font.family: Settings.data.ui.fontFixed
font.pointSize: root.fontSize
font.weight: root.fontWeight
color: root.color
horizontalAlignment: Text.AlignHCenter
}
}
}

View File

@@ -1,5 +1,6 @@
{
description = "Noctalia shell - a Wayland desktop shell built with Quickshell";
description =
"Noctalia shell - a Wayland desktop shell built with Quickshell";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
@@ -11,22 +12,13 @@
};
};
outputs =
{
self,
nixpkgs,
systems,
quickshell,
...
}:
let
eachSystem = nixpkgs.lib.genAttrs (import systems);
in
{
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
outputs = { self, nixpkgs, systems, quickshell, ... }:
let eachSystem = nixpkgs.lib.genAttrs (import systems);
in {
formatter =
eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
packages = eachSystem (
system:
packages = eachSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
qs = quickshell.packages.${system}.default.override {
@@ -34,8 +26,7 @@
withI3 = false;
};
runtimeDeps =
with pkgs;
runtimeDeps = with pkgs;
[
bash
bluez
@@ -50,34 +41,21 @@
matugen
networkmanager
wl-clipboard
]
++ lib.optionals (pkgs.stdenv.hostPlatform.isx86_64) [
gpu-screen-recorder
];
] ++ lib.optionals (pkgs.stdenv.hostPlatform.isx86_64)
[ gpu-screen-recorder ];
fontconfig = pkgs.makeFontsConf {
fontDirectories = [
pkgs.roboto
pkgs.inter-nerdfont
];
fontDirectories = [ pkgs.roboto pkgs.inter-nerdfont ];
};
in
{
in {
default = pkgs.stdenv.mkDerivation {
pname = "noctalia-shell";
version = self.rev or self.dirtyRev or "dirty";
src = ./.;
nativeBuildInputs = [
pkgs.gcc
pkgs.makeWrapper
pkgs.qt6.wrapQtAppsHook
];
buildInputs = [
qs
pkgs.xkeyboard-config
pkgs.qt6.qtbase
];
nativeBuildInputs =
[ pkgs.gcc pkgs.makeWrapper pkgs.qt6.wrapQtAppsHook ];
buildInputs = [ qs pkgs.xkeyboard_config pkgs.qt6.qtbase ];
propagatedBuildInputs = runtimeDeps;
installPhase = ''
@@ -91,14 +69,14 @@
'';
meta = {
description = "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.";
description =
"A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.";
homepage = "https://github.com/noctalia-dev/noctalia-shell";
license = pkgs.lib.licenses.mit;
mainProgram = "noctalia-shell";
};
};
}
);
});
defaultPackage = eachSystem (system: self.packages.${system}.default);
};