Compare commits

..

35 Commits

Author SHA1 Message Date
4b12ae2c12 [Scripts] Fixes for vpn script 2025-11-24 14:25:13 +01:00
5227e5f05e [Scripts] Fix vpn script 2025-11-23 13:36:06 +01:00
b8098eac8b [Scripts] Improve VPN script 2025-11-18 09:38:24 +01:00
5b610643c7 [Install] Fix bt 2025-11-17 09:17:27 +01:00
4fce8c955c [Hyprland] Fix cursor size 2025-11-12 10:32:15 +01:00
eabb972cb8 [Binds] Fix swapescape 2025-11-11 09:58:48 +01:00
688b94c3ad [Binds] Add binds to dynamically enable and disable caps:swapescape 2025-11-11 09:50:31 +01:00
d4689d66cd [FMT] Improve latexindent config 2025-11-11 08:05:53 +01:00
e28e3e6207 [Install] Add usbutils 2025-11-06 17:47:18 +01:00
b45b3d60d1 [Scripts] Add pdf wordcount script 2025-11-04 15:51:49 +01:00
8d2e3cea85 [Hyprland] Move conflicting bind 2025-11-03 17:13:44 +01:00
8a42f9bfa2 [Install] Add timesyncd enable 2025-11-03 10:15:00 +01:00
09916ae68e [Hyprland] Add more launch commands 2025-11-02 10:49:33 +01:00
5b5fa9ad4e [Astal] Fix launch on new astal version 2025-11-01 10:37:30 +01:00
814b975533 [Linecount] Add cloc and alias for it 2025-10-30 15:31:47 +01:00
d0a450d4e8 [Setup] Make cleaner 2025-10-30 10:42:50 +01:00
786d5c5fe0 [Yazi] Fix themes 2025-10-30 10:42:42 +01:00
5ba2894101 [Hyprland] Improve windowrules 2025-10-30 10:24:08 +01:00
499bf793b2 [Hyprland] Add some nice windorules 2025-10-30 10:24:08 +01:00
f1c57b8ecc [Scripts] Add nvim update script 2025-10-29 15:19:18 +01:00
20cc042b64 [Scripts] Add batch video conversion script 2025-10-25 16:26:39 +02:00
ae9dcaa074 [Hyprland] Windowrules: Add for BeatSaber 2025-10-22 17:01:28 +02:00
21b68b1e68 [Hyprland] Fix alvr launch bind typo 2025-10-22 12:55:57 +02:00
fa4929a94b [Hyprland] Screenshot binds exit submap 2025-10-22 12:53:44 +02:00
6b4d0e22b1 [Kitty] Remove default mapping interfering with new nvim bind 2025-10-21 08:44:42 +02:00
d5615892e7 [Hyprland] Add launch command for alvr and bsm 2025-10-17 17:30:04 +02:00
81d0df6023 [Hyprland] Fix notifications submap 2025-10-17 10:58:39 +02:00
9cf14d3b55 [Astal] Format 2025-10-17 10:47:05 +02:00
06325b09b8 [Hyprland] Update readmes 2025-10-17 10:44:33 +02:00
539ec34b4c [Astal] Finish modes, move tray into quickactions menu 2025-10-17 10:44:33 +02:00
00d4f101fc [Hyprland] Remove some quick access binds (unnecessary) 2025-10-17 09:55:15 +02:00
c9a89cf545 [Setup] Fix platform wrong 2025-10-17 09:54:16 +02:00
7ba9a65f19 [Build] Add linter 2025-10-17 09:47:40 +02:00
52ced69697 [Hyprland] Fix submaps 2025-10-17 09:47:25 +02:00
47bbdd9f85 [Astal] Add mode indicator 2025-10-17 09:47:18 +02:00
40 changed files with 1664 additions and 464 deletions

View File

