├── .gitignore
├── LICENSE
├── PKGBUILD
├── README.md
├── screenshots
├── sleex1.png
├── sleex2.png
├── sleex3.png
└── sleex4.png
├── sleex_presentation.mp4
└── src
├── bin
├── fuzzel-emoji
├── rubyshot
└── sleex
└── share
├── sleex
├── .idea
│ ├── .gitignore
│ ├── modules.xml
│ └── sleex.iml
├── assets
│ ├── icons
│ │ ├── ai-openai-symbolic.svg
│ │ ├── ai-oxygen-symbolic.svg
│ │ ├── ai-zukijourney.png
│ │ ├── arch-symbolic.svg
│ │ ├── axos-symbolic.svg
│ │ ├── cachyos-symbolic.svg
│ │ ├── cloudflare-dns-symbolic.svg
│ │ ├── crosshair-symbolic.svg
│ │ ├── debian-symbolic.svg
│ │ ├── endeavouros-symbolic.svg
│ │ ├── fedora-symbolic.svg
│ │ ├── flatpak-symbolic.svg
│ │ ├── github-symbolic.svg
│ │ ├── google-gemini-symbolic.svg
│ │ ├── linux-symbolic.svg
│ │ ├── mona-loading.gif
│ │ ├── nixos-symbolic.svg
│ │ ├── ollama-symbolic.svg
│ │ ├── openai-symbolic.svg
│ │ ├── openrouter-symbolic.svg
│ │ ├── ubuntu-symbolic.svg
│ │ └── weather
│ │ │ ├── weather-clear-night.svg
│ │ │ ├── weather-clear-sky.svg
│ │ │ ├── weather-clouds-night.svg
│ │ │ ├── weather-clouds.svg
│ │ │ ├── weather-few-clouds-night.svg
│ │ │ ├── weather-few-clouds.svg
│ │ │ ├── weather-fog.svg
│ │ │ ├── weather-freezing-rain.svg
│ │ │ ├── weather-overcast-symbolic.svg
│ │ │ ├── weather-overcast.svg
│ │ │ ├── weather-showers-scattered.svg
│ │ │ ├── weather-showers.svg
│ │ │ ├── weather-snow-rain.svg
│ │ │ ├── weather-snow-scattered.svg
│ │ │ ├── weather-snow.svg
│ │ │ ├── weather-storm.svg
│ │ │ └── weather-windy.svg
│ ├── no_music.png
│ └── themes
│ │ ├── sourceviewtheme-dark-monokai-license.txt
│ │ ├── sourceviewtheme-light.xml
│ │ └── sourceviewtheme.xml
├── config.js
├── config_overviewOnly.js
├── init.js
├── modules
│ ├── .commondata
│ │ ├── hyprlanddata.js
│ │ ├── quotes.js
│ │ └── weather.js
│ ├── .commonwidgets
│ │ ├── cairo_circularprogress.js
│ │ ├── cairo_navigationindicator.js
│ │ ├── cairo_roundedcorner.js
│ │ ├── cairo_slider.js
│ │ ├── clickcloseregion.js
│ │ ├── configwidgets.js
│ │ ├── materialicon.js
│ │ ├── notification.js
│ │ ├── statusicons.js
│ │ ├── statusicons_languages.js
│ │ ├── tabcontainer.js
│ │ └── vertical_tabcontainer.js
│ ├── .configuration
│ │ ├── default_config.json
│ │ └── user_options.js
│ ├── .miscutils
│ │ ├── files.js
│ │ ├── icons.js
│ │ ├── mathfuncs.js
│ │ ├── md2pango.js
│ │ └── system.js
│ ├── .widgethacks
│ │ ├── advancedrevealers.js
│ │ └── popupwindow.js
│ ├── .widgetutils
│ │ ├── clickthrough.js
│ │ ├── cursorhover.js
│ │ └── keybind.js
│ ├── bar
│ │ ├── focus
│ │ │ ├── workspaces_hyprland.js
│ │ │ └── workspaces_sway.js
│ │ ├── main.js
│ │ └── normal
│ │ │ ├── monitor.js
│ │ │ ├── music.js
│ │ │ ├── spaceleft.js
│ │ │ ├── spaceright.js
│ │ │ ├── system.js
│ │ │ ├── tray.js
│ │ │ ├── weather.js
│ │ │ ├── workspaces_box.js
│ │ │ ├── workspaces_hyprland.js
│ │ │ └── workspaces_sway.js
│ ├── cheatsheet
│ │ ├── data_keybinds.js
│ │ ├── data_periodictable.js
│ │ ├── globalinfo.js
│ │ ├── keybinds.js
│ │ ├── main.js
│ │ └── periodictable.js
│ ├── dashboard
│ │ ├── calendar.js
│ │ ├── calendar_layout.js
│ │ ├── centermodules
│ │ │ ├── audiocontrols.js
│ │ │ ├── bluetooth.js
│ │ │ ├── configure.js
│ │ │ ├── notificationlist.js
│ │ │ └── wifinetworks.js
│ │ ├── dashboard.js
│ │ ├── dashboard_bak.js
│ │ ├── main.js
│ │ ├── music.js
│ │ ├── name.js
│ │ ├── quicktoggles.js
│ │ ├── quote.js
│ │ ├── tabs
│ │ │ ├── apiwidgets.js
│ │ │ ├── home.js
│ │ │ ├── settings.js
│ │ │ ├── todo.js
│ │ │ └── updates.js
│ │ ├── todolist.js
│ │ ├── weather.js
│ │ └── widgets
│ │ │ ├── apis
│ │ │ ├── ai_chatmessage.js
│ │ │ ├── chatgpt.js
│ │ │ └── gemini.js
│ │ │ ├── calendar.js
│ │ │ ├── calendar_layout.js
│ │ │ ├── clock.js
│ │ │ ├── github.js
│ │ │ ├── music.js
│ │ │ ├── name.js
│ │ │ ├── quicktoggles.js
│ │ │ ├── quote.js
│ │ │ ├── timer.js
│ │ │ ├── todo_tag.js
│ │ │ ├── todolist.js
│ │ │ ├── user_widget.js
│ │ │ └── weather.js
│ ├── desktopbackground
│ │ ├── data_quicklaunches.js
│ │ ├── main.js
│ │ ├── system.js
│ │ ├── timeandlaunches.js
│ │ └── wallpaper.js
│ ├── indicators
│ │ ├── colorscheme.js
│ │ ├── indicatorvalueBrightness.js
│ │ ├── indicatorvalues.js
│ │ ├── indicatorvaluesAudio.js
│ │ ├── main.js
│ │ ├── musiccontrols.js
│ │ └── notificationpopups.js
│ ├── lockscreen
│ │ ├── auth.py
│ │ └── main.js
│ ├── overview
│ │ ├── actions.js
│ │ ├── main.js
│ │ ├── miscfunctions.js
│ │ ├── overview_hyprland.js
│ │ ├── searchbuttons.js
│ │ ├── searchitem.js
│ │ └── windowcontent.js
│ ├── session
│ │ ├── main.js
│ │ └── sessionscreen.js
│ ├── sideleft
│ │ ├── apis
│ │ │ ├── ai_chatmessage.js
│ │ │ ├── chatgpt.js
│ │ │ └── gemini.js
│ │ ├── apiwidgets.js
│ │ ├── main.js
│ │ ├── sideleft.js
│ │ └── updates.js
│ └── wallselect
│ │ └── main.js
├── scripts
│ ├── README.md
│ ├── color_generation
│ │ ├── applycolor.sh
│ │ ├── colorgen.sh
│ │ ├── generate_colors_material.py
│ │ ├── pywal_to_material.scss
│ │ ├── randomwall.sh
│ │ ├── schemes
│ │ │ ├── __pycache__
│ │ │ │ └── scheme_morevibrant.cpython-313.pyc
│ │ │ └── scheme_morevibrant.py
│ │ ├── specials
│ │ │ ├── _material_badapple-l.scss
│ │ │ └── _material_badapple.scss
│ │ ├── switchcolor.sh
│ │ └── switchwall.sh
│ ├── dnd.sh
│ ├── firstRun.sh
│ ├── generate_thumbnails.sh
│ ├── grimblast.sh
│ ├── hyprland
│ │ ├── get_keybinds.py
│ │ └── workspace_action.sh
│ ├── lockscreen
│ │ ├── battery-status
│ │ └── song-status
│ ├── network_scripts
│ │ └── network_bandwidth.py
│ ├── quickscripts
│ │ └── nixos-trim-generations.sh
│ ├── record-script.sh
│ ├── sway
│ │ └── swayToRelativeWs.sh
│ ├── templates
│ │ ├── fuzzel
│ │ │ └── fuzzel.ini
│ │ ├── gradience
│ │ │ └── preset.json
│ │ ├── gtk
│ │ │ └── gtk-colors.css
│ │ └── terminal
│ │ │ ├── scheme-base.json
│ │ │ ├── scheme-monochrome.json
│ │ │ └── sequences.txt
│ └── wayland-idle-inhibitor.py
├── scss
│ ├── _bar.scss
│ ├── _cheatsheet.scss
│ ├── _colors.scss
│ ├── _common.scss
│ ├── _desktopbackground.scss
│ ├── _dock.scss
│ ├── _lib_classes.scss
│ ├── _lib_mixins.scss
│ ├── _lockscreen.scss
│ ├── _music.scss
│ ├── _notifications.scss
│ ├── _osd.scss
│ ├── _osk.scss
│ ├── _overview.scss
│ ├── _session.scss
│ ├── _sidebars.scss
│ ├── _wal.scss
│ ├── _wallpaper.scss
│ ├── fallback
│ │ └── _material.scss
│ └── main.scss
├── services
│ ├── brightness.js
│ ├── darkmode.js
│ ├── gemini.js
│ ├── globalinfo.js
│ ├── gpt.js
│ ├── indicator.js
│ ├── lockscreen.js
│ ├── messages.js
│ ├── sway.js
│ ├── timers.js
│ ├── todo.js
│ ├── wallpaper.js
│ └── weather.js
├── user_options.js
├── variables.js
└── wallpapers
│ ├── aesthetic_deer.jpg
│ ├── apple-light.jpg
│ ├── building_cyber.jpg
│ ├── city-rain.png
│ ├── city-skyline.jpg
│ ├── city.gif
│ ├── deer_and_sunset.jpg
│ ├── earth-from-moon.jpg
│ ├── escape_velocity.jpg
│ ├── evening-landscape.jpg
│ ├── explorer_green_day.jpg
│ ├── explorer_orange_sunset.jpg
│ ├── highlands-grid.jpg
│ ├── landscape-abstract-neon.jpg
│ ├── lofoten-sundown.jpg
│ ├── lofoten2.jpg
│ ├── loupe-mono-dark-preview.png
│ ├── midnight-reflections-moonlit-sea.jpg
│ ├── moonlight.jpg
│ ├── mountain-lake.jpg
│ ├── mountain-sunrise.jpg
│ ├── mountain-winter1.jpg
│ ├── mountain-winter3.jpg
│ ├── mountain_view.jpg
│ ├── nordwall3.jpg
│ ├── oldhouse.gif
│ └── sakura.png
└── wayland-sessions
└── sleex.desktop
/.gitignore:
--------------------------------------------------------------------------------
1 | *.tar.zst
2 | pkg/
--------------------------------------------------------------------------------
/PKGBUILD:
--------------------------------------------------------------------------------
1 | pkgname="sleex"
2 | pkgver="0.28"
3 | pkgrel="1"
4 | pkgdesc="Third desktop environment for AxOS"
5 | arch=("x86_64")
6 | depends=(
7 | "sleex-ags"
8 | "sleex-audio"
9 | "sleex-backlight"
10 | "sleex-basic"
11 | "sleex-bibata-modern-classic-bin"
12 | "sleex-fonts-themes"
13 | "sleex-gnome"
14 | "sleex-gtk"
15 | "sleex-microtex-git"
16 | "sleex-oneui4-icons-git"
17 | "sleex-portal"
18 | "sleex-pymyc-aur"
19 | "sleex-python"
20 | "sleex-screencapture"
21 | "sleex-widgets"
22 | "axskel-hypr"
23 | "axctl"
24 | "sleex-control-center"
25 | "hyprland"
26 | )
27 | optdepends=(
28 | "sleex-optional: Optional packages"
29 | )
30 |
31 |
32 | package() {
33 | mkdir -p "$pkgdir/usr/"
34 | cp -r "$srcdir/bin" "$pkgdir/usr/"
35 | cp -r "$srcdir/share" "$pkgdir/usr/"
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sleex
2 |
3 | Sleex is the third desktop environement of AxOS. It is based on Hyprland with AGS V1.
4 |
5 | ## Features
6 | - Fast and lightweight
7 | - AI chat integration with external providers (Gemini, OpenAI...)
8 | - Smooth animations
9 | - Tiling window management for seamless multitasking
10 | - Adaptative color scheme based on the wallpaper
11 |
12 | ## Screenshots
13 |
14 | | Description | Image |
15 | |--------------------------|-----------------------------------------|
16 | | Video showcase |  |
17 | | Main desktop, adaptative colors according to wallpaper |  |
18 | | Dashboard with numerous widgets |  |
19 | | Wallpaper selector |  |
20 | | Left panel with LLM APIs and Sleex update center |  |
21 | | Workspace overviews with search bar |  |
22 |
23 | ## Installation
24 |
25 | > [!IMPORTANT]
26 | > If you are not using AxOS, you can't install Sleex.
27 |
28 | ```bash
29 | epsi install sleex
30 | ```
31 |
32 | ## License
33 | Sleex is licensed under the GNU General Public License v3.0
34 |
35 | ## Credits
36 | - [@end-4](https://github.com/end-4) for the base of this rice.
37 |
--------------------------------------------------------------------------------
/screenshots/sleex1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/screenshots/sleex1.png
--------------------------------------------------------------------------------
/screenshots/sleex2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/screenshots/sleex2.png
--------------------------------------------------------------------------------
/screenshots/sleex3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/screenshots/sleex3.png
--------------------------------------------------------------------------------
/screenshots/sleex4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/screenshots/sleex4.png
--------------------------------------------------------------------------------
/sleex_presentation.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/sleex_presentation.mp4
--------------------------------------------------------------------------------
/src/bin/rubyshot:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')"
4 | WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))' )"
5 | GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp -f '%x %y %w %h')
6 | wayshot -s "$GEOM" --stdout ${#:+"$@"}
--------------------------------------------------------------------------------
/src/bin/sleex:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | export XDG_CURRENT_DESKTOP="Sleex"
3 | export HYPRLAND_INSTANCE_SIGNATURE=""
4 | systemctl --user import-environment XDG_CURRENT_DESKTOP
5 | Hyprland
6 |
--------------------------------------------------------------------------------
/src/share/sleex/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/src/share/sleex/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/share/sleex/.idea/sleex.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/ai-openai-symbolic.svg:
--------------------------------------------------------------------------------
1 | openai-symbolic.svg
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/ai-oxygen-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
55 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/ai-zukijourney.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/assets/icons/ai-zukijourney.png
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/cloudflare-dns-symbolic.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/crosshair-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
66 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/endeavouros-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
97 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/fedora-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
39 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/flatpak-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
53 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/github-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
41 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/google-gemini-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
57 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/mona-loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/assets/icons/mona-loading.gif
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/nixos-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
78 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/openrouter-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
40 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/ubuntu-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
86 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-clear-night.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-clear-sky.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-clouds-night.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-clouds.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-few-clouds-night.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-few-clouds.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-fog.svg:
--------------------------------------------------------------------------------
1 |
2 |
23 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-freezing-rain.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-overcast-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-overcast.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-showers-scattered.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-showers.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-snow-rain.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-snow-scattered.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-snow.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-storm.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/icons/weather/weather-windy.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/share/sleex/assets/no_music.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/assets/no_music.png
--------------------------------------------------------------------------------
/src/share/sleex/config.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // Import
3 | const { GLib } = imports.gi;
4 | import Gdk from 'gi://Gdk';
5 | import App from 'resource:///com/github/Aylur/ags/app.js'
6 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
7 | const { execAsync } = Utils;
8 | // Stuff
9 | import userOptions from './modules/.configuration/user_options.js'; // Not unused, careful
10 | import { firstRunWelcome, startBatteryWarningService } from './services/messages.js';
11 | import { startAutoDarkModeService } from './services/darkmode.js';
12 | import './services/weather.js'; // service that updates weather every 15 minutes
13 | // Widgets
14 | import { Bar } from './modules/bar/main.js';
15 | import Cheatsheet from './modules/cheatsheet/main.js';
16 | import DesktopBackground from './modules/desktopbackground/main.js';
17 | import Indicator from './modules/indicators/main.js';
18 | import Overview from './modules/overview/main.js';
19 | import Session from './modules/session/main.js';
20 | //import SideLeft from './modules/sideleft/main.js';
21 | import Dashboard from './modules/dashboard/main.js';
22 | import { COMPILED_STYLE_DIR } from './init.js';
23 | import Wallselect from './modules/wallselect/main.js';
24 | import indicatorvalues from './modules/indicators/indicatorvalues.js';
25 |
26 | const range = (length, start = 1) => Array.from({ length }, (_, i) => i + start);
27 | function forMonitors(widget) {
28 | const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
29 | return range(n, 0).map(widget).flat(1);
30 | }
31 | function forMonitorsAsync(widget) {
32 | const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
33 | return range(n, 0).forEach((n) => widget(n).catch(print))
34 | }
35 |
36 | const FirstRunScript = () => {
37 | if (!GLib.file_test(`${GLib.get_user_state_dir()}/ags/user/firstrun.txt`, GLib.FileTest.EXISTS)) {
38 | const firstRunScript = `${App.configDir}/scripts/firstRun.sh`;
39 | if (GLib.file_test(firstRunScript, GLib.FileTest.EXISTS)) {
40 | execAsync([firstRunScript]).catch(print);
41 | }
42 | }
43 | }
44 |
45 | // Start stuff
46 | FirstRunScript();
47 | handleStyles(true);
48 | startAutoDarkModeService().catch(print);
49 | firstRunWelcome().catch(print);
50 |
51 | startBatteryWarningService().catch(print);
52 |
53 | const Windows = () => [
54 | forMonitors(DesktopBackground),
55 | Overview(),
56 | forMonitors(Indicator),
57 | forMonitors(Cheatsheet),
58 | //SideLeft(),
59 | Dashboard(),
60 | forMonitors(Session),
61 | Wallselect(),
62 | forMonitors(indicatorvalues),
63 |
64 | ];
65 |
66 | const CLOSE_ANIM_TIME = 210; // Longer than actual anim time to make sure widgets animate fully
67 | const closeWindowDelays = {}; // For animations
68 | for (let i = 0; i < (Gdk.Display.get_default()?.get_n_monitors() || 1); i++) {
69 | }
70 |
71 | App.config({
72 | css: `${COMPILED_STYLE_DIR}/style.css`,
73 | stackTraceOnError: true,
74 | closeWindowDelay: closeWindowDelays,
75 | windows: Windows().flat(1),
76 | });
77 |
78 | // Stuff that don't need to be toggled. And they're async so ugh...
79 | forMonitorsAsync(Bar);
80 | // Bar().catch(print); // Use this to debug the bar. Single monitor only.
--------------------------------------------------------------------------------
/src/share/sleex/config_overviewOnly.js:
--------------------------------------------------------------------------------
1 | // Import
2 | import App from 'resource:///com/github/Aylur/ags/app.js'
3 | // Widgets
4 | import Overview from './modules/overview/main.js';
5 | import { COMPILED_STYLE_DIR } from './init.js';
6 |
7 | handleStyles(true);
8 |
9 | App.config({
10 | css: `${COMPILED_STYLE_DIR}/style.css`,
11 | stackTraceOnError: true,
12 | windows: [
13 | Overview(),
14 | ],
15 | });
16 |
--------------------------------------------------------------------------------
/src/share/sleex/init.js:
--------------------------------------------------------------------------------
1 | import GLib from 'gi://GLib';
2 | import App from 'resource:///com/github/Aylur/ags/app.js'
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
4 | import { darkMode } from './modules/.miscutils/system.js';
5 |
6 | export const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated`
7 |
8 | globalThis['handleStyles'] = (resetMusic) => {
9 | // Reset
10 | Utils.exec(`mkdir -p "${GLib.get_user_state_dir()}/ags/scss"`);
11 | if (resetMusic) {
12 | Utils.exec(`bash -c 'echo "" > ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`); // reset music styles
13 | Utils.exec(`bash -c 'echo "" > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss'`); // reset music styles
14 | }
15 | // Generate overrides
16 | let lightdark = darkMode.value ? "dark" : "light";
17 | Utils.writeFileSync(
18 | `@mixin symbolic-icon {
19 | -gtk-icon-theme: '${userOptions.icons.symbolicIconTheme[lightdark]}';
20 | }
21 | `,
22 | `${GLib.get_user_state_dir()}/ags/scss/_lib_mixins_overrides.scss`)
23 | // Compile and apply
24 | async function applyStyle() {
25 | Utils.exec(`mkdir -p ${COMPILED_STYLE_DIR}`);
26 | Utils.exec(`sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/main.scss" "${COMPILED_STYLE_DIR}/style.css"`);
27 | App.resetCss();
28 | App.applyCss(`${COMPILED_STYLE_DIR}/style.css`);
29 | console.log('[LOG] Styles loaded')
30 | }
31 | applyStyle().catch(print);
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.commondata/hyprlanddata.js:
--------------------------------------------------------------------------------
1 | const { Gdk } = imports.gi;
2 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
3 | const { exec } = Utils;
4 |
5 | export let monitors;
6 |
7 | // Mixes with Gdk monitor size cuz it reports monitor size scaled
8 | async function updateStuff() {
9 | monitors = JSON.parse(exec('hyprctl monitors -j'))
10 | const display = Gdk.Display.get_default();
11 | monitors.forEach((monitor, i) => {
12 | const gdkMonitor = display.get_monitor(i);
13 | monitor.realWidth = monitor.width;
14 | monitor.realHeight = monitor.height;
15 | if (userOptions.monitors.scaleMethod.toLowerCase == "gdk") {
16 | monitor.width = gdkMonitor.get_geometry().width;
17 | monitor.height = gdkMonitor.get_geometry().height;
18 | }
19 | else { // == "division"
20 | monitor.width = Math.ceil(monitor.realWidth / monitor.scale);
21 | monitor.height = Math.ceil(monitor.realHeight / monitor.scale);
22 | }
23 | });
24 | }
25 |
26 | updateStuff().catch(print);
27 |
28 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.commondata/quotes.js:
--------------------------------------------------------------------------------
1 | export const quotes = [
2 | {
3 | quote: 'Nvidia, fuck you',
4 | author: 'Linus Torvalds',
5 | },
6 | {
7 | quote: 'reproducible system? cock and vagina?',
8 | author: 'vaxry',
9 | },
10 | {
11 | quote: "haha pointers hee hee i love pointe-\\\nProcess Vaxry exited with signal SIGSEGV",
12 | author: 'vaxry',
13 | }
14 | ];
15 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.commondata/weather.js:
--------------------------------------------------------------------------------
1 | export const WWO_CODE = {
2 | "113": "Sunny",
3 | "116": "PartlyCloudy",
4 | "119": "Cloudy",
5 | "122": "VeryCloudy",
6 | "143": "Fog",
7 | "176": "LightShowers",
8 | "179": "LightSleetShowers",
9 | "182": "LightSleet",
10 | "185": "LightSleet",
11 | "200": "ThunderyShowers",
12 | "227": "LightSnow",
13 | "230": "HeavySnow",
14 | "248": "Fog",
15 | "260": "Fog",
16 | "263": "LightShowers",
17 | "266": "LightRain",
18 | "281": "LightSleet",
19 | "284": "LightSleet",
20 | "293": "LightRain",
21 | "296": "LightRain",
22 | "299": "HeavyShowers",
23 | "302": "HeavyRain",
24 | "305": "HeavyShowers",
25 | "308": "HeavyRain",
26 | "311": "LightSleet",
27 | "314": "LightSleet",
28 | "317": "LightSleet",
29 | "320": "LightSnow",
30 | "323": "LightSnowShowers",
31 | "326": "LightSnowShowers",
32 | "329": "HeavySnow",
33 | "332": "HeavySnow",
34 | "335": "HeavySnowShowers",
35 | "338": "HeavySnow",
36 | "350": "LightSleet",
37 | "353": "LightShowers",
38 | "356": "HeavyShowers",
39 | "359": "HeavyRain",
40 | "362": "LightSleetShowers",
41 | "365": "LightSleetShowers",
42 | "368": "LightSnowShowers",
43 | "371": "HeavySnowShowers",
44 | "374": "LightSleetShowers",
45 | "377": "LightSleet",
46 | "386": "ThunderyShowers",
47 | "389": "ThunderyHeavyRain",
48 | "392": "ThunderySnowShowers",
49 | "395": "HeavySnowShowers",
50 | }
51 |
52 | export const WEATHER_SYMBOL = {
53 | "Unknown": "air",
54 | "Cloudy": "cloud",
55 | "Fog": "foggy",
56 | "HeavyRain": "rainy",
57 | "HeavyShowers": "rainy",
58 | "HeavySnow": "snowing",
59 | "HeavySnowShowers": "snowing",
60 | "LightRain": "rainy",
61 | "LightShowers": "rainy",
62 | "LightSleet": "rainy",
63 | "LightSleetShowers": "rainy",
64 | "LightSnow": "cloudy_snowing",
65 | "LightSnowShowers": "cloudy_snowing",
66 | "PartlyCloudy": "partly_cloudy_day",
67 | "Sunny": "clear_day",
68 | "ThunderyHeavyRain": "thunderstorm",
69 | "ThunderyShowers": "thunderstorm",
70 | "ThunderySnowShowers": "thunderstorm",
71 | "VeryCloudy": "cloud",
72 | }
73 |
74 | export const NIGHT_WEATHER_SYMBOL = {
75 | "Unknown": "air",
76 | "Cloudy": "cloud",
77 | "Fog": "foggy",
78 | "HeavyRain": "rainy",
79 | "HeavyShowers": "rainy",
80 | "HeavySnow": "snowing",
81 | "HeavySnowShowers": "snowing",
82 | "LightRain": "rainy",
83 | "LightShowers": "rainy",
84 | "LightSleet": "rainy",
85 | "LightSleetShowers": "rainy",
86 | "LightSnow": "cloudy_snowing",
87 | "LightSnowShowers": "cloudy_snowing",
88 | "PartlyCloudy": "partly_cloudy_night",
89 | "Sunny": "clear_night",
90 | "ThunderyHeavyRain": "thunderstorm",
91 | "ThunderyShowers": "thunderstorm",
92 | "ThunderySnowShowers": "thunderstorm",
93 | "VeryCloudy": "cloud",
94 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/.commonwidgets/cairo_roundedcorner.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Gtk } = imports.gi;
3 | const Lang = imports.lang;
4 |
5 | export const RoundedCorner = (place, props) => Widget.DrawingArea({
6 | ...props,
7 | hpack: place.includes('left') ? 'start' : 'end',
8 | vpack: place.includes('top') ? 'start' : 'end',
9 | setup: (widget) => Utils.timeout(1, () => {
10 | const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL);
11 | const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
12 | widget.set_size_request(r, r);
13 | widget.connect('draw', Lang.bind(widget, (widget, cr) => {
14 | const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL);
15 | const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
16 | // const borderColor = widget.get_style_context().get_property('color', Gtk.StateFlags.NORMAL);
17 | // const borderWidth = widget.get_style_context().get_border(Gtk.StateFlags.NORMAL).left; // ur going to write border-width: something anyway
18 | widget.set_size_request(r, r);
19 |
20 | switch (place) {
21 | case 'topright':
22 | cr.arc(r, r, r, Math.PI, 3 * Math.PI / 2);
23 | cr.lineTo(0, 0);
24 | break;
25 |
26 | case 'topleft':
27 | cr.arc(0, r, r, 3 * Math.PI / 2, 2 * Math.PI);
28 | cr.lineTo(r, 0);
29 | break;
30 |
31 | case 'bottomleft':
32 | cr.arc(r, 0, r, Math.PI / 2, Math.PI);
33 | cr.lineTo(0, r);
34 | break;
35 |
36 | case 'bottomright':
37 | cr.arc(0, 0, r, 0, Math.PI / 2);
38 | cr.lineTo(r, r);
39 | break;
40 | }
41 |
42 | cr.closePath();
43 | cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha);
44 | cr.fill();
45 | // cr.setLineWidth(borderWidth);
46 | // cr.setSourceRGBA(borderColor.red, borderColor.green, borderColor.blue, borderColor.alpha);
47 | // cr.stroke();
48 | }));
49 | }),
50 | });
--------------------------------------------------------------------------------
/src/share/sleex/modules/.commonwidgets/cairo_slider.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Gtk } = imports.gi;
3 | const Lang = imports.lang;
4 |
5 | export const AnimatedSlider = ({
6 | className,
7 | value,
8 | ...rest
9 | }) => {
10 | return Widget.DrawingArea({
11 | className: `${className}`,
12 | setup: (self) => {
13 | self.connect('draw', Lang.bind(self, (self, cr) => {
14 | const styleContext = self.get_style_context();
15 | const allocatedWidth = self.get_allocated_width();
16 | const allocatedHeight = self.get_allocated_height();
17 | console.log(allocatedHeight, allocatedWidth)
18 | const minWidth = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
19 | const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
20 | const radius = styleContext.get_property('border-radius', Gtk.StateFlags.NORMAL);
21 | const bg = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
22 | const fg = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
23 | const value = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 100;
24 | self.set_size_request(-1, minHeight);
25 | const width = allocatedHeight;
26 | const height = minHeight;
27 |
28 | cr.arc(radius, radius, radius, -1 * Math.PI, -0.5 * Math.PI); // Top-left
29 | cr.arc(width - radius, radius, radius, -0.5 * Math.PI, 0); // Top-right
30 | cr.arc(width - radius, height - radius, radius, 0, 0.5 * Math.PI); // Bottom-left
31 | cr.arc(radius, height - radius, radius, 0.5 * Math.PI, 1 * Math.PI); // Bottom-right
32 | cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha);
33 | cr.closePath();
34 | cr.fill();
35 |
36 | // const valueWidth = width * value;
37 | // cr.arc(radius, radius, radius, -1 * Math.PI, -0.5 * Math.PI); // Top-left
38 | // cr.arc(valueWidth - radius, radius, radius, -0.5 * Math.PI, 0); // Top-right
39 | // cr.arc(valueWidth - radius, height - radius, radius, 0, 0.5 * Math.PI); // Bottom-left
40 | // cr.arc(radius, height - radius, radius, 0.5 * Math.PI, 1 * Math.PI); // Bottom-right
41 | // cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha);
42 | // cr.closePath();
43 | // cr.fill();
44 |
45 | }));
46 | },
47 | ...rest,
48 | })
49 | }
50 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.commonwidgets/clickcloseregion.js:
--------------------------------------------------------------------------------
1 | import App from 'resource:///com/github/Aylur/ags/app.js';
2 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
3 | import { monitors } from '../.commondata/hyprlanddata.js';
4 | const { Box, EventBox } = Widget;
5 |
6 | export const clickCloseRegion = ({ name, multimonitor = true, monitor = 0, expand = true, fillMonitor = '' }) => {
7 | return EventBox({
8 | child: Box({
9 | expand: expand,
10 | css: `
11 | min-width: ${fillMonitor.includes('h') ? monitors[monitor].width : 0}px;
12 | min-height: ${fillMonitor.includes('v') ? monitors[monitor].height : 0}px;
13 | `,
14 | }),
15 | setup: (self) => self.on('button-press-event', (self, event) => { // Any mouse button
16 | if (multimonitor) closeWindowOnAllMonitors(name);
17 | else App.closeWindow(name);
18 | }),
19 | })
20 | }
21 |
22 | export default clickCloseRegion;
23 |
24 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.commonwidgets/materialicon.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 |
3 | export const MaterialIcon = (icon, size, props = {}) => Widget.Label({
4 | className: `icon-material txt-${size}`,
5 | label: icon,
6 | ...props,
7 | })
8 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.commonwidgets/statusicons_languages.js:
--------------------------------------------------------------------------------
1 | // For keyboard layout in statusicons.js
2 | // This list is not exhaustive. It just includes known/possible languages of users of my dotfiles
3 | // Add your language here if you use multi-lang xkb input. Else, ignore
4 | // Note that something like "French (Canada)" should go before "French"
5 | // and "English (US)" should go before "English"
6 | export const languages = [
7 | {
8 | layout: 'us',
9 | name: 'English (US)',
10 | flag: '🇺🇸'
11 | },
12 | {
13 | layout: 'ru',
14 | name: 'Russian',
15 | flag: '🇷🇺',
16 | },
17 | {
18 | layout: 'pl',
19 | name: 'Polish',
20 | flag: '🇷🇵🇵🇱',
21 | },
22 | {
23 | layout: 'ro',
24 | name: 'Romanian',
25 | flag: '🇷🇴',
26 | },
27 | {
28 | layout: 'ca',
29 | name: 'French (Canada)',
30 | flag: '🇫🇷',
31 | },
32 | {
33 | layout: 'fr',
34 | name: 'French',
35 | flag: '🇫🇷',
36 | },
37 | {
38 | layout: 'tr',
39 | name: 'Turkish',
40 | flag: '🇹🇷',
41 | },
42 | {
43 | layout: 'jp',
44 | name: 'Japanese',
45 | flag: '🇯🇵',
46 | },
47 | {
48 | layout: 'cn',
49 | name: 'Chinese',
50 | flag: '🇨🇳',
51 | },
52 | {
53 | layout: 'vn',
54 | name: 'Vietnamese',
55 | flag: '🇻🇳',
56 | },
57 | {
58 | layout: 'undef',
59 | name: 'Undefined',
60 | flag: '🧐',
61 | },
62 | ]
--------------------------------------------------------------------------------
/src/share/sleex/modules/.configuration/user_options.js:
--------------------------------------------------------------------------------
1 | const { GLib } = imports.gi;
2 | import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
3 | import userOverrides from "../../user_options.js";
4 |
5 | // Default options.
6 | const defaultConfigPath = `/usr/share/sleex/modules/.configuration/default_config.json`;
7 | let configOptions = {};
8 |
9 |
10 | try {
11 | const defaultConfig = Utils.readFile(defaultConfigPath);
12 | configOptions = JSON.parse(defaultConfig);
13 | } catch (e) {
14 | console.error('Error loading user_options.default.json:', e);
15 | }
16 |
17 | // Override defaults with user's options
18 | let optionsOkay = true;
19 | function overrideConfigRecursive(
20 | userOverrides,
21 | configOptions = {},
22 | check = true
23 | ) {
24 | for (const [key, value] of Object.entries(userOverrides)) {
25 | if (configOptions[key] === undefined && check) {
26 | optionsOkay = false;
27 | } else if (typeof value === "object" && !(value instanceof Array)) {
28 | if (
29 | key === "substitutions" ||
30 | key === "regexSubstitutions" ||
31 | key === "extraGptModels"
32 | ) {
33 | overrideConfigRecursive(value, configOptions[key], false);
34 | } else overrideConfigRecursive(value, configOptions[key]);
35 | } else {
36 | configOptions[key] = value;
37 | }
38 | }
39 | }
40 | overrideConfigRecursive(userOverrides, configOptions);
41 | if (!optionsOkay)
42 | Utils.timeout(2000, () =>
43 | Utils.execAsync([
44 | "notify-send",
45 | "Update your user options",
46 | "One or more config options don't exist",
47 | "-a",
48 | "ags",
49 | ]).catch(print)
50 | );
51 |
52 | globalThis["userOptions"] = configOptions;
53 | export default configOptions;
54 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.miscutils/files.js:
--------------------------------------------------------------------------------
1 | const { Gio, GLib } = imports.gi;
2 |
3 | export function fileExists(filePath) {
4 | let file = Gio.File.new_for_path(filePath);
5 | return file.query_exists(null);
6 | }
7 |
8 | export function expandTilde(path) {
9 | if (path.startsWith('~')) {
10 | return GLib.get_home_dir() + path.slice(1);
11 | } else {
12 | return path;
13 | }
14 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/.miscutils/icons.js:
--------------------------------------------------------------------------------
1 | const { Gtk } = imports.gi;
2 |
3 | export function iconExists(iconName) {
4 | let iconTheme = Gtk.IconTheme.get_default();
5 | return iconTheme.has_icon(iconName);
6 | }
7 |
8 | export function substitute(str) {
9 | // Normal substitutions
10 | if (userOptions.icons.substitutions[str])
11 | return userOptions.icons.substitutions[str];
12 |
13 | // Regex substitutions
14 | for (let i = 0; i < userOptions.icons.regexSubstitutions.length; i++) {
15 | const substitution = userOptions.icons.regexSubstitutions[i];
16 | const replacedName = str.replace(
17 | substitution.regex,
18 | substitution.replace,
19 | );
20 | if (replacedName != str) return replacedName;
21 | }
22 |
23 | // Guess: convert to kebab case
24 | if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, "-");
25 |
26 | // Original string
27 | return str;
28 | }
29 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.miscutils/mathfuncs.js:
--------------------------------------------------------------------------------
1 |
2 | export function clamp(x, min, max) {
3 | return Math.min(Math.max(x, min), max);
4 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/.miscutils/md2pango.js:
--------------------------------------------------------------------------------
1 | // Converts from Markdown to Pango. This does not support code blocks.
2 | // For illogical-impulse, code blocks are treated separately, in their own GtkSourceView widgets.
3 | // Partly inherited from https://github.com/ubunatic/md2pango
4 |
5 | const monospaceFonts = 'JetBrains Mono NF, JetBrains Mono Nerd Font, JetBrains Mono NL, SpaceMono NF, SpaceMono Nerd Font, monospace';
6 |
7 | const replacements = {
8 | 'indents': [
9 | { name: 'BULLET', re: /^(\s*)([\*\-]\s)(.*)(\s*)$/, sub: ' $1- $3' },
10 | { name: 'NUMBERING', re: /^(\s*[0-9]+\.\s)(.*)(\s*)$/, sub: ' $1 $2' },
11 | ],
12 | 'escapes': [
13 | { name: 'COMMENT', re: //, sub: '' },
14 | { name: 'AMPERSTAND', re: /&/g, sub: '&' },
15 | { name: 'LESSTHAN', re: //g, sub: '>' },
17 | ],
18 | 'sections': [
19 | { name: 'H1', re: /^(#\s+)(.*)(\s*)$/, sub: '$2' },
20 | { name: 'H2', re: /^(##\s+)(.*)(\s*)$/, sub: '$2' },
21 | { name: 'H3', re: /^(###\s+)(.*)(\s*)$/, sub: '$2' },
22 | { name: 'H4', re: /^(####\s+)(.*)(\s*)$/, sub: '$2' },
23 | { name: 'H5', re: /^(#####\s+)(.*)(\s*)$/, sub: '$2' },
24 | ],
25 | 'styles': [
26 | { name: 'BOLD', re: /(\*\*)(\S[\s\S]*?\S)(\*\*)/g, sub: "$2" },
27 | { name: 'UND', re: /(__)(\S[\s\S]*?\S)(__)/g, sub: "$2" },
28 | { name: 'EMPH', re: /\*(\S.*?\S)\*/g, sub: "$1" },
29 | // { name: 'EMPH', re: /_(\S.*?\S)_/g, sub: "$1" },
30 | { name: 'HEXCOLOR', re: /#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/g, sub: '#$1' },
31 | { name: 'INLCODE', re: /(`)([^`]*)(`)/g, sub: '$2' },
32 | // { name: 'UND', re: /(__|\*\*)(\S[\s\S]*?\S)(__|\*\*)/g, sub: "$2" },
33 | ],
34 | }
35 |
36 | const replaceCategory = (text, replaces) => {
37 | for (const type of replaces) {
38 | text = text.replace(type.re, type.sub);
39 | }
40 | return text;
41 | }
42 |
43 | // Main function
44 |
45 | export default (text) => {
46 | let lines = text.split('\n')
47 | let output = [];
48 | // Replace
49 | for (const line of lines) {
50 | let result = line;
51 | result = replaceCategory(result, replacements.indents);
52 | result = replaceCategory(result, replacements.escapes);
53 | result = replaceCategory(result, replacements.sections);
54 | result = replaceCategory(result, replacements.styles);
55 | output.push(result)
56 | }
57 | // Remove trailing whitespaces
58 | output = output.map(line => line.replace(/ +$/, ''))
59 | return output.join('\n');
60 | }
61 |
62 | export const markdownTest = `## Inline formatting
63 | - **Bold** *Italics* __Underline__
64 | - \`Monospace text\` 🤓
65 | - Colors
66 | - Nvidia green #7ABB08
67 | - Soundcloud orange #FF5500
68 | ## Code block
69 | \`\`\`cpp
70 | #include
71 | const std::string GREETING="UwU";
72 | int main() { std::cout << GREETING; }
73 | \`\`\`
74 | ## LaTeX
75 | \`\`\`latex
76 | \\frac{d}{dx} \\left( \\frac{x-438}{x^2+23x-7} \\right) = \\frac{-x^2 + 869}{(x^2+23x-7)^2} \\\\ → \\\\ cos(2x) = 2cos^2(x) - 1 = 1 - 2sin^2(x) = cos^2(x) - sin^2(x)
77 | \`\`\`
78 | `;
79 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.miscutils/system.js:
--------------------------------------------------------------------------------
1 | const { GLib } = imports.gi;
2 | import Variable from 'resource:///com/github/Aylur/ags/variable.js';
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
4 | const { execAsync, exec } = Utils;
5 |
6 | export const distroID = exec(`bash -c 'cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2 | sed "s/\\"//g"'`).trim();
7 | export const isDebianDistro = (distroID == 'linuxmint' || distroID == 'ubuntu' || distroID == 'debian' || distroID == 'zorin' || distroID == 'popos' || distroID == 'raspbian' || distroID == 'kali');
8 | export const isArchDistro = (distroID == 'arch' || distroID == 'endeavouros' || distroID == 'cachyos' || distroID == 'axos');
9 | export const hasFlatpak = !!exec(`bash -c 'command -v flatpak'`);
10 |
11 | const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_state_dir()}/ags/user/colormode.txt`;
12 | export const darkMode = Variable(!(Utils.readFile(LIGHTDARK_FILE_LOCATION).split('\n')[0].trim() == 'light'));
13 | darkMode.connect('changed', ({ value }) => {
14 | let lightdark = value ? "dark" : "light";
15 | execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && sed -i "1s/.*/${lightdark}/" ${GLib.get_user_state_dir()}/ags/user/colormode.txt`])
16 | .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`]))
17 | .then(execAsync(['bash', '-c', `command -v darkman && darkman set ${lightdark}`])) // Optional darkman integration
18 | .catch(print);
19 | });
20 | globalThis['darkMode'] = darkMode;
21 | export const hasPlasmaIntegration = !!Utils.exec('bash -c "command -v plasma-browser-integration-host"');
22 |
23 | export const getDistroIcon = () => {
24 | // Arches
25 | if(distroID == 'arch') return 'arch-symbolic';
26 | if(distroID == 'endeavouros') return 'endeavouros-symbolic';
27 | if(distroID == 'cachyos') return 'cachyos-symbolic';
28 | // Funny flake
29 | if(distroID == 'nixos') return 'nixos-symbolic';
30 | // Cool thing
31 | if(distroID == 'axos') return 'axos-symbolic';
32 | // Debians
33 | if(distroID == 'fedora') return 'fedora-symbolic';
34 | if(distroID == 'linuxmint') return 'ubuntu-symbolic';
35 | if(distroID == 'ubuntu') return 'ubuntu-symbolic';
36 | if(distroID == 'debian') return 'debian-symbolic';
37 | if(distroID == 'zorin') return 'ubuntu-symbolic';
38 | if(distroID == 'popos') return 'ubuntu-symbolic';
39 | if(distroID == 'raspbian') return 'debian-symbolic';
40 | if(distroID == 'kali') return 'debian-symbolic';
41 |
42 | return 'linux-symbolic';
43 | }
44 |
45 | export const getDistroName = () => {
46 | // Arches
47 | if(distroID == 'arch') return 'Arch Linux';
48 | if(distroID == 'endeavouros') return 'EndeavourOS';
49 | if(distroID == 'cachyos') return 'CachyOS';
50 | // Funny flake
51 | if(distroID == 'nixos') return 'NixOS';
52 | // Cool thing
53 | if(distroID == 'fedora') return 'Fedora';
54 | if(distroID == 'axos') return 'AxOS';
55 | // Debians
56 | if(distroID == 'linuxmint') return 'Linux Mint';
57 | if(distroID == 'ubuntu') return 'Ubuntu';
58 | if(distroID == 'debian') return 'Debian';
59 | if(distroID == 'zorin') return 'Zorin';
60 | if(distroID == 'popos') return 'Pop!_OS';4
61 | if(distroID == 'raspbian') return 'Raspbian';
62 | if(distroID == 'kali') return 'Kali Linux';
63 |
64 | return 'Linux';
65 | }
66 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.widgethacks/advancedrevealers.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 |
3 | const { Revealer, Scrollable } = Widget;
4 |
5 | export const MarginRevealer = ({
6 | transition = 'slide_down',
7 | child,
8 | revealChild,
9 | showClass = 'element-show', // These are for animation curve, they don't really hide
10 | hideClass = 'element-hide', // Don't put margins in these classes!
11 | extraSetup = () => { },
12 | ...rest
13 | }) => {
14 | const widget = Scrollable({
15 | ...rest,
16 | attribute: {
17 | 'revealChild': true, // It'll be set to false after init if it's supposed to hide
18 | 'transition': transition,
19 | 'show': () => {
20 | if (widget.attribute.revealChild) return;
21 | widget.hscroll = 'never';
22 | widget.vscroll = 'never';
23 | child.toggleClassName(hideClass, false);
24 | child.toggleClassName(showClass, true);
25 | widget.attribute.revealChild = true;
26 | child.css = 'margin: 0px;';
27 | },
28 | 'hide': () => {
29 | if (!widget.attribute.revealChild) return;
30 | child.toggleClassName(hideClass, true);
31 | child.toggleClassName(showClass, false);
32 | widget.attribute.revealChild = false;
33 | if (widget.attribute.transition == 'slide_left')
34 | child.css = `margin-right: -${child.get_allocated_width()}px;`;
35 | else if (widget.attribute.transition == 'slide_right')
36 | child.css = `margin-left: -${child.get_allocated_width()}px;`;
37 | else if (widget.attribute.transition == 'slide_up')
38 | child.css = `margin-bottom: -${child.get_allocated_height()}px;`;
39 | else if (widget.attribute.transition == 'slide_down')
40 | child.css = `margin-top: -${child.get_allocated_height()}px;`;
41 | },
42 | 'toggle': () => {
43 | if (widget.attribute.revealChild) widget.attribute.hide();
44 | else widget.attribute.show();
45 | },
46 | },
47 | child: child,
48 | hscroll: `${revealChild ? 'never' : 'always'}`,
49 | vscroll: `${revealChild ? 'never' : 'always'}`,
50 | setup: (self) => {
51 | extraSetup(self);
52 | }
53 | });
54 | child.toggleClassName(`${revealChild ? showClass : hideClass}`, true);
55 | return widget;
56 | }
57 |
58 | // Allow reveal update by setting initial state and adding update method
59 | MarginRevealer.updateReveal = (widget, shouldReveal) => {
60 | if (shouldReveal) widget.attribute.show();
61 | else widget.attribute.hide();
62 | };
63 | export const DoubleRevealer = ({
64 | transition1 = 'slide_right',
65 | transition2 = 'slide_left',
66 | duration1 = 150,
67 | duration2 = 150,
68 | child,
69 | revealChild,
70 | ...rest
71 | }) => {
72 | const r2 = Revealer({
73 | transition: transition2,
74 | transitionDuration: duration2,
75 | revealChild: revealChild,
76 | child: child,
77 | });
78 | const r1 = Revealer({
79 | transition: transition1,
80 | transitionDuration: duration1,
81 | revealChild: revealChild,
82 | child: r2,
83 | ...rest,
84 | })
85 | r1.toggleRevealChild = (value) => {
86 | r1.revealChild = value;
87 | r2.revealChild = value;
88 | }
89 | return r1;
90 | }
91 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.widgethacks/popupwindow.js:
--------------------------------------------------------------------------------
1 | import App from 'resource:///com/github/Aylur/ags/app.js';
2 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
3 | const { Box, Window } = Widget;
4 |
5 |
6 | export default ({
7 | name,
8 | child,
9 | showClassName = "",
10 | hideClassName = "",
11 | ...props
12 | }) => {
13 | return Window({
14 | name,
15 | visible: false,
16 | layer: 'top',
17 | ...props,
18 |
19 | child: Box({
20 | setup: (self) => {
21 | self.keybind("Escape", () => closeEverything());
22 | if (showClassName != "" && hideClassName !== "") {
23 | self.hook(App, (self, currentName, visible) => {
24 | if (currentName === name) {
25 | self.toggleClassName(hideClassName, !visible);
26 | }
27 | });
28 |
29 | if (showClassName !== "" && hideClassName !== "")
30 | self.className = `${showClassName} ${hideClassName}`;
31 | }
32 | },
33 | child: child,
34 | }),
35 | });
36 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/.widgetutils/clickthrough.js:
--------------------------------------------------------------------------------
1 | import Cairo from 'gi://cairo?version=1.0';
2 |
3 | export const dummyRegion = new Cairo.Region();
4 | export const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion);
--------------------------------------------------------------------------------
/src/share/sleex/modules/.widgetutils/cursorhover.js:
--------------------------------------------------------------------------------
1 | const { Gdk } = imports.gi;
2 |
3 | export function setupCursorHover(button) { // Hand pointing cursor on hover
4 | const display = Gdk.Display.get_default();
5 | button.connect('enter-notify-event', () => {
6 | const cursor = Gdk.Cursor.new_from_name(display, 'pointer');
7 | button.get_window().set_cursor(cursor);
8 | });
9 |
10 | button.connect('leave-notify-event', () => {
11 | const cursor = Gdk.Cursor.new_from_name(display, 'default');
12 | button.get_window().set_cursor(cursor);
13 | });
14 |
15 | }
16 |
17 | export function setupCursorHoverGrab(button) { // Hand ready to grab on hover
18 | button.connect('enter-notify-event', () => {
19 | const display = Gdk.Display.get_default();
20 | const cursor = Gdk.Cursor.new_from_name(display, 'grab');
21 | button.get_window().set_cursor(cursor);
22 | });
23 |
24 | button.connect('leave-notify-event', () => {
25 | const display = Gdk.Display.get_default();
26 | const cursor = Gdk.Cursor.new_from_name(display, 'default');
27 | button.get_window().set_cursor(cursor);
28 | });
29 | }
30 |
31 | export function setupCursorHoverInfo(button) { // "?" mark cursor on hover
32 | const display = Gdk.Display.get_default();
33 | button.connect('enter-notify-event', () => {
34 | const cursor = Gdk.Cursor.new_from_name(display, 'help');
35 | button.get_window().set_cursor(cursor);
36 | });
37 |
38 | button.connect('leave-notify-event', () => {
39 | const cursor = Gdk.Cursor.new_from_name(display, 'default');
40 | button.get_window().set_cursor(cursor);
41 | });
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/.widgetutils/keybind.js:
--------------------------------------------------------------------------------
1 | const { Gdk } = imports.gi;
2 |
3 | const MODS = {
4 | 'Shift': Gdk.ModifierType.SHIFT_MASK,
5 | 'Ctrl': Gdk.ModifierType.CONTROL_MASK,
6 | 'Alt': Gdk.ModifierType.ALT_MASK,
7 | 'Hyper': Gdk.ModifierType.HYPER_MASK,
8 | 'Meta': Gdk.ModifierType.META_MASK
9 | }
10 |
11 | export const checkKeybind = (event, keybind) => {
12 | const pressedModMask = event.get_state()[1];
13 | const pressedKey = event.get_keyval()[1];
14 | const keys = keybind.split('+');
15 | for (let i = 0; i < keys.length; i++) {
16 | if (keys[i] in MODS) {
17 | if (!(pressedModMask & MODS[keys[i]])) {
18 | return false;
19 | }
20 | } else if (pressedKey !== Gdk[`KEY_${keys[i]}`]) {
21 | return false;
22 | }
23 | }
24 | return true;
25 | }
26 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/bar/normal/system.js:
--------------------------------------------------------------------------------
1 | // This is for the right pills of the bar.
2 | import Widget from "resource:///com/github/Aylur/ags/widget.js";
3 | import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
4 | const { execAsync } = Utils;
5 | const { GLib } = imports.gi;
6 | import { RoundedCorner } from "../../.commonwidgets/cairo_roundedcorner.js";
7 |
8 | const time = Variable("", {
9 | poll: [
10 | userOptions.time.interval,
11 | () => GLib.DateTime.new_now_local().format(userOptions.time.format),
12 | ],
13 | });
14 |
15 | const date = Variable("", {
16 | poll: [
17 | userOptions.time.dateInterval,
18 | () => GLib.DateTime.new_now_local().format(userOptions.time.dateFormatLong),
19 | ],
20 | });
21 |
22 | const showTimeDate = () => {
23 | const TIMEDATE_FILE_LOCATION = `${GLib.get_user_state_dir()}/ags/user/show_timedate.txt`;
24 | const actual_show_timedate = Utils.exec(`bash -c "cat ${TIMEDATE_FILE_LOCATION}"`);
25 | actual_show_timedate == null ? actual_show_timedate = userOptions.appearance.showTimeDate : actual_show_timedate;
26 | return actual_show_timedate == 'true' ? true : false;
27 | }
28 | const BarClock = () => {
29 | if (!showTimeDate()) return null;
30 | else return Widget.Box({
31 | vpack: "center",
32 | className: "spacing-h-4 bar-clock-box",
33 | children: [
34 | Widget.Label({
35 | className: "bar-time",
36 | label: time.bind(),
37 | }),
38 | Widget.Label({
39 | className: "txt-norm txt-onLayer1",
40 | label: "•",
41 | }),
42 | Widget.Label({
43 | className: "txt-smallie bar-date",
44 | label: date.bind(),
45 | }),
46 | ],
47 | });
48 | };
49 |
50 | const BarGroup = ({ child }) =>
51 | Widget.Box({
52 | className: "bar-group-margin bar-sides",
53 | children: [
54 | Widget.Box({
55 | className: "bar-group bar-group-standalone bar-group-pad-system bar-group-right",
56 | children: [child],
57 | }),
58 | ],
59 | });
60 |
61 | const switchToRelativeWorkspace = async (self, num) => {
62 | try {
63 | const Hyprland = (
64 | await import("resource:///com/github/Aylur/ags/service/hyprland.js")
65 | ).default;
66 | Hyprland.messageAsync(
67 | `dispatch workspace ${num > 0 ? "+" : ""}${num}`
68 | ).catch(print);
69 | } catch {
70 | execAsync([
71 | `${App.configDir}/scripts/sway/swayToRelativeWs.sh`,
72 | `${num}`,
73 | ]).catch(print);
74 | }
75 | };
76 |
77 | export default () =>
78 | !showTimeDate() ? Widget.Box({}) : Widget.EventBox({
79 | onScrollUp: (self) => switchToRelativeWorkspace(self, -1),
80 | onScrollDown: (self) => switchToRelativeWorkspace(self, +1),
81 | onPrimaryClick: () => App.toggleWindow("dashboard"),
82 | child: Widget.Box({
83 | children: [
84 | // BarCornerBottomright(),
85 | BarGroup({ child: BarClock() }),
86 | // BarCornerTopright(),
87 | ],
88 | }),
89 | });
90 |
91 | // const BarCornerBottomright = () => Widget.Box({
92 | // child: RoundedCorner('bottomright', { className: 'corner', }),
93 | // });
94 | // const BarCornerTopright = () => Widget.Box({
95 | // child: RoundedCorner('topright', { className: 'corner', }),
96 | // });
--------------------------------------------------------------------------------
/src/share/sleex/modules/bar/normal/tray.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js';
3 | const { Box, Icon, Button } = Widget;
4 | const { Gravity } = imports.gi.Gdk;
5 |
6 | const SysTrayItem = (item) => item.id !== null ? Button({
7 | className: 'bar-systray-item',
8 | child: Icon({ hpack: 'center' }).bind('icon', item, 'icon'),
9 | setup: (self) => self
10 | .hook(item, (self) => self.tooltipMarkup = item['tooltip-markup'])
11 | ,
12 | onPrimaryClick: (_, event) => item.activate(event),
13 | onSecondaryClick: (btn, event) => item.menu.popup_at_widget(btn, Gravity.SOUTH, Gravity.NORTH, null),
14 | }) : null;
15 |
16 | export const Tray = (props = {}) => {
17 | const trayContent = Box({
18 | className: 'margin-right-5 spacing-h-15',
19 | setup: (self) => self
20 | .hook(SystemTray, (self) => {
21 | self.children = SystemTray.items.map(SysTrayItem);
22 | self.show_all();
23 | })
24 | ,
25 | });
26 | const trayRevealer = Widget.Revealer({
27 | revealChild: true,
28 | transition: 'slide_left',
29 | transitionDuration: userOptions.animations.durationLarge,
30 | child: trayContent,
31 | });
32 | return Box({
33 | ...props,
34 | children: [trayRevealer],
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/bar/normal/workspaces_box.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 |
3 | export default () => {
4 | return Widget.box({
5 | name: `workspace-corner`,
6 | exclusivity: 'ignore',
7 | visible: true,
8 | });
9 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/cheatsheet/periodictable.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | import { niceTypes, periodicTable, series } from "./data_periodictable.js";
3 | const { Box, Icon, Label } = Widget;
4 |
5 | export default () => {
6 | const ElementTile = (element) => {
7 | return Box({
8 | vertical: true,
9 | tooltipText: element.electronConfig ? `${element.electronConfig}` : null,
10 | className: `cheatsheet-periodictable-${element.type}`,
11 | children: element.name == '' ? null : [
12 | Box({
13 | className: 'padding-left-8 padding-right-8 padding-top-8',
14 | children: [
15 | Label({
16 | label: `${element.number}`,
17 | className: "cheatsheet-periodictable-elementnum txt-tiny txt-bold",
18 | }),
19 | Box({ hexpand: true }),
20 | Label({
21 | label: `${element.weight}`,
22 | className: "txt-smaller",
23 | })
24 | ]
25 | }),
26 | element.icon ? Icon({
27 | icon: element.icon,
28 | className: "txt-hugerass txt-bold",
29 | }) : Label({
30 | label: `${element.symbol}`,
31 | className: "cheatsheet-periodictable-elementsymbol",
32 | }),
33 | Label({
34 | label: `${element.name}`,
35 | className: "txt-tiny",
36 | })
37 | ]
38 | })
39 | }
40 | const BoardColor = (type) => Box({
41 | className: 'spacing-h-5',
42 | children: [
43 | Box({
44 | homogeneous: true,
45 | className: `cheatsheet-periodictable-legend-color-wrapper`,
46 | children: [Box({
47 | className: `cheatsheet-periodictable-legend-color-${type}`,
48 | })]
49 | }),
50 | Label({
51 | label: `${niceTypes[type]}`,
52 | className: "txt txt-small",
53 | })
54 | ]
55 | })
56 | const mainBoard = Box({
57 | hpack: 'center',
58 | vertical: true,
59 | className: "spacing-v-3",
60 | children: periodicTable.map((row, _) => Box({ // Rows
61 | className: "spacing-h-5",
62 | children: row.map((element, _) => ElementTile(element))
63 | })),
64 | });
65 | const seriesBoard = Box({
66 | hpack: 'center',
67 | vertical: true,
68 | className: "spacing-v-3",
69 | children: series.map((row, _) => Box({ // Rows
70 | className: "spacing-h-5",
71 | children: row.map((element, _) => ElementTile(element))
72 | })),
73 | });
74 | const legend = Box({
75 | hpack: 'center',
76 | className: 'spacing-h-20',
77 | children: [
78 | BoardColor('metal'),
79 | BoardColor('nonmetal'),
80 | BoardColor('noblegas'),
81 | BoardColor('lanthanum'),
82 | BoardColor('actinium'),
83 | ]
84 | })
85 | return Box({
86 | vertical: true,
87 | className: 'spacing-v-20',
88 | children: [
89 | mainBoard,
90 | seriesBoard,
91 | legend
92 | ]
93 | })
94 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/calendar_layout.js:
--------------------------------------------------------------------------------
1 | function checkLeapYear(year) {
2 | return (
3 | year % 400 == 0 ||
4 | (year % 4 == 0 && year % 100 != 0));
5 | }
6 |
7 | function getMonthDays(month, year) {
8 | const leapYear = checkLeapYear(year);
9 | if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 31;
10 | if (month == 2 && leapYear) return 29;
11 | if (month == 2 && !leapYear) return 28;
12 | return 30;
13 | }
14 |
15 | function getNextMonthDays(month, year) {
16 | const leapYear = checkLeapYear(year);
17 | if (month == 1 && leapYear) return 29;
18 | if (month == 1 && !leapYear) return 28;
19 | if (month == 12) return 31;
20 | if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 30;
21 | return 31;
22 | }
23 |
24 | function getPrevMonthDays(month, year) {
25 | const leapYear = checkLeapYear(year);
26 | if (month == 3 && leapYear) return 29;
27 | if (month == 3 && !leapYear) return 28;
28 | if (month == 1) return 31;
29 | if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 30;
30 | return 31;
31 | }
32 |
33 | export function getCalendarLayout(dateObject, highlight) {
34 | if (!dateObject) dateObject = new Date();
35 | const weekday = (dateObject.getDay() + 6) % 7; // MONDAY IS THE FIRST DAY OF THE WEEK
36 | const day = dateObject.getDate();
37 | const month = dateObject.getMonth() + 1;
38 | const year = dateObject.getFullYear();
39 | const weekdayOfMonthFirst = (weekday + 35 - (day - 1)) % 7;
40 | const daysInMonth = getMonthDays(month, year);
41 | const daysInNextMonth = getNextMonthDays(month, year);
42 | const daysInPrevMonth = getPrevMonthDays(month, year);
43 |
44 | // Fill
45 | var monthDiff = (weekdayOfMonthFirst == 0 ? 0 : -1);
46 | var toFill, dim;
47 | if(weekdayOfMonthFirst == 0) {
48 | toFill = 1;
49 | dim = daysInMonth;
50 | }
51 | else {
52 | toFill = (daysInPrevMonth - (weekdayOfMonthFirst - 1));
53 | dim = daysInPrevMonth;
54 | }
55 | var calendar = [...Array(6)].map(() => Array(7));
56 | var i = 0, j = 0;
57 | while (i < 6 && j < 7) {
58 | calendar[i][j] = {
59 | "day": toFill,
60 | "today": ((toFill == day && monthDiff == 0 && highlight) ? 1 : (
61 | monthDiff == 0 ? 0 :
62 | -1
63 | ))
64 | };
65 | // Increment
66 | toFill++;
67 | if (toFill > dim) { // Next month?
68 | monthDiff++;
69 | if (monthDiff == 0)
70 | dim = daysInMonth;
71 | else if (monthDiff == 1)
72 | dim = daysInNextMonth;
73 | toFill = 1;
74 | }
75 | // Next tile
76 | j++;
77 | if (j == 7) {
78 | j = 0;
79 | i++;
80 | }
81 |
82 | }
83 | return calendar;
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/main.js:
--------------------------------------------------------------------------------
1 | import PopupWindow from '../.widgethacks/popupwindow.js';
2 | import SidebarRight from "./dashboard.js";
3 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
4 | const { Box } = Widget;
5 | import clickCloseRegion from '../.commonwidgets/clickcloseregion.js';
6 |
7 | export default () => PopupWindow({
8 | keymode: 'on-demand',
9 | name: 'dashboard',
10 | child: Box({
11 | children: [
12 | clickCloseRegion({ name: 'dashboard', multimonitor: false }),
13 | SidebarRight(),
14 | ]
15 | })
16 | });
17 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/name.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
3 | import { setupCursorHover } from '../.widgetutils/cursorhover.js';
4 | const { execAsync } = Utils;
5 | const { Box, Button, Icon, Label } = Widget;
6 |
7 | export default () => Box({
8 | className: 'name techfont',
9 | children: [
10 | Label({
11 | label: 'Sleex'
12 | }),
13 | Box({ hexpand: true }),
14 | Button({
15 | className: 'sidebar-module-btn-arrow',
16 | onClicked: () => execAsync(['xdg-open', 'https://github.com/axos-project/sleex']).catch(print),
17 | child: Icon({
18 | className: 'txt txt-norm',
19 | icon: 'github-symbolic',
20 | }),
21 | setup: setupCursorHover,
22 | })
23 | ]
24 | })
25 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/quote.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box, Label } = Widget;
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
4 |
5 | const MakeRequest = async () => {
6 | try {
7 | const response = await Utils.fetch("https://quotes-api-self.vercel.app/quote");
8 | const data = await response.json();
9 | return {
10 | quote: data.quote,
11 | author: data.author
12 | };
13 | } catch (error) {
14 | console.error('Error fetching quote:', error);
15 | return {
16 | quote: 'Unable to fetch quote',
17 | author: 'Unknown'
18 | };
19 | }
20 | };
21 |
22 | export const QuoteWidget = () => {
23 | const quoteLabel = Label({
24 | wrap: true,
25 | className: 'txt txt-quote',
26 | label: 'Loading...'
27 | });
28 |
29 | const authorLabel = Label({
30 | className: 'txt txt-small',
31 | label: 'Loading...'
32 | });
33 |
34 | // Immediately invoke async function to update labels
35 | (async () => {
36 | const { quote, author } = await MakeRequest();
37 | quoteLabel.label = quote;
38 | authorLabel.label = author;
39 | // console.log('Quote:', quote);
40 | // console.log('Author:', author);
41 | })();
42 |
43 | return Box({
44 | vertical: true,
45 | className: 'spacing-v-5 quote-box',
46 | children: [
47 | quoteLabel,
48 | authorLabel
49 | ]
50 | })
51 | };
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/tabs/home.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box } = Widget;
3 | import { userWidget } from '../widgets/user_widget.js';
4 | import { clockWidget } from '../widgets/clock.js';
5 | import { QuoteWidget } from '../widgets/quote.js';
6 | import { MusicWidget } from '../widgets/music.js';
7 | import { WeatherWidget } from '../widgets/weather.js';
8 | import { githubWidget } from '../widgets/github.js';
9 | import { ModuleCalendar } from "../widgets/calendar.js";
10 | import ModuleNotificationList from "../centermodules/notificationlist.js";
11 |
12 |
13 | export default () => Box({
14 | vertical: true,
15 | children: [
16 | Box({
17 | children: [
18 | Box({
19 | vertical: true,
20 | children: [
21 | userWidget(),
22 | clockWidget(),
23 | githubWidget()
24 | ]
25 | }),
26 | Box({
27 | vertical: true,
28 | children: [
29 | Box({
30 | children: [
31 | Box({
32 | vertical: true,
33 | children: [
34 | ModuleNotificationList(),
35 | ]
36 | }),
37 | Box({
38 | vertical: true,
39 | children: [
40 | MusicWidget(),
41 | WeatherWidget(),
42 | ModuleCalendar(),
43 | ]
44 | })
45 | ]
46 | }),
47 | ]
48 | })
49 |
50 | ]
51 | }),
52 | QuoteWidget()
53 | ]
54 | });
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/tabs/settings.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box } = Widget;
3 | import ModuleAudioControls from "../centermodules/audiocontrols.js";
4 | import ModuleWifiNetworks from "../centermodules/wifinetworks.js";
5 | import ModuleBluetooth from "../centermodules/bluetooth.js";
6 | import ModuleConfigure from "../centermodules/configure.js";
7 |
8 |
9 | export default () => Box({
10 | children: [
11 | ModuleConfigure(),
12 | Box({
13 | vexpand: true,
14 | vertical: true,
15 | className: 'spacing-v-15',
16 | children: [
17 | ModuleBluetooth(),
18 | ModuleAudioControls()
19 | ]
20 | }),
21 | ModuleWifiNetworks(),
22 | ]
23 | });
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/tabs/todo.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box, Label } = Widget;
3 | import { TodoWidget } from "../widgets/todolist.js";
4 | import { todoTabs } from '../widgets/todo_tag.js';
5 | import { UndoneTodoList } from '../widgets/todolist.js';
6 | import { todoItems } from '../widgets/todolist.js';
7 |
8 | export default () => Box({
9 | children: [
10 | UndoneTodoList(),
11 | todoItems(true),
12 | ]
13 | })
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/widgets/calendar_layout.js:
--------------------------------------------------------------------------------
1 | function checkLeapYear(year) {
2 | return (
3 | year % 400 == 0 ||
4 | (year % 4 == 0 && year % 100 != 0));
5 | }
6 |
7 | function getMonthDays(month, year) {
8 | const leapYear = checkLeapYear(year);
9 | if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 31;
10 | if (month == 2 && leapYear) return 29;
11 | if (month == 2 && !leapYear) return 28;
12 | return 30;
13 | }
14 |
15 | function getNextMonthDays(month, year) {
16 | const leapYear = checkLeapYear(year);
17 | if (month == 1 && leapYear) return 29;
18 | if (month == 1 && !leapYear) return 28;
19 | if (month == 12) return 31;
20 | if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 30;
21 | return 31;
22 | }
23 |
24 | function getPrevMonthDays(month, year) {
25 | const leapYear = checkLeapYear(year);
26 | if (month == 3 && leapYear) return 29;
27 | if (month == 3 && !leapYear) return 28;
28 | if (month == 1) return 31;
29 | if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 30;
30 | return 31;
31 | }
32 |
33 | export function getCalendarLayout(dateObject, highlight) {
34 | if (!dateObject) dateObject = new Date();
35 | const weekday = (dateObject.getDay() + 6) % 7; // MONDAY IS THE FIRST DAY OF THE WEEK
36 | const day = dateObject.getDate();
37 | const month = dateObject.getMonth() + 1;
38 | const year = dateObject.getFullYear();
39 | const weekdayOfMonthFirst = (weekday + 35 - (day - 1)) % 7;
40 | const daysInMonth = getMonthDays(month, year);
41 | const daysInNextMonth = getNextMonthDays(month, year);
42 | const daysInPrevMonth = getPrevMonthDays(month, year);
43 |
44 | // Fill
45 | var monthDiff = (weekdayOfMonthFirst == 0 ? 0 : -1);
46 | var toFill, dim;
47 | if(weekdayOfMonthFirst == 0) {
48 | toFill = 1;
49 | dim = daysInMonth;
50 | }
51 | else {
52 | toFill = (daysInPrevMonth - (weekdayOfMonthFirst - 1));
53 | dim = daysInPrevMonth;
54 | }
55 | var calendar = [...Array(6)].map(() => Array(7));
56 | var i = 0, j = 0;
57 | while (i < 6 && j < 7) {
58 | calendar[i][j] = {
59 | "day": toFill,
60 | "today": ((toFill == day && monthDiff == 0 && highlight) ? 1 : (
61 | monthDiff == 0 ? 0 :
62 | -1
63 | ))
64 | };
65 | // Increment
66 | toFill++;
67 | if (toFill > dim) { // Next month?
68 | monthDiff++;
69 | if (monthDiff == 0)
70 | dim = daysInMonth;
71 | else if (monthDiff == 1)
72 | dim = daysInNextMonth;
73 | toFill = 1;
74 | }
75 | // Next tile
76 | j++;
77 | if (j == 7) {
78 | j = 0;
79 | i++;
80 | }
81 |
82 | }
83 | return calendar;
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/widgets/clock.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { GLib } = imports.gi;
3 |
4 | export const clockWidget = () => {
5 |
6 | const time = Variable("", {
7 | poll: [
8 | userOptions.time.interval,
9 | () => GLib.DateTime.new_now_local().format(userOptions.time.format),
10 | ],
11 | });
12 |
13 | const rawDate = new Date();
14 | const date = Variable("", {
15 | poll: [
16 | userOptions.time.dateInterval,
17 | () => rawDate.toLocaleDateString([], { weekday: 'long', month: 'long', day: 'numeric' }),
18 | ],
19 | });
20 |
21 | return Widget.Box({
22 | vertical: true,
23 | className: 'clock-widget dash-widget spacing-v-5',
24 | children: [
25 | Widget.Label({
26 | className: 'txt txt-clock',
27 | hpack: 'center',
28 | label: time.bind(),
29 | }),
30 | Widget.Label({
31 | className: 'txt txt-medium',
32 | hpack: 'center',
33 | label: date.bind(),
34 | }),
35 | ],
36 | });
37 | };
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/widgets/github.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box, Label } = Widget;
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
4 | const { exec } = Utils;
5 |
6 |
7 | const request = async () => {
8 | try {
9 | const url = 'https://github-contributions.vercel.app/api/v1/levraiardox'
10 | const response = await Utils.fetch(url);
11 | const data = await response.json();
12 | return {
13 | contributions: data.years[0].total
14 | }
15 | } catch (error) {
16 | console.error("Unable to fetch contributions: ", error)
17 | return {
18 | contributions: "error"
19 | }
20 | }
21 | }
22 |
23 | export const githubWidget = () => {
24 | const contribLabel = new Label({
25 | className: 'txt txt-contrib',
26 | hpack: 'center',
27 | label: `Loading...`,
28 | });
29 |
30 | (async () => {
31 | const { contributions } = await request();
32 | contribLabel.label = `${contributions}`;
33 | })();
34 |
35 | return new Box({
36 | vertical: true,
37 | className: 'github-widget dash-widget spacing-v-5',
38 | children: [
39 | contribLabel,
40 | new Label({
41 | className: 'txt txt-medium',
42 | hpack: 'center',
43 | label: 'contributions this year',
44 | }),
45 | new Label({
46 | className: 'txt txt-author',
47 | hpack: 'center',
48 | label: '@levraiardox'
49 | })
50 | ],
51 | });
52 | };
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/widgets/name.js:
--------------------------------------------------------------------------------
1 | import Widget from "resource:///com/github/Aylur/ags/widget.js";
2 | import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
3 | import { setupCursorHover } from "../../.widgetutils/cursorhover.js";
4 | const { execAsync } = Utils;
5 | const { Box, Button, Icon, Label } = Widget;
6 |
7 | export default () =>
8 | Box({
9 | className: "name techfont",
10 | children: [
11 | Label({
12 | label: "Sleex",
13 | }),
14 | Box({ hexpand: true }),
15 | Button({
16 | className: "sidebar-module-btn-arrow",
17 | onClicked: () =>
18 | execAsync([
19 | "xdg-open",
20 | "https://github.com/axos-project/sleex",
21 | ]).catch(print),
22 | child: Icon({
23 | className: "txt txt-norm",
24 | icon: "github-symbolic",
25 | }),
26 | setup: setupCursorHover,
27 | }),
28 | ],
29 | });
30 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/widgets/quote.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box, Label } = Widget;
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
4 |
5 | const MakeRequest = async () => {
6 | try {
7 | const response = await Utils.fetch("https://quotes-api-self.vercel.app/quote");
8 | const data = await response.json();
9 | return {
10 | quote: data.quote,
11 | author: data.author
12 | };
13 | } catch (error) {
14 | console.error('Error fetching quote:', error);
15 | return {
16 | quote: 'Unable to fetch quote',
17 | author: 'Unknown'
18 | };
19 | }
20 | };
21 |
22 | export const QuoteWidget = () => {
23 | const quoteLabel = new Label({
24 | wrap: true,
25 | hpack: 'center',
26 | className: 'txt txt-medium',
27 | label: 'Loading...'
28 | });
29 |
30 | const authorLabel = new Label({
31 | hpack: 'center',
32 | className: 'txt txt-small txt-bold txt-quote-author',
33 | label: 'Loading...'
34 | });
35 |
36 | // Immediately invoke async function to update labels
37 | (async () => {
38 | const { quote, author } = await MakeRequest();
39 | quoteLabel.label = quote;
40 | authorLabel.label = author;
41 | // console.log('Quote:', quote);
42 | // console.log('Author:', author);
43 | })();
44 |
45 | return new Box({
46 | vertical: true,
47 | className: 'spacing-v-5 dash-widget quote-widget',
48 | children: [
49 | quoteLabel,
50 | authorLabel
51 | ]
52 | })
53 | };
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/widgets/timer.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box, Label } = Widget;
3 |
4 | export const timerWidget = () => {
5 | return Box({
6 | className: 'dash-widget timer-widget',
7 | vexpand: true,
8 | vertical: true,
9 | children: [
10 | Label({
11 | className: 'txt txt-small timer-title-label',
12 | label: "You've focused for"
13 | }),
14 | Label({
15 | className: 'txt txt-large txt-clock',
16 | label: '3h 45m'
17 | }),
18 | Label({
19 | className: 'txt txt-small timer-title-label',
20 | label: "today"
21 | })
22 | ]
23 | })
24 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/widgets/todo_tag.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box, Label } = Widget;
3 |
4 | export const todoTabs = () => {
5 | return Box({
6 | className: 'dash-widget',
7 | hexpand: true
8 | })
9 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/dashboard/widgets/user_widget.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | const { Box, Label } = Widget;
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
4 | const { exec } = Utils;
5 |
6 | export const userWidget = () => {
7 |
8 | const userName = `${exec('whoami')}`;
9 | const userNameCapitalized = userName.charAt(0).toUpperCase() + userName.slice(1);
10 |
11 | const avatar = Widget.Box({
12 | className: 'avatar-widget',
13 | css: `
14 | background-image: url('file:///var/lib/AccountsService/icons/${userName}');
15 | background-size: cover;
16 | background-position: center;
17 | `,
18 | });
19 |
20 | return Widget.Box({
21 | vexpand: true,
22 | vertical: true,
23 | className: 'user-widget dash-widget spacing-v-5',
24 | children: [
25 | Widget.Box({
26 | hexpand: false,
27 | hpack: 'center',
28 | child: avatar,
29 | }),
30 | Widget.Label({
31 | className: 'txt txt-title',
32 | hpack: 'center',
33 | label: "Welcome back, " + userNameCapitalized,
34 | }),
35 | Widget.Label({
36 | className: 'txt txt-medium',
37 | hpack: 'center',
38 | label: "Today is a good day to have a good day.",
39 | }),
40 | ],
41 | });
42 | };
--------------------------------------------------------------------------------
/src/share/sleex/modules/desktopbackground/data_quicklaunches.js:
--------------------------------------------------------------------------------
1 | export const quickLaunchItems = [
2 | {
3 | "name": "GitHub",
4 | "command": "xdg-open 'https://github.com/ $"
5 | },
6 | {
7 | "name": "Terminal",
8 | "command": "foot &"
9 | },
10 | {
11 | "name": "Discord",
12 | "command": "discord $"
13 | },
14 | ]
15 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/desktopbackground/main.js:
--------------------------------------------------------------------------------
1 | import Widget from "resource:///com/github/Aylur/ags/widget.js";
2 |
3 | import WallpaperImage from "./wallpaper.js";
4 | import TimeAndLaunchesWidget from "./timeandlaunches.js";
5 | import SystemWidget from "./system.js";
6 |
7 | export default (monitor) =>
8 | Widget.Window({
9 | name: `desktopbackground${monitor}`,
10 | // anchor: ['top', 'bottom', 'left', 'right'],
11 | layer: "background",
12 | exclusivity: "ignore",
13 | visible: true,
14 | child: Widget.Overlay({
15 | child: WallpaperImage(monitor),
16 | // child: Widget.Box({}),
17 | overlays: [TimeAndLaunchesWidget(), SystemWidget()],
18 | setup: (self) => {
19 | self.set_overlay_pass_through(self.get_children()[1], true);
20 | },
21 | }),
22 | });
23 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/desktopbackground/timeandlaunches.js:
--------------------------------------------------------------------------------
1 | const { GLib } = imports.gi;
2 | import Widget from "resource:///com/github/Aylur/ags/widget.js";
3 | const { Box, Label, Overlay } = Widget;
4 | import { AnimatedCircProg } from "../.commonwidgets/cairo_circularprogress.js";
5 | import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
6 | const { exec } = Utils;
7 |
8 | const TimeAndDate = () =>
9 | Box({
10 | vertical: true,
11 | hpack: "center",
12 | className: "spacing-v-5",
13 | children: [
14 | Label({
15 | className: "bg-time-clock",
16 | label: GLib.DateTime.new_now_local().format(userOptions.time.format),
17 | setup: (self) =>
18 | self.poll(userOptions.time.interval, (label) => {
19 | label.label = GLib.DateTime.new_now_local().format(
20 | userOptions.time.format
21 | );
22 | }),
23 | }),
24 | Label({
25 | className: "bg-time-date",
26 | hpack: "center",
27 | label: GLib.DateTime.new_now_local().format(
28 | userOptions.time.dateFormatLong
29 | ),
30 | setup: (self) =>
31 | self.poll(userOptions.time.dateInterval, (label) => {
32 | label.label = GLib.DateTime.new_now_local().format(
33 | userOptions.time.dateFormatLong
34 | );
35 | }),
36 | }),
37 | ],
38 | });
39 |
40 | const analogClock = () => {
41 | const HourProgress = () => {
42 | const _updateProgress = (circprog) => {
43 | // Set circular progress value
44 | const hour = (exec("date '+%I'") / 12) * 100;
45 | circprog.css = `font-size: ${hour}px;`;
46 | };
47 | return AnimatedCircProg({
48 | className: "hours-circprog",
49 | vpack: "center",
50 | hpack: "center",
51 | extraSetup: (self) => self.poll(1000, _updateProgress),
52 | });
53 | };
54 | const MinuteProgress = () => {
55 | const _updateProgress = (circprog) => {
56 | // Set circular progress value
57 | const minute = (exec("date '+%M'") / 60) * 100;
58 | circprog.css = `font-size: ${minute}px;`;
59 | };
60 | return AnimatedCircProg({
61 | className: "minutes-circprog",
62 | vpack: "center",
63 | hpack: "center",
64 | extraSetup: (self) => self.poll(1000, _updateProgress),
65 | });
66 | };
67 | return Overlay({
68 | child: Box({
69 | vpack: "center",
70 | hpack: "center",
71 | child: HourProgress(),
72 | }),
73 | overlays: [MinuteProgress()],
74 | });
75 | };
76 |
77 | // const showAnalog = () => {
78 | // const ANALOGCLOCK_FILE_LOCATION = `${GLib.get_user_state_dir()}/ags/user/show_analogclock.txt`;
79 | // const show_analogclock = Utils.readFile(ANALOGCLOCK_FILE_LOCATION);
80 | // show_analogclock == null ? show_analogclock = userOptions.appearance.showAnalogClock : show_analogclock;
81 | // return show_analogclock == 'true' ? true : false;
82 | // }
83 |
84 | export default () => {
85 | // if (showAnalog()) return Box({
86 | // hpack: "center",
87 | // vpack: "center",
88 | // vertical: true,
89 | // className: "bg-time-box",
90 | // children: [
91 | // analogClock(),
92 | // ],
93 | // });
94 | return Box({
95 | hpack: "center",
96 | vpack: "center",
97 | vertical: true,
98 | className: "bg-time-box",
99 | children: [
100 | TimeAndDate(),
101 | ],
102 | });
103 | };
--------------------------------------------------------------------------------
/src/share/sleex/modules/indicators/indicatorvalueBrightness.js:
--------------------------------------------------------------------------------
1 | // This file is for brightness/volume indicators
2 | // noinspection JSFileReferences
3 | import Widget from "resource:///com/github/Aylur/ags/widget.js";
4 | const { Box, Revealer } = Widget;
5 | import Brightness from "../../services/brightness.js";
6 | import Indicator from "../../services/indicator.js";
7 | import { OsdValue } from "./indicatorvalues.js";
8 | import { RoundedCorner } from "../.commonwidgets/cairo_roundedcorner.js";
9 |
10 | const BrightnessOsd = (monitor = 0) => {
11 | const brightnessIndicator = OsdValue({
12 | icon: "light_mode",
13 | extraClassName: "osd-brightness",
14 | extraProgressClassName: "osd-brightness-progress",
15 | labelSetup: (self) =>
16 | self.hook(
17 | Brightness[monitor],
18 | (self) => {
19 | self.label = `${Math.round(
20 | Brightness[monitor].screen_value * 100
21 | )}`;
22 | },
23 | "notify::screen-value"
24 | ),
25 | progressSetup: (self) =>
26 | self.hook(
27 | Brightness[monitor],
28 | (progress) => {
29 | const updateValue = Brightness[monitor].screen_value;
30 | if (updateValue !== progress.value) Indicator.popup(1);
31 | progress.value = updateValue;
32 | },
33 | "notify::screen-value"
34 | ),
35 | });
36 |
37 | return Revealer({
38 | transition: "slide_down",
39 | transitionDuration: userOptions.animations.durationLarge,
40 | revealChild: false,
41 | setup: (self) =>
42 | self.hook(
43 | Indicator,
44 | (revealer, value) => {
45 | if (value > -1) self.revealChild = true;
46 | else self.revealChild = false;
47 | },
48 | "popup"
49 | ),
50 | child: Box({
51 | hpack: "center",
52 | vertical: false,
53 | className: "spacing-h--10",
54 | children: [brightnessIndicator, RoundedCorner('topright', { className: 'corner'})],
55 | }),
56 | });
57 | };
58 |
59 | export default (monitor = 0) => Widget.Window({
60 | name: `indicatorBrightness${monitor}`,
61 | monitor,
62 | className: 'indicator',
63 | layer: 'overlay',
64 | visible: true,
65 | anchor: ['top', 'left'],
66 | child: Widget.Box({
67 | vertical: true,
68 | className: 'osd-window',
69 | css: 'min-height: 2px;',
70 | children: [BrightnessOsd(monitor)]
71 | })
72 | });
73 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/indicators/main.js:
--------------------------------------------------------------------------------
1 | import Widget from "resource:///com/github/Aylur/ags/widget.js";
2 | import Indicator from "../../services/indicator.js";
3 | import ColorScheme from "./colorscheme.js";
4 | import NotificationPopups from "./notificationpopups.js";
5 |
6 | export default (monitor = 0) =>
7 | Widget.Window({
8 | name: `indicator${monitor}`,
9 | monitor,
10 | className: "indicator",
11 | layer: "overlay",
12 | visible: true,
13 | anchor: ["top"],
14 | child: Widget.EventBox({
15 | onHover: () => {
16 | Indicator.popup(-1);
17 | },
18 | child: Widget.Box({
19 | vertical: true,
20 | className: "osd-window",
21 | css: "min-height: 2px;",
22 | children: [NotificationPopups(), ColorScheme()],
23 | }),
24 | }),
25 | });
--------------------------------------------------------------------------------
/src/share/sleex/modules/indicators/notificationpopups.js:
--------------------------------------------------------------------------------
1 | // This file is for popup notifications
2 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
3 | import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
4 | const { Box } = Widget;
5 | import Notification from '../.commonwidgets/notification.js';
6 |
7 | export default () => Box({
8 | vertical: true,
9 | hpack: 'center',
10 | className: 'osd-notifs spacing-v-5-revealer',
11 | attribute: {
12 | 'map': new Map(),
13 | 'dismiss': (box, id, force = false) => {
14 | if (!id || !box.attribute.map.has(id))
15 | return;
16 | const notifWidget = box.attribute.map.get(id);
17 | if (notifWidget == null || notifWidget.attribute.hovered && !force)
18 | return; // cuz already destroyed
19 |
20 | notifWidget.revealChild = false;
21 | notifWidget.attribute.destroyWithAnims();
22 | box.attribute.map.delete(id);
23 | },
24 | 'notify': (box, id) => {
25 | if (!id || Notifications.dnd) return;
26 | if (!Notifications.getNotification(id)) return;
27 |
28 | box.attribute.map.delete(id);
29 |
30 | const notif = Notifications.getNotification(id);
31 | const newNotif = Notification({
32 | notifObject: notif,
33 | isPopup: true,
34 | });
35 | box.attribute.map.set(id, newNotif);
36 | box.pack_end(box.attribute.map.get(id), false, false, 0);
37 | box.show_all();
38 | },
39 | },
40 | setup: (self) => self
41 | .hook(Notifications, (box, id) => box.attribute.notify(box, id), 'notified')
42 | .hook(Notifications, (box, id) => box.attribute.dismiss(box, id), 'dismissed')
43 | .hook(Notifications, (box, id) => box.attribute.dismiss(box, id, true), 'closed')
44 | ,
45 | });
46 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/lockscreen/auth.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | import pam
4 | import sys
5 | import getpass
6 |
7 | print(pam.authenticate(getpass.getuser(), sys.argv[1]));
--------------------------------------------------------------------------------
/src/share/sleex/modules/lockscreen/main.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | import Lockscreen from '../../services/lockscreen.js';
3 | import Layer from 'gi://GtkLayerShell';
4 | import GLib from 'gi://GLib';
5 | import { BarBattery } from '../bar/normal/spaceright.js';
6 |
7 | const PasswordEntry = () => Widget.Box({
8 | children: [
9 | Widget.Entry({
10 | setup: self => self.hook(Lockscreen, () => self.text = '', 'lock'),
11 | visibility: false,
12 | placeholder_text: 'Password',
13 | on_accept: ({ text }) => Lockscreen.auth(text || ''),
14 | hexpand: true,
15 | }),
16 | ],
17 | });
18 |
19 | const time = Variable("", {
20 | poll: [
21 | userOptions.time.interval,
22 | () => GLib.DateTime.new_now_local().format(userOptions.time.format),
23 | ],
24 | });
25 |
26 | const TopBar = () => Widget.Box({
27 | children: [
28 | Widget.Label({label: time.bind()}),
29 | Widget.Box({ hexpand: true }),
30 | BarBattery(),
31 | ]
32 | });
33 |
34 | const username = () => {
35 | const user = GLib.get_user_name();
36 | return user.charAt(0).toUpperCase() + user.slice(1);
37 | }
38 | const iconFile = `/var/lib/AccountsService/icons/${GLib.get_user_name()}`;
39 |
40 | const LockscreenBox = () => Widget.Box({
41 | className: 'lockscreen-container',
42 | vertical: true,
43 | children: [
44 | TopBar(),
45 | Widget.Box({
46 | css: `background-image: url('${iconFile}');`,
47 | className: 'avatar',
48 | hpack: 'center',
49 | vpack: 'center',
50 | }),
51 | Widget.Label({
52 | label: `Welcome back, ${username()}`,
53 | className: 'txt txt-larger',
54 | css: 'margin: 0 2rem 0 2rem;',
55 | }),
56 | PasswordEntry(),
57 | ],
58 | });
59 |
60 | export default (monitor = 0) => {
61 | const win = Widget.Window({
62 | name: `lockscreen${monitor}`,
63 | className: 'lockscreen',
64 | monitor,
65 | layer: 'overlay',
66 | visible: false,
67 | setup: self => self.hook(Lockscreen, (_, lock) => self.visible = lock, 'lock'),
68 | child: Widget.Overlay({
69 | child: Widget.Box({
70 | css: 'min-width: 3000px; min-height: 2000px;',
71 | child: Widget.Box({
72 | className: 'content',
73 | vertical: true,
74 | hexpand: true,
75 | vexpand: true,
76 | hpack: 'center',
77 | vpack: 'center',
78 | children: [
79 | LockscreenBox(),
80 | ],
81 | }),
82 | }),
83 | }),
84 | });
85 |
86 | Layer.set_keyboard_mode(win, Layer.KeyboardMode.EXCLUSIVE);
87 | return win;
88 | };
--------------------------------------------------------------------------------
/src/share/sleex/modules/overview/actions.js:
--------------------------------------------------------------------------------
1 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
2 | import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
3 |
4 | function moveClientToWorkspace(address, workspace) {
5 | Utils.execAsync(['bash', '-c', `hyprctl dispatch movetoworkspacesilent ${workspace},address:${address} &`]);
6 | }
7 |
8 | export function dumpToWorkspace(from, to) {
9 | if (from == to) return;
10 | Hyprland.clients.forEach(client => {
11 | if (client.workspace.id == from) {
12 | moveClientToWorkspace(client.address, to);
13 | }
14 | });
15 | }
16 |
17 | export function swapWorkspace(workspaceA, workspaceB) {
18 | if (workspaceA == workspaceB) return;
19 | const clientsA = [];
20 | const clientsB = [];
21 | Hyprland.clients.forEach(client => {
22 | if (client.workspace.id == workspaceA) clientsA.push(client.address);
23 | if (client.workspace.id == workspaceB) clientsB.push(client.address);
24 | });
25 |
26 | clientsA.forEach((address) => moveClientToWorkspace(address, workspaceB));
27 | clientsB.forEach((address) => moveClientToWorkspace(address, workspaceA));
28 | }
--------------------------------------------------------------------------------
/src/share/sleex/modules/overview/main.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 | import { SearchAndWindows } from "./windowcontent.js";
3 | import PopupWindow from '../.widgethacks/popupwindow.js';
4 | import { clickCloseRegion } from '../.commonwidgets/clickcloseregion.js';
5 |
6 | export default (id = '') => PopupWindow({
7 | name: `overview${id}`,
8 | // exclusivity: 'ignore',
9 | keymode: 'on-demand',
10 | visible: false,
11 | anchor: ['top', 'bottom', 'left', 'right'],
12 | layer: 'top',
13 | child: Widget.Box({
14 | vertical: true,
15 | children: [
16 | clickCloseRegion({ name: 'overview', multimonitor: false, expand: false }),
17 | Widget.Box({
18 | children: [
19 | clickCloseRegion({ name: 'overview', multimonitor: false }),
20 | SearchAndWindows(),
21 | clickCloseRegion({ name: 'overview', multimonitor: false }),
22 | ]
23 | }),
24 | clickCloseRegion({ name: 'overview', multimonitor: false }),
25 | ]
26 | }),
27 | })
28 |
29 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/overview/searchitem.js:
--------------------------------------------------------------------------------
1 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
2 |
3 | export const searchItem = ({ materialIconName, name, actionName, content, onActivate, extraClassName = '', ...rest }) => {
4 | const actionText = Widget.Revealer({
5 | revealChild: false,
6 | transition: "crossfade",
7 | transitionDuration: userOptions.animations.durationLarge,
8 | child: Widget.Label({
9 | className: 'overview-search-results-txt txt txt-small txt-action',
10 | label: `${actionName}`,
11 | })
12 | });
13 | const actionTextRevealer = Widget.Revealer({
14 | revealChild: false,
15 | transition: "slide_left",
16 | transitionDuration: userOptions.animations.durationSmall,
17 | child: actionText,
18 | })
19 | return Widget.Button({
20 | className: `overview-search-result-btn txt ${extraClassName}`,
21 | onClicked: onActivate,
22 | child: Widget.Box({
23 | children: [
24 | Widget.Box({
25 | vertical: false,
26 | children: [
27 | Widget.Label({
28 | className: `icon-material overview-search-results-icon`,
29 | label: `${materialIconName}`,
30 | }),
31 | Widget.Box({
32 | vertical: true,
33 | children: [
34 | Widget.Label({
35 | hpack: 'start',
36 | className: 'overview-search-results-txt txt-smallie txt-subtext',
37 | label: `${name}`,
38 | truncate: "end",
39 | }),
40 | Widget.Label({
41 | hpack: 'start',
42 | className: 'overview-search-results-txt txt-norm',
43 | label: `${content}`,
44 | truncate: "end",
45 | }),
46 | ]
47 | }),
48 | Widget.Box({ hexpand: true }),
49 | actionTextRevealer,
50 | ],
51 | })
52 | ]
53 | }),
54 | setup: (self) => self
55 | .on('focus-in-event', (button) => {
56 | actionText.revealChild = true;
57 | actionTextRevealer.revealChild = true;
58 | })
59 | .on('focus-out-event', (button) => {
60 | actionText.revealChild = false;
61 | actionTextRevealer.revealChild = false;
62 | })
63 | ,
64 | });
65 | }
66 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/session/main.js:
--------------------------------------------------------------------------------
1 | import SessionScreen from "./sessionscreen.js";
2 | import PopupWindow from '../.widgethacks/popupwindow.js';
3 |
4 | export default (id = 0) => PopupWindow({ // On-screen keyboard
5 | monitor: id,
6 | name: `session${id}`,
7 | visible: false,
8 | keymode: 'on-demand',
9 | layer: 'overlay',
10 | exclusivity: 'ignore',
11 | anchor: ['top', 'bottom', 'left', 'right'],
12 | child: SessionScreen({ id: id }),
13 | })
14 |
--------------------------------------------------------------------------------
/src/share/sleex/modules/sideleft/main.js:
--------------------------------------------------------------------------------
1 | import PopupWindow from '../.widgethacks/popupwindow.js';
2 | import SidebarLeft from "./sideleft.js";
3 | import Widget from 'resource:///com/github/Aylur/ags/widget.js';
4 | const { Box } = Widget;
5 | import clickCloseRegion from '../.commonwidgets/clickcloseregion.js';
6 |
7 | export default () => PopupWindow({
8 | keymode: 'on-demand',
9 | anchor: ['left', 'top', 'bottom'],
10 | name: 'sideleft',
11 | layer: 'top',
12 | child: Box({
13 | children: [
14 | SidebarLeft(),
15 | clickCloseRegion({ name: 'sideleft', multimonitor: false, fillMonitor: 'horizontal' }),
16 | ]
17 | })
18 | });
19 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/README.md:
--------------------------------------------------------------------------------
1 | # scripts folder
2 | - For ARM devices, you have to compile C++ files yourself. If there are none, yippee
3 | - It is advised to use services instead of listening/polling scripts, so everything here are just scripts for actions
--------------------------------------------------------------------------------
/src/share/sleex/scripts/color_generation/pywal_to_material.scss:
--------------------------------------------------------------------------------
1 | $primary: lighten($color4, 20%);
2 | $onPrimary: darken($color2, 20%);
3 | $primaryContainer: darken($color2, 10%);
4 | $onPrimaryContainer: lighten($color4, 10%);
5 | $secondary: desaturate(lighten($color5, 20%), 20%);
6 | $onSecondary: desaturate(darken($color3, 20%), 20%);
7 | $secondaryContainer: desaturate(darken($color3, 20%), 20%);
8 | $onSecondaryContainer: desaturate(lighten($color5, 20%), 20%);
9 | $tertiary: adjust-hue(lighten($color4, 20%), 30deg);
10 | $onTertiary: adjust-hue(darken($color2, 20%), 30deg);
11 | $tertiaryContainer: adjust-hue(darken($color2, 10%), 30deg);
12 | $tertiaryContainer: adjust-hue(lighten($color4, 10%), 30deg);
13 | $error: #ffb4a9;
14 | $onError: #680003;
15 | $errorContainer: #930006;
16 | $onErrorContainer: #ffb4a9;
17 | $colorbarbg: $color0;
18 | $background: $color0;
19 | $onBackground: $color7;
20 | $surface: $color0;
21 | $onSurface: $color7;
22 | $surfaceVariant: $color1;
23 | $onSurfaceVariant: $color7;
24 | $outline: $color7;
25 | $shadow: #000000;
26 | $inverseSurface: invert($surface);
27 | $inverseOnSurface: invert($onSurface);
28 | $inversePrimary: invert($primary);
29 |
30 | .primary { color: $primary; }
31 | .onPrimary { color: $onPrimary; }
32 | .primaryContainer { color: $primaryContainer; }
33 | .onPrimaryContainer { color: $onPrimaryContainer; }
34 | .secondary { color: $secondary; }
35 | .onSecondary { color: $onSecondary; }
36 | .secondaryContainer { color: $secondaryContainer; }
37 | .onSecondaryContainer { color: $onSecondaryContainer; }
38 | .tertiary { color: $tertiary; }
39 | .onTertiary { color: $onTertiary; }
40 | .tertiaryContainer { color: $tertiaryContainer; }
41 | .onTertiaryContainer { color: $tertiaryContainer; }
42 | .error { color: $error; }
43 | .onError { color: $onError; }
44 | .errorContainer { color: $errorContainer; }
45 | .onErrorContainer { color: $onErrorContainer; }
46 | .colorbarbg { color: $colorbarbg; }
47 | .background { color: $background; }
48 | .onBackground { color: $onBackground; }
49 | .surface { color: $surface; }
50 | .onSurface { color: $onSurface; }
51 | .surfaceVariant { color: $surfaceVariant; }
52 | .onSurfaceVariant { color: $onSurfaceVariant; }
53 | .outline { color: $outline; }
54 | .shadow { color: $shadow; }
55 | .inverseSurface { color: $inverseSurface; }
56 | .inverseOnSurface { color: $inverseOnSurface; }
57 | .inversePrimary { color: $inversePrimary; }
58 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/color_generation/randomwall.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
3 | CONFIG_DIR="/usr/share/sleex"
4 | USER_WALLPAPER_DIR="$HOME/.sleex/wallpapers"
5 | $CONFIG_DIR/scripts/color_generation/switchwall.sh "$USER_WALLPAPER_DIR -e .png -e .jpg -e .svg | xargs shuf -n1 -e)"
6 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/color_generation/schemes/__pycache__/scheme_morevibrant.cpython-313.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/scripts/color_generation/schemes/__pycache__/scheme_morevibrant.cpython-313.pyc
--------------------------------------------------------------------------------
/src/share/sleex/scripts/color_generation/schemes/scheme_morevibrant.py:
--------------------------------------------------------------------------------
1 | from materialyoucolor.scheme.dynamic_scheme import DynamicSchemeOptions, DynamicScheme
2 | from materialyoucolor.scheme.variant import Variant
3 | from materialyoucolor.palettes.tonal_palette import TonalPalette
4 |
5 |
6 | class SchemeMoreVibrant(DynamicScheme):
7 | hues = [0.0, 41.0, 61.0, 101.0, 131.0, 181.0, 251.0, 301.0, 360.0]
8 | secondary_rotations = [18.0, 15.0, 10.0, 12.0, 15.0, 18.0, 15.0, 12.0, 12.0]
9 | tertiary_rotations = [35.0, 30.0, 20.0, 25.0, 30.0, 35.0, 30.0, 25.0, 25.0]
10 |
11 | def __init__(self, source_color_hct, is_dark, contrast_level):
12 | super().__init__(
13 | DynamicSchemeOptions(
14 | source_color_hct=source_color_hct,
15 | variant=Variant.VIBRANT,
16 | contrast_level=contrast_level,
17 | is_dark=is_dark,
18 | primary_palette=TonalPalette.from_hue_and_chroma(
19 | source_color_hct.hue, 200.0
20 | ),
21 | secondary_palette=TonalPalette.from_hue_and_chroma(
22 | DynamicScheme.get_rotated_hue(
23 | source_color_hct,
24 | SchemeMoreVibrant.hues,
25 | SchemeMoreVibrant.secondary_rotations,
26 | ),
27 | 32.0,
28 | ),
29 | tertiary_palette=TonalPalette.from_hue_and_chroma(
30 | DynamicScheme.get_rotated_hue(
31 | source_color_hct,
32 | SchemeMoreVibrant.hues,
33 | SchemeMoreVibrant.tertiary_rotations,
34 | ),
35 | 32.0,
36 | ),
37 | neutral_palette=TonalPalette.from_hue_and_chroma(
38 | source_color_hct.hue, 13.0
39 | ),
40 | neutral_variant_palette=TonalPalette.from_hue_and_chroma(
41 | source_color_hct.hue, 15.0
42 | ),
43 | )
44 | )
45 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/color_generation/specials/_material_badapple-l.scss:
--------------------------------------------------------------------------------
1 | $darkmode: false;
2 | $primary: #000000;
3 | $onPrimary: #FFFFFF ;
4 | $primaryContainer: #d4d4d4;
5 | $onPrimaryContainer: #000000;
6 | $secondary: #000000;
7 | $onSecondary: #FFFFFF ;
8 | $secondaryContainer: #bebebe;
9 | $onSecondaryContainer: #000000;
10 | $tertiary: #000000;
11 | $onTertiary: #FFFFFF ;
12 | $tertiaryContainer: #FFFFFF ;
13 | $onTertiaryContainer: #000000;
14 | $error: #000000;
15 | $onError: #FFFFFF ;
16 | $errorContainer: #FFFFFF ;
17 | $onErrorContainer: #000000;
18 | $colorbarbg: #FFFFFF ;
19 | $background: #FFFFFF ;
20 | $onBackground: #000000;
21 | $surface: #f0f0f0;
22 | $onSurface: #000000;
23 | $surfaceVariant: #dddddd;
24 | $onSurfaceVariant: #000000;
25 | $outline: #525252;
26 | $shadow: #000000 ;
27 | $inverseSurface: #000000;
28 | $inverseOnSurface: #FFFFFF;
29 | $inversePrimary: #000000;
30 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/color_generation/specials/_material_badapple.scss:
--------------------------------------------------------------------------------
1 | $darkmode: true;
2 | $primary: #e2e2e2;
3 | $onPrimary: #000000;
4 | $primaryContainer: #6b6b6b;
5 | $onPrimaryContainer: #e2e2e2;
6 | $secondary: #e2e2e2;
7 | $onSecondary: #000000;
8 | $secondaryContainer: #313131;
9 | $onSecondaryContainer: #e2e2e2;
10 | $tertiary: #e2e2e2;
11 | $onTertiary: #000000;
12 | $tertiaryContainer: #000000;
13 | $onTertiaryContainer: #e2e2e2;
14 | $error: #e2e2e2;
15 | $onError: #000000;
16 | $errorContainer: #000000;
17 | $onErrorContainer: #e2e2e2;
18 | $colorbarbg: #000000;
19 | $background: #000000;
20 | $onBackground: #e2e2e2;
21 | $surface: #161616;
22 | $onSurface: #e2e2e2;
23 | $surfaceVariant: #242424;
24 | $onSurfaceVariant: #e2e2e2;
25 | $outline: #a1a1a1;
26 | $shadow: #000000;
27 | $inverseSurface: #e2e2e2;
28 | $inverseOnSurface: #000000;
29 | $inversePrimary: #e2e2e2;
30 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/color_generation/switchcolor.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
4 | XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
5 | XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
6 | CONFIG_DIR="/usr/share/sleex"
7 | CACHE_DIR="$XDG_CACHE_HOME/ags"
8 | STATE_DIR="$XDG_STATE_HOME/ags"
9 |
10 | COLORMODE_FILE_DIR="$STATE_DIR/user/colormode.txt"
11 |
12 | if [ "$1" == "--pick" ]; then
13 | color=$(hyprpicker --no-fancy)
14 | elif [[ "$1" = "#"* ]]; then # this is a color
15 | color=$1
16 | else
17 | color=$(cut -f1 "$STATE_DIR/user/color.txt")
18 | fi
19 |
20 | sed -i "1s/.*/$color/" "$STATE_DIR/user/color.txt"
21 |
22 | # Generate colors for ags n stuff
23 | "$CONFIG_DIR"/scripts/color_generation/colorgen.sh "${color}" --apply
24 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/color_generation/switchwall.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
4 | CONFIG_DIR="/usr/share/sleex"
5 |
6 | switch() {
7 | imgpath=$1
8 | read scale screenx screeny screensizey < <(hyprctl monitors -j | jq '.[] | select(.focused) | .scale, .x, .y, .height' | xargs)
9 | cursorposx=$(hyprctl cursorpos -j | jq '.x' 2>/dev/null) || cursorposx=960
10 | cursorposx=$(bc <<< "scale=0; ($cursorposx - $screenx) * $scale / 1")
11 | cursorposy=$(hyprctl cursorpos -j | jq '.y' 2>/dev/null) || cursorposy=540
12 | cursorposy=$(bc <<< "scale=0; ($cursorposy - $screeny) * $scale / 1")
13 | cursorposy_inverted=$((screensizey - cursorposy))
14 |
15 | if [ "$imgpath" == '' ]; then
16 | echo 'Aborted'
17 | exit 0
18 | fi
19 |
20 | # ags run-js "wallpaper.set('')"
21 | # sleep 0.1 && ags run-js "wallpaper.set('${imgpath}')" &
22 | swww img "$imgpath" --transition-step 100 --transition-fps 120 \
23 | --transition-type grow --transition-angle 30 --transition-duration 1 \
24 | --transition-pos "$cursorposx, $cursorposy_inverted"
25 | }
26 |
27 | imgpath=""
28 |
29 | while [[ "$#" -gt 0 ]]; do
30 | case $1 in
31 | --noswitch) noswitch=true ;;
32 | --path) imgpath="$2"; shift ;;
33 | *) imgpath="$1" ;;
34 | esac
35 | shift
36 | done
37 |
38 | if [ "$noswitch" == true ]; then
39 | imgpath=$(swww query | awk -F 'image: ' '{print $2}')
40 | # imgpath=$(ags run-js 'wallpaper.get(0)')
41 | elif [[ "$imgpath" ]]; then
42 | switch "$imgpath"
43 | else
44 | # Select and set image (hyprland)
45 | cd "$(xdg-user-dir PICTURES)" || return 1
46 | switch "$(yad --width 1200 --height 800 --file --add-preview --large-preview --title='Choose wallpaper')"
47 | fi
48 |
49 | # Generate colors for ags n stuff
50 | "$CONFIG_DIR"/scripts/color_generation/colorgen.sh "${imgpath}" --apply --smart
51 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/dnd.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # File to track DND state
4 | STATE_FILE="$HOME/.cache/ags/dnd_state"
5 |
6 | if [ ! -f $STATE_FILE ]; then
7 | # If state file doesn't exist, create it and enable DND
8 | touch $STATE_FILE
9 | dbus-send --session --dest=org.freedesktop.Notifications \
10 | --type=method_call /org/freedesktop/Notifications \
11 | org.freedesktop.Notifications.Inhibit string:"DoNotDisturb"
12 | echo "Do Not Disturb Enabled"
13 | else
14 | # If state file exists, remove it and disable DND
15 | rm $STATE_FILE
16 | dbus-send --session --dest=org.freedesktop.Notifications \
17 | --type=method_call /org/freedesktop/Notifications \
18 | org.freedesktop.Notifications.UnInhibit string:"DoNotDisturb"
19 | echo "Do Not Disturb Disabled"
20 | fi
21 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/firstRun.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # This script is run the first time the user logs in after installing Sleex.
4 | # It sets up the user's environment to use Sleex.
5 |
6 | echo 'Setting up Sleex environment...'
7 | echo 'true' > ~/.local/state/ags/user/show_monitor.txt
8 | echo 'true' > ~/.local/state/ags/user/show_timedate.txt
9 | echo 'true' > ~/.local/state/ags/user/show_workspaces.txt
10 | echo 'true' > ~/.local/state/ags/user/show_wintitle.txt
11 |
12 |
13 | CONFIG_FILE="$HOME/.config/hypr/custom/general.conf"
14 | KEYBINDS_DIR="$HOME/.config/hypr/hyprland"
15 | VC_KEYMAP=$(grep -i KEYMAP /etc/vconsole.conf | cut -d= -f2 | tr -d '"')
16 | LAYOUT=$(echo "$VC_KEYMAP" | awk '{print tolower($0)}')
17 |
18 | echo "Setting keyboard layout to '$LAYOUT' in $CONFIG_FILE."
19 |
20 | if grep -q "^input:kb_layout" "$CONFIG_FILE"; then
21 | sed -i "s/^input:kb_layout.*/input:kb_layout = $LAYOUT/" "$CONFIG_FILE"
22 | else
23 | echo "input:kb_layout = $LAYOUT" >> "$CONFIG_FILE"
24 | fi
25 |
26 | echo "Applying keybinds configuration in $KEYBINDS_DIR."
27 |
28 | if [[ "$LAYOUT" == "us" ]]; then
29 | mv -f "$KEYBINDS_DIR/keybinds_us.conf" "$KEYBINDS_DIR/keybinds.conf"
30 | echo "Switched to US keybinds."
31 | elif [[ "$LAYOUT" == "fr" ]]; then
32 | mv -f "$KEYBINDS_DIR/keybinds_fr.conf" "$KEYBINDS_DIR/keybinds.conf"
33 | echo "Switched to FR keybinds."
34 | else
35 | echo "Unknown layout: $LAYOUT – applying US keybinds."
36 | mv -f "$KEYBINDS_DIR/keybinds_us.conf" "$KEYBINDS_DIR/keybinds.conf"
37 |
38 | fi
39 |
40 | hyprctl reload
41 |
42 | echo 'Complete!'
--------------------------------------------------------------------------------
/src/share/sleex/scripts/generate_thumbnails.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | set -u
4 |
5 | THUMBNAIL_DIR="$HOME/.sleex/wallpapers/thumbnails"
6 | WALLPAPER_DIR="$HOME/.sleex/wallpapers"
7 |
8 | mkdir -p "$THUMBNAIL_DIR"
9 |
10 | for image in "$WALLPAPER_DIR"/*.{jpg,JPG,jpeg,JPEG,png,PNG,gif,GIF,webp,WEBP}; do
11 | [ -e "$image" ] || continue
12 |
13 | filename=$(basename "$image")
14 |
15 | if [ -f "$THUMBNAIL_DIR/$filename" ]; then
16 | echo "Skipping $filename - thumbnail already exists"
17 | continue
18 | fi
19 |
20 | if [[ "$image" =~ \.gif$|\.GIF$ ]]; then
21 | magick "$image[0]" -resize 150x90^ -gravity center -extent 150x90 "$THUMBNAIL_DIR/$filename"
22 | else
23 | magick "$image" -resize 150x90^ -gravity center -extent 150x90 "$THUMBNAIL_DIR/$filename"
24 | fi
25 | done
26 |
27 | notify-send "Thumbnails generation complete"
--------------------------------------------------------------------------------
/src/share/sleex/scripts/hyprland/workspace_action.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | hyprctl dispatch "$1" $(((($(hyprctl activeworkspace -j | jq -r .id) - 1) / 10) * 10 + $2))
3 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/lockscreen/battery-status:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # status="$(acpi -b | grep -ioh "\w*charging\w*")"
4 | # level="$(acpi -b | grep -o -P "[0-9]+(?=%)")"
5 |
6 | status="$(cat /sys/class/power_supply/BAT0/status)"
7 | level="$(cat /sys/class/power_supply/BAT0/capacity)"
8 |
9 | if [[ ("$status" == "Discharging") || ("$status" == "Full") ]]; then
10 | if [[ "$level" -eq "0" ]]; then
11 | printf " "
12 | elif [[ ("$level" -ge "0") && ("$level" -le "25") ]]; then
13 | printf " "
14 | elif [[ ("$level" -ge "25") && ("$level" -le "50") ]]; then
15 | printf " "
16 | elif [[ ("$level" -ge "50") && ("$level" -le "75") ]]; then
17 | printf " "
18 | elif [[ ("$level" -ge "75") && ("$level" -le "100") ]]; then
19 | printf " "
20 | fi
21 | elif [[ "$status" == "Charging" ]]; then
22 | printf " "
23 | fi
24 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/lockscreen/song-status:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | player_name=$(playerctl metadata --format '{{playerName}}')
3 | player_status=$(playerctl status)
4 |
5 | if [[ "$player_status" == "Playing" ]]; then
6 | song_info=$(playerctl metadata --format '{{title}} | {{artist}}')
7 | fi
8 |
9 | echo "$song_info"
10 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/network_scripts/network_bandwidth.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from time import sleep
4 | import sys
5 | import psutil
6 |
7 | try:
8 | direction = sys.argv[1]
9 | except IndexError:
10 | direction = "recv"
11 |
12 | init_bytes = final_bytes = 0
13 |
14 | match direction:
15 | case "recv":
16 | init_bytes = psutil.net_io_counters().bytes_recv
17 | sleep(1)
18 | final_bytes = psutil.net_io_counters().bytes_recv
19 |
20 | case "sent":
21 | init_bytes = psutil.net_io_counters().bytes_sent
22 | sleep(1)
23 | final_bytes = psutil.net_io_counters().bytes_sent
24 |
25 | case _:
26 | print(f"wrong direction: {direction}")
27 | sys.exit()
28 |
29 | i = 0
30 | divider = 1000
31 | bandwidth = int((final_bytes - init_bytes))
32 | units = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]
33 |
34 | while bandwidth >= divider:
35 | i += 1
36 | bandwidth = bandwidth / divider
37 |
38 | print(f"{bandwidth:.1f}" + units[i] + "/s")
39 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/record-script.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | getdate() {
4 | date '+%Y-%m-%d_%H.%M.%S'
5 | }
6 | getaudiooutput() {
7 | pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
8 | }
9 | getactivemonitor() {
10 | hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
11 | }
12 |
13 | mkdir -p "$(xdg-user-dir VIDEOS)"
14 | cd "$(xdg-user-dir VIDEOS)" || exit
15 | if pgrep wf-recorder > /dev/null; then
16 | notify-send "Recording Stopped" "Stopped" -a 'record-script.sh' &
17 | pkill wf-recorder &
18 | else
19 | notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'record-script.sh'
20 | if [[ "$1" == "--sound" ]]; then
21 | wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)" --audio="$(getaudiooutput)" & disown
22 | elif [[ "$1" == "--fullscreen-sound" ]]; then
23 | wf-recorder -o $(getactivemonitor) --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)" & disown
24 | elif [[ "$1" == "--fullscreen" ]]; then
25 | wf-recorder -o $(getactivemonitor) --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t & disown
26 | else
27 | wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)" & disown
28 | fi
29 | fi
30 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/sway/swayToRelativeWs.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Check if sway is running
4 | if ! pgrep -x sway > /dev/null; then
5 | echo "Sway is not running"
6 | exit 1
7 | fi
8 |
9 |
10 | # Get the current workspace number
11 | current=$(swaymsg -t get_workspaces | jq '.[] | select(.focused==true) | .num')
12 |
13 | # Check if a number was passed as an argument
14 | if [[ "$1" =~ ^[+-]?[0-9]+$ ]]; then
15 | new_workspace=$((current + $1))
16 | else
17 | new_workspace=$((current + 1))
18 | fi
19 |
20 | # Check if the new workspace number is out of bounds
21 | if [[ $new_workspace -lt 1 ]]; then
22 | exit 0
23 | fi
24 |
25 | # Switch to the new workspace
26 | if [[ $2 == 'move' ]]; then
27 | swaymsg move container to workspace $new_workspace
28 | else
29 | swaymsg workspace $new_workspace
30 | fi
31 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/templates/fuzzel/fuzzel.ini:
--------------------------------------------------------------------------------
1 | font=Gabarito
2 | terminal=foot -e
3 | prompt=">> "
4 | layer=overlay
5 |
6 | [colors]
7 | background={{ $background }}ff
8 | text={{ $onBackground }}ff
9 | selection={{ $surfaceVariant }}ff
10 | selection-text={{ $onSurfaceVariant }}ff
11 | border={{ $surfaceVariant }}dd
12 | match={{ $primary }}ff
13 | selection-match={{ $primary }}ff
14 |
15 |
16 | [border]
17 | radius=17
18 | width=1
19 |
20 | [dmenu]
21 | exit-immediately-if-empty=yes
22 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/templates/gtk/gtk-colors.css:
--------------------------------------------------------------------------------
1 | /*
2 | * GTK Colors
3 | */
4 |
5 | @define-color accent_color {{ $primary }};
6 | @define-color accent_fg_color {{ $onPrimary }};
7 | @define-color accent_bg_color {{ $primary }};
8 | @define-color window_bg_color {{ $background }};
9 | @define-color window_fg_color {{ $onBackground }};
10 | @define-color headerbar_bg_color {{ $surfaceDim }};
11 | @define-color headerbar_fg_color {{ $onSurface }};
12 | @define-color popover_bg_color {{ $surfaceDim }};
13 | @define-color popover_fg_color {{ $onSurface }};
14 | @define-color view_bg_color {{ $surface }};
15 | @define-color view_fg_color {{ $onSurface }};
16 | @define-color card_bg_color {{ $surface }};
17 | @define-color card_fg_color {{ $onSurface }};
18 | @define-color sidebar_bg_color @window_bg_color;
19 | @define-color sidebar_fg_color @window_fg_color;
20 | @define-color sidebar_border_color @window_bg_color;
21 | @define-color sidebar_backdrop_color @window_bg_color;
--------------------------------------------------------------------------------
/src/share/sleex/scripts/templates/terminal/scheme-base.json:
--------------------------------------------------------------------------------
1 | {
2 | "dark": {
3 | "term0" : "#282828",
4 | "term1" : "#CC241D",
5 | "term2" : "#98971A",
6 | "term3" : "#D79921",
7 | "term4" : "#458588",
8 | "term5" : "#B16286",
9 | "term6" : "#689D6A",
10 | "term7" : "#A89984",
11 | "term8" : "#928374",
12 | "term9" : "#FB4934",
13 | "term10" : "#B8BB26",
14 | "term11" : "#FABD2F",
15 | "term12" : "#83A598",
16 | "term13" : "#D3869B",
17 | "term14" : "#8EC07C",
18 | "term15" : "#EBDBB2"
19 | },
20 | "light": {
21 | "term0" : "#FDF9F3",
22 | "term1" : "#FF6188",
23 | "term2" : "#A9DC76",
24 | "term3" : "#FC9867",
25 | "term4" : "#FFD866",
26 | "term5" : "#F47FD4",
27 | "term6" : "#78DCE8",
28 | "term7" : "#333034",
29 | "term8" : "#121212",
30 | "term9" : "#FF6188",
31 | "term10" : "#A9DC76",
32 | "term11" : "#FC9867",
33 | "term12" : "#FFD866",
34 | "term13" : "#F47FD4",
35 | "term14" : "#78DCE8",
36 | "term15" : "#333034"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/templates/terminal/scheme-monochrome.json:
--------------------------------------------------------------------------------
1 | {
2 | "dark": {
3 | "term0": "#000000",
4 | "term1": "#FFFFFF",
5 | "term2": "#CCCCCC",
6 | "term3": "#8f8f8f",
7 | "term4": "#FFFFFF",
8 | "term5": "#111111",
9 | "term6": "#CCCCCC",
10 | "term7": "#FFFFFF",
11 | "term8": "#404040",
12 | "term9": "#CCCCCC",
13 | "term10": "#FFFFFF",
14 | "term11": "#909090",
15 | "term12": "#CCCCCC",
16 | "term13": "#808080",
17 | "term14": "#CCCCCC",
18 | "term15": "#FFFFFF"
19 | },
20 | "light": {
21 | "term0": "#EAE9EA",
22 | "term1": "#777777",
23 | "term2": "#000000",
24 | "term3": "#000000",
25 | "term4": "#000000",
26 | "term5": "#000000",
27 | "term6": "#000000",
28 | "term7": "#202020",
29 | "term8": "#000000",
30 | "term9": "#000000",
31 | "term10": "#CCCCCC",
32 | "term11": "#808080",
33 | "term12": "#CCCCCC",
34 | "term13": "#FFFFFF"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/templates/terminal/sequences.txt:
--------------------------------------------------------------------------------
1 | ]4;0;#$term0 #\]4;1;#$term1 #\]4;2;#$term2 #\]4;3;#$term3 #\]4;4;#$term4 #\]4;5;#$term5 #\]4;6;#$term6 #\]4;7;#$term7 #\]4;8;#$term8 #\]4;9;#$term9 #\]4;10;#$term10 #\]4;11;#$term11 #\]4;12;#$term12 #\]4;13;#$term13 #\]4;14;#$term14 #\]4;15;#$term15 #\]10;#$term7 #\]11;[$alpha]#$term0 #\]12;#$term7 #\]13;#$term7 #\]17;#$term7 #\]19;#$term0 #\]4;232;#$term7 #\]4;256;#$term7 #\]708;[$alpha]#$term0 #\
2 |
--------------------------------------------------------------------------------
/src/share/sleex/scripts/wayland-idle-inhibitor.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import sys
4 | from dataclasses import dataclass
5 | from signal import SIGINT, SIGTERM, signal
6 | from threading import Event
7 | import setproctitle
8 |
9 | from pywayland.client.display import Display
10 | from pywayland.protocol.idle_inhibit_unstable_v1.zwp_idle_inhibit_manager_v1 import (
11 | ZwpIdleInhibitManagerV1,
12 | )
13 | from pywayland.protocol.wayland.wl_compositor import WlCompositor
14 | from pywayland.protocol.wayland.wl_registry import WlRegistryProxy
15 | from pywayland.protocol.wayland.wl_surface import WlSurface
16 |
17 |
18 | @dataclass
19 | class GlobalRegistry:
20 | surface: WlSurface | None = None
21 | inhibit_manager: ZwpIdleInhibitManagerV1 | None = None
22 |
23 |
24 | def handle_registry_global(
25 | wl_registry: WlRegistryProxy, id_num: int, iface_name: str, version: int
26 | ) -> None:
27 | global_registry: GlobalRegistry = wl_registry.user_data or GlobalRegistry()
28 |
29 | if iface_name == "wl_compositor":
30 | compositor = wl_registry.bind(id_num, WlCompositor, version)
31 | global_registry.surface = compositor.create_surface() # type: ignore
32 | elif iface_name == "zwp_idle_inhibit_manager_v1":
33 | global_registry.inhibit_manager = wl_registry.bind(
34 | id_num, ZwpIdleInhibitManagerV1, version
35 | )
36 |
37 |
38 | def main() -> None:
39 | done = Event()
40 | signal(SIGINT, lambda _, __: done.set())
41 | signal(SIGTERM, lambda _, __: done.set())
42 |
43 | global_registry = GlobalRegistry()
44 |
45 | display = Display()
46 | display.connect()
47 |
48 | registry = display.get_registry() # type: ignore
49 | registry.user_data = global_registry
50 | registry.dispatcher["global"] = handle_registry_global
51 |
52 | def shutdown() -> None:
53 | display.dispatch()
54 | display.roundtrip()
55 | display.disconnect()
56 |
57 | display.dispatch()
58 | display.roundtrip()
59 |
60 | if global_registry.surface is None or global_registry.inhibit_manager is None:
61 | print("Wayland seems not to support idle_inhibit_unstable_v1 protocol.")
62 | shutdown()
63 | sys.exit(1)
64 |
65 | inhibitor = global_registry.inhibit_manager.create_inhibitor( # type: ignore
66 | global_registry.surface
67 | )
68 |
69 | display.dispatch()
70 | display.roundtrip()
71 |
72 | print("Inhibiting idle...")
73 | done.wait()
74 | print("Shutting down...")
75 |
76 | inhibitor.destroy()
77 |
78 | shutdown()
79 |
80 |
81 | if __name__ == "__main__":
82 | setproctitle.setproctitle("wayland-idle-inhibitor.py")
83 | main()
84 |
--------------------------------------------------------------------------------
/src/share/sleex/scss/_desktopbackground.scss:
--------------------------------------------------------------------------------
1 | .bg-wallpaper-transition {
2 | transition: 1000ms cubic-bezier(0.05, 0.7, 0.1, 1);
3 | font-size: 1px;
4 | }
5 |
6 | @mixin bg-textshadow {
7 | // text-shadow: mix($shadow, $secondaryContainer, 50%) 1px 0px 3px;
8 | }
9 |
10 | .bg-time-box {
11 | @include large-rounding;
12 | margin: 2.045rem;
13 | padding: 0.682rem;
14 | margin-bottom: 15rem;
15 | }
16 |
17 | .bg-weather-box {
18 | padding: 0.682rem;
19 | margin-bottom: 15rem;
20 | }
21 |
22 | .bg-time-clock {
23 | @include titlefont;
24 | @include bg-textshadow;
25 | font-weight: 800;
26 | font-size: 10.091rem;
27 | color: $onLayer0;
28 | }
29 |
30 | .bg-time-date {
31 | @include titlefont;
32 | @include bg-textshadow;
33 | font-size: 2.591rem;
34 | color: $onLayer0;
35 | }
36 |
37 | .bg-distro-box {
38 | @include large-rounding;
39 | margin: 2.045rem;
40 | padding: 0.682rem;
41 | }
42 |
43 | .bg-distro-txt {
44 | @include titlefont;
45 | @include bg-textshadow;
46 | font-size: 1.432rem;
47 | color: $onLayer0;
48 | }
49 |
50 | .bg-distro-name {
51 | @include titlefont;
52 | @include bg-textshadow;
53 | font-size: 1.432rem;
54 | color: $onSecondaryContainer;
55 | }
56 |
57 | .bg-graph {
58 | color: rgba(255, 255, 255, 0.5);
59 | border-radius: 0.614rem;
60 | border: 0.682rem solid;
61 | }
62 |
63 | .bg-quicklaunch-title {
64 | @include mainfont;
65 | color: $onSurfaceVariant;
66 | }
67 |
68 | .bg-quicklaunch-btn {
69 | @include mainfont;
70 | @include full-rounding;
71 | background-color: $layer2;
72 | color: $onLayer2;
73 | min-width: 4.432rem;
74 | min-height: 2.045rem;
75 | padding: 0.273rem 0.682rem;
76 | }
77 |
78 | .bg-quicklaunch-btn:hover,
79 | .bg-quicklaunch-btn:focus {
80 | background-color: $layer2Hover;
81 | }
82 |
83 | .bg-quicklaunch-btn:active {
84 | background-color: $layer2Active;
85 | }
86 |
87 | .bg-system-bg {
88 | @include normal-rounding;
89 | // background-color: $background;
90 | }
91 |
92 | .bg-system-circprog {
93 | @include fluent_decel_long;
94 | min-width: 0.205rem; // Trough stroke width
95 | min-height: 4.091rem; // Diameter
96 | font-size: 0px;
97 | padding: 0rem;
98 | background-color: $layer2;
99 | }
100 |
101 | .desktop-weather {
102 | @include normal-rounding;
103 | margin: 2.045rem;
104 | padding: 1rem;
105 | background-color: $layer0;
106 | font-size: 1.5rem;
107 | }
108 |
109 | .txt-weather-location {
110 | margin: 0.682rem 0;
111 | }
112 |
113 | .hours-circprog {
114 | @include fluent_decel_long;
115 | min-width: 1.136rem; // line width
116 | min-height: 15.636rem;
117 | padding: 0rem;
118 | background-color: $battLayer2;
119 | color: $battOnLayer2;
120 | }
121 |
122 | .minutes-circprog {
123 | @include fluent_decel_long;
124 | min-width: 1.136rem; // line width
125 | min-height: 11.636rem;
126 | padding: 0rem;
127 | background-color: $battLayer2;
128 | color: $battOnLayer2;
129 | }
130 |
--------------------------------------------------------------------------------
/src/share/sleex/scss/_dock.scss:
--------------------------------------------------------------------------------
1 | .dock-bg {
2 | @include elevation2;
3 | background-color: $layer0;
4 | padding: 0.682rem;
5 | border-radius: $rounding_large $rounding_large 0rem 0rem;
6 | margin-bottom: 0;
7 | }
8 |
9 | .dock-app-btn-animate {
10 | transition-property: background-color;
11 | transition-duration: 0.5s;
12 | }
13 |
14 | .dock-app-btn {
15 | @include normal-rounding;
16 | padding: 0.273rem;
17 | }
18 |
19 | .pinned-dock-app-btn {
20 | @include normal-rounding;
21 | padding: 0.273rem;
22 | background-color: $layer0Hover;
23 | }
24 |
25 | .dock-app-btn:hover,
26 | .dock-app-btn:focus {
27 | background-color: $layer0Hover;
28 | }
29 |
30 | .dock-app-btn:active {
31 | background-color: $layer0Active;
32 | }
33 |
34 | .dock-app-icon {
35 | min-width: 3.409rem;
36 | min-height: 3.409rem;
37 | font-size: 3.409rem;
38 | }
39 |
40 | .dock-separator {
41 | min-width: 0.068rem;
42 | background-color: $outline;
43 | }
--------------------------------------------------------------------------------
/src/share/sleex/scss/_lockscreen.scss:
--------------------------------------------------------------------------------
1 | .lockscreen {
2 | background-color: rgba(0, 0, 0, 0.25);
3 |
4 | spinner {
5 | margin-top: 0.625rem;
6 | }
7 |
8 | entry {
9 | background-color: $layer1;
10 | margin-top: 1rem;
11 | padding: 1rem;
12 | min-height: 20px;
13 | @include small-rounding;
14 | }
15 |
16 | .avatar {
17 | border-radius: 99px;
18 | min-width: 7rem;
19 | min-height: 7rem;
20 | margin: 1rem;
21 | background-size: cover;
22 | }
23 | }
24 |
25 | .lockscreen-container {
26 | background-color: $layer0;
27 | padding: 2rem;
28 | border: none;
29 | @include normal-rounding;
30 | }
--------------------------------------------------------------------------------
/src/share/sleex/scss/_osk.scss:
--------------------------------------------------------------------------------
1 | $osk_key_height: 2.5rem;
2 | $osk_key_width: 2.5rem;
3 | $osk_key_padding: 0.188rem;
4 | $osk_key_rounding: 0.545rem;
5 | $osk_key_fontsize: 1.091rem;
6 |
7 | .osk-window {
8 | // @include menu_decel_fast;
9 | @include large-rounding;
10 | @include elevation-border;
11 | @include elevation2;
12 | // min-height: 29.591rem;
13 | // min-width: 50rem;
14 | background-color: $layer0;
15 | }
16 |
17 | .osk-body {
18 | padding: 1.023rem;
19 | padding-top: 0rem;
20 | }
21 |
22 | .osk-show {
23 | @include menu_decel_fast;
24 | }
25 |
26 | .osk-hide {
27 | margin-top: 30.682rem;
28 | margin-bottom: -30.682rem;
29 | // opacity: 0;
30 | @include menu_accel_fast;
31 | }
32 |
33 | .osk-dragline {
34 | @include full-rounding;
35 | background-color: $onLayer0Inactive;
36 | min-height: 0.273rem;
37 | min-width: 10.227rem;
38 | margin-top: 0.545rem;
39 | margin-bottom: 0.205rem;
40 | }
41 |
42 | .osk-key {
43 | border-radius: $osk_key_rounding;
44 | background-color: $layer1;
45 | color: $onLayer1;
46 | padding: $osk_key_padding;
47 | font-weight: 500;
48 | font-size: $osk_key_fontsize;
49 | }
50 |
51 | .osk-key:hover,
52 | .osk-key:focus {
53 | background-color: $layer1Hover;
54 | }
55 |
56 | .osk-key:active {
57 | background-color: $layer1Active;
58 | font-size: $osk_key_fontsize;
59 | }
60 |
61 | .osk-key-active {
62 | background-color: $layer1Active;
63 | }
64 |
65 | .osk-key-normal {
66 | min-width: $osk_key_width;
67 | min-height: $osk_key_height;
68 | }
69 |
70 | .osk-key-fn {
71 | min-width: $osk_key_width * 1.005;
72 | min-height: calc($osk_key_height / 2); // dart-sass
73 | // min-height: $osk_key_height / 2; // sassc
74 | }
75 |
76 | .osk-key-tab {
77 | min-width: $osk_key_width * 1.6;
78 | min-height: $osk_key_height;
79 | }
80 |
81 | .osk-key-caps {
82 | min-width: $osk_key_width * 1.9;
83 | min-height: $osk_key_height;
84 | }
85 |
86 | .osk-key-shift {
87 | min-width: $osk_key_width * 2.5;
88 | min-height: $osk_key_height;
89 | }
90 |
91 | .osk-key-control {
92 | min-width: $osk_key_width * 1.3;
93 | min-height: $osk_key_height;
94 | }
95 |
96 | .osk-control-button {
97 | border-radius: $osk_key_rounding;
98 | background-color: $layer1;
99 | color: $onLayer1;
100 | font-weight: 500;
101 | font-size: $osk_key_fontsize;
102 | padding: 0.682rem;
103 | }
104 |
105 | .osk-control-button:hover,
106 | .osk-control-button:focus {
107 | background-color: $layer1Hover;
108 | }
109 |
110 | .osk-control-button:active {
111 | background-color: $layer1Active;
112 | font-size: $osk_key_fontsize;
113 | }
114 |
115 | .osk-key-empty, .osk-key-empty:hover, .osk-key-empty:focus {
116 | min-width: $osk_key_width;
117 | min-height: $osk_key_height;
118 | background-color: transparent;
119 | }
120 |
--------------------------------------------------------------------------------
/src/share/sleex/scss/_overview.scss:
--------------------------------------------------------------------------------
1 | .overview-window {
2 | margin-top: 2.727rem;
3 | }
4 |
5 | .overview-search-box {
6 | @include element_decel;
7 | @include large-rounding;
8 | @include elevation-border;
9 | @include elevation2;
10 | min-width: 13.636rem;
11 | min-height: 3.409rem;
12 | padding: 0rem 1.364rem;
13 | padding-right: 2.864rem;
14 | background-color: $background;
15 | color: $onBackground;
16 |
17 | selection {
18 | background-color: $secondary;
19 | color: $onSecondary;
20 | }
21 |
22 | caret-color: transparent;
23 | }
24 |
25 | .overview-search-box-extended {
26 | min-width: 25.909rem;
27 | caret-color: $onSecondaryContainer;
28 | }
29 |
30 | .overview-search-prompt {
31 | color: $subtext;
32 | }
33 |
34 | .overview-search-icon {
35 | margin: 0rem 1.023rem;
36 | }
37 |
38 | .overview-search-prompt-box {
39 | margin-left: -18.545rem;
40 | margin-right: $elevation_margin + 0.068rem;
41 | }
42 |
43 | .overview-search-icon-box {
44 | margin-left: -18.545rem;
45 | margin-right: $elevation_margin + 0.068rem;
46 | }
47 |
48 | .overview-search-results {
49 | // min-height: 2.813rem;
50 | // min-height: 37.5rem;
51 | @include large-rounding;
52 | @include elevation-border;
53 | @include elevation2;
54 | min-width: 28.773rem;
55 | padding: 0.682rem;
56 | background-color: $layer0;
57 | color: $onLayer0;
58 | }
59 |
60 | .overview-search-results-icon {
61 | margin: 0rem 0.682rem;
62 | font-size: 2.386rem;
63 | min-width: 2.386rem;
64 | min-height: 2.386rem;
65 | }
66 |
67 | .overview-search-results-txt {
68 | margin-right: 0.682rem;
69 | }
70 |
71 | .overview-search-results-txt-cmd {
72 | margin-right: 0.682rem;
73 | @include techfont;
74 | font-size: 1.227rem;
75 | }
76 |
77 | .overview-search-result-btn {
78 | @include normal-rounding;
79 | padding: 0.341rem;
80 | min-width: 2.386rem;
81 | min-height: 2.386rem;
82 |
83 | caret-color: transparent;
84 | }
85 |
86 | .overview-search-result-btn:hover,
87 | .overview-search-result-btn:focus {
88 | background-color: $layer2;
89 | }
90 |
91 | .overview-search-result-btn:active {
92 | background-color: $layer2Hover;
93 | }
94 |
95 | .overview-tasks {
96 | @include large-rounding;
97 | @include elevation-border;
98 | @include elevation2;
99 | padding: 0.341rem;
100 | background-color: $background;
101 | color: $onBackground;
102 | }
103 |
104 | .overview-tasks-workspace {
105 | @include normal-rounding;
106 | // @include elevation-border;
107 | margin: 0.341rem;
108 | background-color: $layer1;
109 | }
110 |
111 | .overview-tasks-workspace-number {
112 | @include mainfont;
113 | color: $onSurfaceVariant;
114 | }
115 |
116 | .overview-tasks-window {
117 | @include normal-rounding;
118 | @include menu_decel;
119 | background-color: transparentize($layer3, 0.2);
120 | color: $onSurface;
121 | border: 0.068rem solid $surfaceContainerHighest;
122 | }
123 |
124 | .overview-tasks-window:hover,
125 | .overview-tasks-window:focus {
126 | background-color: transparentize($secondaryContainer, 0.3);
127 | }
128 |
129 | .overview-tasks-window:active {
130 | background-color: transparentize($secondaryContainer, 0);
131 | }
132 |
133 | .overview-tasks-window-selected {
134 | background-color: transparentize($secondaryContainer, 0.3);
135 | }
136 |
137 | .overview-tasks-window-dragging {
138 | opacity: 0.2;
139 | }
140 |
--------------------------------------------------------------------------------
/src/share/sleex/scss/_session.scss:
--------------------------------------------------------------------------------
1 | .session-bg {
2 | background-color: transparentize($color: $layer0, $amount: 0.4);
3 | }
4 |
5 | .session-button {
6 | @include large-rounding;
7 | min-width: 8.182rem;
8 | min-height: 8.182rem;
9 | background-color: $layer1;
10 | color: $onLayer1;
11 | font-size: 3rem;
12 | }
13 |
14 | .session-button-focused {
15 | background-color: $layer1Hover;
16 | }
17 |
18 | .session-button-desc {
19 | background-color: $layer2;
20 | color: $onLayer2;
21 | border-bottom-left-radius: $rounding_large;
22 | border-bottom-right-radius: $rounding_large;
23 | padding: 0.205rem 0.341rem;
24 | font-weight: 700;
25 | }
26 |
27 | .session-button-cancel {
28 | @include large-rounding;
29 | min-width: 8.182rem;
30 | min-height: 5.455rem;
31 | background-color: $layer1;
32 | color: $onLayer1;
33 | font-size: 3rem;
34 | }
35 |
36 | @for $i from 1 through 7 {
37 | .session-color-#{$i} {
38 | color: nth($sessionColors, $i);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/share/sleex/scss/_wal.scss:
--------------------------------------------------------------------------------
1 | // To prevent errors
2 | $color1: $surfaceVariant;
3 | $color2: $surfaceVariant;
4 | $color3: $surfaceVariant;
5 | $color4: $secondaryContainer;
6 | $color5: $secondaryContainer;
7 | $color6: $secondaryContainer;
8 | $color7: $onBackground;
--------------------------------------------------------------------------------
/src/share/sleex/scss/_wallpaper.scss:
--------------------------------------------------------------------------------
1 | .wallpaper-list {
2 | .preview-box {
3 | min-width: 150px;
4 | min-height: 90px;
5 | background-size: cover;
6 | background-position: center;
7 | border-radius: 8px;
8 | }
9 |
10 | button {
11 | padding: 4px;
12 | margin: 4px;
13 | }
14 |
15 | .scroll-box {
16 | min-height: 110px;
17 | }
18 | }
19 |
20 | .wallpaper-window {
21 | @extend .dashboard;
22 | }
23 |
24 | .wallpaper-placeholder {
25 | padding: 2rem;
26 |
27 | .txt-large {
28 | font-size: 1.2em;
29 | }
30 |
31 | .txt-subtext {
32 | opacity: 0.8;
33 | }
34 | }
35 |
36 | .generate-thumbnails {
37 | padding: 0.5rem 1rem;
38 | border-radius: 9999px;
39 | background-color: $secondaryContainer;
40 | color: $onSecondaryContainer;
41 | margin-right: 0.5rem;
42 |
43 | &:hover {
44 | background-color: mix($secondaryContainer, $onSecondaryContainer, 90%);
45 | }
46 |
47 | &:active {
48 | background-color: mix($secondaryContainer, $onSecondaryContainer, 80%);
49 | }
50 | }
51 |
52 | .wallselect {
53 | background-color: $layer0;
54 | @include normal-rounding;
55 | padding: 1rem;
56 | }
--------------------------------------------------------------------------------
/src/share/sleex/scss/fallback/_material.scss:
--------------------------------------------------------------------------------
1 | $darkmode: True;
2 | $transparent: False;
3 | $glass: #ffffff1a;
4 | $primary_paletteKeyColor: #192830;
5 | $secondary_paletteKeyColor: #4D616C;
6 | $tertiary_paletteKeyColor: #5F5A7D;
7 | $neutral_paletteKeyColor: #6D797D;
8 | $neutral_variant_paletteKeyColor: #6A7A7F;
9 | $background: #0F1417;
10 | $onBackground: #DFE3E7;
11 | $surface: #0F1417;
12 | $surfaceDim: #0F1417;
13 | $surfaceBright: #353A3D;
14 | $surfaceContainerLowest: #0A0F12;
15 | $surfaceContainerLow: #171C1F;
16 | $surfaceContainer: #1B2023;
17 | $surfaceContainerHigh: #262B2E;
18 | $surfaceContainerHighest: #313539;
19 | $onSurface: #DFE3E7;
20 | $surfaceVariant: #40484C;
21 | $onSurfaceVariant: #C0C7CD;
22 | $inverseSurface: #DFE3E7;
23 | $inverseOnSurface: #2C3134;
24 | $outline: #8A9297;
25 | $outlineVariant: #40484C;
26 | $shadow: #000000;
27 | $scrim: #000000;
28 | $surfaceTint: #8DCFF2;
29 | $primary: #8DCFF2;
30 | $onPrimary: #003548;
31 | $primaryContainer: #004D67;
32 | $onPrimaryContainer: #C1E8FF;
33 | $inversePrimary: #186584;
34 | $secondary: #B5CAD7;
35 | $onSecondary: #1F333D;
36 | $secondaryContainer: #364954;
37 | $onSecondaryContainer: #D1E6F3;
38 | $tertiary: #C8C2EA;
39 | $onTertiary: #302C4C;
40 | $tertiaryContainer: #474364;
41 | $onTertiaryContainer: #E5DEFF;
42 | $error: #FFB4AB;
43 | $onError: #690005;
44 | $errorContainer: #93000A;
45 | $onErrorContainer: #FFDAD6;
46 | $primaryFixed: #C1E8FF;
47 | $primaryFixedDim: #8DCFF2;
48 | $onPrimaryFixed: #001E2B;
49 | $onPrimaryFixedVariant: #004D67;
50 | $secondaryFixed: #D1E6F3;
51 | $secondaryFixedDim: #B5CAD7;
52 | $onSecondaryFixed: #091E27;
53 | $onSecondaryFixedVariant: #364954;
54 | $tertiaryFixed: #E5DEFF;
55 | $tertiaryFixedDim: #C8C2EA;
56 | $onTertiaryFixed: #1B1736;
57 | $onTertiaryFixedVariant: #474364;
58 |
--------------------------------------------------------------------------------
/src/share/sleex/scss/main.scss:
--------------------------------------------------------------------------------
1 | // Reset
2 | // * {
3 | // all: unset;
4 | // }
5 | *:not(popover) { all: unset; }
6 |
7 | // Colors
8 | @import 'material'; // Material colors
9 | @import './colors'; // Global color definitions. Uses material colors as base.
10 | @import './lib_mixins';
11 | @import 'lib_mixins_overrides';
12 | @import './lib_classes';
13 | @import './common'; // Context menu n stuff
14 |
15 | // Components
16 | @import './bar';
17 | @import './cheatsheet';
18 | @import './desktopbackground';
19 | @import './osd';
20 | @import './overview';
21 | @import './sidebars';
22 | @import './session';
23 | @import './notifications';
24 | @import './wallpaper';
25 |
26 | // Music is put last as it might mess stuff up with pywal
27 | @import './music'; // Everything related to music is here
28 |
29 | // Classes for interaction
30 | .growingRadial {
31 | transition: 300ms cubic-bezier(0.2, 0.0, 0, 1.0);
32 | }
33 | .fadingRadial {
34 | transition: 50ms cubic-bezier(0.2, 0.0, 0, 1.0);
35 | }
36 | .sidebar-pinned {
37 | margin: 0rem;
38 | border-radius: 0rem;
39 | border-bottom-right-radius: $rounding_large;
40 | border: 0rem solid;
41 | }
42 |
--------------------------------------------------------------------------------
/src/share/sleex/services/darkmode.js:
--------------------------------------------------------------------------------
1 | const { Gio, GLib } = imports.gi;
2 | import Service from 'resource:///com/github/Aylur/ags/service.js';
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
4 | import { darkMode } from '../modules/.miscutils/system.js';
5 | const { exec, execAsync } = Utils;
6 |
7 | const timeBefore = (time1, time2) => { // Arrays of [hour, minute]
8 | if (time1[0] == time2[0]) return time1[1] < time2[1];
9 | return time1[0] < time2[0];
10 | }
11 |
12 | const timeSame = (time1, time2) => // Arrays of [hour, minute]
13 | (time1[0] == time2[0] && time1[1] == time2[1]);
14 |
15 | const timeBeforeOrSame = (time1, time2) => // Arrays of [hour, minute]
16 | (timeBefore(time1, time2) || timeSame(time1, time2));
17 |
18 | const timeInRange = (time, rangeStart, rangeEnd) => { // Arrays of [hour, minute]
19 | if (timeBefore(rangeStart, rangeEnd))
20 | return (timeBeforeOrSame(rangeStart, time) && timeBeforeOrSame(time, rangeEnd))
21 | else { // rangeEnd < rangeStart, meaning it ends the following day
22 | rangeEnd[0] += 24;
23 | if (timeBefore(time, rangeStart)) time[0] += 24;
24 | return (timeBeforeOrSame(rangeStart, time) && timeBeforeOrSame(time, rangeEnd))
25 | }
26 |
27 | }
28 |
29 | export async function startAutoDarkModeService() {
30 | Utils.interval(userOptions.time.interval, () => {
31 | if ((!userOptions.appearance.autoDarkMode.enabled)) return;
32 | const fromTime = (userOptions.appearance.autoDarkMode.from).split(':').map(Number);
33 | const toTime = (userOptions.appearance.autoDarkMode.to).split(':').map(Number);
34 | if (fromTime == toTime) return;
35 | const currentDateTime = GLib.DateTime.new_now_local();
36 | const currentTime = [currentDateTime.get_hour(), currentDateTime.get_minute()];
37 | darkMode.value = timeInRange(currentTime, fromTime, toTime);
38 | })
39 | }
40 |
--------------------------------------------------------------------------------
/src/share/sleex/services/globalinfo.js:
--------------------------------------------------------------------------------
1 | const { Gio, GLib } = imports.gi;
2 | import Service from 'resource:///com/github/Aylur/ags/service.js';
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
4 | const { exec, execAsync } = Utils;
5 |
6 | class CheatService extends Service {
7 | static {
8 | Service.register(
9 | this,
10 | { 'updated': [], },
11 | );
12 | }
13 | _cheatPath = '';
14 | _cheatJson = [];
15 |
16 | refresh(value) {
17 | this.emit('updated', value);
18 | }
19 |
20 | connectWidget(widget, callback) {
21 | this.connect(widget, callback, 'updated');
22 | }
23 |
24 | get todo_json() {
25 | return this._todoJson;
26 | }
27 |
28 | _save() {
29 | Utils.writeFile(JSON.stringify(this._cheatJson), this._cheatPath)
30 | .catch(print);
31 | }
32 |
33 | add(content) {
34 | this._cheatJson.push({ content, done: false });
35 | this._save();
36 | this.emit('updated');
37 | }
38 |
39 | check(index) {
40 | this._cheatJson[index].done = true;
41 | this._save();
42 | this.emit('updated');
43 | }
44 |
45 | uncheck(index) {
46 | this._cheatJson[index].done = false;
47 | this._save();
48 | this.emit('updated');
49 | }
50 |
51 | remove(index) {
52 | this._cheatJson.splice(index, 1);
53 | Utils.writeFile(JSON.stringify(this._cheatJson), this._cheatPath)
54 | .catch(print);
55 | this.emit('updated');
56 | }
57 |
58 | constructor() {
59 | super();
60 | this._cheatPath = `${GLib.get_user_state_dir()}/ags/user/cheat.json`;
61 | try {
62 | const fileContents = Utils.readFile(this._cheatPath);
63 | this._cheatJson = JSON.parse(fileContents);
64 | }
65 | catch {
66 | Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`);
67 | Utils.exec(`touch ${this._cheatPath}`);
68 | Utils.writeFile("[]", this._cheatPath).then(() => {
69 | this._cheatJson = JSON.parse(Utils.readFile(this._cheatPath))
70 | }).catch(print);
71 | }
72 | }
73 | }
74 |
75 | // Singleton instance
76 | const service = new CheatService();
77 |
78 | // make it global for easy use with cli
79 | globalThis.cheatService = service;
80 |
81 | export default service;
--------------------------------------------------------------------------------
/src/share/sleex/services/indicator.js:
--------------------------------------------------------------------------------
1 | import Service from 'resource:///com/github/Aylur/ags/service.js';
2 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
3 |
4 | class IndicatorService extends Service {
5 | static {
6 | Service.register(
7 | this,
8 | { 'popup': ['double'], },
9 | );
10 | }
11 |
12 | _delay = 1500;
13 | _count = 0;
14 |
15 | popup(value) {
16 | this.emit('popup', value);
17 | this._count++;
18 | Utils.timeout(this._delay, () => {
19 | this._count--;
20 |
21 | if (this._count === 0)
22 | this.emit('popup', -1);
23 | });
24 | }
25 |
26 | connectWidget(widget, callback) {
27 | connect(this, widget, callback, 'popup');
28 | }
29 | }
30 |
31 | // the singleton instance
32 | const service = new IndicatorService();
33 |
34 | // make it global for easy use with cli
35 | globalThis['indicator'] = service;
36 |
37 | // export to use in other modules
38 | export default service;
--------------------------------------------------------------------------------
/src/share/sleex/services/lockscreen.js:
--------------------------------------------------------------------------------
1 | import Service from 'resource:///com/github/Aylur/ags/service.js';
2 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
3 | import App from 'resource:///com/github/Aylur/ags/app.js';
4 | const authpy = App.configDir + '/modules/lockscreen/auth.py';
5 |
6 | class Lockscreen extends Service {
7 | static {
8 | Service.register(this, {
9 | 'lock': ['boolean'],
10 | 'authenticating': ['boolean'],
11 | });
12 | }
13 |
14 | lockscreen() { this.emit('lock', true); }
15 |
16 | /** @param {string} password */
17 | auth(password) {
18 | this.emit('authenticating', true);
19 | Utils.execAsync([authpy, password])
20 | .then(out => {
21 | this.emit('lock', out !== 'True');
22 | this.emit('authenticating', false);
23 | })
24 | .catch(err => console.error(err));
25 | }
26 | }
27 |
28 | export default new Lockscreen();
--------------------------------------------------------------------------------
/src/share/sleex/services/messages.js:
--------------------------------------------------------------------------------
1 | const { Notify, GLib, Gio } = imports.gi;
2 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
3 | import Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
4 |
5 | export function fileExists(filePath) {
6 | let file = Gio.File.new_for_path(filePath);
7 | return file.query_exists(null);
8 | }
9 |
10 | const FIRST_RUN_FILE = "firstrun.txt";
11 | const FIRST_RUN_PATH = `${GLib.get_user_state_dir()}/ags/user/${FIRST_RUN_FILE}`;
12 | const FIRST_RUN_FILE_CONTENT = "Just a file to confirm that you have been greeted ;)";
13 | const APP_NAME = "AxOS";
14 | const FIRST_RUN_NOTIF_TITLE = "Welcome!";
15 | const FIRST_RUN_NOTIF_BODY = `First run? 👀 CTRL+SUPER+T to pick a wallpaper (or styles will break!)\nFor a list of keybinds, hit Super + F1.`;
16 |
17 | var batteryWarned = false;
18 | async function batteryMessage() {
19 | const perc = Battery.percent;
20 | const charging = Battery.charging;
21 | if (charging) {
22 | batteryWarned = false;
23 | return;
24 | }
25 | for (let i = userOptions.battery.warnLevels.length - 1; i >= 0; i--) {
26 | if (perc <= userOptions.battery.warnLevels[i] && !charging && !batteryWarned) {
27 | batteryWarned = true;
28 | Utils.execAsync(['bash', '-c',
29 | `notify-send "${userOptions.battery.warnTitles[i]}" "${userOptions.battery.warnMessages[i]}" -u critical -a '${APP_NAME}' -t 69420 &`
30 | ]).catch(print);
31 | break;
32 | }
33 | }
34 | if (perc <= userOptions.battery.suspendThreshold) {
35 | Utils.execAsync(['bash', '-c',
36 | `notify-send "Suspending system" "Critical battery level (${perc}% remaining)" -u critical -a '${APP_NAME}' -t 69420 &`
37 | ]).catch(print);
38 | Utils.execAsync('systemctl suspend').catch(print);
39 | }
40 | }
41 |
42 | export async function startBatteryWarningService() {
43 | Utils.timeout(1, () => {
44 | Battery.connect('changed', () => batteryMessage().catch(print));
45 | })
46 | }
47 |
48 | export async function firstRunWelcome() {
49 | GLib.mkdir_with_parents(`${GLib.get_user_state_dir()}/ags/user`, 755);
50 | if (!fileExists(FIRST_RUN_PATH)) {
51 | Utils.writeFile(FIRST_RUN_FILE_CONTENT, FIRST_RUN_PATH)
52 | .then(() => {
53 | // Note that we add a little delay to make sure the cool circular progress works
54 | Utils.execAsync(['hyprctl', 'keyword', 'bind', "Super,Slash,exec,ags -t cheatsheet"]).catch(print);
55 | Utils.execAsync(['bash', '-c', `sleep 0.5; notify-send "Millis since epoch" "$(date +%s%N | cut -b1-13)"; sleep 0.5; notify-send '${FIRST_RUN_NOTIF_TITLE}' '${FIRST_RUN_NOTIF_BODY}' -a '${APP_NAME}' &` ]).catch(print)
56 | Utils.execAsync(['bash', '-c', `sh /usr/share/sleex/scripts/color_generation/switchwall.sh --path /usr/share/sleex/wallpapers/sakura.png`, '&']).catch(print);
57 | })
58 | .catch(print);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/share/sleex/services/todo.js:
--------------------------------------------------------------------------------
1 | const { Gio, GLib } = imports.gi;
2 | import Service from 'resource:///com/github/Aylur/ags/service.js';
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
4 | const { exec, execAsync } = Utils;
5 |
6 | class TodoService extends Service {
7 | static {
8 | Service.register(
9 | this,
10 | { 'updated': [], },
11 | );
12 | }
13 |
14 | _todoPath = '';
15 | _todoJson = [];
16 |
17 | refresh(value) {
18 | this.emit('updated', value);
19 | }
20 |
21 | connectWidget(widget, callback) {
22 | this.connect(widget, callback, 'updated');
23 | }
24 |
25 | get todo_json() {
26 | return this._todoJson;
27 | }
28 |
29 | _save() {
30 | Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
31 | .catch(print);
32 | }
33 |
34 | add(content) {
35 | this._todoJson.push({ content, done: false });
36 | this._save();
37 | this.emit('updated');
38 | }
39 |
40 | check(index) {
41 | this._todoJson[index].done = true;
42 | this._save();
43 | this.emit('updated');
44 | }
45 |
46 | uncheck(index) {
47 | this._todoJson[index].done = false;
48 | this._save();
49 | this.emit('updated');
50 | }
51 |
52 | remove(index) {
53 | this._todoJson.splice(index, 1);
54 | Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
55 | .catch(print);
56 | this.emit('updated');
57 | }
58 |
59 | constructor() {
60 | super();
61 | this._todoPath = `${GLib.get_user_state_dir()}/ags/user/todo.json`;
62 | try {
63 | const fileContents = Utils.readFile(this._todoPath);
64 | this._todoJson = JSON.parse(fileContents);
65 | }
66 | catch {
67 | Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`);
68 | Utils.exec(`touch ${this._todoPath}`);
69 | Utils.writeFile("[]", this._todoPath).then(() => {
70 | this._todoJson = JSON.parse(Utils.readFile(this._todoPath))
71 | }).catch(print);
72 | }
73 | }
74 | }
75 |
76 | // the singleton instance
77 | const service = new TodoService();
78 |
79 | // make it global for easy use with cli
80 | globalThis.todo = service;
81 |
82 | // export to use in other modules
83 | export default service;
--------------------------------------------------------------------------------
/src/share/sleex/services/wallpaper.js:
--------------------------------------------------------------------------------
1 | const { Gdk, GLib } = imports.gi;
2 | import Service from 'resource:///com/github/Aylur/ags/service.js';
3 | import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
4 |
5 | const WALLPAPER_CONFIG_PATH = `${GLib.get_user_state_dir()}/ags/user/wallpaper.json`;
6 |
7 | class WallpaperService extends Service {
8 | static {
9 | Service.register(
10 | this,
11 | { 'updated': [], },
12 | );
13 | }
14 |
15 | _wallPath = '';
16 | _wallJson = [];
17 | _monitorCount = 1;
18 |
19 | _save() {
20 | Utils.writeFile(JSON.stringify(this._wallJson), this._wallPath)
21 | .catch(print);
22 | }
23 |
24 | add(path) {
25 | this._wallJson.push(path);
26 | this._save();
27 | this.emit('updated');
28 | }
29 |
30 | set(path, monitor = -1) {
31 | this._monitorCount = Gdk.Display.get_default()?.get_n_monitors() || 1;
32 | if (this._wallJson.length < this._monitorCount) this._wallJson[this._monitorCount - 1] = "";
33 | if (monitor == -1)
34 | this._wallJson.fill(path);
35 | else
36 | this._wallJson[monitor] = path;
37 |
38 | this._save();
39 | this.emit('updated');
40 | }
41 |
42 | get(monitor = 0) {
43 | return this._wallJson[monitor];
44 | }
45 |
46 | constructor() {
47 | super();
48 | // How many screens?
49 | this._monitorCount = Gdk.Display.get_default()?.get_n_monitors() || 1;
50 | // Read config
51 | this._wallPath = WALLPAPER_CONFIG_PATH;
52 | try {
53 | const fileContents = Utils.readFile(this._wallPath);
54 | this._wallJson = JSON.parse(fileContents);
55 | }
56 | catch {
57 | Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`);
58 | Utils.exec(`touch ${this._wallPath}`);
59 | Utils.writeFile('[]', this._wallPath).then(() => {
60 | this._wallJson = JSON.parse(Utils.readFile(this._wallPath))
61 | }).catch(print);
62 | }
63 | }
64 | }
65 |
66 | // instance
67 | const service = new WallpaperService();
68 | // make it global for easy use with cli
69 | globalThis['wallpaper'] = service;
70 | export default service;
--------------------------------------------------------------------------------
/src/share/sleex/services/weather.js:
--------------------------------------------------------------------------------
1 | // This is for the right pills of the bar.
2 | import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
3 | const { execAsync } = Utils;
4 | const { GLib } = imports.gi;
5 |
6 | const WEATHER_CACHE_FOLDER = `${GLib.get_user_cache_dir()}/ags/weather`;
7 | Utils.exec(`mkdir -p ${WEATHER_CACHE_FOLDER}`);
8 |
9 | Utils.interval(900000, async () => {
10 | const WEATHER_CACHE_PATH = WEATHER_CACHE_FOLDER + "/wttr.in.txt";
11 | const updateWeatherForCity = (city) =>
12 | execAsync(`curl https://wttr.in/${city.replace(/ /g, "%20")}?format=j1`)
13 | .then((output) => {
14 | const weather = JSON.parse(output);
15 | Utils.writeFile(JSON.stringify(weather),WEATHER_CACHE_PATH).catch(print);
16 | }).catch(print);
17 |
18 | if (userOptions.weather.city != "" && userOptions.weather.city != null) {
19 | updateWeatherForCity(userOptions.weather.city.replace(/ /g, "%20"));
20 | } else {
21 | Utils.execAsync("curl ipinfo.io")
22 | .then((output) => {
23 | return JSON.parse(output)["city"].toLowerCase();
24 | })
25 | .then(updateWeatherForCity)
26 | .catch(print);
27 | }
28 | });
--------------------------------------------------------------------------------
/src/share/sleex/user_options.js:
--------------------------------------------------------------------------------
1 | const userConfigOptions = {
2 | }
3 |
4 | export default userConfigOptions;
5 |
--------------------------------------------------------------------------------
/src/share/sleex/variables.js:
--------------------------------------------------------------------------------
1 | const { Gdk, Gtk } = imports.gi;
2 | import App from 'resource:///com/github/Aylur/ags/app.js'
3 | import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
4 | import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
5 | import Variable from 'resource:///com/github/Aylur/ags/variable.js';
6 |
7 | Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets/icons`);
8 |
9 | // Global vars for external control (through keybinds)
10 | export const showMusicControls = Variable(false, {})
11 | export const showColorScheme = Variable(false, {})
12 | globalThis['openMusicControls'] = showMusicControls;
13 | globalThis['openColorScheme'] = showColorScheme;
14 | globalThis['mpris'] = Mpris;
15 |
16 | // load monitor shell modes from userOptions
17 | const initialMonitorShellModes = () => {
18 | const numberOfMonitors = Gdk.Display.get_default()?.get_n_monitors() || 1;
19 | const monitorBarConfigs = [];
20 | for (let i = 0; i < numberOfMonitors; i++) {
21 | if (userOptions.bar.modes[i]) {
22 | monitorBarConfigs.push(userOptions.bar.modes[i])
23 | } else {
24 | monitorBarConfigs.push('normal')
25 | }
26 | }
27 | return monitorBarConfigs;
28 |
29 | }
30 | export const currentShellMode = Variable(initialMonitorShellModes(), {}) // normal, focus
31 |
32 | // Mode switching
33 | const updateMonitorShellMode = (monitorShellModes, monitor, mode) => {
34 | const newValue = [...monitorShellModes.value];
35 | newValue[monitor] = mode;
36 | monitorShellModes.value = newValue;
37 | }
38 | globalThis['currentMode'] = currentShellMode;
39 | globalThis['cycleMode'] = () => {
40 | const monitor = Hyprland.active.monitor.id || 0;
41 |
42 | if (currentShellMode.value[monitor] === 'normal') {
43 | updateMonitorShellMode(currentShellMode, monitor, 'focus')
44 | }
45 | else if (currentShellMode.value[monitor] === 'focus') {
46 | updateMonitorShellMode(currentShellMode, monitor, 'nothing')
47 | }
48 | else {
49 | updateMonitorShellMode(currentShellMode, monitor, 'normal')
50 | }
51 | }
52 |
53 | // Window controls
54 | const range = (length, start = 1) => Array.from({ length }, (_, i) => i + start);
55 | globalThis['toggleWindowOnAllMonitors'] = (name) => {
56 | range(Gdk.Display.get_default()?.get_n_monitors() || 1, 0).forEach(id => {
57 | App.toggleWindow(`${name}${id}`);
58 | });
59 | }
60 | globalThis['closeWindowOnAllMonitors'] = (name) => {
61 | range(Gdk.Display.get_default()?.get_n_monitors() || 1, 0).forEach(id => {
62 | App.closeWindow(`${name}${id}`);
63 | });
64 | }
65 | globalThis['openWindowOnAllMonitors'] = (name) => {
66 | range(Gdk.Display.get_default()?.get_n_monitors() || 1, 0).forEach(id => {
67 | App.openWindow(`${name}${id}`);
68 | });
69 | }
70 |
71 | globalThis['closeEverything'] = () => {
72 | const numMonitors = Gdk.Display.get_default()?.get_n_monitors() || 1;
73 | for (let i = 0; i < numMonitors; i++) {
74 | App.closeWindow(`cheatsheet${i}`);
75 | App.closeWindow(`session${i}`);
76 | }
77 | App.closeWindow('sideleft');
78 | App.closeWindow('dashboard');
79 | App.closeWindow('overview');
80 | };
81 |
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/aesthetic_deer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/aesthetic_deer.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/apple-light.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/apple-light.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/building_cyber.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/building_cyber.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/city-rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/city-rain.png
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/city-skyline.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/city-skyline.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/city.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/city.gif
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/deer_and_sunset.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/deer_and_sunset.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/earth-from-moon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/earth-from-moon.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/escape_velocity.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/escape_velocity.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/evening-landscape.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/evening-landscape.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/explorer_green_day.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/explorer_green_day.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/explorer_orange_sunset.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/explorer_orange_sunset.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/highlands-grid.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/highlands-grid.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/landscape-abstract-neon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/landscape-abstract-neon.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/lofoten-sundown.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/lofoten-sundown.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/lofoten2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/lofoten2.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/loupe-mono-dark-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/loupe-mono-dark-preview.png
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/midnight-reflections-moonlit-sea.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/midnight-reflections-moonlit-sea.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/moonlight.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/moonlight.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/mountain-lake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/mountain-lake.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/mountain-sunrise.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/mountain-sunrise.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/mountain-winter1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/mountain-winter1.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/mountain-winter3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/mountain-winter3.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/mountain_view.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/mountain_view.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/nordwall3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/nordwall3.jpg
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/oldhouse.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/oldhouse.gif
--------------------------------------------------------------------------------
/src/share/sleex/wallpapers/sakura.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AxOS-project/Sleex/79c902686994cd0359702d41de09ed622c101b00/src/share/sleex/wallpapers/sakura.png
--------------------------------------------------------------------------------
/src/share/wayland-sessions/sleex.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Sleex
3 | Comment=AxOS's third desktop environment
4 | Exec=sleex
5 | Icon=
6 | Type=Application
--------------------------------------------------------------------------------