Compare commits
15 Commits
4bc368dfd9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 37e8efa795 | |||
| 6847a123d5 | |||
| f28788b71a | |||
| 5aec12cd1f | |||
| 6efa5d599c | |||
| 9248f9b33f | |||
| 252c88b869 | |||
| 63f99ff01a | |||
| 586e4b6320 | |||
| c2b28df404 | |||
| 8d95eeb892 | |||
| d0d3fc23e8 | |||
| 98235a8c18 | |||
| e645ea1641 | |||
| 7824d27555 |
@ -11,6 +11,7 @@ default_roles:
|
|||||||
# - ssh
|
# - ssh
|
||||||
- hyprland
|
- hyprland
|
||||||
- ghostty
|
- ghostty
|
||||||
|
- quickshell
|
||||||
|
|
||||||
temp:
|
temp:
|
||||||
- docker # Container platform
|
- docker # Container platform
|
||||||
@ -67,4 +68,5 @@ aur_packages:
|
|||||||
- pgformatter-git
|
- pgformatter-git
|
||||||
# - jetbrains-toolbox
|
# - jetbrains-toolbox
|
||||||
|
|
||||||
|
home_dir: "/home/johannes"
|
||||||
config_dir: "/home/johannes/.config"
|
config_dir: "/home/johannes/.config"
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
- name: Setup dotfiles configuration
|
- name: Setup dotfiles configuration
|
||||||
hosts: localhost
|
hosts: localhost
|
||||||
connection: local
|
connection: local
|
||||||
become: false
|
become: true
|
||||||
|
become_user: johannes
|
||||||
vars:
|
vars:
|
||||||
dotfiles_dir: "{{ ansible_env.HOME }}/dotfiles"
|
dotfiles_dir: "{{ ansible_env.HOME }}/dotfiles"
|
||||||
# roles:
|
# roles:
|
||||||
|
|||||||
@ -14,7 +14,7 @@ source = ./workspaces.conf
|
|||||||
# See https://wiki.hypr.land/Configuring/Keywords/
|
# See https://wiki.hypr.land/Configuring/Keywords/
|
||||||
|
|
||||||
$terminal = ghostty
|
$terminal = ghostty
|
||||||
$fileManager = thunar
|
$fileManager = ghostty -e yazi
|
||||||
$menu = env XDG_CURRENT_DESKTOP=Hyprland wofi --show drun
|
$menu = env XDG_CURRENT_DESKTOP=Hyprland wofi --show drun
|
||||||
|
|
||||||
#################
|
#################
|
||||||
@ -25,7 +25,8 @@ $menu = env XDG_CURRENT_DESKTOP=Hyprland wofi --show drun
|
|||||||
# Or execute your favorite apps at launch like this:
|
# Or execute your favorite apps at launch like this:
|
||||||
# exec-once = waybar
|
# exec-once = waybar
|
||||||
exec-once = /usr/lib/hyprpolkitagent/hyprpolkitagent
|
exec-once = /usr/lib/hyprpolkitagent/hyprpolkitagent
|
||||||
exec-once = hyprpanel
|
# exec-once = hyprpanel
|
||||||
|
exec-once = quickshell
|
||||||
exec-once = dunst
|
exec-once = dunst
|
||||||
exec-once = firefox
|
exec-once = firefox
|
||||||
exec-once = hyprpaper
|
exec-once = hyprpaper
|
||||||
@ -45,7 +46,7 @@ debug:disable_logs = false
|
|||||||
# See https://wiki.hypr.land/Configuring/Environment-variables/
|
# See https://wiki.hypr.land/Configuring/Environment-variables/
|
||||||
env = XCURSOR_SIZE,24
|
env = XCURSOR_SIZE,24
|
||||||
env = HYPRCURSOR_SIZE,24
|
env = HYPRCURSOR_SIZE,24
|
||||||
env = QT_QPA_PLATFORM,waylandl;xcb
|
env = QT_QPA_PLATFORM,wayland
|
||||||
env = QT_QPA_PLATFORMTHEME,qt5ct
|
env = QT_QPA_PLATFORMTHEME,qt5ct
|
||||||
env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1
|
env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1
|
||||||
env = QT_AUTO_SCREEN_SCALE_FACTOR,1
|
env = QT_AUTO_SCREEN_SCALE_FACTOR,1
|
||||||
@ -90,18 +91,13 @@ env = _JAVA_AWT_WM_NONREPARENTING, 1
|
|||||||
# https://wiki.hypr.land/Configuring/Variables/#general
|
# https://wiki.hypr.land/Configuring/Variables/#general
|
||||||
general {
|
general {
|
||||||
gaps_in = 5
|
gaps_in = 5
|
||||||
gaps_out = 20
|
gaps_out = 10
|
||||||
|
|
||||||
border_size = 2
|
border_size = 3
|
||||||
|
|
||||||
# https://wiki.hypr.land/Configuring/Variables/#variable-types for info about colors
|
# https://wiki.hypr.land/Configuring/Variables/#variable-types for info about colors
|
||||||
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
|
col.active_border = rgba(9b1a1aee) rgba(c94040ee) 45deg
|
||||||
col.inactive_border = rgba(595959aa)
|
col.inactive_border = rgba(595959aa)
|
||||||
# Active: cosmic purple -> neon cyan gradient
|
|
||||||
# col.active_border = rgba(7f5af0ee) rgba(00d1ffee) 45deg
|
|
||||||
#
|
|
||||||
# # Inactive: deep space slate
|
|
||||||
# col.inactive_border = rgba(0c0e14aa)
|
|
||||||
|
|
||||||
# Set to true enable resizing windows by clicking and dragging on borders and gaps
|
# Set to true enable resizing windows by clicking and dragging on borders and gaps
|
||||||
resize_on_border = false
|
resize_on_border = false
|
||||||
@ -200,7 +196,7 @@ animations {
|
|||||||
|
|
||||||
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
|
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
|
||||||
dwindle {
|
dwindle {
|
||||||
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
# pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
||||||
preserve_split = true # You probably want this
|
preserve_split = true # You probably want this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,6 +330,13 @@ windowrule {
|
|||||||
suppress_event = maximize
|
suppress_event = maximize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xwayland {
|
||||||
|
enabled = true
|
||||||
|
use_nearest_neighbor = true
|
||||||
|
force_zero_scaling = false
|
||||||
|
create_abstract_socket = false
|
||||||
|
}
|
||||||
|
|
||||||
# Fix some dragging issues with XWayland
|
# Fix some dragging issues with XWayland
|
||||||
windowrule {
|
windowrule {
|
||||||
name = fix-xwayland-drags
|
name = fix-xwayland-drags
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
preload = /home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
preload = /home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||||
|
|
||||||
wallpaper {
|
wallpaper {
|
||||||
monitor = DP-1
|
monitor = DP-1
|
||||||
path =/home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
path =/home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||||
# fitmode = cover
|
# fitmode = cover
|
||||||
}
|
}
|
||||||
wallpaper {
|
wallpaper {
|
||||||
monitor = DP-2
|
monitor = DP-2
|
||||||
path =/home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
path =/home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||||
# fitmode = cover
|
# fitmode = cover
|
||||||
}
|
}
|
||||||
wallpaper {
|
wallpaper {
|
||||||
monitor = HDMI-A-1
|
monitor = HDMI-A-1
|
||||||
path =/home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
path =/home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||||
# fitmode = cover
|
# fitmode = cover
|
||||||
}
|
}
|
||||||
wallpaper {
|
wallpaper {
|
||||||
monitor = HDMI-A-2
|
monitor = HDMI-A-2
|
||||||
path =/home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
path =/home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||||
# fitmode = cover
|
# fitmode = cover
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
roles/hyprland/files/hypr/monitors_jk-nb.conf
Normal file
3
roles/hyprland/files/hypr/monitors_jk-nb.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Generated by nwg-displays on 2026-04-10 at 16:05:43. Do not edit manually.
|
||||||
|
|
||||||
|
monitor=eDP-1,2880x1800@90.0,0x0,1.4999999999999996
|
||||||
@ -1,7 +1,7 @@
|
|||||||
# Generated by nwg-displays on 2026-02-28 at 11:47:04. Do not edit manually.
|
# Generated by nwg-displays on 2026-04-20 at 01:17:12. Do not edit manually.
|
||||||
|
|
||||||
monitor=DP-1,2560x1440@165.0,6626x876,1.0
|
monitor=DP-1,2560x1440@165.0,6626x876,1.0
|
||||||
monitor=DP-2,2560x1440@165.0,4066x876,1.0
|
monitor=DP-2,2560x1440@165.0,4066x876,1.0
|
||||||
monitor=HDMI-A-1,disable
|
|
||||||
monitor=HDMI-A-2,1920x1080@60.0,9186x396,1.0
|
monitor=HDMI-A-2,1920x1080@60.0,9186x396,1.0
|
||||||
monitor=HDMI-A-2,transform,3
|
monitor=HDMI-A-2,transform,3
|
||||||
|
monitor=HDMI-A-1,disable
|
||||||
|
|||||||
@ -41,5 +41,8 @@
|
|||||||
"bar.battery.label": false,
|
"bar.battery.label": false,
|
||||||
"bar.battery.hideLabelWhenFull": true,
|
"bar.battery.hideLabelWhenFull": true,
|
||||||
"theme.bar.buttons.battery.enableBorder": false,
|
"theme.bar.buttons.battery.enableBorder": false,
|
||||||
"theme.osd.enable": true
|
"theme.osd.enable": true,
|
||||||
|
"theme.bar.buttons.y_margins": "0.2em",
|
||||||
|
"theme.bar.dropdownGap": "2.9em",
|
||||||
|
"theme.bar.outer_spacing": "1.6em"
|
||||||
}
|
}
|
||||||
@ -15,32 +15,31 @@
|
|||||||
path: "{{ config_dir }}/hypr"
|
path: "{{ config_dir }}/hypr"
|
||||||
state: directory
|
state: directory
|
||||||
mode: '0755'
|
mode: '0755'
|
||||||
owner: johannes
|
|
||||||
group: johannes
|
|
||||||
|
|
||||||
- name: Symlink hyprland config files
|
- name: Symlink hyprland config files
|
||||||
file:
|
file:
|
||||||
src: "{{ role_path }}/files/hypr/{{ item }}"
|
src: "{{ role_path }}/files/hypr/{{ conf_file }}"
|
||||||
dest: "{{ config_dir }}/hypr/{{ item }}"
|
dest: "{{ config_dir }}/hypr/{{ conf_file }}"
|
||||||
state: link
|
state: link
|
||||||
force: true
|
force: true
|
||||||
loop:
|
loop:
|
||||||
- hyprland.conf
|
- hyprland.conf
|
||||||
- hyprlock.conf
|
- hyprlock.conf
|
||||||
- hyprpaper.conf
|
- hyprpaper.conf
|
||||||
# - workspaces.conf
|
|
||||||
- scripts
|
- scripts
|
||||||
|
loop_control:
|
||||||
|
loop_var: conf_file
|
||||||
|
|
||||||
- name: Symlink host-specific monitors.conf
|
- name: Symlink host-specific monitors.conf
|
||||||
file:
|
file:
|
||||||
src: "{{ role_path }}/files/hypr/monitors_{{ ansible_hostname }}.conf"
|
src: "{{ role_path }}/files/hypr/monitors_{{ ansible_facts['hostname'] }}.conf"
|
||||||
dest: "{{ config_dir }}/hypr/monitors.conf"
|
dest: "{{ config_dir }}/hypr/monitors.conf"
|
||||||
state: link
|
state: link
|
||||||
force: true
|
force: true
|
||||||
|
|
||||||
- name: Symlink host-specific workspaces.conf
|
- name: Symlink host-specific workspaces.conf
|
||||||
file:
|
file:
|
||||||
src: "{{ role_path }}/files/hypr/workspaces_{{ ansible_hostname }}.conf"
|
src: "{{ role_path }}/files/hypr/workspaces_{{ ansible_facts['hostname'] }}.conf"
|
||||||
dest: "{{ config_dir }}/hypr/workspaces.conf"
|
dest: "{{ config_dir }}/hypr/workspaces.conf"
|
||||||
state: link
|
state: link
|
||||||
force: true
|
force: true
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
vim.opt_local.shiftwidth = 4
|
vim.opt_local.shiftwidth = 2
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"LuaSnip": { "branch": "master", "commit": "5a1e39223db9a0498024a77b8441169d260c8c25" },
|
"LuaSnip": { "branch": "master", "commit": "642b0c595e11608b4c18219e93b88d7637af27bc" },
|
||||||
"alpha-nvim": { "branch": "main", "commit": "a9d8fb72213c8b461e791409e7feabb74eb6ce73" },
|
"alpha-nvim": { "branch": "main", "commit": "a9d8fb72213c8b461e791409e7feabb74eb6ce73" },
|
||||||
"auto-session": { "branch": "main", "commit": "62437532b38495551410b3f377bcf4aaac574ebe" },
|
"auto-session": { "branch": "main", "commit": "62437532b38495551410b3f377bcf4aaac574ebe" },
|
||||||
"bufdelete.nvim": { "branch": "master", "commit": "f6bcea78afb3060b198125256f897040538bcb81" },
|
"bufdelete.nvim": { "branch": "master", "commit": "f6bcea78afb3060b198125256f897040538bcb81" },
|
||||||
@ -12,28 +12,29 @@
|
|||||||
"dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
|
"dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
|
||||||
"flutter-tools.nvim": { "branch": "main", "commit": "677cc07c16e8b89999108d2ebeefcfc5f539b73c" },
|
"flutter-tools.nvim": { "branch": "main", "commit": "677cc07c16e8b89999108d2ebeefcfc5f539b73c" },
|
||||||
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
|
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
|
||||||
|
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
|
||||||
"lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" },
|
"lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" },
|
||||||
"lazygit.nvim": { "branch": "main", "commit": "a04ad0dbc725134edbee3a5eea29290976695357" },
|
"lazygit.nvim": { "branch": "main", "commit": "a04ad0dbc725134edbee3a5eea29290976695357" },
|
||||||
"lspkind.nvim": { "branch": "master", "commit": "c7274c48137396526b59d86232eabcdc7fed8a32" },
|
"lspkind.nvim": { "branch": "master", "commit": "c7274c48137396526b59d86232eabcdc7fed8a32" },
|
||||||
"lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" },
|
"lualine.nvim": { "branch": "master", "commit": "8811f3f3f4dc09d740c67e9ce399e7a541e2e5b2" },
|
||||||
"mason-lspconfig.nvim": { "branch": "main", "commit": "a676ab7282da8d651e175118bcf54483ca11e46d" },
|
"mason-lspconfig.nvim": { "branch": "main", "commit": "63a3c6a80538de1003373a619e29aeda27809ad3" },
|
||||||
"mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" },
|
"mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" },
|
||||||
"mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" },
|
"mason.nvim": { "branch": "main", "commit": "b03fb0f20bc1d43daf558cda981a2be22e73ac42" },
|
||||||
"monokai-nightasty.nvim": { "branch": "main", "commit": "8be5e1c6e1d59873505e81b161e923264dfa5c1a" },
|
"monokai-nightasty.nvim": { "branch": "main", "commit": "1e9b92006782a1217d0a7a871b871768f1cbf5ed" },
|
||||||
"none-ls-extras.nvim": { "branch": "main", "commit": "c6fa39ac52814182c05552cb5d3750cae23ff0f0" },
|
"none-ls-extras.nvim": { "branch": "main", "commit": "14fa31ce8c0268a3b2c9cc14979ecf771982d433" },
|
||||||
"none-ls.nvim": { "branch": "main", "commit": "c9317c2a8629d4e39e7cf47be74cb67f3ab37cda" },
|
"none-ls.nvim": { "branch": "main", "commit": "7f9301e416533b5d74e2fb3b1ce5059eeaed748b" },
|
||||||
"nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
|
"nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
|
||||||
"nvim-cmp": { "branch": "main", "commit": "da88697d7f45d16852c6b2769dc52387d1ddc45f" },
|
"nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" },
|
||||||
"nvim-lsp-file-operations": { "branch": "master", "commit": "b9c795d3973e8eec22706af14959bc60c579e771" },
|
"nvim-lsp-file-operations": { "branch": "master", "commit": "b9c795d3973e8eec22706af14959bc60c579e771" },
|
||||||
"nvim-lspconfig": { "branch": "master", "commit": "1d13d2b0df9a0a02904c76d7ad6810f71d404406" },
|
"nvim-lspconfig": { "branch": "master", "commit": "fb5fa30626ae10f7f79f740059d3769993936ecb" },
|
||||||
"nvim-surround": { "branch": "main", "commit": "61319d4bd1c5e336e197defa15bd104c51f0fb29" },
|
"nvim-surround": { "branch": "main", "commit": "61319d4bd1c5e336e197defa15bd104c51f0fb29" },
|
||||||
"nvim-tree.lua": { "branch": "master", "commit": "b3772adec8db61ba9098c5624a0823a77be3a23d" },
|
"nvim-tree.lua": { "branch": "master", "commit": "509962f21ab7289d8dcd28568af539be39a8c01e" },
|
||||||
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
|
"nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
|
||||||
"nvim-ts-autotag": { "branch": "main", "commit": "8e1c0a389f20bf7f5b0dd0e00306c1247bda2595" },
|
"nvim-ts-autotag": { "branch": "main", "commit": "8e1c0a389f20bf7f5b0dd0e00306c1247bda2595" },
|
||||||
"nvim-web-devicons": { "branch": "master", "commit": "d7462543c9e366c0d196c7f67a945eaaf5d99414" },
|
"nvim-web-devicons": { "branch": "master", "commit": "95b7a002d5dba1a42eb58f5fac5c565a485eefd0" },
|
||||||
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
|
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
|
||||||
"telescope-fzf-native.nvim": { "branch": "main", "commit": "6fea601bd2b694c6f2ae08a6c6fab14930c60e2c" },
|
"telescope-fzf-native.nvim": { "branch": "main", "commit": "6fea601bd2b694c6f2ae08a6c6fab14930c60e2c" },
|
||||||
"telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" },
|
"telescope.nvim": { "branch": "master", "commit": "7ef4d6dccb78ee71e552bbd866176762ad328afa" },
|
||||||
"vim-floaterm": { "branch": "master", "commit": "0ab5eb8135dc884bc543a819ac7033c15e72a76b" },
|
"vim-floaterm": { "branch": "master", "commit": "0ab5eb8135dc884bc543a819ac7033c15e72a76b" },
|
||||||
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
|
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,10 @@ keymap.set('n', 'j', 'gj', opts)
|
|||||||
keymap.set('n', '<UP>', 'gk', opts)
|
keymap.set('n', '<UP>', 'gk', opts)
|
||||||
keymap.set('n', '<DOWN>', 'gj', opts)
|
keymap.set('n', '<DOWN>', 'gj', opts)
|
||||||
|
|
||||||
|
-- bind tabswitching to Tab and Shift-Tab
|
||||||
|
keymap.set('n', '<Tab>', ':tabnext<CR>', opts)
|
||||||
|
keymap.set('n', '<S-Tab>', ':tabprev<CR>', opts)
|
||||||
|
|
||||||
-- COMMANDS
|
-- COMMANDS
|
||||||
|
|
||||||
-- print current working directory
|
-- print current working directory
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
local opt = vim.opt
|
local opt = vim.opt
|
||||||
|
|
||||||
|
vim.filetype.add({ extension = { mdx = 'mdx' } })
|
||||||
|
|
||||||
-- numbers
|
-- numbers
|
||||||
opt.relativenumber = true
|
opt.relativenumber = true
|
||||||
opt.number = true
|
opt.number = true
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
local lazypath = vim.fn.stdpath('data') .. 'lazy/lazy.nvim'
|
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
|
||||||
|
|
||||||
-- bootstrap lazy
|
-- bootstrap lazy
|
||||||
if not (vim.uv or vim.loop).fs_stat(lazypath) then
|
if not (vim.uv or vim.loop).fs_stat(lazypath) then
|
||||||
|
|||||||
@ -59,6 +59,7 @@ return {
|
|||||||
keymap.set('n', '<leader>sl', '<cmd>AutoSession search<cr>', opts)
|
keymap.set('n', '<leader>sl', '<cmd>AutoSession search<cr>', opts)
|
||||||
keymap.set('n', '<leader>sr', session_restore, opts)
|
keymap.set('n', '<leader>sr', session_restore, opts)
|
||||||
keymap.set('n', '<leader>sd', '<cmd>AutoSession delete<cr>', opts)
|
keymap.set('n', '<leader>sd', '<cmd>AutoSession delete<cr>', opts)
|
||||||
|
keymap.set('n', '<leader>qa', '<cmd>qa<cr>', opts)
|
||||||
|
|
||||||
local function named_save()
|
local function named_save()
|
||||||
vim.ui.input({ prompt = 'Enter Session name: ' }, function(input)
|
vim.ui.input({ prompt = 'Enter Session name: ' }, function(input)
|
||||||
@ -70,12 +71,14 @@ return {
|
|||||||
local wk = require('which-key')
|
local wk = require('which-key')
|
||||||
wk.add({
|
wk.add({
|
||||||
{ '<leader>s', group = 'auto-session' },
|
{ '<leader>s', group = 'auto-session' },
|
||||||
|
{ '<leader>q', group = 'quit' },
|
||||||
{ '<leader>ss', desc = 'SessionSave' },
|
{ '<leader>ss', desc = 'SessionSave' },
|
||||||
{ '<leader>sS', desc = 'NamedSessionSave' },
|
{ '<leader>sS', desc = 'NamedSessionSave' },
|
||||||
{ '<leader>sq', desc = 'Save and Quit' },
|
{ '<leader>sq', desc = 'Save and Quit' },
|
||||||
{ '<leader>sl', desc = 'SessionList (SessionSearch)'},
|
{ '<leader>sl', desc = 'SessionList (SessionSearch)'},
|
||||||
{ '<leader>sr', desc = 'SessionRestore'},
|
{ '<leader>sr', desc = 'SessionRestore'},
|
||||||
{ '<leader>sd', desc = 'SessionDelete'},
|
{ '<leader>sd', desc = 'SessionDelete'},
|
||||||
|
{ '<leader>qa', desc = 'Quit All'},
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,7 +85,16 @@ return {
|
|||||||
-- Explicitly enable all mason-managed servers.
|
-- Explicitly enable all mason-managed servers.
|
||||||
-- This is belt-and-suspenders alongside mason-lspconfig's automatic_enable,
|
-- This is belt-and-suspenders alongside mason-lspconfig's automatic_enable,
|
||||||
-- ensuring servers start regardless of mason-lspconfig version.
|
-- ensuring servers start regardless of mason-lspconfig version.
|
||||||
vim.lsp.enable({ 'lua_ls', 'html', 'cssls', 'bashls', 'ts_ls', 'eslint', 'tailwindcss' })
|
vim.lsp.enable({ 'lua_ls', 'html', 'cssls', 'bashls', 'ts_ls', 'eslint', 'tailwindcss', 'mdx_analyzer' })
|
||||||
|
|
||||||
|
-- mdx_analyzer: needs typescript SDK path to find tsserverlibrary.js
|
||||||
|
vim.lsp.config('mdx_analyzer', {
|
||||||
|
init_options = {
|
||||||
|
typescript = {
|
||||||
|
tsdk = vim.fn.stdpath('data') .. '/mason/packages/typescript-language-server/node_modules/typescript/lib',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
-- hyprls: not managed by mason, enable manually
|
-- hyprls: not managed by mason, enable manually
|
||||||
vim.lsp.config('hyprls', {
|
vim.lsp.config('hyprls', {
|
||||||
@ -96,5 +105,11 @@ return {
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
vim.lsp.enable('hyprls')
|
vim.lsp.enable('hyprls')
|
||||||
|
|
||||||
|
vim.lsp.config('qmlls', {
|
||||||
|
cmd = { 'qmlls6' },
|
||||||
|
})
|
||||||
|
vim.lsp.enable('qmlls')
|
||||||
|
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
return {
|
return {
|
||||||
'nvim-telescope/telescope.nvim',
|
'nvim-telescope/telescope.nvim', -- 0.1.x dropped; master has nvim 0.12 compat (ft_to_lang removal)
|
||||||
branch = '0.1.x',
|
|
||||||
dependencies = {
|
dependencies = {
|
||||||
'nvim-lua/plenary.nvim',
|
'nvim-lua/plenary.nvim',
|
||||||
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
|
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
|
||||||
|
|||||||
@ -1,23 +1,18 @@
|
|||||||
return {
|
return {
|
||||||
"nvim-treesitter/nvim-treesitter",
|
"nvim-treesitter/nvim-treesitter",
|
||||||
|
branch = "main", -- master is frozen; main required for nvim 0.12+
|
||||||
event = { "BufReadPre", "BufNewFile" },
|
event = { "BufReadPre", "BufNewFile" },
|
||||||
build = ":TSUpdate",
|
build = ":TSUpdate",
|
||||||
dependencies = {
|
dependencies = {
|
||||||
"windwp/nvim-ts-autotag",
|
"windwp/nvim-ts-autotag",
|
||||||
},
|
},
|
||||||
config = function()
|
config = function()
|
||||||
local treesitter = require('nvim-treesitter.configs')
|
require('nvim-treesitter').setup({
|
||||||
|
|
||||||
treesitter.setup({
|
|
||||||
auto_install = true,
|
auto_install = true,
|
||||||
sync_install = false,
|
prefer_git = true, -- tarball downloads fail for some parsers on 0.12
|
||||||
highlight = {
|
highlight = { enable = true },
|
||||||
enable = true,
|
|
||||||
additional_vim_regex_highlighting = false,
|
|
||||||
},
|
|
||||||
indent = { enable = true },
|
indent = { enable = true },
|
||||||
autotag = { enable = true },
|
install = {
|
||||||
ensure_installed = {
|
|
||||||
'json',
|
'json',
|
||||||
'javascript',
|
'javascript',
|
||||||
'typescript',
|
'typescript',
|
||||||
@ -35,17 +30,10 @@ return {
|
|||||||
'python',
|
'python',
|
||||||
'gitignore',
|
'gitignore',
|
||||||
'c',
|
'c',
|
||||||
},
|
'mdx',
|
||||||
ignore_install = {},
|
|
||||||
incremental_selection = {
|
|
||||||
enable = true,
|
|
||||||
keymaps = {
|
|
||||||
init_selection = '<C-Space>',
|
|
||||||
node_incremental = '<C-Space>',
|
|
||||||
scope_incremental = false,
|
|
||||||
node_decremental = '<bs>',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
require('nvim-ts-autotag').setup()
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
---
|
---
|
||||||
- name: Install neovim
|
- name: Install neovim
|
||||||
package:
|
package:
|
||||||
name: neovim
|
name: neovim
|
||||||
state: present
|
state: present
|
||||||
|
become: yes
|
||||||
|
become_user: root
|
||||||
|
|
||||||
- name: Symlink neovim
|
- name: Symlink neovim
|
||||||
file:
|
file:
|
||||||
src: "{{ role_path }}/files"
|
src: "{{ role_path }}/files"
|
||||||
dest: "{{ config_dir }}/nvim"
|
dest: "{{ config_dir }}/nvim"
|
||||||
state: link
|
state: link
|
||||||
|
|||||||
140
roles/quickshell/files/bar/Bar.qml
Normal file
140
roles/quickshell/files/bar/Bar.qml
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var modelData
|
||||||
|
screen: modelData
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
implicitHeight: 40
|
||||||
|
color: Theme.bg
|
||||||
|
|
||||||
|
readonly property string screenName: modelData.name
|
||||||
|
property string activePopup: ""
|
||||||
|
|
||||||
|
readonly property int bw: Theme.borderWidth // 2
|
||||||
|
readonly property int pad: Theme.enclosureMargin // 3
|
||||||
|
|
||||||
|
// ── Bar bottom border ─────────────────────────────────────────────
|
||||||
|
Rectangle {
|
||||||
|
anchors { bottom: parent.bottom; left: parent.left; right: parent.right }
|
||||||
|
height: root.bw
|
||||||
|
color: Theme.border
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bar content ───────────────────────────────────────────────────
|
||||||
|
Item {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: 8
|
||||||
|
rightMargin: 8
|
||||||
|
topMargin: root.bw
|
||||||
|
bottomMargin: root.bw
|
||||||
|
}
|
||||||
|
|
||||||
|
Workspaces {
|
||||||
|
anchors { left: parent.left; verticalCenter: parent.verticalCenter }
|
||||||
|
screenName: root.screenName
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicPlayer {
|
||||||
|
id: musicChip
|
||||||
|
anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
|
||||||
|
onClicked: root.activePopup = root.activePopup === "controls" ? "" : "controls"
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: rightRow
|
||||||
|
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
SysTray {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
barWindow: root
|
||||||
|
}
|
||||||
|
Battery {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
NetworkStatus {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
VolumeControl {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClickedLeft: root.activePopup = root.activePopup === "mixer" ? "" : "mixer"
|
||||||
|
}
|
||||||
|
Clock {
|
||||||
|
id: clockDisp
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: root.activePopup = root.activePopup === "calendar" ? "" : "calendar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Music controls popup ──────────────────────────────────────────
|
||||||
|
PopoutWindow {
|
||||||
|
popupName: "controls"
|
||||||
|
activePopup: root.activePopup
|
||||||
|
|
||||||
|
anchor.window: root
|
||||||
|
anchor.rect.y: root.implicitHeight - root.bw - Theme.radius
|
||||||
|
anchor.rect.x: Math.round((root.width - implicitWidth) / 2)
|
||||||
|
|
||||||
|
implicitWidth: Math.max(musicChip.implicitWidth + 2 * root.pad,
|
||||||
|
ctrlContent.implicitWidth + 2 * root.bw)
|
||||||
|
implicitHeight: ctrlContent.implicitHeight + root.bw + Theme.radius
|
||||||
|
|
||||||
|
MusicPlayerControls {
|
||||||
|
id: ctrlContent
|
||||||
|
anchors { left: parent.left; right: parent.right }
|
||||||
|
player: musicChip.player
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Calendar popup ────────────────────────────────────────────────
|
||||||
|
PopoutWindow {
|
||||||
|
popupName: "calendar"
|
||||||
|
activePopup: root.activePopup
|
||||||
|
|
||||||
|
anchor.window: root
|
||||||
|
anchor.rect.y: root.implicitHeight - root.bw - Theme.radius
|
||||||
|
readonly property real pw: Math.max(rightRow.width + 2 * root.pad,
|
||||||
|
calContent.implicitWidth + 2 * root.bw)
|
||||||
|
anchor.rect.x: root.width - pw
|
||||||
|
|
||||||
|
implicitWidth: pw
|
||||||
|
implicitHeight: calContent.implicitHeight + root.bw + Theme.radius
|
||||||
|
|
||||||
|
CalendarContent {
|
||||||
|
id: calContent
|
||||||
|
anchors { left: parent.left; right: parent.right }
|
||||||
|
now: clockDisp.now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Volume mixer popup ────────────────────────────────────────────
|
||||||
|
PopoutWindow {
|
||||||
|
popupName: "mixer"
|
||||||
|
activePopup: root.activePopup
|
||||||
|
|
||||||
|
anchor.window: root
|
||||||
|
anchor.rect.y: root.implicitHeight - root.bw - Theme.radius
|
||||||
|
readonly property real pw: Math.max(rightRow.width + 2 * root.pad,
|
||||||
|
mixContent.implicitWidth + 2 * root.bw)
|
||||||
|
anchor.rect.x: root.width - pw
|
||||||
|
|
||||||
|
implicitWidth: pw
|
||||||
|
implicitHeight: mixContent.implicitHeight + root.bw + Theme.radius
|
||||||
|
|
||||||
|
VolumeMixerContent {
|
||||||
|
id: mixContent
|
||||||
|
anchors { left: parent.left; right: parent.right }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
roles/quickshell/files/bar/Battery.qml
Normal file
84
roles/quickshell/files/bar/Battery.qml
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import QtQuick
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
implicitWidth: hasBattery ? row.implicitWidth + 10 : 0
|
||||||
|
implicitHeight: 24
|
||||||
|
visible: hasBattery
|
||||||
|
|
||||||
|
property bool hasBattery: false
|
||||||
|
property int percent: 0
|
||||||
|
property string status: "Unknown" // "Charging", "Discharging", "Full", "Unknown"
|
||||||
|
|
||||||
|
// \uf0e7 = bolt (charging), \uf240-\uf244 = FA battery full…empty
|
||||||
|
readonly property string battIcon: {
|
||||||
|
if (status === "Charging" || status === "Full") return "\uf0e7"
|
||||||
|
if (percent <= 10) return "\uf244"
|
||||||
|
if (percent <= 25) return "\uf243"
|
||||||
|
if (percent <= 50) return "\uf242"
|
||||||
|
if (percent <= 75) return "\uf241"
|
||||||
|
return "\uf240"
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property bool isLow: percent <= 20 && status === "Discharging"
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 30000
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: battProc.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: battProc
|
||||||
|
// Find first BAT* supply; print "NONE" if none exists
|
||||||
|
command: ["sh", "-c",
|
||||||
|
"BAT=$(ls /sys/class/power_supply/ 2>/dev/null | grep -m1 '^BAT'); " +
|
||||||
|
"[ -z \"$BAT\" ] && echo NONE && exit 0; " +
|
||||||
|
"echo \"$(cat /sys/class/power_supply/$BAT/capacity):$(cat /sys/class/power_supply/$BAT/status)\""
|
||||||
|
]
|
||||||
|
stdout: SplitParser {
|
||||||
|
splitMarker: "\n"
|
||||||
|
onRead: data => {
|
||||||
|
const line = data.trim()
|
||||||
|
if (line === "" || line === "NONE") {
|
||||||
|
root.hasBattery = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const sep = line.indexOf(":")
|
||||||
|
if (sep < 0) return
|
||||||
|
root.percent = parseInt(line.substring(0, sep)) || 0
|
||||||
|
root.status = line.substring(sep + 1)
|
||||||
|
root.hasBattery = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.battIcon
|
||||||
|
font.family: "JetBrainsMono Nerd Font Mono"
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: root.status === "Charging" ? Theme.accent
|
||||||
|
: root.isLow ? "#cc3333"
|
||||||
|
: Theme.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.percent + "%"
|
||||||
|
font.pixelSize: 11
|
||||||
|
color: root.isLow ? "#cc3333" : Theme.textDim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
139
roles/quickshell/files/bar/CalendarContent.qml
Normal file
139
roles/quickshell/files/bar/CalendarContent.qml
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import QtQuick
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
// Calendar grid – a plain Item so it can live inside an expanding section border.
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var now: new Date()
|
||||||
|
|
||||||
|
property int viewYear: now.getFullYear()
|
||||||
|
property int viewMonth: now.getMonth() // 0-based
|
||||||
|
|
||||||
|
readonly property var monthNames: [
|
||||||
|
"January","February","March","April","May","June",
|
||||||
|
"July","August","September","October","November","December"
|
||||||
|
]
|
||||||
|
readonly property int daysInMonth: new Date(viewYear, viewMonth + 1, 0).getDate()
|
||||||
|
// First weekday of month: Mon=0 … Sun=6
|
||||||
|
readonly property int startOffset: (new Date(viewYear, viewMonth, 1).getDay() + 6) % 7
|
||||||
|
readonly property int totalCells: Math.ceil((startOffset + daysInMonth) / 7) * 7
|
||||||
|
|
||||||
|
readonly property int todayYear: now.getFullYear()
|
||||||
|
readonly property int todayMonth: now.getMonth()
|
||||||
|
readonly property int todayDay: now.getDate()
|
||||||
|
|
||||||
|
// All widths derived from this constant — never from parent.width —
|
||||||
|
// to avoid Column polish loops when the popup is wider than the grid.
|
||||||
|
readonly property int cellW: 28
|
||||||
|
readonly property int cellGap: 2
|
||||||
|
readonly property int gridW: 7 * cellW + 6 * cellGap // 208
|
||||||
|
|
||||||
|
implicitWidth: gridW + 24
|
||||||
|
implicitHeight: calLayout.implicitHeight + 16
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: calLayout
|
||||||
|
// Centre the fixed-width grid inside the (potentially wider) popup.
|
||||||
|
// No left+right anchors → implicitWidth stays at gridW, no resize loop.
|
||||||
|
anchors { top: parent.top; topMargin: 8; horizontalCenter: parent.horizontalCenter }
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
// Month navigation — all child widths are explicit constants
|
||||||
|
Row {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "‹"
|
||||||
|
color: Theme.text
|
||||||
|
font.pixelSize: 16
|
||||||
|
width: 20
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (root.viewMonth === 0) { root.viewMonth = 11; root.viewYear-- }
|
||||||
|
else root.viewMonth--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.monthNames[root.viewMonth] + " " + root.viewYear
|
||||||
|
color: Theme.text
|
||||||
|
font.pixelSize: 13
|
||||||
|
font.bold: true
|
||||||
|
width: root.gridW - 40 // fixed constant, not parent.width
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "›"
|
||||||
|
color: Theme.text
|
||||||
|
font.pixelSize: 16
|
||||||
|
width: 20
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (root.viewMonth === 11) { root.viewMonth = 0; root.viewYear++ }
|
||||||
|
else root.viewMonth++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weekday headers
|
||||||
|
Row {
|
||||||
|
spacing: root.cellGap
|
||||||
|
Repeater {
|
||||||
|
model: ["Mo","Tu","We","Th","Fr","Sa","Su"]
|
||||||
|
Text {
|
||||||
|
text: modelData
|
||||||
|
color: Theme.textDim
|
||||||
|
font.pixelSize: 10
|
||||||
|
width: root.cellW
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Day grid
|
||||||
|
Grid {
|
||||||
|
columns: 7
|
||||||
|
spacing: root.cellGap
|
||||||
|
Repeater {
|
||||||
|
model: root.totalCells
|
||||||
|
delegate: Item {
|
||||||
|
width: root.cellW
|
||||||
|
height: 22
|
||||||
|
readonly property int dayNum: index - root.startOffset + 1
|
||||||
|
readonly property bool valid:
|
||||||
|
index >= root.startOffset &&
|
||||||
|
index < root.startOffset + root.daysInMonth
|
||||||
|
readonly property bool isToday:
|
||||||
|
valid &&
|
||||||
|
root.viewYear === root.todayYear &&
|
||||||
|
root.viewMonth === root.todayMonth &&
|
||||||
|
dayNum === root.todayDay
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 3
|
||||||
|
color: isToday ? Theme.accent : "transparent"
|
||||||
|
visible: isToday
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: parent.valid ? String(parent.dayNum) : ""
|
||||||
|
color: parent.isToday ? Theme.text : Theme.textDim
|
||||||
|
font.pixelSize: 11
|
||||||
|
font.bold: parent.isToday
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
roles/quickshell/files/bar/Clock.qml
Normal file
47
roles/quickshell/files/bar/Clock.qml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import QtQuick
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal clicked
|
||||||
|
|
||||||
|
implicitWidth: timeLabel.implicitWidth + 16
|
||||||
|
implicitHeight: 24
|
||||||
|
|
||||||
|
property var now: new Date()
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1000
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
onTriggered: root.now = new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string display: {
|
||||||
|
const d = now
|
||||||
|
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
|
||||||
|
const dd = String(d.getDate()).padStart(2, "0")
|
||||||
|
const mm = String(d.getMonth() + 1).padStart(2, "0")
|
||||||
|
const yyyy = d.getFullYear()
|
||||||
|
const h = String(d.getHours()).padStart(2, "0")
|
||||||
|
const m = String(d.getMinutes()).padStart(2, "0")
|
||||||
|
const s = String(d.getSeconds()).padStart(2, "0")
|
||||||
|
return days[d.getDay()] + " " + dd + "." + mm + "." + yyyy + " " + h + ":" + m + ":" + s
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: timeLabel
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: root.display
|
||||||
|
color: Theme.text
|
||||||
|
font.pixelSize: 12
|
||||||
|
font.family: "JetBrainsMono Nerd Font Mono"
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
38
roles/quickshell/files/bar/MusicPlayer.qml
Normal file
38
roles/quickshell/files/bar/MusicPlayer.qml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import QtQuick
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal clicked
|
||||||
|
|
||||||
|
// Pick the first playing player, fall back to first available
|
||||||
|
readonly property var player: {
|
||||||
|
const vals = Mpris.players.values
|
||||||
|
for (const p of vals) {
|
||||||
|
if (p.isPlaying) return p
|
||||||
|
}
|
||||||
|
return vals.length > 0 ? vals[0] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: player !== null
|
||||||
|
implicitWidth: player ? Math.min(240, titleLabel.implicitWidth + 20) : 0
|
||||||
|
implicitHeight: 24
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: titleLabel
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - 20
|
||||||
|
text: root.player?.trackTitle ?? ""
|
||||||
|
color: Theme.text
|
||||||
|
font.pixelSize: 12
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
151
roles/quickshell/files/bar/MusicPlayerControls.qml
Normal file
151
roles/quickshell/files/bar/MusicPlayerControls.qml
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
// Plain Item – lives inside the expanding center section border in Bar.qml.
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var player
|
||||||
|
|
||||||
|
implicitWidth: 280
|
||||||
|
implicitHeight: layout.implicitHeight + 28
|
||||||
|
|
||||||
|
// Keep the progress binding live while playing, pause while seeking.
|
||||||
|
FrameAnimation {
|
||||||
|
running: (root.player?.isPlaying ?? false) && !progressArea.pressed
|
||||||
|
onTriggered: root.player.positionChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
anchors { fill: parent; margins: 14 }
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
// Track info
|
||||||
|
Text {
|
||||||
|
text: root.player?.trackTitle ?? ""
|
||||||
|
color: Theme.text
|
||||||
|
font.pixelSize: 13
|
||||||
|
font.bold: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.player?.trackArtist ?? ""
|
||||||
|
color: Theme.textDim
|
||||||
|
font.pixelSize: 11
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
visible: (root.player?.trackArtist ?? "") !== ""
|
||||||
|
Layout.topMargin: -4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress bar
|
||||||
|
Item {
|
||||||
|
visible: root.player?.positionSupported && root.player?.lengthSupported
|
||||||
|
&& (root.player?.length ?? 0) > 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 12
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: parent.width
|
||||||
|
height: 3
|
||||||
|
radius: 2
|
||||||
|
color: Theme.progressTrack
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: root.player?.length > 0
|
||||||
|
? parent.width * (root.player.position / root.player.length)
|
||||||
|
: 0
|
||||||
|
height: parent.height
|
||||||
|
radius: parent.radius
|
||||||
|
color: Theme.accent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: progressArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
function seek(mx) {
|
||||||
|
if (!(root.player?.canSeek)) return
|
||||||
|
const ratio = Math.max(0, Math.min(1, mx / width))
|
||||||
|
root.player.position = ratio * (root.player?.length ?? 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: seek(mouseX)
|
||||||
|
onPositionChanged: if (pressed) seek(mouseX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Media controls
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
|
||||||
|
ControlButton {
|
||||||
|
icon: "⏮"
|
||||||
|
iconSize: 18
|
||||||
|
enabled: root.player?.canGoPrevious ?? false
|
||||||
|
onActivated: root.player.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 8 }
|
||||||
|
|
||||||
|
ControlButton {
|
||||||
|
icon: root.player?.isPlaying ? "⏸" : "▶"
|
||||||
|
iconSize: 22
|
||||||
|
enabled: true
|
||||||
|
onActivated: root.player?.togglePlaying()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 8 }
|
||||||
|
|
||||||
|
ControlButton {
|
||||||
|
icon: "⏭"
|
||||||
|
iconSize: 18
|
||||||
|
enabled: root.player?.canGoNext ?? false
|
||||||
|
onActivated: root.player.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component ControlButton: Rectangle {
|
||||||
|
id: btn
|
||||||
|
required property string icon
|
||||||
|
required property int iconSize
|
||||||
|
required property bool enabled
|
||||||
|
signal activated
|
||||||
|
|
||||||
|
implicitWidth: label.implicitWidth + 16
|
||||||
|
implicitHeight: label.implicitHeight + 10
|
||||||
|
radius: Theme.radius
|
||||||
|
color: btnArea.containsMouse ? Qt.rgba(1, 1, 1, 0.08) : 'transparent'
|
||||||
|
Behavior on color { ColorAnimation { duration: 80 } }
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: btn.icon
|
||||||
|
font.pixelSize: btn.iconSize
|
||||||
|
color: btn.enabled ? Theme.text : Theme.textDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: btnArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: if (btn.enabled) btn.activated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
roles/quickshell/files/bar/NetworkStatus.qml
Normal file
86
roles/quickshell/files/bar/NetworkStatus.qml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import QtQuick
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
implicitWidth: row.implicitWidth + 10
|
||||||
|
implicitHeight: 24
|
||||||
|
|
||||||
|
property string connType: "none" // "wifi", "ethernet", "none"
|
||||||
|
property string connName: ""
|
||||||
|
|
||||||
|
// accumulate results here, flush to displayed props on exit
|
||||||
|
property string _pendingType: "none"
|
||||||
|
property string _pendingName: ""
|
||||||
|
|
||||||
|
// \uf1eb = FA wifi, \uf0e8 = FA sitemap (wired proxy), \uf127 = FA chain-broken
|
||||||
|
readonly property string netIcon:
|
||||||
|
connType === "wifi" ? "\uf1eb" :
|
||||||
|
connType === "ethernet" ? "\uf0e8" : "\uf127"
|
||||||
|
|
||||||
|
function parseLine(line) {
|
||||||
|
const idx1 = line.indexOf(":")
|
||||||
|
if (idx1 < 0) return
|
||||||
|
const idx2 = line.indexOf(":", idx1 + 1)
|
||||||
|
if (idx2 < 0) return
|
||||||
|
const type = line.substring(0, idx1)
|
||||||
|
const state = line.substring(idx1 + 1, idx2)
|
||||||
|
const conn = line.substring(idx2 + 1).trim()
|
||||||
|
if (state === "connected" && (type === "wifi" || type === "ethernet")) {
|
||||||
|
if (root._pendingType === "none" || type === "wifi") {
|
||||||
|
root._pendingType = type
|
||||||
|
root._pendingName = conn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 5000
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: {
|
||||||
|
root._pendingType = "none"
|
||||||
|
root._pendingName = ""
|
||||||
|
netProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: netProc
|
||||||
|
command: ["nmcli", "-t", "-f", "TYPE,STATE,CONNECTION", "dev"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
splitMarker: "\n"
|
||||||
|
onRead: data => root.parseLine(data)
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
root.connType = root._pendingType
|
||||||
|
root.connName = root._pendingName
|
||||||
|
running = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.netIcon
|
||||||
|
font.family: "JetBrainsMono Nerd Font Mono"
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: root.connType !== "none" ? Theme.text : Theme.textDim
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.connName.length > 16 ? root.connName.substring(0, 15) + "…" : root.connName
|
||||||
|
font.pixelSize: 11
|
||||||
|
color: Theme.textDim
|
||||||
|
visible: root.connName !== ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
162
roles/quickshell/files/bar/PopoutWindow.qml
Normal file
162
roles/quickshell/files/bar/PopoutWindow.qml
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Shapes
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: self
|
||||||
|
|
||||||
|
property string popupName: ""
|
||||||
|
property string activePopup: ""
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
default property alias content: contentSlot.data
|
||||||
|
|
||||||
|
readonly property int bw: Theme.borderWidth
|
||||||
|
readonly property real r: Theme.radius
|
||||||
|
|
||||||
|
readonly property bool _open: activePopup === popupName
|
||||||
|
on_OpenChanged: {
|
||||||
|
if (_open) {
|
||||||
|
hideTimer.stop();
|
||||||
|
self.visible = true;
|
||||||
|
fade.opacity = 1.0;
|
||||||
|
} else {
|
||||||
|
fade.opacity = 0.0;
|
||||||
|
hideTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
id: hideTimer
|
||||||
|
interval: 200
|
||||||
|
onTriggered: self.visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: fade
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: 0.0
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 180
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background + border in one Shape so fill matches stroke exactly
|
||||||
|
Shape {
|
||||||
|
id: pbs
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
ShapePath {
|
||||||
|
strokeWidth: self.bw
|
||||||
|
strokeColor: Theme.border
|
||||||
|
fillColor: Theme.bg
|
||||||
|
|
||||||
|
// Start flush at top-left, hidden under bar
|
||||||
|
startX: 0
|
||||||
|
startY: self.r
|
||||||
|
|
||||||
|
// Left side down to bottom-left corner
|
||||||
|
PathLine {
|
||||||
|
x: 0
|
||||||
|
y: pbs.height - self.r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom-left corner — Counterclockwise for outward curve
|
||||||
|
PathArc {
|
||||||
|
x: self.r
|
||||||
|
y: pbs.height
|
||||||
|
radiusX: self.r
|
||||||
|
radiusY: self.r
|
||||||
|
direction: PathArc.Counterclockwise
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom edge
|
||||||
|
PathLine {
|
||||||
|
x: pbs.width - self.r
|
||||||
|
y: pbs.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom-right corner — Counterclockwise for outward curve
|
||||||
|
PathArc {
|
||||||
|
x: pbs.width
|
||||||
|
y: pbs.height - self.r
|
||||||
|
radiusX: self.r
|
||||||
|
radiusY: self.r
|
||||||
|
direction: PathArc.Counterclockwise
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right side up, flush to top, hidden under bar
|
||||||
|
PathLine {
|
||||||
|
x: pbs.width
|
||||||
|
y: self.r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Top-left concave corner
|
||||||
|
// Shape {
|
||||||
|
// x: 0
|
||||||
|
// y: 0
|
||||||
|
// width: self.r
|
||||||
|
// height: self.r
|
||||||
|
// ShapePath {
|
||||||
|
// fillColor: Theme.bg
|
||||||
|
// strokeColor: "transparent"
|
||||||
|
// startX: 0
|
||||||
|
// startY: 0
|
||||||
|
// PathLine {
|
||||||
|
// x: self.r
|
||||||
|
// y: 0
|
||||||
|
// }
|
||||||
|
// PathArc {
|
||||||
|
// x: 0
|
||||||
|
// y: self.r
|
||||||
|
// radiusX: self.r
|
||||||
|
// radiusY: self.r
|
||||||
|
// direction: PathArc.Clockwise
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Top-right concave corner
|
||||||
|
// Shape {
|
||||||
|
// x: parent.width - self.r
|
||||||
|
// y: 0
|
||||||
|
// width: self.r
|
||||||
|
// height: self.r
|
||||||
|
// ShapePath {
|
||||||
|
// fillColor: Theme.bg
|
||||||
|
// strokeColor: "transparent"
|
||||||
|
// startX: self.r
|
||||||
|
// startY: 0
|
||||||
|
// PathLine {
|
||||||
|
// x: 0
|
||||||
|
// y: 0
|
||||||
|
// }
|
||||||
|
// PathArc {
|
||||||
|
// x: self.r
|
||||||
|
// y: self.r
|
||||||
|
// radiusX: self.r
|
||||||
|
// radiusY: self.r
|
||||||
|
// direction: PathArc.Counterclockwise
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentSlot
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: self.r + self.bw
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: self.bw
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: self.bw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
roles/quickshell/files/bar/SysTray.qml
Normal file
65
roles/quickshell/files/bar/SysTray.qml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var barWindow
|
||||||
|
implicitWidth: trayRow.width
|
||||||
|
implicitHeight: 24
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: trayRow
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: SystemTray.items
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: trayDelegate
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
|
||||||
|
// Hover highlight
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Theme.radius
|
||||||
|
color: hoverArea.containsMouse ? Qt.rgba(1,1,1,0.08) : "transparent"
|
||||||
|
Behavior on color { ColorAnimation { duration: 80 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
TrayIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
icon: trayDelegate.modelData.icon
|
||||||
|
size: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: hoverArea
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onClicked: mouse => {
|
||||||
|
const item = trayDelegate.modelData
|
||||||
|
const wantsMenu = mouse.button === Qt.RightButton || item.onlyMenu
|
||||||
|
if (wantsMenu && item.hasMenu) {
|
||||||
|
// display() needs the quickshell PanelWindow (not QQuickWindow).
|
||||||
|
// mapToItem(null) gives scene/window-local coordinates.
|
||||||
|
const pos = trayDelegate.mapToItem(null, 0, trayDelegate.height)
|
||||||
|
item.display(root.barWindow, Math.round(pos.x), Math.round(pos.y))
|
||||||
|
} else if (!item.onlyMenu) {
|
||||||
|
item.activate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
roles/quickshell/files/bar/VolumeControl.qml
Normal file
74
roles/quickshell/files/bar/VolumeControl.qml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
implicitWidth: chip.implicitWidth + 10
|
||||||
|
implicitHeight: 24
|
||||||
|
|
||||||
|
signal clickedLeft
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
objects: Pipewire.nodes.values
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeVolume(node) {
|
||||||
|
if (!node || !node.ready || !node.audio) return 0
|
||||||
|
const v = node.audio.volume
|
||||||
|
return (v !== undefined && !isNaN(v)) ? v : 0
|
||||||
|
}
|
||||||
|
function setVolume(node, v) {
|
||||||
|
if (!node || !node.ready || !node.audio) return
|
||||||
|
node.audio.volume = Math.max(0, Math.min(1, v))
|
||||||
|
}
|
||||||
|
function volIcon(vol, muted) {
|
||||||
|
if (muted) return "\uf6a9"
|
||||||
|
if (vol > 0.6) return "\uf028"
|
||||||
|
if (vol > 0.2) return "\uf027"
|
||||||
|
return "\uf026"
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var defaultSink: Pipewire.defaultAudioSink
|
||||||
|
readonly property real defaultVolume: safeVolume(defaultSink)
|
||||||
|
readonly property bool defaultMuted: defaultSink?.audio?.muted ?? false
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: chip
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.volIcon(root.defaultVolume, root.defaultMuted)
|
||||||
|
font.family: "JetBrainsMono Nerd Font Mono"
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: root.defaultMuted ? Theme.textDim : Theme.text
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.defaultMuted ? "mute" : Math.round(root.defaultVolume * 100) + "%"
|
||||||
|
font.pixelSize: 11
|
||||||
|
color: Theme.textDim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
if (root.defaultSink?.ready && root.defaultSink?.audio)
|
||||||
|
root.defaultSink.audio.muted = !root.defaultSink.audio.muted
|
||||||
|
} else {
|
||||||
|
root.clickedLeft()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onWheel: wheel => {
|
||||||
|
if (!root.defaultSink?.ready) return
|
||||||
|
root.setVolume(root.defaultSink, root.defaultVolume + wheel.angleDelta.y / 120 * 0.05)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
143
roles/quickshell/files/bar/VolumeMixerContent.qml
Normal file
143
roles/quickshell/files/bar/VolumeMixerContent.qml
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
// Volume mixer – a plain Item so it can live inside an expanding section border.
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
implicitWidth: 300
|
||||||
|
implicitHeight: mixerCol.implicitHeight + 24
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
objects: Pipewire.nodes.values
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeVolume(node) {
|
||||||
|
if (!node || !node.ready || !node.audio) return 0
|
||||||
|
const v = node.audio.volume
|
||||||
|
return (v !== undefined && !isNaN(v)) ? v : 0
|
||||||
|
}
|
||||||
|
function setVolume(node, v) {
|
||||||
|
if (!node || !node.ready || !node.audio) return
|
||||||
|
node.audio.volume = Math.max(0, Math.min(1, v))
|
||||||
|
}
|
||||||
|
function volIcon(vol, muted) {
|
||||||
|
if (muted) return "\uf6a9"
|
||||||
|
if (vol > 0.6) return "\uf028"
|
||||||
|
if (vol > 0.2) return "\uf027"
|
||||||
|
return "\uf026"
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mixerCol
|
||||||
|
anchors { fill: parent; margins: 12 }
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Output Devices"
|
||||||
|
color: Theme.textDim
|
||||||
|
font.pixelSize: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.bottomMargin: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Pipewire.nodes
|
||||||
|
delegate: NodeRow {
|
||||||
|
required property var modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
node: modelData
|
||||||
|
visible: modelData.isSink && !modelData.isStream && modelData.ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Applications"
|
||||||
|
color: Theme.textDim
|
||||||
|
font.pixelSize: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 6
|
||||||
|
Layout.bottomMargin: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Pipewire.nodes
|
||||||
|
delegate: NodeRow {
|
||||||
|
required property var modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
node: modelData
|
||||||
|
visible: modelData.isStream && !modelData.isSink && modelData.ready
|
||||||
|
&& !(modelData.description ?? "").toLowerCase().includes("monitor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component NodeRow: RowLayout {
|
||||||
|
id: row
|
||||||
|
required property var node
|
||||||
|
spacing: 8
|
||||||
|
implicitHeight: 28
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.volIcon(root.safeVolume(row.node), row.node.audio?.muted ?? false)
|
||||||
|
font.family: "JetBrainsMono Nerd Font Mono"
|
||||||
|
font.pixelSize: 13
|
||||||
|
color: (row.node.audio?.muted ?? false) ? Theme.textDim : Theme.text
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (row.node.ready && row.node.audio)
|
||||||
|
row.node.audio.muted = !row.node.audio.muted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: row.node.description || row.node.nickname || row.node.name || "?"
|
||||||
|
font.pixelSize: 11
|
||||||
|
color: Theme.textDim
|
||||||
|
Layout.preferredWidth: 90
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 16
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: parent.width
|
||||||
|
height: 3
|
||||||
|
radius: 2
|
||||||
|
color: Theme.progressTrack
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width * Math.min(1, root.safeVolume(row.node))
|
||||||
|
height: parent.height
|
||||||
|
radius: parent.radius
|
||||||
|
color: Theme.accent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors { fill: parent; topMargin: -4; bottomMargin: -4 }
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
function seek(mx) { root.setVolume(row.node, mx / width) }
|
||||||
|
onClicked: seek(mouseX)
|
||||||
|
onPositionChanged: if (pressed) seek(mouseX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: Math.round(root.safeVolume(row.node) * 100) + "%"
|
||||||
|
font.pixelSize: 10
|
||||||
|
color: Theme.textDim
|
||||||
|
Layout.preferredWidth: 30
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
roles/quickshell/files/bar/WorkspaceButton.qml
Normal file
70
roles/quickshell/files/bar/WorkspaceButton.qml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
// import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
required property var workspace
|
||||||
|
required property string screenName
|
||||||
|
|
||||||
|
visible: workspace?.lastIpcObject?.monitor === screenName
|
||||||
|
|
||||||
|
implicitWidth: Math.max(32, iconsRow.implicitWidth + 14)
|
||||||
|
implicitHeight: 18
|
||||||
|
radius: 6
|
||||||
|
color: workspace?.active ? Theme.accent : 'transparent'
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: iconsRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.workspace?.toplevels
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: iconItem
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
implicitWidth: 16
|
||||||
|
implicitHeight: 16
|
||||||
|
|
||||||
|
property string appClass: ""
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
var cls = modelData?.lastIpcObject?.["class"] ?? ""
|
||||||
|
if (cls !== "") {
|
||||||
|
appClass = cls
|
||||||
|
} else if (modelData) {
|
||||||
|
modelData.lastIpcObjectChanged.connect(function() {
|
||||||
|
var c = iconItem.modelData?.lastIpcObject?.["class"] ?? ""
|
||||||
|
if (c !== "") iconItem.appClass = c
|
||||||
|
})
|
||||||
|
Qt.callLater(Hyprland.refreshToplevels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TrayIcon {
|
||||||
|
anchors.fill: parent
|
||||||
|
icon: iconItem.appClass
|
||||||
|
size: 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workspace number shown when no windows are open
|
||||||
|
Text {
|
||||||
|
visible: (root.workspace?.toplevels?.values?.length ?? 0) === 0
|
||||||
|
text: root.workspace?.id ?? ""
|
||||||
|
color: root.workspace?.active ? Theme.text : Theme.textDim
|
||||||
|
font.pixelSize: 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: Hyprland.dispatch("workspace " + root.workspace.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
22
roles/quickshell/files/bar/Workspaces.qml
Normal file
22
roles/quickshell/files/bar/Workspaces.qml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: root
|
||||||
|
required property string screenName
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: workspacesRepeater
|
||||||
|
model: Hyprland.workspaces
|
||||||
|
property string screenName: root.screenName
|
||||||
|
|
||||||
|
delegate: WorkspaceButton {
|
||||||
|
required property var modelData
|
||||||
|
workspace: modelData
|
||||||
|
screenName: workspacesRepeater.screenName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
roles/quickshell/files/bar/qmldir
Normal file
13
roles/quickshell/files/bar/qmldir
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Bar 1.0 Bar.qml
|
||||||
|
Battery 1.0 Battery.qml
|
||||||
|
PopoutWindow 1.0 PopoutWindow.qml
|
||||||
|
CalendarContent 1.0 CalendarContent.qml
|
||||||
|
Clock 1.0 Clock.qml
|
||||||
|
MusicPlayer 1.0 MusicPlayer.qml
|
||||||
|
MusicPlayerControls 1.0 MusicPlayerControls.qml
|
||||||
|
NetworkStatus 1.0 NetworkStatus.qml
|
||||||
|
SysTray 1.0 SysTray.qml
|
||||||
|
VolumeControl 1.0 VolumeControl.qml
|
||||||
|
VolumeMixerContent 1.0 VolumeMixerContent.qml
|
||||||
|
Workspaces 1.0 Workspaces.qml
|
||||||
|
WorkspaceButton 1.0 WorkspaceButton.qml
|
||||||
11
roles/quickshell/files/components/Enclosure.qml
Normal file
11
roles/quickshell/files/components/Enclosure.qml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "."
|
||||||
|
|
||||||
|
WrapperRectangle {
|
||||||
|
radius: Theme.radius
|
||||||
|
color: 'transparent'
|
||||||
|
border.color: Theme.border
|
||||||
|
border.width: Theme.borderWidth
|
||||||
|
margin: Theme.enclosureMargin
|
||||||
|
}
|
||||||
19
roles/quickshell/files/components/Theme.qml
Normal file
19
roles/quickshell/files/components/Theme.qml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
// Core palette
|
||||||
|
readonly property color bg: '#D9000000' // bar background (semi-transparent)
|
||||||
|
readonly property color bgPopup: '#000000' // popup background (fully opaque)
|
||||||
|
readonly property color accent: '#9B1A1A'
|
||||||
|
readonly property color border: '#FFFFFF'
|
||||||
|
readonly property color text: '#FFFFFF'
|
||||||
|
readonly property color textDim: '#888888'
|
||||||
|
readonly property color textDisabled: '#444444'
|
||||||
|
readonly property color progressTrack: '#333333'
|
||||||
|
|
||||||
|
// Shape / sizing
|
||||||
|
readonly property int radius: 16
|
||||||
|
readonly property int borderWidth: 2
|
||||||
|
readonly property int enclosureMargin: 3
|
||||||
|
}
|
||||||
103
roles/quickshell/files/components/TrayIcon.qml
Normal file
103
roles/quickshell/files/components/TrayIcon.qml
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// import QtQuick
|
||||||
|
// import Quickshell.Widgets
|
||||||
|
//
|
||||||
|
// Item {
|
||||||
|
// id: root
|
||||||
|
//
|
||||||
|
// property string icon: ""
|
||||||
|
// property int size: 16
|
||||||
|
//
|
||||||
|
// implicitWidth: size
|
||||||
|
// implicitHeight: size
|
||||||
|
//
|
||||||
|
// readonly property url resolvedSource: {
|
||||||
|
// if (!icon) return ""
|
||||||
|
// if (icon.startsWith("/") || icon.startsWith("file://") || icon.startsWith("image://"))
|
||||||
|
// return icon
|
||||||
|
// if (icon.includes("?path=")) {
|
||||||
|
// const [name, path] = icon.split("?path=")
|
||||||
|
// const baseName = name.slice(name.lastIndexOf("/") + 1)
|
||||||
|
// return `file://${path}/${baseName}`
|
||||||
|
// }
|
||||||
|
// return `image://icon/${icon}`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// readonly property bool isIconTheme: resolvedSource.toString().startsWith("image://icon/")
|
||||||
|
// readonly property bool hasSource: resolvedSource.toString() !== ""
|
||||||
|
//
|
||||||
|
// IconImage {
|
||||||
|
// anchors.fill: parent
|
||||||
|
// source: root.resolvedSource
|
||||||
|
// visible: root.hasSource && root.isIconTheme
|
||||||
|
// asynchronous: true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Image {
|
||||||
|
// anchors.fill: parent
|
||||||
|
// source: root.hasSource && !root.isIconTheme ? root.resolvedSource : ""
|
||||||
|
// visible: root.hasSource && !root.isIconTheme
|
||||||
|
// asynchronous: true
|
||||||
|
// fillMode: Image.PreserveAspectFit
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
// import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string icon: ""
|
||||||
|
property int size: 16
|
||||||
|
|
||||||
|
implicitWidth: size
|
||||||
|
implicitHeight: size
|
||||||
|
|
||||||
|
readonly property url resolvedSource: {
|
||||||
|
if (!icon) return ""
|
||||||
|
if (icon.startsWith("/") || icon.startsWith("file://") || icon.startsWith("image://"))
|
||||||
|
return icon
|
||||||
|
if (icon.includes("?path=")) {
|
||||||
|
const [name, path] = icon.split("?path=")
|
||||||
|
const baseName = name.slice(name.lastIndexOf("/") + 1)
|
||||||
|
return `file://${path}/${baseName}`
|
||||||
|
}
|
||||||
|
// Use Quickshell's icon image provider instead of iconPath()
|
||||||
|
return `image://icon/${icon}`
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
// asynchronous: true
|
||||||
|
sourceComponent: root.resolvedSource && root.resolvedSource.toString() !== ""
|
||||||
|
? root.resolvedSource.toString().startsWith("image://icon/")
|
||||||
|
? iconComponent
|
||||||
|
: imageComponent
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: iconComponent
|
||||||
|
IconImage {
|
||||||
|
source: root.resolvedSource
|
||||||
|
asynchronous: true
|
||||||
|
// mipmap: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: imageComponent
|
||||||
|
Image {
|
||||||
|
source: root.resolvedSource
|
||||||
|
asynchronous: true
|
||||||
|
// mipmap: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
3
roles/quickshell/files/components/qmldir
Normal file
3
roles/quickshell/files/components/qmldir
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
singleton Theme 1.0 Theme.qml
|
||||||
|
Enclosure 1.0 Enclosure.qml
|
||||||
|
TrayIcon 1.0 TrayIcon.qml
|
||||||
13
roles/quickshell/files/shell.qml
Normal file
13
roles/quickshell/files/shell.qml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//@ pragma UseQApplication
|
||||||
|
import Quickshell
|
||||||
|
import "bar"
|
||||||
|
|
||||||
|
ShellRoot {
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens
|
||||||
|
|
||||||
|
Bar { }
|
||||||
|
// // modelData injected by Variants; Bar.qml binds screen: modelData internally
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
51
roles/quickshell/tasks/main.yml
Normal file
51
roles/quickshell/tasks/main.yml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
- name: Check if quickshell path is a symlink
|
||||||
|
stat:
|
||||||
|
path: "{{ config_dir }}/quickshell"
|
||||||
|
register: quickshell_stat
|
||||||
|
|
||||||
|
- name: Remove existing quickshell symlink if present
|
||||||
|
file:
|
||||||
|
path: "{{ config_dir }}/quickshell"
|
||||||
|
state: absent
|
||||||
|
when: quickshell_stat.stat.exists and quickshell_stat.stat.islnk
|
||||||
|
|
||||||
|
- name: Create quickshell config directories
|
||||||
|
file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
loop:
|
||||||
|
- "{{ config_dir }}/quickshell"
|
||||||
|
- "{{ config_dir }}/quickshell/bar"
|
||||||
|
- "{{ config_dir }}/quickshell/components"
|
||||||
|
|
||||||
|
- name: Symlink quickshell config files
|
||||||
|
file:
|
||||||
|
src: "{{ role_path }}/files/{{ item.src }}"
|
||||||
|
dest: "{{ config_dir }}/quickshell/{{ item.dest }}"
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
loop:
|
||||||
|
# Root
|
||||||
|
- { src: shell.qml, dest: shell.qml }
|
||||||
|
# Shared components module
|
||||||
|
- { src: components/Theme.qml, dest: components/Theme.qml }
|
||||||
|
- { src: components/Enclosure.qml, dest: components/Enclosure.qml }
|
||||||
|
- { src: components/TrayIcon.qml, dest: components/TrayIcon.qml }
|
||||||
|
- { src: components/qmldir, dest: components/qmldir }
|
||||||
|
# Bar module (imported as "bar" in shell.qml)
|
||||||
|
- { src: bar/Bar.qml, dest: bar/Bar.qml }
|
||||||
|
- { src: bar/Battery.qml, dest: bar/Battery.qml }
|
||||||
|
- { src: bar/CalendarContent.qml, dest: bar/CalendarContent.qml }
|
||||||
|
- { src: bar/Clock.qml, dest: bar/Clock.qml }
|
||||||
|
- { src: bar/MusicPlayer.qml, dest: bar/MusicPlayer.qml }
|
||||||
|
- { src: bar/MusicPlayerControls.qml, dest: bar/MusicPlayerControls.qml }
|
||||||
|
- { src: bar/NetworkStatus.qml, dest: bar/NetworkStatus.qml }
|
||||||
|
- { src: bar/PopoutWindow.qml, dest: bar/PopoutWindow.qml }
|
||||||
|
- { src: bar/SysTray.qml, dest: bar/SysTray.qml }
|
||||||
|
- { src: bar/VolumeControl.qml, dest: bar/VolumeControl.qml }
|
||||||
|
- { src: bar/VolumeMixerContent.qml, dest: bar/VolumeMixerContent.qml }
|
||||||
|
- { src: bar/Workspaces.qml, dest: bar/Workspaces.qml }
|
||||||
|
- { src: bar/WorkspaceButton.qml, dest: bar/WorkspaceButton.qml }
|
||||||
|
- { src: bar/qmldir, dest: bar/qmldir }
|
||||||
@ -4,12 +4,14 @@
|
|||||||
update_cache: yes
|
update_cache: yes
|
||||||
when: ansible_facts.os_family == "Archlinux"
|
when: ansible_facts.os_family == "Archlinux"
|
||||||
become: yes
|
become: yes
|
||||||
|
become_user: root
|
||||||
|
|
||||||
- name: Install system packages
|
- name: Install system packages
|
||||||
package:
|
package:
|
||||||
name: "{{ system_packages }}"
|
name: "{{ system_packages }}"
|
||||||
state: present
|
state: present
|
||||||
become: yes
|
become: yes
|
||||||
|
become_user: root
|
||||||
|
|
||||||
- name: Ensure .config directory exists
|
- name: Ensure .config directory exists
|
||||||
file:
|
file:
|
||||||
|
|||||||
8
roles/zsh/files/.zshenv
Normal file
8
roles/zsh/files/.zshenv
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export PATH="/home/johannes/.local/bin:$PATH"
|
||||||
|
# pnpm
|
||||||
|
export PNPM_HOME="$HOME/.local/share/pnpm"
|
||||||
|
case ":$PATH:" in
|
||||||
|
*":$PNPM_HOME:"*) ;;
|
||||||
|
*) export PATH="$PNPM_HOME:$PATH" ;;
|
||||||
|
esac
|
||||||
|
# pnpm end
|
||||||
@ -52,3 +52,7 @@ alias drun='docker run -it --network=host --device=/dev/kfd --device=/dev/dri --
|
|||||||
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/gcr/ssh"
|
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/gcr/ssh"
|
||||||
|
|
||||||
bindkey '^H' backward-kill-word
|
bindkey '^H' backward-kill-word
|
||||||
|
bindkey '^R' fzf-history-widget
|
||||||
|
|
||||||
|
eval "$(zoxide init zsh)"
|
||||||
|
|
||||||
|
|||||||
@ -18,3 +18,7 @@ rm() {
|
|||||||
print "⚠️ To use real rm, use /bin/rm"
|
print "⚠️ To use real rm, use /bin/rm"
|
||||||
trash-put "$@"
|
trash-put "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alias lg='lazygit'
|
||||||
|
|
||||||
|
alias tt='taskwarrior-tui'
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
source /usr/share/autojump/autojump.zsh
|
source <(fzf --zsh)
|
||||||
|
|||||||
@ -3,22 +3,25 @@
|
|||||||
package:
|
package:
|
||||||
name: zsh
|
name: zsh
|
||||||
state: present
|
state: present
|
||||||
|
become: yes
|
||||||
|
become_user: root
|
||||||
|
|
||||||
- name: Install oh-my-zsh
|
- name: Install oh-my-zsh
|
||||||
shell: |
|
shell: |
|
||||||
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended
|
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended
|
||||||
args:
|
args:
|
||||||
creates: "{{ ansible_facts.env.HOME }}/.oh-my-zsh"
|
creates: "{{ home_dir }}/.oh-my-zsh"
|
||||||
|
|
||||||
- name: Check for custom zshrc
|
- name: Symlink .zshenv
|
||||||
stat:
|
|
||||||
path: "{{ role_path }}/files/.zshrc"
|
|
||||||
register: custom_zshrc
|
|
||||||
|
|
||||||
- name: Symlink custom zshrc
|
|
||||||
file:
|
file:
|
||||||
src: "{{ role_path }}/files/.zshrc"
|
src: "{{ role_path }}/files/.zshenv"
|
||||||
dest: "{{ ansible_facts.env.HOME }}/.zshrc"
|
dest: "{{ home_dir }}/.zshenv"
|
||||||
|
state: link
|
||||||
|
force: yes
|
||||||
|
|
||||||
|
- name: Symlink .zshrc
|
||||||
|
file:
|
||||||
|
src: "{{ role_path }}/files/.zshrc"
|
||||||
|
dest: "{{ home_dir }}/.zshrc"
|
||||||
state: link
|
state: link
|
||||||
force: yes
|
force: yes
|
||||||
when: custom_zshrc.stat.exists
|
|
||||||
|
|||||||
Reference in New Issue
Block a user