@@ -9,10 +9,10 @@ For my neovim config, see [here](https://git.janishutz.com/janishutz/nvim)
![screenshot of desktop with 2 screens](./assets/screenshot.png) ![screenshot of desktop with 2 screens](./assets/screenshot.png)
I am currently working on redoing my keybinds for Hyprland, in what I call `hyprmode`: I am currently working on redoing my keybinds for Hyprland, in what I call `hyprvim`:
They are going to use submaps and will be significantly different, yet still familiar. They are going to use submaps and will be significantly different, yet still familiar.
This will enable me to have many more keybinds with reasonable starter bindings. This will enable me to have many more keybinds with reasonable starter bindings.
I will also update Astal to have a mode indicator if `hyprmode` is enabled. I will also update Astal to have a mode indicator if `hyprvim` is enabled.
## Features ## Features
- Astal4 based Status Bar and Quick Actions menu - Astal4 based Status Bar and Quick Actions menu

View File

@@ -9,7 +9,9 @@ const renderColourAsHex = util.renderColourAsHex;
// │ Theme generator (returns theme as object) │ // │ Theme generator (returns theme as object) │
// ╰───────────────────────────────────────────────╯ // ╰───────────────────────────────────────────────╯
// ─────────────────────────────────────────────────────────────────── // ───────────────────────────────────────────────────────────────────
module.exports.generateTheme = ( theme, wallpaper, lockpaper, palette ) => { module.exports.generateTheme = (
theme, wallpaper, lockpaper, palette
) => {
return { return {
'wallpaper-path': wallpaper, 'wallpaper-path': wallpaper,
'lockpaper-path': lockpaper, 'lockpaper-path': lockpaper,
@@ -49,14 +51,30 @@ module.exports.generateTheme = ( theme, wallpaper, lockpaper, palette ) => {
'colour-accent-rgba-007': renderColourAsRGBA( palette[ 0 ], 0.07 ), 'colour-accent-rgba-007': renderColourAsRGBA( palette[ 0 ], 0.07 ),
'colour-accent-hyprland': util.renderColourAsRGBAHex( palette[ 0 ], 0.8 ), 'colour-accent-hyprland': util.renderColourAsRGBAHex( palette[ 0 ], 0.8 ),
// ─────────────────────────────────────────────────────────────────── // ───────────────────────────────────────────────────────────────────
'colour-accent-gradient-1-hex': renderColourAsHex( util.getGradientColour( palette[ 0 ], 1, gradientMultipliers[ theme ] ) ), 'colour-accent-gradient-1-hex': renderColourAsHex( util.getGradientColour(
'colour-accent-gradient-2-hex': renderColourAsHex( util.getGradientColour( palette[ 0 ], 2, gradientMultipliers[ theme ] ) ), palette[ 0 ], 1, gradientMultipliers[ theme ]
'colour-accent-gradient-3-hex': renderColourAsHex( util.getGradientColour( palette[ 0 ], 3, gradientMultipliers[ theme ] ) ), ) ),
'colour-accent-gradient-4-hex': renderColourAsHex( util.getGradientColour( palette[ 0 ], 4, gradientMultipliers[ theme ] ) ), 'colour-accent-gradient-2-hex': renderColourAsHex( util.getGradientColour(
'colour-accent-gradient-5-hex': renderColourAsHex( util.getGradientColour( palette[ 0 ], 5, gradientMultipliers[ theme ] ) ), palette[ 0 ], 2, gradientMultipliers[ theme ]
'colour-accent-gradient-inverse-1-hex': renderColourAsHex( util.getGradientColour( palette[ 0 ], 1, 1 / gradientMultipliers[ theme ] ) ), ) ),
'colour-accent-gradient-inverse-2-hex': renderColourAsHex( util.getGradientColour( palette[ 0 ], 1, 1 / gradientMultipliers[ theme ] ) ), 'colour-accent-gradient-3-hex': renderColourAsHex( util.getGradientColour(
'colour-accent-gradient-inverse-3-hex': renderColourAsHex( util.getGradientColour( palette[ 0 ], 1, 1 / gradientMultipliers[ theme ] ) ), palette[ 0 ], 3, gradientMultipliers[ theme ]
) ),
'colour-accent-gradient-4-hex': renderColourAsHex( util.getGradientColour(
palette[ 0 ], 4, gradientMultipliers[ theme ]
) ),
'colour-accent-gradient-5-hex': renderColourAsHex( util.getGradientColour(
palette[ 0 ], 5, gradientMultipliers[ theme ]
) ),
'colour-accent-gradient-inverse-1-hex': renderColourAsHex( util.getGradientColour(
palette[ 0 ], 1, 1 / gradientMultipliers[ theme ]
) ),
'colour-accent-gradient-inverse-2-hex': renderColourAsHex( util.getGradientColour(
palette[ 0 ], 1, 1 / gradientMultipliers[ theme ]
) ),
'colour-accent-gradient-inverse-3-hex': renderColourAsHex( util.getGradientColour(
palette[ 0 ], 1, 1 / gradientMultipliers[ theme ]
) ),
// ── Secondary accent ───────────────────────────────────────────── // ── Secondary accent ─────────────────────────────────────────────
'colour-accent-2-hex': renderColourAsHex( palette[ 1 ] ), 'colour-accent-2-hex': renderColourAsHex( palette[ 1 ] ),
@@ -155,8 +173,8 @@ module.exports.generateTheme = ( theme, wallpaper, lockpaper, palette ) => {
// │ Path to this repo on disk │ // │ Path to this repo on disk │
// └ ┘ // └ ┘
'path-to-dotfiles': __dirname.slice( 0, __dirname.length - 5 ), 'path-to-dotfiles': __dirname.slice( 0, __dirname.length - 5 ),
} };
} };
// ─────────────────────────────────────────────────────────────────── // ───────────────────────────────────────────────────────────────────
@@ -171,74 +189,265 @@ const gradientMultipliers = {
'light': 1.1, 'light': 1.1,
'bright': 1.15, 'bright': 1.15,
'test': 0.75 'test': 0.75
} };
const colours = { const colours = {
foreground: { 'foreground': {
'nordic': [ 200, 220, 255 ], 'nordic': [
'deep-dark': [ 230, 230, 230 ], 200,
'material': [ 255, 255, 255 ], // TODO: Will be calculated by material theme generator 220,
'light': [ 40, 40, 40 ], 255
'bright': [ 0, 0, 0 ], ],
'test': [ 0, 0, 0 ], 'deep-dark': [
230,
230,
230
],
'material': [
255,
255,
255
], // TODO: Will be calculated by material theme generator
'light': [
40,
40,
40
],
'bright': [
0,
0,
0
],
'test': [
0,
0,
0
],
}, },
'foreground-accent': { 'foreground-accent': {
'nordic': [ 255, 255, 255 ], 'nordic': [
'deep-dark': [ 255, 255, 255 ], 255,
'material': [ 200, 200, 200 ], // TODO: Will be calculated by material theme generator 255,
'light': [ 0, 0, 0 ], 255
'bright': [ 50, 50, 50 ], ],
'test': [ 0, 0, 0 ], 'deep-dark': [
255,
255,
255
],
'material': [
200,
200,
200
], // TODO: Will be calculated by material theme generator
'light': [
0,
0,
0
],
'bright': [
50,
50,
50
],
'test': [
0,
0,
0
],
}, },
background: { 'background': {
'nordic': [ 10, 10, 15 ], 'nordic': [
'deep-dark': [ 20, 20, 20 ], 10,
'material': [ 30, 30, 30 ], // TODO: Will be calculated by material theme generator 10,
'light': [ 230, 230, 230 ], 15
'bright': [ 255, 255, 255 ], ],
'test': [ 255, 255, 255 ], 'deep-dark': [
20,
20,
20
],
'material': [
30,
30,
30
], // TODO: Will be calculated by material theme generator
'light': [
230,
230,
230
],
'bright': [
255,
255,
255
],
'test': [
255,
255,
255
],
}, },
'background-alternative': { 'background-alternative': {
'nordic': [ 20, 20, 25 ], 'nordic': [
'deep-dark': [ 30, 30, 30 ], 20,
'material': [ 40, 40, 40 ], // TODO: Will be calculated by material theme generator 20,
'light': [ 210, 210, 210 ], 25
'bright': [ 230, 230, 230 ], ],
'test': [ 255, 255, 0 ] // brown 'deep-dark': [
30,
30,
30
],
'material': [
40,
40,
40
], // TODO: Will be calculated by material theme generator
'light': [
210,
210,
210
],
'bright': [
230,
230,
230
],
'test': [
255,
255,
0
] // brown
}, },
'background-tertiary': { 'background-tertiary': {
'nordic': [ 0, 0, 0 ], 'nordic': [
'deep-dark': [ 45, 45, 45 ], 0,
'material': [ 0, 0, 0 ], // TODO: Will be calculated by material theme generator 0,
'light': [ 180, 180, 180 ], 0
'bright': [ 200, 200, 200 ], ],
'test': [ 255, 0, 255 ] // purple 'deep-dark': [
45,
45,
45
],
'material': [
0,
0,
0
], // TODO: Will be calculated by material theme generator
'light': [
180,
180,
180
],
'bright': [
200,
200,
200
],
'test': [
255,
0,
255
] // purple
}, },
shadow: { 'shadow': {
'nordic': [ 0, 0, 2 ], 'nordic': [
'deep-dark': [ 40, 40, 40 ], 0,
'material': [ 30, 30, 30 ], // TODO: Will be calculated by material theme generator 0,
'light': [ 190, 190, 190 ], 2
'bright': [ 150, 150, 150 ], ],
'test': [ 120, 0, 0 ] // dark red 'deep-dark': [
40,
40,
40
],
'material': [
30,
30,
30
], // TODO: Will be calculated by material theme generator
'light': [
190,
190,
190
],
'bright': [
150,
150,
150
],
'test': [
120,
0,
0
] // dark red
}, },
inactive: { 'inactive': {
'nordic': [ 200, 200, 200 ], 'nordic': [
'deep-dark': [ 200, 200, 200 ], 200,
'material': [ 200, 200, 200 ], // TODO: Will be calculated by material theme generator 200,
'light': [ 65, 65, 65 ], 200
'bright': [ 60, 60, 60 ], ],
'test': [ 150, 150, 150 ] 'deep-dark': [
200,
200,
200
],
'material': [
200,
200,
200
], // TODO: Will be calculated by material theme generator
'light': [
65,
65,
65
],
'bright': [
60,
60,
60
],
'test': [
150,
150,
150
]
}, },
'inactive-background': { 'inactive-background': {
'nordic': [ 0, 0, 0 ], 'nordic': [
'deep-dark': [ 0, 0, 0 ], 0,
'material': [ 255, 255, 255 ], // TODO: Will be calculated by material theme generator 0,
'light': [ 80, 80, 80 ], 0
'bright': [ 60, 60, 60 ], ],
'test': [ 60, 60, 60 ] 'deep-dark': [
0,
0,
0
],
'material': [
255,
255,
255
], // TODO: Will be calculated by material theme generator
'light': [
80,
80,
80
],
'bright': [
60,
60,
60
],
'test': [
60,
60,
60
]
} }
} };
const fonts = { const fonts = {
'primary': { 'primary': {
'nordic': 'Comfortaa', 'nordic': 'Comfortaa',
@@ -261,20 +470,18 @@ const fonts = {
'light': 'Jetbrains Mono', 'light': 'Jetbrains Mono',
'bright': 'Jetbrains Mono', 'bright': 'Jetbrains Mono',
} }
} };
const iconTheme = { const iconTheme = {
'nordic': 'Candy', 'nordic': 'Candy',
'deep-dark': 'Candy', 'deep-dark': 'Candy',
'material': 'Candy', 'material': 'Candy',
'light': 'Candy', 'light': 'Candy',
'bright': 'Candy' 'bright': 'Candy'
} };
const yaziThemes = { const yaziThemes = {
'nordic': 'tokyo-night', 'nordic': 'tokyo-night',
'deep-dark': 'vscode-dark-modern', 'deep-dark': 'vscode-dark-modern',
'material': 'dracula', 'material': 'dracula',
'light': 'vscode-light-modern', 'light': 'vscode-light-modern',
'bright': 'vscode-light-modern', 'bright': 'vscode-light-modern',
} };

View File

@@ -1,10 +1,13 @@
const fs = require( 'fs' ); const fs = require( 'fs' );
const path = require( 'path' ); const path = require( 'path' );
const data = '' + fs.readFileSync( '/usr/share/themes/Material-Black-Blueberry/gtk-4.0/gtk.css' ); const data = '' + fs.readFileSync( '/usr/share/themes/Material-Black-Blueberry/gtk-4.0/gtk.css' );
let lineNumber = 1; let lineNumber = 1;
const indexer = {}; const indexer = {};
for ( let i = 0; i < data.length; i++ ) { for ( let i = 0; i < data.length; i++ ) {
const char = data[i]; const char = data[i];
@@ -13,17 +16,22 @@ for (let i = 0; i < data.length; i++) {
} else if ( char === '#' ) { } else if ( char === '#' ) {
const extract = data.substring( i ); const extract = data.substring( i );
const col = extract.slice( 0, extract.indexOf( '\n' ) ); const col = extract.slice( 0, extract.indexOf( '\n' ) );
if ( !indexer[ col ] ) { if ( !indexer[ col ] ) {
indexer[ col ] = []; indexer[ col ] = [];
} }
indexer[ col ].push( lineNumber ); indexer[ col ].push( lineNumber );
} else if ( char === 'r' ) { } else if ( char === 'r' ) {
const extract = data.substring( i ); const extract = data.substring( i );
if ( extract.slice( 0, 3 ) === 'rgb' ) { if ( extract.slice( 0, 3 ) === 'rgb' ) {
const col = extract.slice( 0, extract.indexOf( '\n' ) ); const col = extract.slice( 0, extract.indexOf( '\n' ) );
if ( !indexer[ col ] ) { if ( !indexer[ col ] ) {
indexer[ col ] = []; indexer[ col ] = [];
} }
indexer[ col ].push( lineNumber ); indexer[ col ].push( lineNumber );
} }
} }
@@ -32,6 +40,7 @@ for (let i = 0; i < data.length; i++) {
// Output // Output
const keys = Object.keys( indexer ); const keys = Object.keys( indexer );
for ( let i = 0; i < keys.length; i++ ) { for ( let i = 0; i < keys.length; i++ ) {
const element = keys[i]; const element = keys[i];

View File

@@ -1,24 +1,29 @@
import { App } from "astal/gtk4" import {
import style from "./style.scss" App
import Bar from "./components/bar/Bar"; } from 'astal/gtk4';
import AstalHyprland from "gi://AstalHyprland?version=0.1"; import AstalHyprland from 'gi://AstalHyprland?version=0.1';
import { hyprToGdk } from "./util/hyprland"; import Bar from './components/bar/Bar';
import Brightness from "./util/brightness"; import Brightness from './util/brightness';
import {
hyprToGdk
} from './util/hyprland';
import style from './style.scss';
App.start( { App.start( {
instanceName: "runner", 'instanceName': 'runner',
css: style, 'css': style,
main () { main () {
const hypr = AstalHyprland.get_default(); const hypr = AstalHyprland.get_default();
const bars = new Map<number, string>(); const bars = new Map<number, string>();
const barCreator = ( monitor: AstalHyprland.Monitor ) => { const barCreator = ( monitor: AstalHyprland.Monitor ) => {
const gdkMonitor = hyprToGdk( monitor ); const gdkMonitor = hyprToGdk( monitor );
if ( gdkMonitor ) { if ( gdkMonitor ) {
print( 'Bar added for screen ' + monitor.get_id() ); print( 'Bar added for screen ' + monitor.get_id() );
bars.set( monitor.get_id(), Bar.BarLauncher( gdkMonitor ) ); bars.set( monitor.get_id(), Bar.BarLauncher( gdkMonitor ) );
} }
} };
for ( const monitor of hypr.monitors ) { for ( const monitor of hypr.monitors ) {
barCreator( monitor ); barCreator( monitor );
@@ -30,14 +35,17 @@ App.start({
hypr.connect( 'monitor-removed', ( _, monitor ) => { hypr.connect( 'monitor-removed', ( _, monitor ) => {
const windowName = bars.get( monitor ); const windowName = bars.get( monitor );
if ( windowName ) { if ( windowName ) {
const win = App.get_window( windowName ); const win = App.get_window( windowName );
if ( win ) { if ( win ) {
App.toggle_window( windowName ); App.toggle_window( windowName );
win.set_child( null ); win.set_child( null );
App.remove_window( win ); App.remove_window( win );
print( 'Bar removed for screen', monitor ); print( 'Bar removed for screen', monitor );
} }
bars.delete( monitor ); bars.delete( monitor );
} }
} ); } );
@@ -60,6 +68,7 @@ App.start({
} else if ( args[ 0 ] === 'brightness' ) { } else if ( args[ 0 ] === 'brightness' ) {
try { try {
const brightness = Brightness.get_default(); const brightness = Brightness.get_default();
if ( brightness.screenAvailable ) { if ( brightness.screenAvailable ) {
if ( args[ 1 ] === 'increase' ) { if ( args[ 1 ] === 'increase' ) {
brightness.screen += args.length > 1 ? parseInt( args[ 2 ] ) / 100 : 1; brightness.screen += args.length > 1 ? parseInt( args[ 2 ] ) / 100 : 1;
@@ -70,12 +79,15 @@ App.start({
brightness.screen = parseInt( args[ 2 ] ) / 100; brightness.screen = parseInt( args[ 2 ] ) / 100;
} else { } else {
res( 'Argument <brightness> unspecified' ); res( 'Argument <brightness> unspecified' );
return; return;
} }
} else { } else {
res( 'Unknown command ' + args[ 1 ] ); res( 'Unknown command ' + args[ 1 ] );
return; return;
} }
res( 'Ok' ); res( 'Ok' );
} else { } else {
res( 'No controllable screen available' ); res( 'No controllable screen available' );
@@ -99,5 +111,4 @@ App.start({
// } // }
// } // }
}, },
}) } );

View File

@@ -1,44 +1,58 @@
import { Gtk } from "astal/gtk4"; import Audio from './modules/Audio/Audio';
import Power from "./modules/Power"; import {
import Audio from "./modules/Audio/Audio"; BatteryBox
import Bluetooth from "./modules/Bluetooth/Bluetooth"; } from './modules/Battery';
import Brightness from "./modules/Brightness/Brightness"; import Bluetooth from './modules/Bluetooth/Bluetooth';
import Player from "./modules/Player/Player"; import Brightness from './modules/Brightness/Brightness';
import { BatteryBox } from "./modules/Battery"; import {
import { exec } from "astal"; Gtk
import Network from "./modules/Networking/Network"; } from 'astal/gtk4';
import Network from './modules/Networking/Network';
import Player from './modules/Player/Player';
import Power from './modules/Power';
import SysTray from './modules/SysTray';
import {
exec
} from 'astal';
const QuickActions = () => { const QuickActions = () => {
const popover = new Gtk.Popover({ cssClasses: ["quick-actions-wrapper"] }); const popover = new Gtk.Popover( {
'cssClasses': [ 'quick-actions-wrapper' ]
} );
popover.set_child( renderQuickActions() ); popover.set_child( renderQuickActions() );
return popover; return popover;
}; };
const renderQuickActions = () => { const renderQuickActions = () => {
const user = exec("/bin/sh -c whoami"); const user = exec( '/bin/sh -c whoami' );
const profile = exec("/bin/fish -c get-profile-picture"); const profile = exec( '/bin/fish -c get-profile-picture' );
const cwd = exec("pwd"); const cwd = exec( 'pwd' );
const um = Power.UserMenu(); const um = Power.UserMenu();
return ( return (
<box visible cssClasses={["quick-actions", "popover-box"]} vertical> <box visible cssClasses={[
'quick-actions',
'popover-box'
]} vertical>
<centerbox <centerbox
startWidget={ startWidget={
<button <button
onClicked={() => um.popup()} onClicked={() => um.popup()}
cssClasses={["stealthy-button"]} cssClasses={[ 'stealthy-button' ]}
child={ child={
<box> <box>
{um} {um}
<Gtk.Frame <Gtk.Frame
cssClasses={["avatar-icon"]} cssClasses={[ 'avatar-icon' ]}
child={ child={
<image <image
file={ file={
profile !== "" profile !== ''
? profile ? profile
: cwd + : cwd
"/no-avatar-icon.jpg" + '/no-avatar-icon.jpg'
} }
></image> ></image>
} }
@@ -53,6 +67,7 @@ const renderQuickActions = () => {
hexpand={false} hexpand={false}
> >
<BatteryBox></BatteryBox> <BatteryBox></BatteryBox>
<SysTray.SystemTray></SysTray.SystemTray>
<Power.Power></Power.Power> <Power.Power></Power.Power>
</box> </box>
} }

View File

@@ -7,6 +7,7 @@ const BrightnessModule = () => {
const setBrightness = (value: number) => { const setBrightness = (value: number) => {
brightness.screen = value; brightness.screen = value;
} }
return ( return (
<box visible={bind(brightness, 'screenAvailable')}> <box visible={bind(brightness, 'screenAvailable')}>
<image iconName={"brightness-high-symbolic"}></image> <image iconName={"brightness-high-symbolic"}></image>

View File

@@ -1,48 +1,56 @@
import { exec } from "astal"; import {
import { Gtk } from "astal/gtk4"; exec
} from 'astal';
import {
Gtk
} from 'astal/gtk4';
const PowerMenu = (): Gtk.Popover => { const PowerMenu = (): Gtk.Popover => {
const popover = new Gtk.Popover({ cssClasses: ["PowerMenu"] }); const popover = new Gtk.Popover( {
'cssClasses': [ 'PowerMenu' ]
} );
const powerMenuBox = () => { const powerMenuBox = () => {
return ( return (
<box> <box>
<button <button
cssClasses={["power-button"]} cssClasses={[ 'power-button' ]}
child={ child={
<image iconName={"system-shutdown-symbolic"}></image> <image iconName={'system-shutdown-symbolic'}></image>
} }
onClicked={() => exec("/bin/sh -c 'shutdown now'")} onClicked={() => exec( '/bin/sh -c \'shutdown now\'' )}
></button> ></button>
<button <button
cssClasses={["power-button"]} cssClasses={[ 'power-button' ]}
child={<image iconName={"system-reboot-symbolic"}></image>} child={<image iconName={'system-reboot-symbolic'}></image>}
onClicked={() => exec("/bin/sh -c 'reboot'")} onClicked={() => exec( '/bin/sh -c \'reboot\'' )}
></button> ></button>
<button <button
cssClasses={["power-button"]} cssClasses={[ 'power-button' ]}
child={<image iconName={"system-suspend-symbolic"}></image>} child={<image iconName={'system-suspend-symbolic'}></image>}
onClicked={() => exec("/bin/sh -c 'systemctl suspend'")} onClicked={() => exec( '/bin/sh -c \'systemctl suspend\'' )}
></button> ></button>
</box> </box>
); );
}; };
popover.set_child( powerMenuBox() ); popover.set_child( powerMenuBox() );
return popover; return popover;
}; };
const Power = () => { const Power = () => {
const pm = PowerMenu(); const pm = PowerMenu();
return ( return (
<button <button
widthRequest={0} widthRequest={0}
hexpand={false} hexpand={false}
vexpand={false} vexpand={false}
cssClasses={["power-menu-button"]} cssClasses={[ 'power-menu-button' ]}
child={ child={
<box> <box>
<image iconName={"system-shutdown-symbolic"}></image> <image iconName={'system-shutdown-symbolic'}></image>
{pm} {pm}
</box> </box>
} }
@@ -58,17 +66,16 @@ const UserMenu = (): Gtk.Popover => {
return ( return (
<box> <box>
<button <button
cssClasses={["power-button"]} cssClasses={[ 'power-button' ]}
child={ child={
<image iconName={"system-lock-screen-symbolic"}></image> <image iconName={'system-lock-screen-symbolic'}></image>
} }
onClicked={() => exec("/bin/sh -c 'hyprlock'")} onClicked={() => exec( '/bin/sh -c \'hyprlock\'' )}
></button> ></button>
<button <button
cssClasses={["power-button"]} cssClasses={[ 'power-button' ]}
child={<image iconName={"system-log-out-symbolic"}></image>} child={<image iconName={'system-log-out-symbolic'}></image>}
onClicked={() => onClicked={() => exec( '/bin/sh -c \'hyprctl dispatch exit 0\'' )
exec("/bin/sh -c 'hyprctl dispatch exit 0'")
} }
></button> ></button>
</box> </box>
@@ -76,6 +83,7 @@ const UserMenu = (): Gtk.Popover => {
}; };
popover.set_child( powerMenuBox() ); popover.set_child( powerMenuBox() );
return popover; return popover;
}; };

View File

@@ -0,0 +1,88 @@
import AstalTray from 'gi://AstalTray';
import {
GObject
} from 'astal';
import {
Gtk
} from 'astal/gtk4';
const SYNC = GObject.BindingFlags.SYNC_CREATE;
const SysTray = () => {
const trayBox = new Gtk.Box( {
'cssClasses': [ '' ]
} );
const tray = AstalTray.get_default();
const trayItems = new Map<string, Gtk.MenuButton>();
const trayAddedHandler = tray.connect( 'item-added', ( _, id ) => {
const item = tray.get_item( id );
const popover = Gtk.PopoverMenu.new_from_model( item.menu_model );
const icon = new Gtk.Image();
const button = new Gtk.MenuButton( {
popover,
'child': icon,
'cssClasses': [ 'tray-item' ],
} );
item.bind_property(
'gicon', icon, 'gicon', SYNC
);
popover.insert_action_group( 'dbusmenu', item.action_group );
item.connect( 'notify::action-group', () => {
popover.insert_action_group( 'dbusmenu', item.action_group );
} );
trayItems.set( id, button );
trayBox.append( button );
} );
const trayRemovedHandler = tray.connect( 'item-removed', ( _, id ) => {
const button = trayItems.get( id );
if ( button ) {
trayBox.remove( button );
button.run_dispose();
trayItems.delete( id );
}
} );
trayBox.connect( 'destroy', () => {
tray.disconnect( trayAddedHandler );
tray.disconnect( trayRemovedHandler );
} );
return trayBox;
};
const TrayPopover = () => {
const popover = new Gtk.Popover( {
'cssClasses': [ 'TrayPopover' ]
} );
popover.set_child( SysTray() );
return popover;
};
const SystemTray = () => {
const systray = TrayPopover();
return (
<button
widthRequest={0}
hexpand={false}
vexpand={false}
cssClasses={[ 'power-menu-button' ]}
child={
<box>
<image iconName={'systemtray'}></image>
{systray}
</box>
}
onClicked={() => systray.popup()}
/>
);
};
export default {
SystemTray
};

View File

@@ -1,19 +1,30 @@
import { App, Astal, Gdk, Gtk } from "astal/gtk4"; import {
import Hyprland from "./modules/Hyprland"; App, Astal, Gdk, Gtk
import Calendar from "./modules/Calendar"; } from 'astal/gtk4';
import QuickView from "./modules/QuickView"; import Calendar from './modules/Calendar';
import SystemInfo from "./modules/SystemInfo"; import {
import { CenterBox } from "astal/gtk4/widget"; CenterBox
} from 'astal/gtk4/widget';
import Hyprland from './modules/Hyprland';
import QuickView from './modules/QuickView';
import SystemInfo from './modules/SystemInfo';
const Bar = ( { gdkmonitor, name }: { gdkmonitor: Gdk.Monitor, name: string } ) => { const Bar = ( {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor; gdkmonitor, name
}: {
'gdkmonitor': Gdk.Monitor,
'name': string
} ) => {
const {
TOP, LEFT, RIGHT
} = Astal.WindowAnchor;
return ( return (
<window <window
gdkmonitor={gdkmonitor} gdkmonitor={gdkmonitor}
cssClasses={["Bar"]} cssClasses={[ 'Bar' ]}
name={name} name={name}
namespace={"bar"} namespace={'bar'}
exclusivity={Astal.Exclusivity.EXCLUSIVE} exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT} anchor={TOP | LEFT | RIGHT}
visible visible
@@ -26,8 +37,8 @@ const Bar = ( { gdkmonitor, name }: { gdkmonitor: Gdk.Monitor, name: string } )
hexpand hexpand
halign={Gtk.Align.START} halign={Gtk.Align.START}
> >
<Hyprland.ModeStatus />
<Calendar.Time /> <Calendar.Time />
<SystemInfo.SystemInfo />
<Hyprland.Workspace /> <Hyprland.Workspace />
</box> </box>
} }
@@ -36,9 +47,9 @@ const Bar = ( { gdkmonitor, name }: { gdkmonitor: Gdk.Monitor, name: string } )
<box <box
hexpand hexpand
halign={Gtk.Align.END} halign={Gtk.Align.END}
cssClasses={["BarRight"]} cssClasses={[ 'BarRight' ]}
> >
<Hyprland.SysTray /> <SystemInfo.SystemInfo />
<QuickView.QuickView /> <QuickView.QuickView />
</box> </box>
} }
@@ -49,20 +60,23 @@ const Bar = ( { gdkmonitor, name }: { gdkmonitor: Gdk.Monitor, name: string } )
}; };
const cliHandler = ( args: string[] ): string => { const cliHandler = ( args: string[] ): string => {
return "Not implemented"; console.debug( args );
return 'Not implemented';
}; };
const BarLauncher = ( monitor: Gdk.Monitor ) => { const BarLauncher = ( monitor: Gdk.Monitor ) => {
const windowName = `bar-${monitor.get_connector()}` const windowName = `bar-${ monitor.get_connector() }`;
const createBar = () => { const createBar = () => {
return <Bar gdkmonitor={monitor} name={windowName}></Bar> return <Bar gdkmonitor={monitor} name={windowName}></Bar>;
} };
// Actually start the bar // Actually start the bar
createBar(); createBar();
return windowName; return windowName;
} };
export default { export default {
BarLauncher, BarLauncher,

View File

@@ -12,6 +12,19 @@ window.Bar {
/* margin: 8px; */ /* margin: 8px; */
/* } */ /* } */
.mode-status { .mode-status {
color: white;
background-color: #00002dff;
padding-left: 10px;
padding-right: 10px;
margin-left: 5px;
border-radius: 20px;
font-family: $monospace-font;
&.command-mode {
background-color: darkslategray;
color: white;
}
&.windowing-mode { &.windowing-mode {
background-color: darkslategray; background-color: darkslategray;
color: white; color: white;
@@ -85,6 +98,7 @@ window.Bar {
.time { .time {
min-width: 11rem; min-width: 11rem;
padding: 3px; padding: 3px;
& button { & button {
box-shadow: none; box-shadow: none;
padding: 0; padding: 0;

View File

@@ -1,76 +1,34 @@
import AstalTray from "gi://AstalTray"; import {
import { bind, GObject } from "astal"; bind,
import AstalHyprland from "gi://AstalHyprland"; exec,
import { Gtk } from "astal/gtk4"; readFile
import Pango from "gi://Pango?version=1.0"; } from 'astal';
import AstalHyprland from 'gi://AstalHyprland';
import {
Gtk
} from 'astal/gtk4';
import Pango from 'gi://Pango?version=1.0';
const hypr = AstalHyprland.get_default(); const hypr = AstalHyprland.get_default();
const SYNC = GObject.BindingFlags.SYNC_CREATE;
const SysTray = () => {
const trayBox = new Gtk.Box({ cssClasses: ["bar-button"] });
const tray = AstalTray.get_default();
const trayItems = new Map<string, Gtk.MenuButton>();
const trayAddedHandler = tray.connect("item-added", (_, id) => {
const item = tray.get_item(id);
const popover = Gtk.PopoverMenu.new_from_model(item.menu_model);
const icon = new Gtk.Image();
const button = new Gtk.MenuButton({
popover,
child: icon,
cssClasses: ["tray-item"],
});
item.bind_property("gicon", icon, "gicon", SYNC);
popover.insert_action_group("dbusmenu", item.action_group);
item.connect("notify::action-group", () => {
popover.insert_action_group("dbusmenu", item.action_group);
});
trayItems.set(id, button);
trayBox.append(button);
});
const trayRemovedHandler = tray.connect("item-removed", (_, id) => {
const button = trayItems.get(id);
if (button) {
trayBox.remove(button);
button.run_dispose();
trayItems.delete(id);
}
});
trayBox.connect("destroy", () => {
tray.disconnect(trayAddedHandler);
tray.disconnect(trayRemovedHandler);
});
return trayBox;
};
const Workspace = () => { const Workspace = () => {
return ( return (
<box> <box>
{bind(hypr, "workspaces").as(wss => {bind( hypr, 'workspaces' ).as( wss => wss
wss
.filter( ws => !( ws.id >= -99 && ws.id <= -2 ) ) // filter out special workspaces .filter( ws => !( ws.id >= -99 && ws.id <= -2 ) ) // filter out special workspaces
.sort( ( a, b ) => a.id - b.id ) .sort( ( a, b ) => a.id - b.id )
.map( ws => ( .map( ws => (
<button <button
cssClasses={bind(hypr, "focusedWorkspace").as(fw => cssClasses={bind( hypr, 'focusedWorkspace' ).as( fw => ws === fw
ws === fw
? [ ? [
"focused-workspace-button", 'focused-workspace-button',
"workspace-button", 'workspace-button',
] ]
: ["workspace-button"], : [ 'workspace-button' ], )}
)}
onButtonPressed={() => ws.focus()} onButtonPressed={() => ws.focus()}
child={<label label={String( ws.id )}></label>} child={<label label={String( ws.id )}></label>}
></button> ></button>
)), ) ), )}
)}
</box> </box>
); );
}; };
@@ -80,15 +38,15 @@ const Workspace = () => {
* displaying all available clients * displaying all available clients
*/ */
const ActiveWindow = () => { const ActiveWindow = () => {
const focused = bind(hypr, "focusedClient"); const focused = bind( hypr, 'focusedClient' );
const WindowPopover = (): Gtk.Popover => { const WindowPopover = (): Gtk.Popover => {
// Set up boxes + Popover // Set up boxes + Popover
const popover = new Gtk.Popover(); const popover = new Gtk.Popover();
const popoverBox = WindowPopoverBox(); const popoverBox = WindowPopoverBox();
popover.set_child( popoverBox ); popover.set_child( popoverBox );
return popover; return popover;
}; };
@@ -101,44 +59,67 @@ const ActiveWindow = () => {
<box visible={focused.as( Boolean )}> <box visible={focused.as( Boolean )}>
<button <button
onClicked={() => windowPopover.popup()} onClicked={() => windowPopover.popup()}
cssClasses={["bar-button"]} cssClasses={[ 'bar-button' ]}
child={ child={
focused.as( focused.as( client => client && (
client =>
client && (
<label <label
maxWidthChars={70} maxWidthChars={70}
ellipsize={Pango.EllipsizeMode.END} ellipsize={Pango.EllipsizeMode.END}
label={bind(client, "title").as(String)} /> label={bind( client, 'title' ).as( String )} />
), ), )
)
}></button> }></button>
{windowPopover} {windowPopover}
</box > </box >
); );
}; };
type submaps = 'device' | 'launch' | 'workspace' | 'windowing' | 'screenshotting' | 'notifications' | '';
const ModeStatus = () => { const ModeStatus = () => {
let isUsingHyprvim = false;
try {
const path = exec( 'bash -c "cd ~ && pwd"' ) + '/.config/hyprvim';
isUsingHyprvim = readFile( path ).trim() === 'y';
} catch ( e ) {
printerr( 'Failed to read hyprvim config', e );
}
const label = new Gtk.Label(); const label = new Gtk.Label();
if ( !isUsingHyprvim ) return label;
print( '==> Using hyprvim config' );
const map = { const map = {
"device": "D", 'device': 'DEV',
"launch": "L", 'launch': 'LNC',
"workspace": "W", 'workspace': 'WSP',
"windowing": "M", 'windowing': 'WIN',
"screenshotting": "S", 'screenshotting': 'SCS',
"notifications": "N", 'notifications': 'NOT',
} '': 'NRM'
};
label.label = map[''];
label.cssClasses = [ 'mode-status' ];
// TODO: Possibly add popover to it that lists binds // TODO: Possibly add popover to it that lists binds
hypr.connect("submap", (name: string) => { hypr.connect( 'submap', ( _, name: submaps ) => {
label.label = map[name]; label.label = map[name];
label.cssClasses = ["mode-status", name + '-mode'] label.cssClasses = [
}) 'mode-status',
return name + '-mode'
} ];
} );
return label;
};
const WindowPopoverBox = () => { const WindowPopoverBox = () => {
return <box vertical> return <box vertical>
<label label={"Available Windows"} cssClasses={['title-2']}></label> <label label={'Available Windows'} cssClasses={[ 'title-2' ]}></label>
<Gtk.Separator marginTop={5} marginBottom={5}></Gtk.Separator> <Gtk.Separator marginTop={5} marginBottom={5}></Gtk.Separator>
<box vertical> <box vertical>
{bind( hypr, 'clients' ).as( clients => { {bind( hypr, 'clients' ).as( clients => {
@@ -151,16 +132,15 @@ const WindowPopoverBox = () => {
</box> </box>
} }
onClicked={() => client.focus()} onClicked={() => client.focus()}
></button> ></button>;
}) } );
} )} } )}
</box> </box>
</box> </box>;
} };
export default { export default {
Workspace, Workspace,
ActiveWindow, ActiveWindow,
SysTray,
ModeStatus ModeStatus
}; };

View File

@@ -1,17 +1,24 @@
import { bind } from "astal"; import AstalBattery from 'gi://AstalBattery';
import { execAsync } from "astal"; import AstalBluetooth from 'gi://AstalBluetooth';
import AstalBattery from "gi://AstalBattery"; import AstalNetwork from 'gi://AstalNetwork';
import AstalBluetooth from "gi://AstalBluetooth"; import AstalWp from 'gi://AstalWp';
import AstalNetwork from "gi://AstalNetwork"; import Brightness from '../../../util/brightness';
import AstalWp from "gi://AstalWp"; import {
import { Gtk } from "astal/gtk4"; Gtk
import Brightness from "../../../util/brightness"; } from 'astal/gtk4';
import QuickActions from "../../QuickActions/QuickActions"; import QuickActions from '../../QuickActions/QuickActions';
import {
bind
} from 'astal';
import {
execAsync
} from 'astal';
const STATE = AstalNetwork.DeviceState; const STATE = AstalNetwork.DeviceState;
const QuickView = () => { const QuickView = () => {
const qa = QuickActions.QuickActions(); const qa = QuickActions.QuickActions();
const showQuickActions = () => { const showQuickActions = () => {
qa.popup(); qa.popup();
}; };
@@ -19,14 +26,14 @@ const QuickView = () => {
return ( return (
<button <button
onClicked={() => showQuickActions()} onClicked={() => showQuickActions()}
cssClasses={["quick-action-button"]} cssClasses={[ 'quick-action-button' ]}
child={ child={
<box> <box>
<BatteryWidget></BatteryWidget> <BatteryWidget></BatteryWidget>
<Audio></Audio> <Audio></Audio>
<BluetoothWidget></BluetoothWidget> <BluetoothWidget></BluetoothWidget>
<NetworkWidget></NetworkWidget> <NetworkWidget></NetworkWidget>
<image iconName={"system-shutdown-symbolic"}></image> <image iconName={'system-shutdown-symbolic'}></image>
{qa} {qa}
</box> </box>
} }
@@ -40,37 +47,39 @@ const NetworkWidget = () => {
return ( return (
<box> <box>
<image <image
iconName={bind(network, "state").as(state => { iconName={bind( network, 'state' ).as( state => {
if ( state === AstalNetwork.State.CONNECTING ) { if ( state === AstalNetwork.State.CONNECTING ) {
return "chronometer-reset-symbolic"; return 'chronometer-reset-symbolic';
} else if ( } else if (
state === AstalNetwork.State.CONNECTED_LOCAL || state === AstalNetwork.State.CONNECTED_LOCAL
state === AstalNetwork.State.CONNECTED_SITE || || state === AstalNetwork.State.CONNECTED_SITE
state === AstalNetwork.State.CONNECTED_GLOBAL || state === AstalNetwork.State.CONNECTED_GLOBAL
) { ) {
return "network-wired-activated-symbolic"; return 'network-wired-activated-symbolic';
} else { } else {
return "paint-unknown-symbolic"; return 'paint-unknown-symbolic';
} }
} )} } )}
cssClasses={["network-widget", "quick-view-symbol"]} cssClasses={[
visible={bind(network.wifi, "state").as( 'network-widget',
state => state !== STATE.ACTIVATED, 'quick-view-symbol'
)} ]}
visible={bind( network.wifi, 'state' ).as( state => state !== STATE.ACTIVATED, )}
></image> ></image>
<image <image
iconName={bind(network.wifi, "state").as(state => { iconName={bind( network.wifi, 'state' ).as( state => {
if ( state === STATE.ACTIVATED ) { if ( state === STATE.ACTIVATED ) {
return network.wifi.iconName; return network.wifi.iconName;
} else { } else {
return ""; return '';
} }
} )} } )}
tooltipText={bind( network.wifi, 'ssid' )} tooltipText={bind( network.wifi, 'ssid' )}
cssClasses={["network-widget", "quick-view-symbol"]} cssClasses={[
visible={bind(network.wifi, "state").as( 'network-widget',
state => state === STATE.ACTIVATED, 'quick-view-symbol'
)} ]}
visible={bind( network.wifi, 'state' ).as( state => state === STATE.ACTIVATED, )}
></image> ></image>
</box> </box>
); );
@@ -78,40 +87,36 @@ const NetworkWidget = () => {
const BluetoothWidget = () => { const BluetoothWidget = () => {
const bluetooth = AstalBluetooth.get_default(); const bluetooth = AstalBluetooth.get_default();
const enabled = bind(bluetooth, "isPowered"); const enabled = bind( bluetooth, 'isPowered' );
const connected = bind(bluetooth, "isConnected"); const connected = bind( bluetooth, 'isConnected' );
// For each connected BT device, render status // For each connected BT device, render status
return ( return (
<box> <box>
<box visible={enabled.as( e => e )}> <box visible={enabled.as( e => e )}>
<image <image
iconName={"bluetooth-active-symbolic"} iconName={'bluetooth-active-symbolic'}
visible={connected.as( c => c )} visible={connected.as( c => c )}
></image> ></image>
<image <image
iconName={"bluetooth-disconnected-symbolic"} iconName={'bluetooth-disconnected-symbolic'}
visible={connected.as( c => !c )} visible={connected.as( c => !c )}
></image> ></image>
</box> </box>
<image <image
iconName={"bluetooth-disabled-symbolic"} iconName={'bluetooth-disabled-symbolic'}
visible={enabled.as( e => !e )} visible={enabled.as( e => !e )}
></image> ></image>
<box> <box>
{bind(bluetooth, "devices").as(devices => { {bind( bluetooth, 'devices' ).as( devices => {
return devices.map( device => { return devices.map( device => {
return ( return (
<image <image
iconName={bind(device, "icon").as( iconName={bind( device, 'icon' ).as( icon => icon, )}
icon => icon, visible={bind( device, 'connected' )}
)} tooltipText={bind( device, 'batteryPercentage' ).as( n => {
visible={bind(device, "connected")} return device.get_name() + ': ' + n + '%';
tooltipText={bind(device, "batteryPercentage").as( }, )}
n => {
return device.get_name() + ': ' + n + "%";
},
)}
></image> ></image>
); );
} ); } );
@@ -123,20 +128,24 @@ const BluetoothWidget = () => {
let hasSentNotification = false; let hasSentNotification = false;
const BatteryWidget = () => { const BatteryWidget = () => {
const battery = AstalBattery.get_default(); const battery = AstalBattery.get_default();
if ( battery.get_is_present() ) { if ( battery.get_is_present() ) {
return ( return (
<image <image
iconName={bind(battery, "batteryIconName").as(icon => icon)} iconName={bind( battery, 'batteryIconName' ).as( icon => icon )}
cssClasses={["quick-view-symbol"]} cssClasses={[ 'quick-view-symbol' ]}
tooltipText={bind( battery, 'percentage' ).as( p => { tooltipText={bind( battery, 'percentage' ).as( p => {
const level = Math.round(p * 100) const level = Math.round( p * 100 );
if ( level < 20 && !hasSentNotification ) { if ( level < 20 && !hasSentNotification ) {
hasSentNotification = true; hasSentNotification = true;
execAsync( 'bash -c "notify-send \'Battery level below 20%\'"' ); execAsync( 'bash -c "notify-send \'Battery level below 20%\'"' );
} }
return `Battery Level: ${level}%`
return `Battery Level: ${ level }%`;
} )} } )}
></image> ></image>
); );
@@ -148,14 +157,14 @@ const BatteryWidget = () => {
const BrightnessWidget = () => { const BrightnessWidget = () => {
const brightness = Brightness.get_default(); const brightness = Brightness.get_default();
const screen_brightness = bind(brightness, "screen"); const screen_brightness = bind( brightness, 'screen' );
return ( return (
<box cssClasses={["quick-view-symbol"]}> <box cssClasses={[ 'quick-view-symbol' ]}>
<image iconName={"brightness-high-symbolic"}></image> <image iconName={'brightness-high-symbolic'}></image>
<label <label
label={screen_brightness.as( b => '' + Math.round( 100 * b ) )} label={screen_brightness.as( b => '' + Math.round( 100 * b ) )}
visible={bind(brightness, "screenAvailable")} visible={bind( brightness, 'screenAvailable' )}
></label> ></label>
</box> </box>
); );
@@ -163,31 +172,27 @@ const BrightnessWidget = () => {
const Audio = () => { const Audio = () => {
const wireplumber = AstalWp.get_default(); const wireplumber = AstalWp.get_default();
if ( wireplumber ) { if ( wireplumber ) {
return ( return (
<box orientation={Gtk.Orientation.HORIZONTAL}> <box orientation={Gtk.Orientation.HORIZONTAL}>
<image <image
iconName={bind(wireplumber.defaultSpeaker, "volumeIcon").as( iconName={bind( wireplumber.defaultSpeaker, 'volumeIcon' ).as( icon => icon, )}
icon => icon, cssClasses={[ 'quick-view-symbol' ]}
)}
cssClasses={["quick-view-symbol"]}
tooltipText={bind( wireplumber.defaultSpeaker, 'volume' ).as( v => Math.round( 100 * v ) + '%' )} tooltipText={bind( wireplumber.defaultSpeaker, 'volume' ).as( v => Math.round( 100 * v ) + '%' )}
></image> ></image>
<image <image
iconName={bind( iconName={bind( wireplumber.defaultMicrophone,
wireplumber.defaultMicrophone, 'volumeIcon', ).as( icon => icon )}
"volumeIcon", cssClasses={[ 'quick-view-symbol' ]}
).as(icon => icon)}
cssClasses={["quick-view-symbol"]}
tooltipText={bind( wireplumber.defaultMicrophone, 'volume' ).as( v => Math.round( 100 * v ) + '%' )} tooltipText={bind( wireplumber.defaultMicrophone, 'volume' ).as( v => Math.round( 100 * v ) + '%' )}
></image> ></image>
</box> </box>
); );
} else { } else {
print( print( '[ WirePlumber ] Could not connect, Audio support in bar will be missing', );
"[ WirePlumber ] Could not connect, Audio support in bar will be missing",
); return <image iconName={'action-unavailable-symbolic'}></image>;
return <image iconName={"action-unavailable-symbolic"}></image>;
} }
}; };
@@ -195,4 +200,5 @@ const Audio = () => {
export default { export default {
QuickView, QuickView,
BrightnessWidget
}; };

View File

@@ -1,13 +1,17 @@
import { execAsync } from "astal"; import {
import { Gtk } from "astal/gtk4"; Gtk
import sysinfo from "../sysinfo"; } from 'astal/gtk4';
import {
execAsync
} from 'astal';
import sysinfo from '../sysinfo';
const info = () => { const info = () => {
return ( return (
<box vertical> <box vertical>
<label <label
label={"System Information"} label={'System Information'}
cssClasses={["title-2"]} cssClasses={[ 'title-2' ]}
></label> ></label>
<Gtk.Separator marginTop={5} marginBottom={10}></Gtk.Separator> <Gtk.Separator marginTop={5} marginBottom={10}></Gtk.Separator>
<label <label
@@ -15,7 +19,7 @@ const info = () => {
halign={Gtk.Align.START} halign={Gtk.Align.START}
hexpand hexpand
label={sysinfo.ramUsed( used => { label={sysinfo.ramUsed( used => {
return "RAM: " + used + ` (${sysinfo.ramUtil.get()}%)`; return 'RAM: ' + used + ` (${ sysinfo.ramUtil.get() }%)`;
} )} } )}
></label> ></label>
<label <label
@@ -27,9 +31,9 @@ Kernel: ${stats.kernel}`;
></label> ></label>
<Gtk.Separator marginTop={10}></Gtk.Separator> <Gtk.Separator marginTop={10}></Gtk.Separator>
<button <button
onClicked={() => execAsync(`/bin/sh -c "kitty --hold fish -c 'fastfetch'"`)} onClicked={() => execAsync( '/bin/sh -c "kitty --hold fish -c \'fastfetch\'"' )}
child={ child={
<label label={"View FastFetch"}></label> <label label={'View FastFetch'}></label>
}></button> }></button>
</box> </box>
); );
@@ -64,7 +68,7 @@ const SystemInfo = () => {
cssClasses={[ 'quick-view-symbol' ]} cssClasses={[ 'quick-view-symbol' ]}
> >
<image <image
iconName={"power-profile-performance-symbolic"} iconName={'power-profile-performance-symbolic'}
marginEnd={1} marginEnd={1}
></image> ></image>
<label <label
@@ -75,23 +79,23 @@ const SystemInfo = () => {
<box <box
cssClasses={[ 'quick-view-symbol' ]} cssClasses={[ 'quick-view-symbol' ]}
> >
<image iconName={"memory"}></image> <image iconName={'memory'}></image>
<label label={sysinfo.ramUtil( util => util )}></label> <label label={sysinfo.ramUtil( util => util )}></label>
</box> </box>
<box <box
cssClasses={[ 'quick-view-symbol' ]} cssClasses={[ 'quick-view-symbol' ]}
> >
<image iconName={"show-gpu-effects-symbolic"}></image> <image iconName={'show-gpu-effects-symbolic'}></image>
<label label={sysinfo.gpuUtil( util => util )}></label> <label label={sysinfo.gpuUtil( util => util )}></label>
</box> </box>
{panel} {panel}
</box> </box>
} }
cssClasses={["bar-button"]} cssClasses={[ 'bar-button' ]}
></button> ></button>
); );
} else { } else {
return <image iconName={"action-unavailable-symbolic"}></image>; return <image iconName={'action-unavailable-symbolic'}></image>;
} }
}; };

View File

@@ -3,3 +3,4 @@ $bg-color: #0A0A0F;
$accent-color: #591641; $accent-color: #591641;
$accent-color-2: #97103A; $accent-color-2: #97103A;
$shadow-color: rgba(0, 0, 2, 0.3); $shadow-color: rgba(0, 0, 2, 0.3);
$monospace-font: Source Code Pro

View File

@@ -26,6 +26,7 @@ alias ff='fastfetch'
alias p='nvimpager -p' alias p='nvimpager -p'
alias latexdocs='zathura ~/projects/latex/docs/docs.pdf &>> /dev/null & disown' alias latexdocs='zathura ~/projects/latex/docs/docs.pdf &>> /dev/null & disown'
alias gccerr='gcc -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -std=c99' alias gccerr='gcc -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -std=c99'
alias linecount='cloc --vcs git .'
# Add scripts in ~/projects/dotfiles/scripts/ to path # Add scripts in ~/projects/dotfiles/scripts/ to path
fish_add_path -P ~/projects/dotfiles/scripts/ fish_add_path -P ~/projects/dotfiles/scripts/

View File

@@ -9,21 +9,18 @@ exec-once = ~/.config/hypr/xdg-portal-hyprland
exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XAUTHORITY DISPLAY exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XAUTHORITY DISPLAY
exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
# exec-once = waybar
exec-once = hypridle exec-once = hypridle
exec-once = nm-applet exec-once = nm-applet
exec-once = nextcloud --background exec-once = nextcloud --background
exec-once = sleep 2 && bash -c "ags run -d ~/projects/dotfiles/config/astal/ --gtk 4 >> /tmp/runner-log 2>&1" exec-once = sleep 2 && bash -c "ags run -d ~/projects/dotfiles/config/astal/ --gtk 4 >> /tmp/runner-log 2>&1"
# exec-once = sleep 2 && bash -c "ags run -d ~/projects/active/dotfiles/config/astal/ --gtk4" exec-once = bash -c "ags run -d ~/projects/dotfiles/config/ags/notifications/ --gtk 3 >> /tmp/notifier-log 2>&1"
exec-once = bash -c "ags run -d ~/projects/dotfiles/config/ags/notifications/ >> /tmp/notifier-log 2>&1"
# exec-once = bash -c "ags run -d ~/projects/active/dotfiles/config/ags/notifications/"
# ── wlhist ────────────────────────────────────────────────────────── # ── wlhist ──────────────────────────────────────────────────────────
exec-once = wl-paste --type text --watch cliphist store # Stores only text data exec-once = wl-paste --type text --watch cliphist store # Stores only text data
exec-once = wl-paste --type image --watch cliphist store # Stores only image data exec-once = wl-paste --type image --watch cliphist store # Stores only image data
exec = hyprctl setcursor oreo_spark_blue_cursors 24
env = QT_QPA_PLATFORM,wayland env = QT_QPA_PLATFORM,wayland
env = QT_QPA_PLATFORM_THEME,qt6ct env = QT_QPA_PLATFORM_THEME,qt6ct
env = OLLAMA_HOST,0.0.0.0 env = OLLAMA_HOST,0.0.0.0

View File

@@ -1,4 +1,4 @@
# Hyprmode # hyprvim
Using Vim Motions and in general vim-style commands is really neat - so why not apply to the Window Manager as well? Using Vim Motions and in general vim-style commands is really neat - so why not apply to the Window Manager as well?
## Mapping ## Mapping

View File

@@ -19,6 +19,11 @@ bind = $mainMod, D, submap, device
submap = device submap = device
# ── Swapescape ──────────────────────────────────────────────────────
bind = , S, exec, fish -c "toggle-swapescape"
bind = , S, submap, reset
# ── FPV goggles binds ─────────────────────────────────────────────── # ── FPV goggles binds ───────────────────────────────────────────────
bind = CTRL, D, exec, hyprctl keyword monitor HDMI-A-1, 1280x720@60, 1920x0, 1, mirror, DP-1 && notify-send 'Set FPV goggles to mirror main screen' --app-name="Hyprctl" bind = CTRL, D, exec, hyprctl keyword monitor HDMI-A-1, 1280x720@60, 1920x0, 1, mirror, DP-1 && notify-send 'Set FPV goggles to mirror main screen' --app-name="Hyprctl"
bind = CTRL, E, exec, hyprctl keyword monitor HDMI-A-1, 1280x720@60, 3840x0, 1 && notify-send 'Set to expand FPV goggles' --app-name="Hyprctl" bind = CTRL, E, exec, hyprctl keyword monitor HDMI-A-1, 1280x720@60, 3840x0, 1 && notify-send 'Set to expand FPV goggles' --app-name="Hyprctl"

View File

@@ -2,9 +2,6 @@
# ╭────────────────────────────────────────────────╮ # ╭────────────────────────────────────────────────╮
# │ App launching │ # │ App launching │
# ╰────────────────────────────────────────────────╯ # ╰────────────────────────────────────────────────╯
bind = $mainMod SHIFT, L, exec, librewolf
bind = $mainMod SHIFT, D, exec, vesktop
bind = $mainMod SHIFT, Z, exec, zathura
bind = $mainMod, Return, exec, kitty bind = $mainMod, Return, exec, kitty
@@ -23,26 +20,58 @@ bind = $mainMod, escape, exec, wlogout
# │ Launch submap │ # │ Launch submap │
# ╰────────────────────────────────────────────────╯ # ╰────────────────────────────────────────────────╯
bind = $mainMod, X, submap, launch bind = $mainMod, X, submap, launch
submap = launch, reset submap = launch
# ── Kitty ─────────────────────────────────────────────────────────── # ── Kitty ───────────────────────────────────────────────────────────
bind = , K, exec, kitty bind = , K, exec, kitty
bind = , K, submap, reset
bind = , Return, exec, kitty bind = , Return, exec, kitty
bind = , Return, submap, reset
# ── Others ────────────────────────────────────────────────────────── # ── Others ──────────────────────────────────────────────────────────
bind = , L, exec, librewolf bind = , L, exec, librewolf
bind = , L, submap, reset
bind = , E, exec, thunar bind = , E, exec, thunar
bind = , E, submap, reset
bind = , D, exec, vesktop bind = , D, exec, vesktop
bind = , D, submap, reset
bind = SHIFT, B, exec, brave
bind = SHIFT, B, submap, reset
bind = , F, exec, filezilla
bind = , F, submap, reset
bind = , V, exec, vscodium bind = , V, exec, vscodium
bind = , V, submap, reset
bind = , T, exec, thunderbird bind = , T, exec, thunderbird
bind = , T, submap, reset
bind = , H, exec, heroic bind = , H, exec, heroic
bind = , H, submap, reset
bind = , Z, exec, zathura bind = , Z, exec, zathura
bind = , Z, submap, reset
bind = , B, exec, /opt/bs-manager/bs-manager
bind = , B, submap, reset
bind = , P, exec, alvr_dashboard
bind = , P, submap, reset
bind = , A, exec, notify-send 'AirPlay server starting...' --app-name="AirPlay Audio" && terminator -T "hidden-terminator" -e "systemctl start avahi-daemon && shairport-sync -a LinuxPlay" bind = , A, exec, notify-send 'AirPlay server starting...' --app-name="AirPlay Audio" && terminator -T "hidden-terminator" -e "systemctl start avahi-daemon && shairport-sync -a LinuxPlay"
bind = , A, submap, reset
bind = SHIFT, A, exec, notify-send 'AirPlay video server starting...' --app-name="AirPlay Video" && terminator -T "hidden-terminator" -e "systemctl start avahi-daemon && sleep 5 && uxplay -n LinuxVideoPlay -nh" bind = SHIFT, A, exec, notify-send 'AirPlay video server starting...' --app-name="AirPlay Video" && terminator -T "hidden-terminator" -e "systemctl start avahi-daemon && sleep 5 && uxplay -n LinuxVideoPlay -nh"
bind = SHIFT, A, submap, reset
bind = , S, exec, notify-send 'Steam is launching...' --app-name="Steam" && steam bind = , S, exec, notify-send 'Steam is launching...' --app-name="Steam" && steam
bind = , O, exec, terminator -e "~/projects/dotfiles/ai.sh" bind = , S, submap, reset
# ── Exit submap ───────────────────────────────────────────────────── # ── Exit submap ─────────────────────────────────────────────────────

View File

@@ -9,10 +9,19 @@ submap = notifications
bind = , T, exec, astal -i notifier toggle bind = , T, exec, astal -i notifier toggle
bind = , T, submap, reset
bind = , D, exec, astal -i notifier clear-newest bind = , D, exec, astal -i notifier clear-newest
bind = , D, submap, reset
bind = , C, exec, astal -i notifier clear bind = , C, exec, astal -i notifier clear
bind = , C, submap, reset
bind = , S, exec, astal -i notifier show bind = , S, exec, astal -i notifier show
bind = , S, submap, reset
bind = , H, exec, astal -i notifier hide bind = , H, exec, astal -i notifier hide
bind = , H, submap, reset
# ── Exit submap ───────────────────────────────────────────────────── # ── Exit submap ─────────────────────────────────────────────────────

View File

@@ -6,11 +6,17 @@ bind = $mainMod, S, submap, screenshotting
submap = screenshotting submap = screenshotting
bind = , Y, exec, grimblast --notify copy area bind = , Y, exec, grimblast --notify copy area
bind = , Y, submap, reset
bind = , C, exec, grimblast --notify copysave area bind = , C, exec, grimblast --notify copysave area
bind = , C, submap, reset
bind = , S, exec, grimblast --notify save area bind = , S, exec, grimblast --notify save area
bind = , S, submap, reset
bind = SHIFT, Y, exec, grimblast --notify copy screen bind = SHIFT, Y, exec, grimblast --notify copy screen
bind = SHIFT, Y, submap, reset
bind = SHIFT, C, exec, grimblast --notify copysave screen bind = SHIFT, C, exec, grimblast --notify copysave screen
bind = SHIFT, C, submap, reset
bind = SHIFT, S, exec, grimblast --notify save screen bind = SHIFT, S, exec, grimblast --notify save screen
bind = SHIFT, S, submap, reset
# ── Exit submap ───────────────────────────────────────────────────── # ── Exit submap ─────────────────────────────────────────────────────

View File

@@ -1,6 +1,7 @@
$mainMod = SUPER $mainMod = SUPER
source=./modal-binds/workspace.conf source=./modal-binds/workspace.conf
source=./modal-binds/launch.conf source=./modal-binds/launch.conf
source=./modal-binds/notifications.conf
source=./modal-binds/window-management.conf source=./modal-binds/window-management.conf
source=./modal-binds/screenshot.conf source=./modal-binds/screenshot.conf
source=./modal-binds/device.conf source=./modal-binds/device.conf

View File

@@ -1,17 +1,3 @@
# ────────────────────────────────────────────────────────────────────
# ╭────────────────────────────────────────────────╮
# │ WORKSPACE RULES │
# ╰────────────────────────────────────────────────╯
# ────────────────────────────────────────────────────────────────────
# Display full sized (without gaps), if only window on screen
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
# windowrule = rounding 0, floating:0, onworkspace:f[1]
$mainMod = SUPER $mainMod = SUPER
# ──────────────────────────────────────────────────────────────────── # ────────────────────────────────────────────────────────────────────
@@ -19,16 +5,20 @@ $mainMod = SUPER
# │ WINDOW RULES │ # │ WINDOW RULES │
# ╰────────────────────────────────────────────────╯ # ╰────────────────────────────────────────────────╯
# ──────────────────────────────────────────────────────────────────── # ────────────────────────────────────────────────────────────────────
windowrule = move 1450 50, title:^(.*)(Power menu)$ windowrule = move 1450 50, title:(.*)Power menu$
windowrule = workspace 2, class:evince windowrule = workspace 2, class:evince
windowrule = workspace 2, title:.*(Okular).* windowrule = workspace 2, title:.*Okular.*
windowrule = workspace 2, class:org.pwmt.zathura windowrule = workspace 2, class:org.pwmt.zathura
windowrule = fullscreen, title:wlogout windowrule = fullscreen, title:wlogout
windowrule = workspace 2, class:librewolf windowrule = workspace 2, class:librewolf
windowrule = workspace 2, title:LibreWolf windowrule = workspace 2, title:LibreWolf
windowrule = workspace 2, title:(.*)(Discord)(.*) windowrule = workspace 2, title:BSManager
windowrule = workspace 1, title:ALVR(.*)
windowrule = workspace 2, title:(.*)Beat Saber(.*)
windowrule = fullscreen, title:(.*)Beat Saber(.*)
windowrule = workspace 2, title:(.*)Discord(.*)
windowrule = workspace 3, title:^(Steam)(.*)$ windowrule = workspace 3, title:^(Steam)(.*)$
windowrule = workspace 1, title:^(.*)(VSCodium)$ windowrule = workspace 1, title:(.*)(VSCodium)$
windowrule = center, title:^(.*)(VSCodium)$ windowrule = center, title:^(.*)(VSCodium)$
windowrule = workspace 3, class:minecraft-launcher windowrule = workspace 3, class:minecraft-launcher
windowrule = tile, class:minecraft-launcher windowrule = tile, class:minecraft-launcher
@@ -46,6 +36,7 @@ windowrule = float, title:^(.*)hidden-terminator*(.*)$
windowrule = float, title:^(.*)termfilechooser*(.*)$ windowrule = float, title:^(.*)termfilechooser*(.*)$
windowrule = size 1400 800, title:^(.*)termfilechooser*(.*)$ windowrule = size 1400 800, title:^(.*)termfilechooser*(.*)$
windowrule = center, title:^(.*)termfilechooser*(.*)$ windowrule = center, title:^(.*)termfilechooser*(.*)$
windowrule = dimaround, title:^(.*)termfilechooser*(.*)$
# ┌ ┐ # ┌ ┐
# │ Set floating windows & position them centered │ # │ Set floating windows & position them centered │
@@ -107,6 +98,7 @@ windowrule = center, class:pavucontrol
windowrule = float, class:file-roller windowrule = float, class:file-roller
windowrule = center, class:file-roller windowrule = center, class:file-roller
# idleinhibit
windowrule = idleinhibit focus, title:^(Rocket League)(.*)$ windowrule = idleinhibit focus, title:^(Rocket League)(.*)$
windowrule = fullscreen, title:^(Steam Big Picture)$ windowrule = fullscreen, title:^(Steam Big Picture)$
windowrule = idleinhibit always, class:steam windowrule = idleinhibit always, class:steam
@@ -116,6 +108,12 @@ windowrule = idleinhibit focus, class:supertuxkart
windowrule = idleinhibit fullscreen, title:^(.*)(Discord)(.*)$ windowrule = idleinhibit fullscreen, title:^(.*)(Discord)(.*)$
windowrule = idleinhibit fullscreen, title:^(.*)(~)(.*)$ windowrule = idleinhibit fullscreen, title:^(.*)(~)(.*)$
# Do not screenshare bitwarden windows
windowrule = noscreenshare, title:(.*)(Bitwarden)(.*)
windowrule = noscreenshare, class:nm-connection-editor
windowrule = noscreenshare, title:(.*)(secret)(.*)
windowrule = noscreenshare, class:(.*)[aA]uthentication(.*)
# ┌ ┐ # ┌ ┐
# │ Layer rules │ # │ Layer rules │

View File

@@ -20,6 +20,7 @@
monitor=DP-1, 1920x1080@144, 0x0, 1, vrr, 2 monitor=DP-1, 1920x1080@144, 0x0, 1, vrr, 2
monitor=DP-2, 1920x1080@75, 1920x0, 1 monitor=DP-2, 1920x1080@75, 1920x0, 1
exec = hyprctl setcursor oreo_spark_blue_cursors 24
source=./hyprland/binds.conf source=./hyprland/binds.conf
source=./hyprland/general.conf source=./hyprland/general.conf

View File

@@ -32,7 +32,7 @@ source=./hyprland/windowrules.conf
exec = hyprctl setcursor oreo_spark_blue_cursors 36 exec = hyprctl setcursor oreo_spark_blue_cursors 36
env = HYPRCURSOR_THEME, Oreo_spark_blue_cursor env = HYPRCURSOR_THEME, Oreo_spark_blue_cursor
env = X_CURSOR_THEME, Oreo_spark_blue_cursor env = X_CURSOR_THEME, Oreo_spark_blue_cursor
env = XCURSOR_SIZE, 24 env = XCURSOR_SIZE, 36
env = ELECTRON_ENABLE_HIGHDPI_SUPPORT, 1 env = ELECTRON_ENABLE_HIGHDPI_SUPPORT, 1
env = XDG_SESSION_TYPE, wayland env = XDG_SESSION_TYPE, wayland
env = QT_QPA_PLATFORM, wayland env = QT_QPA_PLATFORM, wayland

View File

@@ -29,3 +29,8 @@ url_prefixes file ftp ftps gemini git gopher http https irc ircs kitty mailto ne
detect_urls yes detect_urls yes
show_hyperlink_targets yes show_hyperlink_targets yes
include current-theme.conf include current-theme.conf
# ┌ ┐
# │ Mappings │
# └ ┘
map kitty_mod+e

View File

@@ -4,6 +4,12 @@ defaultIndent: " "
# Limit Number of backups # Limit Number of backups
maxNumberOfBackups: 3 maxNumberOfBackups: 3
verbatimEnvironments:
verbatim: 1
lstlisting: 1
minted: 1
code: 1
indentRules: indentRules:
recall: " " recall: " "
remarks: " " remarks: " "

View File

@@ -1,2 +1,3 @@
[flavor] [flavor]
use = "tokyo-night" dark = "tokyo-night"
light = "tokyo-night"

724
eslint.config.mjs Normal file
View File

@@ -0,0 +1,724 @@
import eslint from '@eslint/js';
import globals from 'globals';
import stylistic from '@stylistic/eslint-plugin';
import tseslint from 'typescript-eslint';
import typescript from '@typescript-eslint/eslint-plugin';
import vue from 'eslint-plugin-vue';
const style = {
'plugins': {
'@stylistic': stylistic,
'@stylistic/js': stylistic,
'@stylistic/ts': stylistic,
},
'files': [
'**/*.ts',
'**/*.js',
'**/*.mjs',
'**/*.cjs',
'**/*.tsx',
'**/*.jsx'
],
'rules': {
'sort-imports': [
'warn',
{
'ignoreCase': false,
'ignoreDeclarationSort': false,
'ignoreMemberSort': false,
'memberSyntaxSortOrder': [
'none',
'all',
'multiple',
'single'
],
'allowSeparatedGroups': false
}
],
// Formatting
'@stylistic/array-bracket-newline': [
'error',
{
'multiline': true,
'minItems': 2
}
],
'@stylistic/array-bracket-spacing': [
'error',
'always',
{
'singleValue': true,
'arraysInArrays': true,
'objectsInArrays': true
}
],
'@stylistic/array-element-newline': [
'error',
{
'multiline': true,
'minItems': 2
}
],
'@stylistic/arrow-parens': [
'error',
'as-needed'
],
'@stylistic/arrow-spacing': [
'error',
{
'before': true,
'after': true
}
],
'@stylistic/block-spacing': [
'error',
'always'
],
'@stylistic/brace-style': [
'error',
'1tbs'
],
'@stylistic/comma-spacing': [
'error',
{
'before': false,
'after': true
}
],
'@stylistic/comma-style': [
'error',
'last'
],
'@stylistic/dot-location': [
'error',
'property'
],
'@stylistic/eol-last': [
'error',
'always'
],
'@stylistic/function-call-spacing': [
'error',
'never'
],
'@stylistic/function-paren-newline': [
'error',
{
'minItems': 3
}
],
'@stylistic/function-call-argument-newline': [
'error',
'consistent'
],
'@stylistic/implicit-arrow-linebreak': [
'error',
'beside'
],
'@stylistic/indent': [
'error',
4
],
'@stylistic/key-spacing': [
'error',
{
'beforeColon': false,
'afterColon': true
}
],
'@stylistic/keyword-spacing': [
'error',
{
'before': true,
'after': true
}
],
'@stylistic/lines-between-class-members': [
'error',
'always'
],
'@stylistic/max-len': [
'warn',
{
'code': 120,
'comments': 140,
'ignoreComments': false,
'ignoreUrls': true,
'ignoreStrings': false
}
],
'@stylistic/new-parens': [
'error',
'always'
],
'@stylistic/newline-per-chained-call': [ 'error' ],
'@stylistic/no-extra-parens': [
'error',
'all',
{
'nestedBinaryExpressions': false,
'ternaryOperandBinaryExpressions': false,
'ignoreJSX': 'multi-line',
'nestedConditionalExpressions': false
}
],
'@stylistic/no-extra-semi': 'error',
'@stylistic/no-floating-decimal': 'error',
'@stylistic/no-mixed-operators': 'error',
'@stylistic/no-mixed-spaces-and-tabs': 'error',
'@stylistic/no-multi-spaces': 'error',
'@stylistic/no-multiple-empty-lines': [
'error',
{
'max': 3,
'maxEOF': 2
}
],
'@stylistic/no-tabs': 'error',
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/no-whitespace-before-property': 'error',
'@stylistic/object-curly-newline': [
'error',
{
'multiline': true,
'minProperties': 1
}
],
'@stylistic/object-curly-spacing': [
'error',
'always'
],
'@stylistic/object-property-newline': 'error',
'@stylistic/operator-linebreak': [
'error',
'before'
],
'@stylistic/one-var-declaration-per-line': 'error',
'@stylistic/padded-blocks': [
'error',
{
'blocks': 'never',
'classes': 'always',
'switches': 'never',
}
],
// Padding lines. The most in-depth part of this config
'@stylistic/padding-line-between-statements': [
'error',
// Variables, Constants
{
'blankLine': 'never',
'prev': 'var',
'next': 'var'
},
{
'blankLine': 'never',
'prev': 'let',
'next': 'let'
},
{
'blankLine': 'never',
'prev': 'const',
'next': 'const'
},
{
'blankLine': 'always',
'prev': 'var',
'next': [
'block',
'block-like',
'break',
'cjs-export',
'cjs-import',
'class',
'const',
'continue',
'debugger',
'directive',
'do',
'empty',
'export',
'expression',
'for',
'function',
'if',
'iife',
'import',
'let',
'return',
'switch',
'throw',
'try',
'var',
'with'
]
},
{
'blankLine': 'always',
'prev': 'let',
'next': [
'block',
'block-like',
'break',
'cjs-export',
'cjs-import',
'class',
'const',
'continue',
'debugger',
'directive',
'do',
'empty',
'export',
'expression',
'for',
'function',
'if',
'iife',
'import',
'return',
'switch',
'throw',
'try',
'var',
'while',
'with'
]
},
{
'blankLine': 'always',
'prev': 'const',
'next': [
'block',
'block-like',
'break',
'cjs-export',
'cjs-import',
'class',
'continue',
'debugger',
'directive',
'do',
'empty',
'export',
'expression',
'for',
'function',
'if',
'iife',
'import',
'let',
'return',
'switch',
'throw',
'try',
'var',
'while',
'with'
]
},
// Import
{
'blankLine': 'never',
'prev': 'import',
'next': 'import'
},
{
'blankLine': 'never',
'prev': 'cjs-import',
'next': 'cjs-import'
},
{
'blankLine': 'always',
'prev': [
'block',
'block-like',
'break',
'cjs-export',
'class',
'const',
'continue',
'debugger',
'directive',
'do',
'empty',
'export',
'expression',
'for',
'function',
'if',
'iife',
'let',
'return',
'switch',
'throw',
'try',
'var',
'while',
'with'
],
'next': 'cjs-import'
},
{
'blankLine': 'always',
'prev': 'cjs-import',
'next': [
'block',
'block-like',
'break',
'cjs-export',
'class',
'const',
'continue',
'debugger',
'directive',
'do',
'empty',
'export',
'expression',
'for',
'function',
'if',
'iife',
'let',
'return',
'switch',
'throw',
'try',
'var',
'while',
'with'
]
},
{
'blankLine': 'always',
'prev': [
'block',
'block-like',
'break',
'cjs-export',
'class',
'const',
'continue',
'debugger',
'directive',
'do',
'empty',
'export',
'expression',
'for',
'function',
'if',
'iife',
'let',
'return',
'switch',
'throw',
'try',
'var',
'while',
'with'
],
'next': 'import'
},
{
'blankLine': 'always',
'prev': 'import',
'next': [
'block',
'block-like',
'break',
'cjs-export',
'class',
'const',
'continue',
'debugger',
'directive',
'do',
'empty',
'export',
'expression',
'for',
'function',
'if',
'iife',
'let',
'return',
'switch',
'throw',
'try',
'var',
'while',
'with'
]
},
// If
{
'blankLine': 'always',
'prev': '*',
'next': 'if'
},
{
'blankLine': 'always',
'prev': 'if',
'next': '*'
},
// For
{
'blankLine': 'always',
'prev': '*',
'next': 'for'
},
{
'blankLine': 'always',
'prev': 'for',
'next': '*'
},
// While
{
'blankLine': 'always',
'prev': '*',
'next': 'while'
},
{
'blankLine': 'always',
'prev': 'while',
'next': '*'
},
// Functions
{
'blankLine': 'always',
'prev': '*',
'next': 'function'
},
{
'blankLine': 'always',
'prev': 'function',
'next': '*'
},
// Block Statements
{
'blankLine': 'always',
'prev': '*',
'next': 'block-like'
},
{
'blankLine': 'always',
'prev': 'block-like',
'next': '*'
},
// Switch
{
'blankLine': 'always',
'prev': '*',
'next': 'switch'
},
{
'blankLine': 'always',
'prev': 'switch',
'next': '*'
},
// Try-Catch
{
'blankLine': 'always',
'prev': '*',
'next': 'try'
},
{
'blankLine': 'always',
'prev': 'try',
'next': '*'
},
// Throw
{
'blankLine': 'always',
'prev': '*',
'next': 'throw'
},
{
'blankLine': 'always',
'prev': 'throw',
'next': '*'
},
// Return
{
'blankLine': 'never',
'prev': 'return',
'next': '*'
},
{
'blankLine': 'always',
'prev': '*',
'next': 'return'
},
// Export
{
'blankLine': 'always',
'prev': '*',
'next': 'export'
},
{
'blankLine': 'always',
'prev': 'export',
'next': '*'
},
{
'blankLine': 'always',
'prev': '*',
'next': 'cjs-export'
},
{
'blankLine': 'always',
'prev': 'cjs-export',
'next': '*'
},
// Classes
{
'blankLine': 'always',
'prev': '*',
'next': 'class'
},
{
'blankLine': 'always',
'prev': 'class',
'next': '*'
},
],
'@stylistic/quote-props': [
'error',
'always'
],
'@stylistic/quotes': [
'error',
'single'
],
'@stylistic/rest-spread-spacing': [
'error',
'never'
],
'@stylistic/semi': [
'error',
'always'
],
'@stylistic/semi-spacing': [
'error',
{
'before': false,
'after': true
}
],
'@stylistic/semi-style': [
'error',
'last'
],
'@stylistic/space-before-blocks': [
'error',
'always'
],
'@stylistic/space-before-function-paren': [
'error',
'always'
],
'@stylistic/space-in-parens': [
'error',
'always'
],
'@stylistic/space-infix-ops': [
'error',
{
'int32Hint': false
}
],
'@stylistic/space-unary-ops': 'error',
'@stylistic/spaced-comment': [
'error',
'always'
],
'@stylistic/template-curly-spacing': [
'error',
'always'
],
'@stylistic/switch-colon-spacing': 'error',
'@stylistic/wrap-iife': [
'error',
'inside'
],
'@stylistic/wrap-regex': 'error',
'@stylistic/ts/type-annotation-spacing': 'error',
}
};
/** @type {import('eslint').Linter.Config} */
export default tseslint.config(
// Base JavaScript rules
eslint.configs.recommended,
tseslint.configs.recommended,
style,
// Vue support (including TS and JSX inside SFCs)
{
'files': [ '**/*.vue' ],
'languageOptions': {
'sourceType': 'module',
'ecmaVersion': 'latest',
'globals': globals.browser,
'parserOptions': {
'parser': tseslint.parser,
},
},
'plugins': {
'vue': vue,
'@stylistic': stylistic,
'@stylistic/js': stylistic,
'@stylistic/ts': stylistic,
'@typescript-eslint': typescript,
},
'extends': [
eslint.configs.recommended,
...vue.configs['flat/recommended']
],
'rules': {
...typescript.configs.recommended.rules,
...style.rules,
// Vue specific rules
'@stylistic/indent': 'off',
'vue/html-indent': [
'error',
4
],
'vue/html-comment-indent': [
'error',
4
],
'vue/script-indent': [
'error',
4,
{
'baseIndent': 1,
'switchCase': 1
}
],
'vue/html-self-closing': [
'error',
{
'html': {
'void': 'never',
'normal': 'never',
'component': 'always'
},
'svg': 'always',
'math': 'never'
}
],
'vue/max-attributes-per-line': [
'error',
{
'singleline': 3,
'multiline': 1,
}
],
},
},
);

View File

@@ -84,7 +84,7 @@ echo "
==> Installing other utilities... ==> Installing other utilities...
" "
sleep 2 sleep 2
yay -S --noconfirm --noremovemake okular vlc nextcloud-client p7zip zip unzip noto-fonts pavucontrol light fastfetch bashtop hugo uxplay upower gdu dig nwg-look serpl fcitx5 qalculate-gtk openconnect light blueman xdg-desktop-portal-termfilechooser-hunkyburrito-git lazygit tldr yay -S --noconfirm --noremovemake okular vlc nextcloud-client p7zip zip unzip noto-fonts pavucontrol light fastfetch bashtop hugo uxplay upower gdu dig nwg-look serpl fcitx5 qalculate-gtk openconnect light blueman xdg-desktop-portal-termfilechooser-hunkyburrito-git lazygit tldr cloc usbutils bluez-utils bluez
# LaTeX # LaTeX
echo " echo "
@@ -128,6 +128,7 @@ cd nvim
xdg-settings set default-web-browser librewolf.desktop xdg-settings set default-web-browser librewolf.desktop
xdg-mime default org.pwmt.zathura.desktop application/pdf xdg-mime default org.pwmt.zathura.desktop application/pdf
sudo systemctl disable rustdesk sudo systemctl disable rustdesk
sudo systemctl enable --now systemd-timesyncd
hyprpm update hyprpm update
git-credential-manager configure git-credential-manager configure

8
package.json Normal file
View File

@@ -0,0 +1,8 @@
{
"devDependencies": {
"@eslint/js": "^9.37.0",
"@stylistic/eslint-plugin": "^5.4.0",
"eslint-plugin-vue": "^10.5.1",
"typescript-eslint": "^8.46.1"
}
}

View File

@@ -3,3 +3,4 @@ $bg-color: {{ colour-background-hex }};
$accent-color: {{ colour-accent-hex }}; $accent-color: {{ colour-accent-hex }};
$accent-color-2: {{ colour-accent-2-hex }}; $accent-color-2: {{ colour-accent-2-hex }};
$shadow-color: {{ colour-shadow-rgba-03 }}; $shadow-color: {{ colour-shadow-rgba-03 }};
$monospace-font: {{ font-mono }}

View File

@@ -1,2 +1,3 @@
[flavor] [flavor]
use = "{{ yazi-theme }}" dark = "{{ yazi-theme }}"
light = "{{ yazi-theme }}"

14
scripts/convert-to-mp4 Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
extension="out"
read -p "Pick extension to convert from: " extension
outfolder="out"
outext="mp4"
read -p "Output folder: " outfolder
read -p "Output extension: " outext
mkdir $outfolder
for file in *.$extension; do
ffmpeg -i "$file" "$outfolder/${file%.$extension}.$outext"
echo "\n\n==> Conversion complete\n\n"
done

View File

@@ -2,11 +2,18 @@
connect() { connect() {
read -sp $'Please enter your Encryption Password: ' encpass read -sp $'Please enter your Encryption Password: ' encpass
echo " ==> Connecting" echo "
==> Connecting"
TOKEN=$(cat ~/.local/share/ethz-vpn-connect/ethzvpntoken.secret | openssl enc -aes-256-cbc -pbkdf2 -d -a -k $encpass) TOKEN=$(cat ~/.local/share/ethz-vpn-connect/ethzvpntoken.secret | openssl enc -aes-256-cbc -pbkdf2 -d -a -k $encpass)
PASSWORD=$(cat ~/.local/share/ethz-vpn-connect/ethzvpnpass.secret | openssl enc -aes-256-cbc -pbkdf2 -d -a -k $encpass) PASSWORD=$(cat ~/.local/share/ethz-vpn-connect/ethzvpnpass.secret | openssl enc -aes-256-cbc -pbkdf2 -d -a -k $encpass)
USERNAME=$(cat ~/.local/share/ethz-vpn-connect/ethzvpnusername.txt) USERNAME=$(cat ~/.local/share/ethz-vpn-connect/ethzvpnusername.txt)
echo $PASSWORD | sudo openconnect -b -u $USERNAME@student-net.ethz.ch -g student-net --useragent=AnyConnect --passwd-on-stdin --token-mode=totp --token-secret=sha1:base32:$TOKEN sslvpn.ethz.ch echo $PASSWORD | sudo openconnect -b -u $USERNAME@student-net.ethz.ch -g student-net --useragent=AnyConnect --no-external-auth --passwd-on-stdin --token-mode=totp --token-secret=sha1:base32:$TOKEN sslvpn.ethz.ch
if [ $? -ne 0 ]; then
echo ' ==> Failed to connect <=='
else
echo ' ==> Connected <==
'
fi
encpass="" encpass=""
PASSWORD="" PASSWORD=""
TOKEN="" TOKEN=""
@@ -19,10 +26,16 @@ disconnect() {
setup() { setup() {
echo 'You are about to overwrite your secrets. Press ctrl + C to cancel.' echo 'You are about to overwrite your secrets. Press ctrl + C to cancel.'
read -p $'Please enter your ETHZ-Username: ' USERNAME read -p 'Please enter your ETHZ-Username: ' USERNAME
read -sp $'Please choose and enter your Encryption Password (will be required when launching): ' encpass read -sp 'Please choose and enter your Encryption Password (will be required when launching): ' encpass
read -sp $'Please enter your ETHZ WLAN (= Radius) Password: ' PASSWORD echo ""
read -sp $'Please enter your ETHZ OTP Secret: ' TOKEN read -sp 'Please enter your ETHZ WLAN (= Radius) Password: ' PASSWORD
echo ""
read -sp 'Please enter your ETHZ OTP Secret: ' TOKEN
echo ""
if [[ -d ~/.local/share/ethz-vpn-connect ]]; then
rm -rf ~/.local/share/ethz-vpn-connect
fi
mkdir ~/.local/share/ethz-vpn-connect mkdir ~/.local/share/ethz-vpn-connect
echo $PASSWORD | openssl enc -aes-256-cbc -pbkdf2 -a -k $encpass >~/.local/share/ethz-vpn-connect/ethzvpnpass.secret echo $PASSWORD | openssl enc -aes-256-cbc -pbkdf2 -a -k $encpass >~/.local/share/ethz-vpn-connect/ethzvpnpass.secret
echo $TOKEN | openssl enc -aes-256-cbc -pbkdf2 -a -k $encpass >~/.local/share/ethz-vpn-connect/ethzvpntoken.secret echo $TOKEN | openssl enc -aes-256-cbc -pbkdf2 -a -k $encpass >~/.local/share/ethz-vpn-connect/ethzvpntoken.secret
@@ -30,7 +43,11 @@ setup() {
encpass="" encpass=""
PASSWORD="" PASSWORD=""
TOKEN="" TOKEN=""
echo $' ==> Secrets set <==\n' if [ $? -ne 0 ]; then
echo ' ==> Failed to set secrets <=='
else
echo ' ==> Secrets set <==\n'
fi
} }
case "$1" in case "$1" in

3
scripts/pdf-wordcount Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
echo "Word count is $(pdftotext $1 - | tr -d '.' | wc -w)"

View File

@@ -10,4 +10,4 @@ killall ags
sleep 4 sleep 4
ags run -d ~/projects/dotfiles/config/astal --gtk 4 & disown ags run -d ~/projects/dotfiles/config/astal --gtk 4 & disown
sleep 2 sleep 2
ags run -d ~/projects/dotfiles/config/ags/notifications & disown ags run -d ~/projects/dotfiles/config/ags/notifications --gtk 3 & disown

10
scripts/toggle-swapescape Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
enabled=$(hyprctl getoption input:kb_options | grep "caps:swapescape")
if [[ $enabled == "" ]]; then
hyprctl keyword input:kb_options "caps:swapescape"
notify-send "Swapescape enabled"
else
hyprctl keyword input:kb_options ""
notify-send "Swapescape disabled"
fi

5
scripts/update-nvim Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
cd ~/projects/nvim/
git pull
./nvim-install.sh

33
setup
View File

@@ -28,7 +28,6 @@ trap 'echo -e "\nCaught Ctrl+C, exiting..."; exit 130' SIGINT
# Read platform to install on (only if no platform file present in ~/.config/) # Read platform to install on (only if no platform file present in ~/.config/)
platform="" platform=""
if [[ -f ~/.config/platform ]]; then if [[ -f ~/.config/platform ]]; then
echo "Config type already selected, skipping"
platform=$(cat ~/.config/platform) platform=$(cat ~/.config/platform)
else else
read -p "Choose the configs to install, Laptop or Desktop (l/D): " platform read -p "Choose the configs to install, Laptop or Desktop (l/D): " platform
@@ -55,19 +54,18 @@ else
fi fi
# ──────────────────────────────────────────────────────────────────── # ────────────────────────────────────────────────────────────────────
# Hyprmode config # hyprvim config
hyprmode="" hyprvim=""
if [[ -f ~/.config/hyprmode ]]; then if [[ -f ~/.config/hyprvim ]]; then
echo "Hyprmode config already specified, skipping" hyprvim=$(cat ~/.config/hyprvim)
platform=$(cat ~/.config/hyprmode)
else else
read -p "Would you like to use Hyprmode? (Y/n) " hyprmode read -p "Would you like to use hyprvim? (Y/n) " hyprvim
fi fi
hyprmode=$(echo "$hyprmode" | tr '[:upper:]' '[:lower:]') hyprvim=$(echo "$hyprvim" | tr '[:upper:]' '[:lower:]')
if [[ "$hyprmode" == "" ]]; then if [[ "$hyprvim" == "" ]]; then
hyprmode="y" hyprvim="y"
fi fi
echo "$hyprmode" >~/.config/hyprmode echo "$hyprvim" >~/.config/hyprvim
# ──────────────────────────────────────────────────────────────────── # ────────────────────────────────────────────────────────────────────
echo "=> Moving configs to correct destinations" echo "=> Moving configs to correct destinations"
@@ -81,19 +79,19 @@ cp -r ./config/xdg-desktop-portal/ ~/.config/
# Depending on platform, remove one or the other config and rename remaining one # Depending on platform, remove one or the other config and rename remaining one
if [[ "$platform" == "d" ]]; then if [[ "$platform" == "d" ]]; then
echo "Running on desktop" echo "==> Running on desktop"
cp -f ~/.config/hypr/hyprland_desktop.conf ~/.config/hypr/hyprland.conf cp -f ~/.config/hypr/hyprland_desktop.conf ~/.config/hypr/hyprland.conf
else else
echo "Running on laptop" echo "==> Running on laptop"
cp -f ~/.config/hypr/hyprland_laptop.conf ~/.config/hypr/hyprland.conf cp -f ~/.config/hypr/hyprland_laptop.conf ~/.config/hypr/hyprland.conf
fi fi
# Enable or disable "Hyprmode" (using hyprland with vim-inspired modes) # Enable or disable "hyprvim" (using hyprland with vim-inspired modes)
if [[ "$hyprmode" == "y" ]]; then if [[ "$hyprvim" == "y" ]]; then
echo "Enabling hyprmode" echo "==> Enabling hyprvim"
mv -f ~/.config/hypr/hyprland/mode-binds.conf ~/.config/hypr/hyprland/binds.conf mv -f ~/.config/hypr/hyprland/mode-binds.conf ~/.config/hypr/hyprland/binds.conf
else else
echo "Disabling hyprmode" echo "==> Disabling hyprvim"
rm -rf ~/.config/hypr/hyprland/modal-binds rm -rf ~/.config/hypr/hyprland/modal-binds
rm ~/.config/hypr/hyprland/mode-binds.conf rm ~/.config/hypr/hyprland/mode-binds.conf
fi fi
@@ -114,6 +112,7 @@ cp ./config/lint/indentconfig.yaml ~/.indentconfig.yaml
echo " echo "
=> Installing yazi plugins => Installing yazi plugins
" "
rm -rf ~/.config/yazi/plugins/*
ya pkg upgrade ya pkg upgrade
if [[ "$restart" == "y" ]]; then if [[ "$restart" == "y" ]]; then